@@ -6,9 +6,12 @@ import { api } from "./api.js";
66import {
77 appendConfig ,
88 getLastEntry ,
9+ readGlobalConfig ,
910 resolveApiKey ,
11+ resolveApiKeyOptional ,
1012 resolveMagicToken ,
1113 resolveSlug ,
14+ writeGlobalConfig ,
1215} from "./config.js" ;
1316import {
1417 bold ,
@@ -43,6 +46,7 @@ program
4346 . option ( "--title <title>" , "Document title" )
4447 . option ( "--expected-reviews <n>" , "Number of expected reviews" , parseInt )
4548 . option ( "--review-deadline <date>" , "Review deadline (ISO date)" )
49+ . option ( "--api-key <key>" , "Account API key (required for private docs)" )
4650 . option ( "--json" , "Output raw JSON response" )
4751 . action ( async ( file : string , opts ) => {
4852 info ( "Creating document..." ) ;
@@ -55,6 +59,13 @@ program
5559 process . exit ( 1 ) ;
5660 }
5761
62+ // Resolve API key (required for private, optional for public)
63+ const entry = await getLastEntry ( ) ;
64+ const global = await readGlobalConfig ( ) ;
65+ const apiKey = opts . private
66+ ? resolveApiKey ( opts , entry , global )
67+ : resolveApiKeyOptional ( opts , entry , global ) ;
68+
5869 const body : Record < string , unknown > = { content } ;
5970 if ( opts . title ) body . title = opts . title ;
6071 if ( opts . private ) body . visibility = "private" ;
@@ -69,6 +80,7 @@ program
6980 } > ( "/docs" , {
7081 method : "POST" ,
7182 body,
83+ apiKey,
7284 } ) ;
7385
7486 if ( ! res . ok ) {
@@ -92,6 +104,10 @@ program
92104 } else {
93105 success ( "Document created\n" ) ;
94106 process . stdout . write ( `${ label ( "URL" , cyan ( shareUrl ) ) } \n` ) ;
107+ if ( opts . private ) {
108+ const privateUrl = `${ shareUrl } ?token=${ doc . magic_token } ` ;
109+ process . stdout . write ( `${ label ( "Private URL" , cyan ( privateUrl ) ) } \n` ) ;
110+ }
95111 process . stdout . write ( `${ label ( "Slug" , doc . slug ) } \n` ) ;
96112 process . stdout . write ( `${ label ( "Magic Token" , doc . magic_token ) } \n` ) ;
97113 process . stdout . write ( `${ label ( "API Key" , doc . api_key ) } \n` ) ;
@@ -110,8 +126,9 @@ program
110126 . option ( "--json" , "Output raw JSON response" )
111127 . action ( async ( slugArg : string | undefined , opts ) => {
112128 const entry = await getLastEntry ( ) ;
129+ const global = await readGlobalConfig ( ) ;
113130 const slug = resolveSlug ( slugArg , entry ) ;
114- const apiKey = resolveApiKey ( opts , entry ) ;
131+ const apiKey = resolveApiKey ( opts , entry , global ) ;
115132
116133 const res = await api < Record < string , unknown > > ( `/docs/${ slug } ` , {
117134 apiKey,
@@ -163,8 +180,9 @@ program
163180 . option ( "--json" , "Output raw JSON response" )
164181 . action ( async ( slugArg : string | undefined , opts ) => {
165182 const entry = await getLastEntry ( ) ;
183+ const global = await readGlobalConfig ( ) ;
166184 const slug = resolveSlug ( slugArg , entry ) ;
167- const apiKey = resolveApiKey ( opts , entry ) ;
185+ const apiKey = resolveApiKey ( opts , entry , global ) ;
168186
169187 const params : Record < string , string > = { } ;
170188 if ( opts . status ) params . status = opts . status ;
@@ -210,6 +228,7 @@ program
210228 . option ( "--json" , "Output raw JSON response" )
211229 . action ( async ( first : string , second : string | undefined , opts ) => {
212230 const entry = await getLastEntry ( ) ;
231+ const global = await readGlobalConfig ( ) ;
213232
214233 // If only one positional arg, it's the body and slug comes from config
215234 let slug : string ;
@@ -222,7 +241,7 @@ program
222241 body = second ;
223242 }
224243
225- const apiKey = resolveApiKey ( opts , entry ) ;
244+ const apiKey = resolveApiKey ( opts , entry , global ) ;
226245
227246 const payload : Record < string , unknown > = { body } ;
228247 if ( opts . author ) payload . author = opts . author ;
@@ -268,8 +287,9 @@ program
268287 . option ( "--json" , "Output raw JSON response" )
269288 . action ( async ( slugArg : string | undefined , opts ) => {
270289 const entry = await getLastEntry ( ) ;
290+ const global = await readGlobalConfig ( ) ;
271291 const slug = resolveSlug ( slugArg , entry ) ;
272- const apiKey = resolveApiKey ( opts , entry ) ;
292+ const apiKey = resolveApiKey ( opts , entry , global ) ;
273293
274294 const payload : Record < string , unknown > = { } ;
275295 if ( opts . name ) payload . reviewer_name = opts . name ;
@@ -309,8 +329,9 @@ program
309329 . option ( "--api-key <key>" , "API key for authentication" )
310330 . action ( async ( slugArg : string | undefined , opts ) => {
311331 const entry = await getLastEntry ( ) ;
332+ const global = await readGlobalConfig ( ) ;
312333 const slug = resolveSlug ( slugArg , entry ) ;
313- const apiKey = resolveApiKey ( opts , entry ) ;
334+ const apiKey = resolveApiKey ( opts , entry , global ) ;
314335
315336 const res = await api < string > ( `/docs/${ slug } ` , {
316337 apiKey,
@@ -336,8 +357,9 @@ program
336357 . option ( "--json" , "Output raw JSON response" )
337358 . action ( async ( slugArg : string | undefined , opts ) => {
338359 const entry = await getLastEntry ( ) ;
360+ const global = await readGlobalConfig ( ) ;
339361 const slug = resolveSlug ( slugArg , entry ) ;
340- const magicToken = resolveMagicToken ( opts , entry ) ;
362+ const magicToken = resolveMagicToken ( opts , entry , global ) ;
341363
342364 const res = await api ( `/docs/${ slug } ` , {
343365 method : "PATCH" ,
@@ -369,8 +391,9 @@ program
369391 . option ( "--json" , "Output raw JSON response" )
370392 . action ( async ( slugArg : string | undefined , opts ) => {
371393 const entry = await getLastEntry ( ) ;
394+ const global = await readGlobalConfig ( ) ;
372395 const slug = resolveSlug ( slugArg , entry ) ;
373- const magicToken = resolveMagicToken ( opts , entry ) ;
396+ const magicToken = resolveMagicToken ( opts , entry , global ) ;
374397
375398 const res = await api ( `/docs/${ slug } ` , {
376399 method : "PATCH" ,
@@ -408,8 +431,9 @@ program
408431 }
409432
410433 const entry = await getLastEntry ( ) ;
434+ const global = await readGlobalConfig ( ) ;
411435 const slug = resolveSlug ( slugArg , entry ) ;
412- const magicToken = resolveMagicToken ( opts , entry ) ;
436+ const magicToken = resolveMagicToken ( opts , entry , global ) ;
413437
414438 const res = await api ( `/docs/${ slug } ` , {
415439 method : "DELETE" ,
@@ -429,4 +453,79 @@ program
429453 }
430454 } ) ;
431455
456+ // ---------------------------------------------------------------------------
457+ // dm login
458+ // ---------------------------------------------------------------------------
459+ program
460+ . command ( "login" )
461+ . description ( "Save account credentials globally (~/.config/draftmark/config.json)" )
462+ . option ( "--api-key <key>" , "Account API key" )
463+ . option ( "--magic-token <token>" , "Default magic token" )
464+ . action ( async ( opts ) => {
465+ if ( ! opts . apiKey && ! opts . magicToken ) {
466+ error ( "Provide at least one of --api-key or --magic-token." ) ;
467+ process . exit ( 1 ) ;
468+ }
469+
470+ const existing = await readGlobalConfig ( ) ;
471+ const updated = { ...existing } ;
472+ if ( opts . apiKey ) updated . api_key = opts . apiKey ;
473+ if ( opts . magicToken ) updated . magic_token = opts . magicToken ;
474+
475+ await writeGlobalConfig ( updated ) ;
476+ success ( "Credentials saved to ~/.config/draftmark/config.json" ) ;
477+ } ) ;
478+
479+ // ---------------------------------------------------------------------------
480+ // dm logout
481+ // ---------------------------------------------------------------------------
482+ program
483+ . command ( "logout" )
484+ . description ( "Remove saved global credentials" )
485+ . action ( async ( ) => {
486+ await writeGlobalConfig ( { } ) ;
487+ success ( "Global credentials removed." ) ;
488+ } ) ;
489+
490+ // ---------------------------------------------------------------------------
491+ // dm whoami
492+ // ---------------------------------------------------------------------------
493+ program
494+ . command ( "whoami" )
495+ . description ( "Show current authentication sources" )
496+ . action ( async ( ) => {
497+ const entry = await getLastEntry ( ) ;
498+ const global = await readGlobalConfig ( ) ;
499+
500+ process . stdout . write ( "\n" ) ;
501+
502+ // Global config
503+ if ( global . api_key ) {
504+ process . stdout . write (
505+ `${ label ( "Global API Key" , green ( global . api_key . slice ( 0 , 12 ) + "..." ) ) } \n`
506+ ) ;
507+ } else {
508+ process . stdout . write ( `${ label ( "Global API Key" , dim ( "not set" ) ) } \n` ) ;
509+ }
510+
511+ // Env vars
512+ if ( process . env . DM_API_KEY ) {
513+ process . stdout . write ( `${ label ( "DM_API_KEY env" , green ( "set" ) ) } \n` ) ;
514+ }
515+ if ( process . env . DM_MAGIC_TOKEN ) {
516+ process . stdout . write ( `${ label ( "DM_MAGIC_TOKEN env" , green ( "set" ) ) } \n` ) ;
517+ }
518+
519+ // Local config
520+ if ( entry ) {
521+ process . stdout . write (
522+ `${ label ( "Local config" , green ( `.draftmark.json (${ entry . slug } )` ) ) } \n`
523+ ) ;
524+ } else {
525+ process . stdout . write ( `${ label ( "Local config" , dim ( "no .draftmark.json" ) ) } \n` ) ;
526+ }
527+
528+ process . stdout . write ( "\n" ) ;
529+ } ) ;
530+
432531program . parse ( ) ;
0 commit comments