@@ -6,27 +6,136 @@ import { workspaceError } from "../../errors.js";
66import type { WorkspaceAuth } from "./session.js" ;
77
88interface LoginOptions extends JsonOption {
9+ readonly complete ?: boolean ;
910 readonly passwordStdin ?: boolean ;
10- readonly username : string ;
11+ readonly username ? : string ;
1112}
1213
1314export function createAuthCommands ( auth : WorkspaceAuth ) : readonly Command [ ] {
1415 const login = new Command ( "login" )
15- . description ( "Log in to the configured Workspace" )
16- . requiredOption ( "--username <name>" , "Workspace username" )
17- . option ( "--password-stdin" , "read the password from stdin" )
16+ . description ( "Start or complete a user-approved Workspace browser login" )
17+ . option (
18+ "--complete" ,
19+ "complete the pending browser login after the user confirms approval" ,
20+ )
21+ . option (
22+ "--username <name>" ,
23+ "use Workspace username and password instead of browser approval" ,
24+ )
25+ . option ( "--password-stdin" , "read the password from stdin (requires --username)" )
1826 . option ( "--json" , "write structured JSON" )
27+ . addHelpText (
28+ "after" ,
29+ [
30+ "" ,
31+ "Agent browser-login workflow:" ,
32+ " 1. Run `univer-workspace-cli login`; it prints an approval URL and exits." ,
33+ " 2. Send the URL and verification code to the user, then wait for their reply." ,
34+ " 3. Only after the user confirms approval, run `univer-workspace-cli login --complete`." ,
35+ " Do not ask the user for a password and do not poll --complete while waiting." ,
36+ "" ,
37+ ] . join ( "\n" ) ,
38+ )
1939 . action ( async ( options : LoginOptions ) => {
20- const result = await executeCommand ( login , async ( ) => {
21- const password = (
22- await readPassword ( options . passwordStdin === true ? "stdin" : "interactive" )
23- ) . replace ( / \r ? \n $ / u, "" ) ;
24- if ( password === "" ) {
25- throw workspaceError ( "workspace-argument-invalid" , "Password is empty." ) ;
40+ if ( options . username !== undefined ) {
41+ const username = options . username ;
42+ const result = await executeCommand ( login , async ( ) => {
43+ if ( options . complete === true ) {
44+ throw workspaceError (
45+ "workspace-argument-invalid" ,
46+ "--complete cannot be combined with --username." ,
47+ ) ;
48+ }
49+ const password = (
50+ await readPassword ( options . passwordStdin === true ? "stdin" : "interactive" )
51+ ) . replace ( / \r ? \n $ / u, "" ) ;
52+ if ( password === "" ) {
53+ throw workspaceError ( "workspace-argument-invalid" , "Password is empty." ) ;
54+ }
55+ return await auth . login ( { password, username } ) ;
56+ } ) ;
57+ present ( login , options , result , `Logged in to ${ result . origin } as ${ result . subject . name } ` ) ;
58+ return ;
59+ }
60+
61+ if ( options . passwordStdin === true ) {
62+ await executeCommand ( login , async ( ) => {
63+ throw workspaceError (
64+ "workspace-argument-invalid" ,
65+ "--password-stdin requires --username." ,
66+ ) ;
67+ } ) ;
68+ return ;
69+ }
70+
71+ if ( options . complete === true ) {
72+ const result = await executeCommand ( login , async ( ) => {
73+ const pending = await auth . pendingCliLogin ( ) ;
74+ if ( pending === undefined ) {
75+ throw workspaceError (
76+ "workspace-cli-authorization-missing" ,
77+ "No pending browser login exists. Run login first." ,
78+ ) ;
79+ }
80+ const completion = await auth . completeCliLogin ( pending ) ;
81+ if ( completion . status === "pending" ) {
82+ return {
83+ status : "authorization_pending" as const ,
84+ origin : pending . origin ,
85+ userCode : pending . userCode ,
86+ verificationUrl : pending . verificationUrl ,
87+ } ;
88+ }
89+ return completion ;
90+ } ) ;
91+ if ( result . status === "authorization_pending" ) {
92+ present (
93+ login ,
94+ options ,
95+ result ,
96+ [
97+ "Browser approval has not completed." ,
98+ "This command has exited and is not waiting." ,
99+ "Ask the user to finish the approval, then run login --complete once more." ,
100+ ] . join ( "\n" ) ,
101+ ) ;
102+ return ;
26103 }
27- return await auth . login ( { password, username : options . username } ) ;
28- } ) ;
29- present ( login , options , result , `Logged in to ${ result . origin } as ${ result . subject . name } ` ) ;
104+ present (
105+ login ,
106+ options ,
107+ result ,
108+ `Logged in to ${ result . origin } as ${ result . subject . name } ` ,
109+ ) ;
110+ return ;
111+ }
112+
113+ const pending = await executeCommand ( login , async ( ) => await auth . startCliLogin ( ) ) ;
114+ const result = {
115+ status : "authorization_required" as const ,
116+ origin : pending . origin ,
117+ userCode : pending . userCode ,
118+ verificationUrl : pending . verificationUrl ,
119+ expiresAt : new Date ( pending . expiresAt ) . toISOString ( ) ,
120+ nextCommand : "univer-workspace-cli login --complete" ,
121+ } ;
122+ present (
123+ login ,
124+ options ,
125+ result ,
126+ [
127+ "Browser approval required." ,
128+ "" ,
129+ "Send this URL and verification code to the user:" ,
130+ pending . verificationUrl ,
131+ `Verification code: ${ pending . userCode } ` ,
132+ "" ,
133+ "This command has exited and is not waiting." ,
134+ "Wait for the user to confirm approval. Do not poll in the meantime." ,
135+ "After the user confirms, run:" ,
136+ " univer-workspace-cli login --complete" ,
137+ ] . join ( "\n" ) ,
138+ ) ;
30139 } ) ;
31140
32141 const whoami = new Command ( "whoami" )
0 commit comments