From 3da9626e68368818bb94a0115cc5de0c543b9ec9 Mon Sep 17 00:00:00 2001 From: Aly Raffauf Date: Wed, 11 Feb 2026 06:55:28 -0500 Subject: [PATCH] add config file at ~/.config/dewy/config.json --- package-lock.json | 80 +++++++++++++++++++++++++++++++++++++++++++++-- package.json | 1 + source/app.tsx | 13 +++++--- source/config.ts | 25 +++++++++++++++ 4 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 source/config.ts diff --git a/package-lock.json b/package-lock.json index d319637..385ae51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,9 +7,10 @@ "": { "name": "dewy", "version": "0.0.0", - "license": "MIT", + "license": "GPL-3.0-or-later", "dependencies": { "@doist/todoist-api-typescript": "^6.4.0", + "@folder/xdg": "^4.0.1", "ink": "^4.1.0", "ink-text-input": "^5.0.1", "meow": "^11.0.0", @@ -170,6 +171,34 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@folder/xdg": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@folder/xdg/-/xdg-4.0.1.tgz", + "integrity": "sha512-mIBEyXvNK6sX341FOR6ZyGBmq84sSSkG2ocwTDmi4kJfAF6z8W//6aeMAi6vD/FuTcd4q0Px0HMVyP3ggOT/bg==", + "license": "MIT", + "dependencies": { + "ini": "^4.1.1", + "picomatch": "^2.3.1", + "toml": "^3.0.0", + "write": "^2.0.0", + "yaml": "^2.3.1" + } + }, + "node_modules/@folder/xdg/node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -715,6 +744,18 @@ "node": ">=0.4.0" } }, + "node_modules/add-filename-increment": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-filename-increment/-/add-filename-increment-1.0.0.tgz", + "integrity": "sha512-pFV8VZX8jxuVMIycKvGZkWF/ihnUubu9lbQVnOnZWp7noVxbKQTNj7zG2y9fXdPcuZ6lAN3Drr517HaivGCjdQ==", + "license": "MIT", + "dependencies": { + "strip-filename-increment": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/aggregate-error": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", @@ -4474,6 +4515,15 @@ "dev": true, "license": "ISC" }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/ink": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/ink/-/ink-4.4.1.tgz", @@ -6506,7 +6556,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -7886,6 +7935,15 @@ "node": ">=4" } }, + "node_modules/strip-filename-increment": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-filename-increment/-/strip-filename-increment-2.0.1.tgz", + "integrity": "sha512-+v5xsiTTsdYqkPj7qz1zlngIsjZedhHDi3xp/9bMurV8kXe9DAr732gNVqtt4X8sI3hOqS3nlFfps5gyVcux6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -8133,6 +8191,12 @@ "node": ">=8.0" } }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "license": "MIT" + }, "node_modules/trim-newlines": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", @@ -8772,6 +8836,18 @@ "dev": true, "license": "ISC" }, + "node_modules/write": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/write/-/write-2.0.0.tgz", + "integrity": "sha512-yam9TAqN8sAZokECAejo9HpT2j2s39OgK8i8yxadrFBVo+iSWLfnipRVFulfAw1d2dz5vSuGmlMHYRKG4fysOA==", + "license": "MIT", + "dependencies": { + "add-filename-increment": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/write-file-atomic": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", diff --git a/package.json b/package.json index bb1128f..76dfddd 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ ], "dependencies": { "@doist/todoist-api-typescript": "^6.4.0", + "@folder/xdg": "^4.0.1", "ink": "^4.1.0", "ink-text-input": "^5.0.1", "meow": "^11.0.0", diff --git a/source/app.tsx b/source/app.tsx index 441fa67..497fabf 100644 --- a/source/app.tsx +++ b/source/app.tsx @@ -3,14 +3,19 @@ import {Box, Text, useApp} from 'ink'; import TextInput from 'ink-text-input'; import {TodoistApi, type Task} from '@doist/todoist-api-typescript'; import {commands} from './commands.js'; +import {loadConfig, configPath} from './config.js'; -const token = process.env['TODOIST_API_TOKEN']; -if (!token) { - console.error('Set TODOIST_API_TOKEN environment variable'); +const config = loadConfig(); +const apiToken = config?.apiToken ?? process.env['TODOIST_API_TOKEN']; + +if (!apiToken) { + console.error( + `No API token found. Either:\n 1. Create ${configPath} with contents:\n { "apiToken": "your-todoist-api-token" }\n 2. Set the TODOIST_API_TOKEN environment variable`, + ); process.exit(1); } -const api = new TodoistApi(token); +const api = new TodoistApi(apiToken); export default function App() { const {exit} = useApp(); diff --git a/source/config.ts b/source/config.ts new file mode 100644 index 0000000..b6e5b43 --- /dev/null +++ b/source/config.ts @@ -0,0 +1,25 @@ +import {mkdirSync, readFileSync, writeFileSync, existsSync} from 'node:fs'; +import {join} from 'node:path'; +// @ts-expect-error no type declarations +import xdg from '@folder/xdg'; + +export type Config = { + apiToken: string; +}; + +const dirs = xdg({subdir: 'dewy'}) as {config: string}; +export const configPath = join(dirs.config, 'config.json'); + +export function loadConfig(): Config | undefined { + if (!existsSync(configPath)) { + return undefined; + } + + const raw = readFileSync(configPath, 'utf-8'); + return JSON.parse(raw) as Config; +} + +export function saveConfig(config: Config): void { + mkdirSync(dirs.config, {recursive: true}); + writeFileSync(configPath, JSON.stringify(config, null, '\t') + '\n'); +} -- 2.51.2