Skip to content

Commit 3e7f848

Browse files
facundofariasclaude
andcommitted
Fix account key resolution for private doc creation
Account-level keys (acct_...) from global config were shadowed by doc-level keys (key_...) from .draftmark.json. Now create --private uses resolveAccountApiKey which checks global config before local, so dm login --api-key acct_... works correctly. dm config now shows Account API Key and Doc API Key separately. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a066f40 commit 3e7f848

3 files changed

Lines changed: 45 additions & 10 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "draftmark",
3-
"version": "0.2.0",
3+
"version": "0.2.1",
44
"description": "CLI for Draftmark",
55
"type": "module",
66
"bin": {

src/cli.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
getShareBaseUrl,
1313
readConfig,
1414
readGlobalConfig,
15+
resolveAccountApiKey,
1516
resolveApiKey,
1617
resolveApiKeyOptional,
1718
resolveMagicToken,
@@ -118,7 +119,7 @@ const program = new Command();
118119
program
119120
.name("dm")
120121
.description("CLI for Draftmark — markdown sharing for async collaboration")
121-
.version("0.2.0")
122+
.version("0.2.1")
122123
.option("-q, --quiet", "Suppress all stderr output")
123124
.option("--base-url <url>", "Override API base URL (default: https://draftmark.app/api/v1)");
124125

@@ -149,11 +150,11 @@ program
149150

150151
const content = await readContentFromFileOrStdin(file);
151152

152-
// Resolve API key (required for private, optional for public)
153+
// Resolve API key: private docs need an account key (acct_...), public docs are optional
153154
const entry = await getLastEntry();
154155
const global = await readGlobalConfig();
155156
const apiKey = opts.private
156-
? resolveApiKey(opts, entry, global)
157+
? resolveAccountApiKey(opts, entry, global)
157158
: resolveApiKeyOptional(opts, entry, global);
158159

159160
const body: Record<string, unknown> = { content };
@@ -782,21 +783,28 @@ program
782783
const global = await readGlobalConfig();
783784
const entry = entries.length > 0 ? entries[entries.length - 1] : null;
784785

786+
const envKey = process.env.DM_API_KEY;
787+
const localKey = entry?.api_key;
788+
const globalKey = global.api_key;
789+
785790
const resolved = {
786791
base_url: getBaseUrl(),
787792
share_base_url: getShareBaseUrl(),
788-
api_key: {
793+
account_api_key: {
789794
source: (() => {
790-
if (process.env.DM_API_KEY) return "env (DM_API_KEY)";
791-
if (entry?.api_key) return ".draftmark.json";
792-
if (global.api_key) return "~/.config/draftmark/config.json";
795+
if (envKey) return "env (DM_API_KEY)";
796+
if (globalKey) return "~/.config/draftmark/config.json";
797+
if (localKey) return ".draftmark.json";
793798
return null;
794799
})(),
795800
value: (() => {
796-
const key = process.env.DM_API_KEY || entry?.api_key || global.api_key;
801+
const key = envKey || globalKey || localKey;
797802
return key ? key.slice(0, 12) + "..." : null;
798803
})(),
799804
},
805+
doc_api_key: localKey
806+
? { source: ".draftmark.json", value: localKey.slice(0, 12) + "..." }
807+
: null,
800808
magic_token: {
801809
source: (() => {
802810
if (process.env.DM_MAGIC_TOKEN) return "env (DM_MAGIC_TOKEN)";
@@ -818,8 +826,13 @@ program
818826
process.stdout.write(`${label("Share Base URL", resolved.share_base_url)}\n`);
819827
process.stdout.write("\n");
820828
process.stdout.write(
821-
`${label("API Key", resolved.api_key.value ? `${green(resolved.api_key.value)} ${dim(`(${resolved.api_key.source})`)}` : dim("not set"))}\n`
829+
`${label("Account API Key", resolved.account_api_key.value ? `${green(resolved.account_api_key.value)} ${dim(`(${resolved.account_api_key.source})`)}` : dim("not set"))}\n`
822830
);
831+
if (resolved.doc_api_key) {
832+
process.stdout.write(
833+
`${label("Doc API Key", `${dim(resolved.doc_api_key.value)} ${dim(`(${resolved.doc_api_key.source})`)}`)} \n`
834+
);
835+
}
823836
process.stdout.write(
824837
`${label("Magic Token", resolved.magic_token.set ? `${green("set")} ${dim(`(${resolved.magic_token.source})`)}` : dim("not set"))}\n`
825838
);

src/config.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,28 @@ export function resolveApiKeyOptional(
112112
return undefined;
113113
}
114114

115+
/**
116+
* Resolve an account-level API key (acct_...) for operations that require
117+
* account auth (e.g. creating private docs). Prefers account keys over
118+
* doc-level keys: flag → env → global config (acct_) → local config.
119+
*/
120+
export function resolveAccountApiKey(
121+
opts: { apiKey?: string },
122+
entry: DraftmarkEntry | null,
123+
global?: GlobalConfig
124+
): string {
125+
// Explicit flag always wins
126+
if (opts.apiKey) return opts.apiKey;
127+
// Env var — user controls what they put here
128+
if (process.env.DM_API_KEY) return process.env.DM_API_KEY;
129+
// Global config first (likely acct_...), then local (likely key_...)
130+
if (global?.api_key) return global.api_key;
131+
if (entry?.api_key) return entry.api_key;
132+
throw new Error(
133+
"No account API key found. Provide --api-key, set DM_API_KEY, or run `dm login --api-key acct_...`."
134+
);
135+
}
136+
115137
export function resolveMagicToken(
116138
opts: { magicToken?: string },
117139
entry: DraftmarkEntry | null,

0 commit comments

Comments
 (0)