donebear 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +10 -10
- package/LICENSE.md +21 -0
- package/README.md +20 -8
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +1302 -407
- package/dist/cli.mjs.map +1 -1
- package/dist/context-store-BuhPB0I9.mjs +2 -0
- package/dist/{context-store-BBEVWudm.mjs → context-store-ByUXl7Cn.mjs} +18 -3
- package/dist/context-store-ByUXl7Cn.mjs.map +1 -0
- package/dist/{prompt-GfcWO85b.mjs → prompt-Bv9qkjNw.mjs} +16 -4
- package/dist/prompt-Bv9qkjNw.mjs.map +1 -0
- package/dist/prompt-ujp1V6r9.mjs +2 -0
- package/package.json +20 -18
- package/dist/context-store-BBEVWudm.mjs.map +0 -1
- package/dist/context-store-ClAx1T2N.mjs +0 -3
- package/dist/prompt-B7mDo4K7.mjs +0 -3
- package/dist/prompt-GfcWO85b.mjs.map +0 -1
|
@@ -12,6 +12,7 @@ const DONEBEAR_SUPABASE_URL_ENV = "DONEBEAR_SUPABASE_URL";
|
|
|
12
12
|
const DONEBEAR_SUPABASE_KEY_ENV = "DONEBEAR_SUPABASE_PUBLISHABLE_KEY";
|
|
13
13
|
const DONEBEAR_API_URL_ENV = "DONEBEAR_API_URL";
|
|
14
14
|
const DEFAULT_API_URL = "https://api.donebear.com";
|
|
15
|
+
const DEFAULT_APP_URL = "https://donebear.com";
|
|
15
16
|
const DEFAULT_SUPABASE_URL = "https://kdarvputxmlxqemisnbh.supabase.co";
|
|
16
17
|
const DEFAULT_SUPABASE_PUBLISHABLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtkYXJ2cHV0eG1seHFlbWlzbmJoIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzIzNTcyNjcsImV4cCI6MjA4NzkzMzI2N30.A8sDPWNe_1EcCy0-GsUw4h1icxSGz_VDc9E9tPTYEU8";
|
|
17
18
|
const CLI_OAUTH_CLIENT_ID = "9cc84e5c-6a0e-4a6d-93a2-334d8d307122";
|
|
@@ -57,6 +58,19 @@ var CliError = class extends Error {
|
|
|
57
58
|
this.hint = hint ?? null;
|
|
58
59
|
}
|
|
59
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* `/sync/mutate` answered, but refused at least one transaction. Each
|
|
63
|
+
* transaction is its own server transaction, so the others in the batch may
|
|
64
|
+
* have landed; `results` says which.
|
|
65
|
+
*/
|
|
66
|
+
var SyncMutationRejectedError = class extends CliError {
|
|
67
|
+
results;
|
|
68
|
+
constructor(message, results) {
|
|
69
|
+
super(message, EXIT_CODES.ERROR);
|
|
70
|
+
this.name = "SyncMutationRejectedError";
|
|
71
|
+
this.results = results;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
60
74
|
const toCliError = (error) => {
|
|
61
75
|
if (error instanceof CliError) return error;
|
|
62
76
|
if (error instanceof Error) {
|
|
@@ -104,7 +118,8 @@ const writeContext = async (context) => {
|
|
|
104
118
|
};
|
|
105
119
|
const readLocalContext = async () => {
|
|
106
120
|
try {
|
|
107
|
-
|
|
121
|
+
const raw = await readFile(CONTEXT_FILE_PATH, "utf-8");
|
|
122
|
+
return parseContextFile(raw).context;
|
|
108
123
|
} catch (error) {
|
|
109
124
|
if (isRecord(error) && error.code === "ENOENT") return null;
|
|
110
125
|
if (error instanceof CliError) throw error;
|
|
@@ -130,6 +145,6 @@ const setCurrentWorkspace = async (workspaceId) => {
|
|
|
130
145
|
return nextContext;
|
|
131
146
|
};
|
|
132
147
|
//#endregion
|
|
133
|
-
export {
|
|
148
|
+
export { DONEBEAR_TOKEN_ENV as S, DONEBEAR_API_URL_ENV as _, SyncMutationRejectedError as a, DONEBEAR_SUPABASE_KEY_ENV as b, CLI_NAME as c, DEFAULT_API_URL as d, DEFAULT_APP_URL as f, DEFAULT_SUPABASE_URL as g, DEFAULT_SUPABASE_PUBLISHABLE_KEY as h, EXIT_CODES as i, CLI_OAUTH_CLIENT_ID as l, DEFAULT_OAUTH_CALLBACK_PORT as m, setCurrentWorkspace as n, toCliError as o, DEFAULT_OAUTH_CALLBACK_PATH as p, CliError as r, AUTH_FILE_PATH as s, ensureLocalContext as t, CONFIG_DIR as u, DONEBEAR_DEBUG_ENV as v, DONEBEAR_SUPABASE_URL_ENV as x, DONEBEAR_OAUTH_CLIENT_ID_ENV as y };
|
|
134
149
|
|
|
135
|
-
//# sourceMappingURL=context-store-
|
|
150
|
+
//# sourceMappingURL=context-store-ByUXl7Cn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-store-ByUXl7Cn.mjs","names":[],"sources":["../src/constants.ts","../src/errors.ts","../src/context-store.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const CLI_NAME = \"donebear\";\n\nexport const DONEBEAR_TOKEN_ENV = \"DONEBEAR_TOKEN\";\nexport const DONEBEAR_DEBUG_ENV = \"DONEBEAR_DEBUG\";\nconst DONEBEAR_CONFIG_DIR_ENV = \"DONEBEAR_CONFIG_DIR\";\nexport const DONEBEAR_SUPABASE_URL_ENV = \"DONEBEAR_SUPABASE_URL\";\nexport const DONEBEAR_SUPABASE_KEY_ENV = \"DONEBEAR_SUPABASE_PUBLISHABLE_KEY\";\nexport const DONEBEAR_API_URL_ENV = \"DONEBEAR_API_URL\";\n\nexport const DEFAULT_API_URL = \"https://api.donebear.com\";\nexport const DEFAULT_APP_URL = \"https://donebear.com\";\n\nexport const DEFAULT_SUPABASE_URL = \"https://kdarvputxmlxqemisnbh.supabase.co\";\nexport const DEFAULT_SUPABASE_PUBLISHABLE_KEY =\n \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtkYXJ2cHV0eG1seHFlbWlzbmJoIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzIzNTcyNjcsImV4cCI6MjA4NzkzMzI2N30.A8sDPWNe_1EcCy0-GsUw4h1icxSGz_VDc9E9tPTYEU8\";\n\nexport const CLI_OAUTH_CLIENT_ID = \"9cc84e5c-6a0e-4a6d-93a2-334d8d307122\";\nexport const DONEBEAR_OAUTH_CLIENT_ID_ENV = \"DONEBEAR_OAUTH_CLIENT_ID\";\n\nexport const DEFAULT_OAUTH_CALLBACK_PORT = 8787;\nexport const DEFAULT_OAUTH_CALLBACK_PATH = \"/auth/callback\";\nexport const DEFAULT_OAUTH_TIMEOUT_SECONDS = 180;\n\nconst getDefaultConfigBaseDir = (): string => {\n if (process.platform === \"win32\") {\n return process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\n }\n\n return process.env.XDG_CONFIG_HOME ?? join(homedir(), \".config\");\n};\n\nconst configBaseDir = getDefaultConfigBaseDir();\n\nexport const CONFIG_DIR =\n process.env[DONEBEAR_CONFIG_DIR_ENV] ?? join(configBaseDir, CLI_NAME);\n\nexport const AUTH_FILE_PATH = join(CONFIG_DIR, \"auth.json\");\nexport const CONTEXT_FILE_PATH = join(CONFIG_DIR, \"context.json\");\n","export const EXIT_CODES = {\n AUTH_REQUIRED: 4,\n CANCELLED: 2,\n ERROR: 1,\n NETWORK: 5,\n SUCCESS: 0,\n VALIDATION: 3,\n} as const;\n\ntype ExitCode = (typeof EXIT_CODES)[keyof typeof EXIT_CODES];\n\ntype ErrorCode =\n | \"unknown_error\"\n | \"cancelled\"\n | \"validation_error\"\n | \"auth_error\"\n | \"network_error\";\n\nconst EXIT_CODE_TO_ERROR_CODE: Record<ExitCode, ErrorCode> = {\n [EXIT_CODES.SUCCESS]: \"unknown_error\",\n [EXIT_CODES.ERROR]: \"unknown_error\",\n [EXIT_CODES.CANCELLED]: \"cancelled\",\n [EXIT_CODES.VALIDATION]: \"validation_error\",\n [EXIT_CODES.AUTH_REQUIRED]: \"auth_error\",\n [EXIT_CODES.NETWORK]: \"network_error\",\n};\n\nconst exitCodeToErrorCode = (exitCode: ExitCode): ErrorCode =>\n EXIT_CODE_TO_ERROR_CODE[exitCode] ?? \"unknown_error\";\n\nexport class CliError extends Error {\n readonly exitCode: ExitCode;\n readonly code: ErrorCode;\n readonly hint: string | null;\n\n constructor(\n message: string,\n exitCode: ExitCode = EXIT_CODES.ERROR,\n hint?: string\n ) {\n super(message);\n this.name = \"CliError\";\n this.exitCode = exitCode;\n this.code = exitCodeToErrorCode(exitCode);\n this.hint = hint ?? null;\n }\n}\n\n/**\n * `/sync/mutate` answered, but refused at least one transaction. Each\n * transaction is its own server transaction, so the others in the batch may\n * have landed; `results` says which.\n */\nexport class SyncMutationRejectedError extends CliError {\n readonly results: readonly {\n clientTxId: string;\n success: boolean;\n error?: string;\n }[];\n\n constructor(message: string, results: SyncMutationRejectedError[\"results\"]) {\n super(message, EXIT_CODES.ERROR);\n this.name = \"SyncMutationRejectedError\";\n this.results = results;\n }\n}\n\nexport const toCliError = (error: unknown): CliError => {\n if (error instanceof CliError) {\n return error;\n }\n\n if (error instanceof Error) {\n // Network errors (fetch failures)\n if (error instanceof TypeError && error.message.includes(\"fetch\")) {\n return new CliError(\n \"Cannot connect to Done Bear API.\",\n EXIT_CODES.NETWORK,\n \"Check your internet connection and API URL configuration.\"\n );\n }\n\n // Timeout errors\n if (error.name === \"TimeoutError\" || error.name === \"AbortError\") {\n return new CliError(\n \"Request timed out.\",\n EXIT_CODES.NETWORK,\n \"The API may be unavailable. Try again later.\"\n );\n }\n\n return new CliError(error.message, EXIT_CODES.ERROR);\n }\n\n return new CliError(\"Unknown error\", EXIT_CODES.ERROR);\n};\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\n\nimport { CONFIG_DIR, CONTEXT_FILE_PATH } from \"./constants\";\nimport { CliError, EXIT_CODES } from \"./errors\";\nimport type { CliContextFileData, LocalCliContext } from \"./types\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst parseLocalContext = (value: unknown): LocalCliContext | null => {\n if (!isRecord(value)) {\n return null;\n }\n\n if (value.workspaceId !== null && typeof value.workspaceId !== \"string\") {\n return null;\n }\n\n if (typeof value.clientId !== \"string\" || value.clientId.length === 0) {\n return null;\n }\n\n return {\n clientId: value.clientId,\n workspaceId: value.workspaceId,\n };\n};\n\nconst parseContextFile = (payload: string): CliContextFileData => {\n const parsed = JSON.parse(payload) as unknown;\n\n if (!isRecord(parsed) || parsed.version !== 1) {\n throw new CliError(\n `Invalid CLI context format in ${CONTEXT_FILE_PATH}`,\n EXIT_CODES.ERROR\n );\n }\n\n const context = parseLocalContext(parsed.context);\n\n if (!context) {\n throw new CliError(\n `Invalid CLI context in ${CONTEXT_FILE_PATH}`,\n EXIT_CODES.ERROR\n );\n }\n\n return {\n context,\n version: 1,\n };\n};\n\nconst writeContext = async (context: LocalCliContext): Promise<void> => {\n const payload: CliContextFileData = {\n context,\n version: 1,\n };\n\n await mkdir(CONFIG_DIR, {\n mode: 0o700,\n recursive: true,\n });\n\n await writeFile(CONTEXT_FILE_PATH, `${JSON.stringify(payload, null, 2)}\\n`, {\n encoding: \"utf-8\",\n mode: 0o600,\n });\n};\n\nconst readLocalContext = async (): Promise<LocalCliContext | null> => {\n try {\n const raw = await readFile(CONTEXT_FILE_PATH, \"utf-8\");\n return parseContextFile(raw).context;\n } catch (error) {\n if (isRecord(error) && error.code === \"ENOENT\") {\n return null;\n }\n\n if (error instanceof CliError) {\n throw error;\n }\n\n throw new CliError(\n `Failed to read context from ${CONTEXT_FILE_PATH}`,\n EXIT_CODES.ERROR\n );\n }\n};\n\nexport const ensureLocalContext = async (): Promise<LocalCliContext> => {\n const existing = await readLocalContext();\n\n if (existing) {\n return existing;\n }\n\n const created: LocalCliContext = {\n clientId: randomUUID(),\n workspaceId: null,\n };\n\n await writeContext(created);\n return created;\n};\n\nexport const setCurrentWorkspace = async (\n workspaceId: string | null\n): Promise<LocalCliContext> => {\n const context = await ensureLocalContext();\n\n const nextContext: LocalCliContext = {\n ...context,\n workspaceId,\n };\n\n await writeContext(nextContext);\n return nextContext;\n};\n"],"mappings":"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"}
|
|
@@ -5,17 +5,29 @@ import { createInterface } from "node:readline";
|
|
|
5
5
|
* Prompt for a value interactively.
|
|
6
6
|
* Returns null if not in a TTY or if the user enters an empty string.
|
|
7
7
|
*/
|
|
8
|
-
const promptForValue = (context, label) => {
|
|
8
|
+
const promptForValue = (context, label, signal) => {
|
|
9
9
|
if (!process.stdin.isTTY || context.json || context.quiet) return Promise.resolve(null);
|
|
10
10
|
const rl = createInterface({
|
|
11
11
|
input: process.stdin,
|
|
12
12
|
output: process.stderr
|
|
13
13
|
});
|
|
14
14
|
return new Promise((resolve) => {
|
|
15
|
-
|
|
15
|
+
const finish = (value) => {
|
|
16
|
+
signal?.removeEventListener("abort", onAbort);
|
|
16
17
|
rl.close();
|
|
18
|
+
resolve(value);
|
|
19
|
+
};
|
|
20
|
+
const onAbort = () => {
|
|
21
|
+
finish(null);
|
|
22
|
+
};
|
|
23
|
+
if (signal?.aborted) {
|
|
24
|
+
finish(null);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
28
|
+
rl.question(`${label}: `, (answer) => {
|
|
17
29
|
const trimmed = answer.trim();
|
|
18
|
-
|
|
30
|
+
finish(trimmed.length > 0 ? trimmed : null);
|
|
19
31
|
});
|
|
20
32
|
});
|
|
21
33
|
};
|
|
@@ -33,4 +45,4 @@ const readStdin = async () => {
|
|
|
33
45
|
//#endregion
|
|
34
46
|
export { readStdin as n, promptForValue as t };
|
|
35
47
|
|
|
36
|
-
//# sourceMappingURL=prompt-
|
|
48
|
+
//# sourceMappingURL=prompt-Bv9qkjNw.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-Bv9qkjNw.mjs","names":[],"sources":["../src/prompt.ts"],"sourcesContent":["import { createInterface } from \"node:readline\";\n\nimport type { CliContext } from \"./types\";\n\n/**\n * Prompt for a value interactively.\n * Returns null if not in a TTY or if the user enters an empty string.\n */\nexport const promptForValue = (\n context: CliContext,\n label: string,\n signal?: AbortSignal\n): Promise<string | null> => {\n if (!process.stdin.isTTY || context.json || context.quiet) {\n return Promise.resolve(null);\n }\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n return new Promise((resolve) => {\n const finish = (value: string | null): void => {\n signal?.removeEventListener(\"abort\", onAbort);\n rl.close();\n resolve(value);\n };\n\n const onAbort = (): void => {\n finish(null);\n };\n\n if (signal?.aborted) {\n finish(null);\n return;\n }\n\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n rl.question(`${label}: `, (answer) => {\n const trimmed = answer.trim();\n finish(trimmed.length > 0 ? trimmed : null);\n });\n });\n};\n\n/**\n * Read all of stdin as a string. Returns null if stdin is a TTY\n * (nothing piped) or if it's empty.\n */\nexport const readStdin = async (): Promise<string | null> => {\n if (process.stdin.isTTY) {\n return null;\n }\n\n const chunks: Buffer[] = [];\n\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n\n const text = Buffer.concat(chunks).toString(\"utf-8\").trim();\n return text.length > 0 ? text : null;\n};\n"],"mappings":";;;;;;;AAQA,MAAa,kBACX,SACA,OACA,WAC2B;CAC3B,IAAI,CAAC,QAAQ,MAAM,SAAS,QAAQ,QAAQ,QAAQ,OAClD,OAAO,QAAQ,QAAQ,IAAI;CAG7B,MAAM,KAAK,gBAAgB;EACzB,OAAO,QAAQ;EACf,QAAQ,QAAQ;CAClB,CAAC;CAED,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,UAAU,UAA+B;GAC7C,QAAQ,oBAAoB,SAAS,OAAO;GAC5C,GAAG,MAAM;GACT,QAAQ,KAAK;EACf;EAEA,MAAM,gBAAsB;GAC1B,OAAO,IAAI;EACb;EAEA,IAAI,QAAQ,SAAS;GACnB,OAAO,IAAI;GACX;EACF;EAEA,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EAEzD,GAAG,SAAS,GAAG,MAAM,MAAM,WAAW;GACpC,MAAM,UAAU,OAAO,KAAK;GAC5B,OAAO,QAAQ,SAAS,IAAI,UAAU,IAAI;EAC5C,CAAC;CACH,CAAC;AACH;;;;;AAMA,MAAa,YAAY,YAAoC;CAC3D,IAAI,QAAQ,MAAM,OAChB,OAAO;CAGT,MAAM,SAAmB,CAAC;CAE1B,WAAW,MAAM,SAAS,QAAQ,OAChC,OAAO,KAAK,KAAe;CAG7B,MAAM,OAAO,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,OAAO,CAAC,CAAC,KAAK;CAC1D,OAAO,KAAK,SAAS,IAAI,OAAO;AAClC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "donebear",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Done Bear CLI with OAuth authentication",
|
|
5
5
|
"homepage": "https://donebear.com",
|
|
6
6
|
"bugs": {
|
|
@@ -23,37 +23,39 @@
|
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
|
-
"scripts": {
|
|
27
|
-
"build": "tsdown",
|
|
28
|
-
"dev": "tsdown --watch",
|
|
29
|
-
"start": "node dist/cli.mjs",
|
|
30
|
-
"check-types": "tsc --noEmit",
|
|
31
|
-
"test": "vitest run"
|
|
32
|
-
},
|
|
33
26
|
"dependencies": {
|
|
34
27
|
"better-sqlite3": "^12.9.0",
|
|
35
28
|
"commander": "^14.0.3",
|
|
36
29
|
"dotenv": "^17.4.2",
|
|
37
30
|
"jmespath": "^0.16.0",
|
|
38
|
-
"rrule": "^2.8.1",
|
|
39
31
|
"yaml": "^2.9.0"
|
|
40
32
|
},
|
|
41
33
|
"devDependencies": {
|
|
42
|
-
"@donebear/date-tz": "
|
|
43
|
-
"@donebear/import-core": "
|
|
44
|
-
"@donebear/manage-client": "
|
|
45
|
-
"@donebear/rank-logic": "
|
|
46
|
-
"@donebear/sync-ids": "
|
|
47
|
-
"@donebear/sync-schema": "
|
|
48
|
-
"@donebear/task-logic": "
|
|
34
|
+
"@donebear/date-tz": "0.0.0",
|
|
35
|
+
"@donebear/import-core": "0.0.0",
|
|
36
|
+
"@donebear/manage-client": "0.0.0",
|
|
37
|
+
"@donebear/rank-logic": "0.0.0",
|
|
38
|
+
"@donebear/sync-ids": "0.0.0",
|
|
39
|
+
"@donebear/sync-schema": "0.0.0",
|
|
40
|
+
"@donebear/task-logic": "0.0.0",
|
|
49
41
|
"@types/better-sqlite3": "^7.6.13",
|
|
50
42
|
"@types/jmespath": "^0.15.2",
|
|
51
43
|
"@types/node": "^25.7.0",
|
|
52
|
-
"tsdown": "^0.
|
|
44
|
+
"tsdown": "^0.23.0",
|
|
53
45
|
"typescript": "^6.0.3",
|
|
54
46
|
"vitest": "^4.1.6"
|
|
55
47
|
},
|
|
48
|
+
"optionalDependencies": {
|
|
49
|
+
"posthog-node": "^5.48.1"
|
|
50
|
+
},
|
|
56
51
|
"engines": {
|
|
57
52
|
"node": ">=18"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsdown",
|
|
56
|
+
"dev": "tsdown --watch",
|
|
57
|
+
"start": "node dist/cli.mjs",
|
|
58
|
+
"check-types": "tsc --noEmit",
|
|
59
|
+
"test": "vitest run"
|
|
58
60
|
}
|
|
59
|
-
}
|
|
61
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context-store-BBEVWudm.mjs","names":[],"sources":["../src/constants.ts","../src/errors.ts","../src/context-store.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const CLI_NAME = \"donebear\";\n\nexport const DONEBEAR_TOKEN_ENV = \"DONEBEAR_TOKEN\";\nexport const DONEBEAR_DEBUG_ENV = \"DONEBEAR_DEBUG\";\nconst DONEBEAR_CONFIG_DIR_ENV = \"DONEBEAR_CONFIG_DIR\";\nexport const DONEBEAR_SUPABASE_URL_ENV = \"DONEBEAR_SUPABASE_URL\";\nexport const DONEBEAR_SUPABASE_KEY_ENV = \"DONEBEAR_SUPABASE_PUBLISHABLE_KEY\";\nexport const DONEBEAR_API_URL_ENV = \"DONEBEAR_API_URL\";\n\nexport const DEFAULT_API_URL = \"https://api.donebear.com\";\n\nexport const DEFAULT_SUPABASE_URL = \"https://kdarvputxmlxqemisnbh.supabase.co\";\nexport const DEFAULT_SUPABASE_PUBLISHABLE_KEY =\n \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtkYXJ2cHV0eG1seHFlbWlzbmJoIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzIzNTcyNjcsImV4cCI6MjA4NzkzMzI2N30.A8sDPWNe_1EcCy0-GsUw4h1icxSGz_VDc9E9tPTYEU8\";\n\nexport const CLI_OAUTH_CLIENT_ID = \"9cc84e5c-6a0e-4a6d-93a2-334d8d307122\";\nexport const DONEBEAR_OAUTH_CLIENT_ID_ENV = \"DONEBEAR_OAUTH_CLIENT_ID\";\n\nexport const DEFAULT_OAUTH_CALLBACK_PORT = 8787;\nexport const DEFAULT_OAUTH_CALLBACK_PATH = \"/auth/callback\";\nexport const DEFAULT_OAUTH_TIMEOUT_SECONDS = 180;\n\nconst getDefaultConfigBaseDir = (): string => {\n if (process.platform === \"win32\") {\n return process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\n }\n\n return process.env.XDG_CONFIG_HOME ?? join(homedir(), \".config\");\n};\n\nconst configBaseDir = getDefaultConfigBaseDir();\n\nexport const CONFIG_DIR =\n process.env[DONEBEAR_CONFIG_DIR_ENV] ?? join(configBaseDir, CLI_NAME);\n\nexport const AUTH_FILE_PATH = join(CONFIG_DIR, \"auth.json\");\nexport const CONTEXT_FILE_PATH = join(CONFIG_DIR, \"context.json\");\n","export const EXIT_CODES = {\n AUTH_REQUIRED: 4,\n CANCELLED: 2,\n ERROR: 1,\n NETWORK: 5,\n SUCCESS: 0,\n VALIDATION: 3,\n} as const;\n\ntype ExitCode = (typeof EXIT_CODES)[keyof typeof EXIT_CODES];\n\ntype ErrorCode =\n | \"unknown_error\"\n | \"cancelled\"\n | \"validation_error\"\n | \"auth_error\"\n | \"network_error\";\n\nconst EXIT_CODE_TO_ERROR_CODE: Record<ExitCode, ErrorCode> = {\n [EXIT_CODES.SUCCESS]: \"unknown_error\",\n [EXIT_CODES.ERROR]: \"unknown_error\",\n [EXIT_CODES.CANCELLED]: \"cancelled\",\n [EXIT_CODES.VALIDATION]: \"validation_error\",\n [EXIT_CODES.AUTH_REQUIRED]: \"auth_error\",\n [EXIT_CODES.NETWORK]: \"network_error\",\n};\n\nconst exitCodeToErrorCode = (exitCode: ExitCode): ErrorCode =>\n EXIT_CODE_TO_ERROR_CODE[exitCode] ?? \"unknown_error\";\n\nexport class CliError extends Error {\n readonly exitCode: ExitCode;\n readonly code: ErrorCode;\n readonly hint: string | null;\n\n constructor(\n message: string,\n exitCode: ExitCode = EXIT_CODES.ERROR,\n hint?: string\n ) {\n super(message);\n this.name = \"CliError\";\n this.exitCode = exitCode;\n this.code = exitCodeToErrorCode(exitCode);\n this.hint = hint ?? null;\n }\n}\n\nexport const toCliError = (error: unknown): CliError => {\n if (error instanceof CliError) {\n return error;\n }\n\n if (error instanceof Error) {\n // Network errors (fetch failures)\n if (error instanceof TypeError && error.message.includes(\"fetch\")) {\n return new CliError(\n \"Cannot connect to Done Bear API.\",\n EXIT_CODES.NETWORK,\n \"Check your internet connection and API URL configuration.\"\n );\n }\n\n // Timeout errors\n if (error.name === \"TimeoutError\" || error.name === \"AbortError\") {\n return new CliError(\n \"Request timed out.\",\n EXIT_CODES.NETWORK,\n \"The API may be unavailable. Try again later.\"\n );\n }\n\n return new CliError(error.message, EXIT_CODES.ERROR);\n }\n\n return new CliError(\"Unknown error\", EXIT_CODES.ERROR);\n};\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\n\nimport { CONFIG_DIR, CONTEXT_FILE_PATH } from \"./constants\";\nimport { CliError, EXIT_CODES } from \"./errors\";\nimport type { CliContextFileData, LocalCliContext } from \"./types\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst parseLocalContext = (value: unknown): LocalCliContext | null => {\n if (!isRecord(value)) {\n return null;\n }\n\n if (value.workspaceId !== null && typeof value.workspaceId !== \"string\") {\n return null;\n }\n\n if (typeof value.clientId !== \"string\" || value.clientId.length === 0) {\n return null;\n }\n\n return {\n clientId: value.clientId,\n workspaceId: value.workspaceId,\n };\n};\n\nconst parseContextFile = (payload: string): CliContextFileData => {\n const parsed = JSON.parse(payload) as unknown;\n\n if (!isRecord(parsed) || parsed.version !== 1) {\n throw new CliError(\n `Invalid CLI context format in ${CONTEXT_FILE_PATH}`,\n EXIT_CODES.ERROR\n );\n }\n\n const context = parseLocalContext(parsed.context);\n\n if (!context) {\n throw new CliError(\n `Invalid CLI context in ${CONTEXT_FILE_PATH}`,\n EXIT_CODES.ERROR\n );\n }\n\n return {\n context,\n version: 1,\n };\n};\n\nconst writeContext = async (context: LocalCliContext): Promise<void> => {\n const payload: CliContextFileData = {\n context,\n version: 1,\n };\n\n await mkdir(CONFIG_DIR, {\n mode: 0o700,\n recursive: true,\n });\n\n await writeFile(CONTEXT_FILE_PATH, `${JSON.stringify(payload, null, 2)}\\n`, {\n encoding: \"utf-8\",\n mode: 0o600,\n });\n};\n\nconst readLocalContext = async (): Promise<LocalCliContext | null> => {\n try {\n const raw = await readFile(CONTEXT_FILE_PATH, \"utf-8\");\n return parseContextFile(raw).context;\n } catch (error) {\n if (isRecord(error) && error.code === \"ENOENT\") {\n return null;\n }\n\n if (error instanceof CliError) {\n throw error;\n }\n\n throw new CliError(\n `Failed to read context from ${CONTEXT_FILE_PATH}`,\n EXIT_CODES.ERROR\n );\n }\n};\n\nexport const ensureLocalContext = async (): Promise<LocalCliContext> => {\n const existing = await readLocalContext();\n\n if (existing) {\n return existing;\n }\n\n const created: LocalCliContext = {\n clientId: randomUUID(),\n workspaceId: null,\n };\n\n await writeContext(created);\n return created;\n};\n\nexport const setCurrentWorkspace = async (\n workspaceId: string | null\n): Promise<LocalCliContext> => {\n const context = await ensureLocalContext();\n\n const nextContext: LocalCliContext = {\n ...context,\n workspaceId,\n };\n\n await writeContext(nextContext);\n return nextContext;\n};\n"],"mappings":"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"}
|
package/dist/prompt-B7mDo4K7.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-GfcWO85b.mjs","names":[],"sources":["../src/prompt.ts"],"sourcesContent":["import { createInterface } from \"node:readline\";\n\nimport type { CliContext } from \"./types\";\n\n/**\n * Prompt for a value interactively.\n * Returns null if not in a TTY or if the user enters an empty string.\n */\nexport const promptForValue = (\n context: CliContext,\n label: string\n): Promise<string | null> => {\n if (!process.stdin.isTTY || context.json || context.quiet) {\n return Promise.resolve(null);\n }\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n return new Promise((resolve) => {\n rl.question(`${label}: `, (answer) => {\n rl.close();\n const trimmed = answer.trim();\n resolve(trimmed.length > 0 ? trimmed : null);\n });\n });\n};\n\n/**\n * Read all of stdin as a string. Returns null if stdin is a TTY\n * (nothing piped) or if it's empty.\n */\nexport const readStdin = async (): Promise<string | null> => {\n if (process.stdin.isTTY) {\n return null;\n }\n\n const chunks: Buffer[] = [];\n\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n\n const text = Buffer.concat(chunks).toString(\"utf-8\").trim();\n return text.length > 0 ? text : null;\n};\n"],"mappings":";;;;;;;AAQA,MAAa,kBACX,SACA,UAC2B;CAC3B,IAAI,CAAC,QAAQ,MAAM,SAAS,QAAQ,QAAQ,QAAQ,OAClD,OAAO,QAAQ,QAAQ,KAAK;CAG9B,MAAM,KAAK,gBAAgB;EACzB,OAAO,QAAQ;EACf,QAAQ,QAAQ;EACjB,CAAC;CAEF,OAAO,IAAI,SAAS,YAAY;EAC9B,GAAG,SAAS,GAAG,MAAM,MAAM,WAAW;GACpC,GAAG,OAAO;GACV,MAAM,UAAU,OAAO,MAAM;GAC7B,QAAQ,QAAQ,SAAS,IAAI,UAAU,KAAK;IAC5C;GACF;;;;;;AAOJ,MAAa,YAAY,YAAoC;CAC3D,IAAI,QAAQ,MAAM,OAChB,OAAO;CAGT,MAAM,SAAmB,EAAE;CAE3B,WAAW,MAAM,SAAS,QAAQ,OAChC,OAAO,KAAK,MAAgB;CAG9B,MAAM,OAAO,OAAO,OAAO,OAAO,CAAC,SAAS,QAAQ,CAAC,MAAM;CAC3D,OAAO,KAAK,SAAS,IAAI,OAAO"}
|