nolo-cli 0.27.1 → 0.29.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.
Files changed (90) hide show
  1. package/{_executeModel-M6KLIXW5.js → _executeModel-YQ57CFSN.js} +23 -23
  2. package/{action-5MNAXR5B.js → action-6BM26ZYM.js} +4 -4
  3. package/{addContentAction-UNSYTVTB.js → addContentAction-HLTFX56O.js} +3 -3
  4. package/{addDialogAgentAction-LJGNP2AO.js → addDialogAgentAction-Q2TYWDO6.js} +2 -2
  5. package/{agentDeleteCommand-VFHDZJG3.js → agentDeleteCommand-VP4VLCMG.js} +12 -12
  6. package/{agentEmailCommands-NQ3SJXZX.js → agentEmailCommands-PSLJLQRR.js} +28 -28
  7. package/{agentListCommands-BDDUYHLP.js → agentListCommands-MJJJG3OW.js} +13 -13
  8. package/{agentMachineCommands-5WU7B2UY.js → agentMachineCommands-IP2A26V7.js} +12 -12
  9. package/{agentRecordCommands-FFFARZTU.js → agentRecordCommands-3HK3AR3H.js} +27 -27
  10. package/{agentRunCommand-GZYQTIYF.js → agentRunCommand-2IX2ELJQ.js} +28 -28
  11. package/{agentRuntimeLocal-552R4CZA.js → agentRuntimeLocal-VI3NHLDU.js} +9 -9
  12. package/{agentSlice-N7E7SG25.js → agentSlice-RR6OT2BW.js} +3 -3
  13. package/{appDeleteCommands-IPJT6IYW.js → appDeleteCommands-SF2V6QYZ.js} +13 -13
  14. package/{appDeployCommands-24P4CADQ.js → appDeployCommands-3EPVR545.js} +13 -13
  15. package/{appDeployStatusCommands-3DRZOUAG.js → appDeployStatusCommands-O65Y747J.js} +13 -13
  16. package/{appGetCommands-JEOSBLUL.js → appGetCommands-AHDOF4SS.js} +13 -13
  17. package/{appListCommands-EIBM54WR.js → appListCommands-Y7YCXODU.js} +13 -13
  18. package/{authCommand-MKMYHD52.js → authCommand-XMQHBK5L.js} +470 -40
  19. package/chunk-2FX23RGA.js +162 -0
  20. package/{chunk-ESKT7YVG.js → chunk-2IN43Q72.js} +3 -3
  21. package/{chunk-6MBOZ4UP.js → chunk-2TYLTEAH.js} +1 -1
  22. package/{chunk-TTPVLL5F.js → chunk-33K2M3AU.js} +9 -9
  23. package/{chunk-54VHTARN.js → chunk-3LJ4RSSF.js} +1 -1
  24. package/{chunk-J2GZXLI7.js → chunk-4S7B6YC5.js} +9 -9
  25. package/{chunk-I6RJGORZ.js → chunk-BOPFLKHA.js} +1 -1
  26. package/{chunk-FL42U2NZ.js → chunk-BYHN2CP5.js} +1 -1
  27. package/{chunk-OXLLFZNF.js → chunk-DBUNOYLF.js} +1 -1
  28. package/{chunk-S6KVFOUG.js → chunk-ENUJPSZN.js} +2 -2
  29. package/{chunk-O5VFD7V6.js → chunk-FVY6KI5A.js} +1 -1
  30. package/{chunk-ZNFZGHKL.js → chunk-I5TJCJQP.js} +1 -1
  31. package/{chunk-ROJI6DRT.js → chunk-INWBJPQC.js} +2 -2
  32. package/{chunk-FON47ACC.js → chunk-JYSXE6LE.js} +3 -3
  33. package/{chunk-C4KV25UH.js → chunk-KJJ6EWVW.js} +28 -69
  34. package/{chunk-KXEWHQAW.js → chunk-MHHYH3JA.js} +1 -1
  35. package/{chunk-NCRWA6ZB.js → chunk-NV2VP27U.js} +2 -2
  36. package/{chunk-YZV4676I.js → chunk-P6TB7JIA.js} +160 -14
  37. package/{chunk-WCP5FDTF.js → chunk-PFTK5SEB.js} +21 -5
  38. package/{chunk-PPAJNYFB.js → chunk-PJI44RVC.js} +9 -9
  39. package/{chunk-AL2V7U3E.js → chunk-PJY5AH5A.js} +25 -8
  40. package/{chunk-JQ3LNEMS.js → chunk-PMSG3OGF.js} +51 -4
  41. package/{chunk-NWDOO373.js → chunk-POANOUUC.js} +3 -3
  42. package/chunk-PSK2RUHV.js +529 -0
  43. package/{chunk-BAKXXTAC.js → chunk-RLQXGHUQ.js} +2 -2
  44. package/{chunk-7ORW7XI4.js → chunk-SIZWJA7H.js} +4 -4
  45. package/{chunk-6V6MYXPV.js → chunk-SRFEVECQ.js} +1 -1
  46. package/{chunk-7WMLPRXP.js → chunk-STOUEEBU.js} +1 -1
  47. package/{chunk-4OHJCHIL.js → chunk-VEHCU5XV.js} +35 -105
  48. package/{chunk-AYQYOZYH.js → chunk-VNMOLPVO.js} +23 -23
  49. package/{chunk-MALVZUYL.js → chunk-WPRSBZYN.js} +1 -1
  50. package/{chunk-VV4TYWNV.js → chunk-WX3ZOSTL.js} +376 -656
  51. package/{chunk-PVWFM7JP.js → chunk-XISENOC2.js} +1 -1
  52. package/{chunk-X43U4JUJ.js → chunk-YRMUYZ5H.js} +2 -2
  53. package/{chunk-JMSCXQUG.js → chunk-Z3LLUGD5.js} +1 -1
  54. package/{chunk-7OBMZCRD.js → chunk-ZVZMT3EA.js} +7 -5
  55. package/{chunk-4RXTDRTJ.js → chunk-ZXXAMM3K.js} +2 -2
  56. package/{chunk-MAFUFDFV.js → chunk-ZZYNGFTB.js} +81 -48
  57. package/{createAgentAutomationAction-GU766YRL.js → createAgentAutomationAction-PIKVVL2R.js} +8 -8
  58. package/{createDialogAction-ADMDJITX.js → createDialogAction-DQCM7HHO.js} +9 -9
  59. package/{createPageAction-N7EZJVO5.js → createPageAction-W2DRG2J7.js} +8 -8
  60. package/{dbSlice-5BKDURDL.js → dbSlice-GOKRGQ47.js} +2 -2
  61. package/{dialogCommands-O4CJJRTQ.js → dialogCommands-N4XKI6ZU.js} +13 -13
  62. package/{dialogSlice-JB5YIV5K.js → dialogSlice-DZUQZISK.js} +8 -8
  63. package/{docCreateCommands-2U5AIF3O.js → docCreateCommands-DTDKANXX.js} +12 -12
  64. package/{docDeleteCommands-EDCAUQEF.js → docDeleteCommands-U4NB6OHK.js} +13 -13
  65. package/{docReadCommands-KFMBFGPY.js → docReadCommands-SDVQM3BH.js} +12 -12
  66. package/{docStore-EXPYXKAO.js → docStore-NXDBQRDQ.js} +9 -9
  67. package/{docUpdateCommands-57M4XOT7.js → docUpdateCommands-O2O644FV.js} +12 -12
  68. package/{handleSendMessageAction-5E3MBI4X.js → handleSendMessageAction-3MMYF664.js} +8 -8
  69. package/index.js +46 -46
  70. package/{machineCommands-UPDXXKGX.js → machineCommands-QFDT6G2Q.js} +26 -26
  71. package/{machineWsRunDispatch-M43IR6SD.js → machineWsRunDispatch-J6IBIDYG.js} +26 -26
  72. package/package.json +1 -1
  73. package/{read-F3L3T2WJ.js → read-A23NKT3O.js} +1 -1
  74. package/{readDocTool-FOHWVA4R.js → readDocTool-JAM5BI5Q.js} +1 -1
  75. package/{readlineWorkspace-NEADCABU.js → readlineWorkspace-FU7PWLJU.js} +52 -55
  76. package/{removeDialogAgentAction-QCFGIUIH.js → removeDialogAgentAction-K4HOWFHO.js} +4 -4
  77. package/{runAgentClientLoop-NYY3IGNZ.js → runAgentClientLoop-P6J4YSE4.js} +19 -19
  78. package/{setDialogExtraReferencesAction-DK4CKERU.js → setDialogExtraReferencesAction-JD7J7JM4.js} +3 -3
  79. package/{setPrimaryDialogAgentAction-SJSO6N27.js → setPrimaryDialogAgentAction-YWSA5XEU.js} +4 -4
  80. package/{settingSlice-XNM5AFPU.js → settingSlice-22GTX6CA.js} +3 -3
  81. package/{skillDiscovery-KJWT7JBU.js → skillDiscovery-ITA7BI7A.js} +4 -2
  82. package/{spaceCommands-XK46FRYG.js → spaceCommands-WM6W3WMQ.js} +12 -12
  83. package/{spaceSlice-S6HBDF4N.js → spaceSlice-SVEWO5JX.js} +8 -8
  84. package/{streamAgentChatTurn-2NP4RW7O.js → streamAgentChatTurn-XJRY424B.js} +79 -35
  85. package/{switchDialogAgentAction-4OAC6QRN.js → switchDialogAgentAction-6JKFBJVR.js} +2 -2
  86. package/{tools-2HPUZOTG.js → tools-ZN6GWGYP.js} +15 -15
  87. package/{updateDialogTitleAction-27EH4CEC.js → updateDialogTitleAction-ZJ2HF5AQ.js} +8 -8
  88. package/chunk-UTYJ47H2.js +0 -51
  89. package/chunk-ZJ7WLPDP.js +0 -553
  90. package/{chunk-ATWUOTJP.js → chunk-NWT2WUCY.js} +1 -1
@@ -6,19 +6,48 @@ import {
6
6
  parseFlagWithOptionalValue,
7
7
  runCloudflareOAuthLogin,
8
8
  upsertEnvVariable
9
- } from "./chunk-6MBOZ4UP.js";
9
+ } from "./chunk-2TYLTEAH.js";
10
10
  import "./chunk-6HYVJV7B.js";
11
11
  import {
12
+ ANTHROPIC_OAUTH_AUTHORIZE_URL,
13
+ ANTHROPIC_OAUTH_CALLBACK_PORT,
14
+ ANTHROPIC_OAUTH_CLIENT_ID,
15
+ ANTHROPIC_OAUTH_REDIRECT_URI,
16
+ ANTHROPIC_OAUTH_SCOPES,
17
+ ANTHROPIC_OAUTH_TOKEN_URL,
18
+ CURSOR_ACCESS_TOKEN_CLIENT_SKEW_MS,
19
+ CURSOR_LOGIN_URL,
20
+ CURSOR_POLL_BACKOFF_MULTIPLIER,
21
+ CURSOR_POLL_BASE_DELAY_MS,
22
+ CURSOR_POLL_MAX_ATTEMPTS,
23
+ CURSOR_POLL_MAX_CONSECUTIVE_ERRORS,
24
+ CURSOR_POLL_MAX_DELAY_MS,
25
+ CURSOR_POLL_URL,
26
+ XAI_ACCESS_TOKEN_CLIENT_SKEW_MS,
27
+ XAI_DEVICE_CODE_DEFAULT_EXPIRES_MS,
28
+ XAI_DEVICE_CODE_DEFAULT_INTERVAL_MS,
29
+ XAI_DEVICE_CODE_GRANT_TYPE,
30
+ XAI_DEVICE_CODE_MIN_INTERVAL_MS,
31
+ XAI_DEVICE_CODE_SLOW_DOWN_INCREMENT_MS,
32
+ XAI_OAUTH_CLIENT_ID,
33
+ XAI_OAUTH_DOCS_URL,
34
+ XAI_OAUTH_REDIRECT_HOST,
35
+ XAI_OAUTH_REDIRECT_PATH,
36
+ XAI_OAUTH_REDIRECT_PORT,
37
+ XAI_OAUTH_SCOPE,
38
+ XAI_TOKEN_REQUEST_TIMEOUT_MS,
39
+ decodeJwtExp,
12
40
  generatePkcePair,
41
+ normalizeAnthropicTokenPayload,
42
+ readJsonBody,
13
43
  runAntigravityOAuthLogin,
14
- runCursorOAuthLogin,
15
44
  runOpenAiCodexBrowserPkce,
16
45
  runOpenAiCodexDeviceCode,
17
- runXaiOAuthDeviceCode,
18
- runXaiOAuthLogin,
19
- startCallbackServer
20
- } from "./chunk-VV4TYWNV.js";
21
- import "./chunk-UTYJ47H2.js";
46
+ startCallbackServer,
47
+ validateXAIEndpoint,
48
+ xaiOAuthDiscovery
49
+ } from "./chunk-WX3ZOSTL.js";
50
+ import "./chunk-2FX23RGA.js";
22
51
  import "./chunk-WJRJDX2W.js";
23
52
  import "./chunk-XS7UPVR7.js";
24
53
  import {
@@ -39,35 +68,358 @@ import "./chunk-A7GB5UGH.js";
39
68
  import "./chunk-OLZBQWZU.js";
40
69
  import "./chunk-YTA2QOEE.js";
41
70
  import "./chunk-NRY4GCKJ.js";
42
- import "./chunk-WWC7I6TA.js";
71
+ import {
72
+ asOptionalFiniteNumber
73
+ } from "./chunk-WWC7I6TA.js";
43
74
  import "./chunk-I42VC5KR.js";
44
75
  import "./chunk-QSJ3HFEF.js";
45
- import "./chunk-3EUW3WEO.js";
76
+ import {
77
+ asTrimmedString
78
+ } from "./chunk-3EUW3WEO.js";
46
79
  import "./chunk-U6VC4MU4.js";
47
- import "./chunk-KUG52IAP.js";
80
+ import {
81
+ isRecord
82
+ } from "./chunk-KUG52IAP.js";
48
83
  import "./chunk-LCBAMY2M.js";
49
84
  import "./chunk-4SN6J65J.js";
50
85
  import "./chunk-CF7PE763.js";
51
86
  import "./chunk-G3SUCT4W.js";
52
87
  import "./chunk-4VNS5WPM.js";
53
88
 
89
+ // packages/cli/oauth/flows/xai.ts
90
+ function buildXAIAuthorizeUrl(opts) {
91
+ const params = new URLSearchParams({
92
+ response_type: "code",
93
+ client_id: XAI_OAUTH_CLIENT_ID,
94
+ redirect_uri: opts.redirectUri,
95
+ scope: XAI_OAUTH_SCOPE,
96
+ code_challenge: opts.codeChallenge,
97
+ code_challenge_method: "S256",
98
+ state: opts.state,
99
+ nonce: opts.nonce,
100
+ plan: "generic"
101
+ });
102
+ return `${opts.authorizationEndpoint}?${params.toString()}`;
103
+ }
104
+ function generateState() {
105
+ const bytes = new Uint8Array(16);
106
+ crypto.getRandomValues(bytes);
107
+ return Array.from(bytes).map((value) => value.toString(16).padStart(2, "0")).join("");
108
+ }
109
+ function parsePositiveSecondsToMs(value, fallbackMs) {
110
+ const sec = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : NaN;
111
+ if (!Number.isFinite(sec) || sec <= 0) return fallbackMs;
112
+ return Math.max(1, Math.floor(sec * 1e3));
113
+ }
114
+ function parseOAuthTokenPayload(data, context, options = {}) {
115
+ if (!isRecord(data)) {
116
+ throw new Error(`${context} missing data`);
117
+ }
118
+ if (typeof data.access_token !== "string" || !data.access_token) {
119
+ throw new Error(`${context} missing access_token`);
120
+ }
121
+ const refreshToken = typeof data.refresh_token === "string" && data.refresh_token ? data.refresh_token : void 0;
122
+ if (options.requireRefreshToken && !refreshToken) {
123
+ throw new Error(`${context} missing refresh_token`);
124
+ }
125
+ const expiresIn = asOptionalFiniteNumber(data.expires_in);
126
+ if (expiresIn === void 0) {
127
+ throw new Error(`${context} missing expires_in`);
128
+ }
129
+ return {
130
+ accessToken: data.access_token,
131
+ ...refreshToken ? { refreshToken } : {},
132
+ expiresIn,
133
+ scope: typeof data.scope === "string" ? data.scope : void 0,
134
+ idToken: typeof data.id_token === "string" ? data.id_token : void 0
135
+ };
136
+ }
137
+ function tokenToCredential(token, now) {
138
+ const expiresIn = token.expiresIn;
139
+ return {
140
+ provider: "xai",
141
+ accessToken: token.accessToken,
142
+ refreshToken: token.refreshToken,
143
+ expiresAt: now + expiresIn * 1e3 - XAI_ACCESS_TOKEN_CLIENT_SKEW_MS,
144
+ scope: token.scope,
145
+ idToken: token.idToken,
146
+ obtainedAt: now
147
+ };
148
+ }
149
+ async function exchangeXAIToken(fetchImpl, code, redirectUri, verifier) {
150
+ const discovery = await xaiOAuthDiscovery(fetchImpl);
151
+ const tokenEndpoint = validateXAIEndpoint(discovery.token_endpoint, "token_endpoint");
152
+ const body = new URLSearchParams({
153
+ grant_type: "authorization_code",
154
+ client_id: XAI_OAUTH_CLIENT_ID,
155
+ code,
156
+ redirect_uri: redirectUri,
157
+ code_verifier: verifier
158
+ });
159
+ let response;
160
+ try {
161
+ response = await fetchImpl(tokenEndpoint, {
162
+ method: "POST",
163
+ headers: {
164
+ "Content-Type": "application/x-www-form-urlencoded",
165
+ Accept: "application/json"
166
+ },
167
+ body,
168
+ signal: AbortSignal.timeout(XAI_TOKEN_REQUEST_TIMEOUT_MS)
169
+ });
170
+ } catch (error) {
171
+ throw new Error(
172
+ `xAI token exchange failed: ${toErrorMessage(error)}`
173
+ );
174
+ }
175
+ const data = await readJsonBody(response, "xAI token exchange");
176
+ if (!response.ok) {
177
+ const detail = typeof data.error_description === "string" && data.error_description || typeof data.error === "string" && data.error || "";
178
+ throw new Error(
179
+ `xAI token exchange failed: ${response.status}${detail ? ` ${detail}` : ""}`
180
+ );
181
+ }
182
+ return parseOAuthTokenPayload(data, "xAI token exchange response", {
183
+ requireRefreshToken: true
184
+ });
185
+ }
186
+ async function requestXaiDeviceCode(fetchImpl, deviceAuthorizationEndpoint) {
187
+ const endpoint = validateXAIEndpoint(
188
+ deviceAuthorizationEndpoint,
189
+ "device_authorization_endpoint"
190
+ );
191
+ let response;
192
+ try {
193
+ response = await fetchImpl(endpoint, {
194
+ method: "POST",
195
+ headers: {
196
+ "Content-Type": "application/x-www-form-urlencoded",
197
+ Accept: "application/json"
198
+ },
199
+ body: new URLSearchParams({
200
+ client_id: XAI_OAUTH_CLIENT_ID,
201
+ scope: XAI_OAUTH_SCOPE
202
+ }),
203
+ signal: AbortSignal.timeout(XAI_TOKEN_REQUEST_TIMEOUT_MS)
204
+ });
205
+ } catch (error) {
206
+ throw new Error(
207
+ `xAI device code request failed: ${toErrorMessage(error)}`
208
+ );
209
+ }
210
+ const data = await readJsonBody(response, "xAI device code request");
211
+ if (!response.ok) {
212
+ const detail = typeof data.error_description === "string" && data.error_description || typeof data.error === "string" && data.error || "";
213
+ throw new Error(
214
+ `xAI device code request failed (${response.status})${detail ? `: ${detail}` : ""}`
215
+ );
216
+ }
217
+ const deviceCode = asTrimmedString(data.device_code);
218
+ const userCode = asTrimmedString(data.user_code);
219
+ const verificationUri = asTrimmedString(data.verification_uri);
220
+ const verificationUriComplete = asTrimmedString(
221
+ data.verification_uri_complete
222
+ );
223
+ if (!deviceCode || !userCode || !verificationUri) {
224
+ throw new Error(
225
+ "xAI device code response is missing device_code, user_code, or verification_uri"
226
+ );
227
+ }
228
+ validateXAIEndpoint(verificationUri, "verification_uri");
229
+ if (verificationUriComplete) {
230
+ validateXAIEndpoint(verificationUriComplete, "verification_uri_complete");
231
+ }
232
+ return {
233
+ deviceCode,
234
+ userCode,
235
+ verificationUri,
236
+ ...verificationUriComplete ? { verificationUriComplete } : {},
237
+ expiresInMs: parsePositiveSecondsToMs(
238
+ data.expires_in,
239
+ XAI_DEVICE_CODE_DEFAULT_EXPIRES_MS
240
+ ),
241
+ intervalMs: Math.max(
242
+ parsePositiveSecondsToMs(data.interval, XAI_DEVICE_CODE_DEFAULT_INTERVAL_MS),
243
+ XAI_DEVICE_CODE_MIN_INTERVAL_MS
244
+ )
245
+ };
246
+ }
247
+ async function pollXaiDeviceCodeToken(args) {
248
+ const tokenEndpoint = validateXAIEndpoint(args.tokenEndpoint, "token_endpoint");
249
+ const deadlineMs = args.now() + args.expiresInMs;
250
+ let intervalMs = args.intervalMs;
251
+ while (args.now() < deadlineMs) {
252
+ const remainingMs = Math.max(0, deadlineMs - args.now());
253
+ const delayMs = Math.min(Math.max(intervalMs, XAI_DEVICE_CODE_MIN_INTERVAL_MS), remainingMs);
254
+ if (delayMs > 0) {
255
+ await args.sleep(delayMs);
256
+ }
257
+ if (args.now() >= deadlineMs) break;
258
+ let response;
259
+ try {
260
+ response = await args.fetchImpl(tokenEndpoint, {
261
+ method: "POST",
262
+ headers: {
263
+ "Content-Type": "application/x-www-form-urlencoded",
264
+ Accept: "application/json"
265
+ },
266
+ body: new URLSearchParams({
267
+ grant_type: XAI_DEVICE_CODE_GRANT_TYPE,
268
+ client_id: XAI_OAUTH_CLIENT_ID,
269
+ device_code: args.deviceCode
270
+ }),
271
+ signal: AbortSignal.timeout(XAI_TOKEN_REQUEST_TIMEOUT_MS)
272
+ });
273
+ } catch (error) {
274
+ const message = toErrorMessage(error);
275
+ if (/abort|timeout/i.test(message)) {
276
+ throw new Error(`xAI device token poll aborted or timed out: ${message}`);
277
+ }
278
+ throw new Error(`xAI device token poll failed: ${message}`);
279
+ }
280
+ let data;
281
+ try {
282
+ data = await readJsonBody(response, "xAI device token exchange");
283
+ } catch (error) {
284
+ throw error;
285
+ }
286
+ if (response.ok) {
287
+ return parseOAuthTokenPayload(data, "xAI device token exchange response", {
288
+ requireRefreshToken: true
289
+ });
290
+ }
291
+ const oauthError = typeof data.error === "string" ? data.error : "";
292
+ if (oauthError === "authorization_pending") {
293
+ continue;
294
+ }
295
+ if (oauthError === "slow_down") {
296
+ intervalMs += XAI_DEVICE_CODE_SLOW_DOWN_INCREMENT_MS;
297
+ continue;
298
+ }
299
+ if (oauthError === "access_denied" || oauthError === "authorization_denied") {
300
+ throw new Error("xAI device authorization was denied");
301
+ }
302
+ if (oauthError === "expired_token") {
303
+ throw new Error("xAI device code expired. Re-run the login.");
304
+ }
305
+ const detail = typeof data.error_description === "string" && data.error_description || typeof data.error === "string" && data.error || "";
306
+ throw new Error(
307
+ `xAI device token exchange failed (${response.status})${detail ? `: ${detail}` : ""}`
308
+ );
309
+ }
310
+ throw new Error("xAI device authorization timed out. Re-run the login.");
311
+ }
312
+ async function runXaiOAuthLogin(deps = {}) {
313
+ const fetchImpl = deps.fetchImpl ?? fetch;
314
+ const output = deps.output ?? console;
315
+ const error = deps.error ?? console;
316
+ const redirectUri = `http://${XAI_OAUTH_REDIRECT_HOST}:${XAI_OAUTH_REDIRECT_PORT}${XAI_OAUTH_REDIRECT_PATH}`;
317
+ let handle;
318
+ try {
319
+ handle = await startCallbackServer({
320
+ port: XAI_OAUTH_REDIRECT_PORT,
321
+ hostname: XAI_OAUTH_REDIRECT_HOST,
322
+ timeoutMs: 5 * 6e4
323
+ });
324
+ } catch (err) {
325
+ throw new Error(
326
+ `Failed to start xAI OAuth callback server on ${XAI_OAUTH_REDIRECT_PORT}: ${toErrorMessage(err)}`
327
+ );
328
+ }
329
+ try {
330
+ const state = generateState();
331
+ const pkce = await generatePkcePair();
332
+ const nonce = crypto.randomUUID().replace(/-/g, "");
333
+ const discovery = await xaiOAuthDiscovery(fetchImpl);
334
+ const authUrl = buildXAIAuthorizeUrl({
335
+ authorizationEndpoint: discovery.authorization_endpoint,
336
+ redirectUri,
337
+ codeChallenge: pkce.challenge,
338
+ state,
339
+ nonce
340
+ });
341
+ output.log(
342
+ `Open the following URL in your browser to log in to xAI Grok (SuperGrok):
343
+ ${authUrl}
344
+
345
+ Docs: ${XAI_OAUTH_DOCS_URL}`
346
+ );
347
+ if (deps.openBrowser) {
348
+ try {
349
+ await deps.openBrowser(authUrl);
350
+ } catch (err) {
351
+ error.error(
352
+ `Failed to open browser automatically: ${toErrorMessage(err)}`
353
+ );
354
+ }
355
+ }
356
+ const callback = await handle.waitForCode();
357
+ const token = await exchangeXAIToken(
358
+ fetchImpl,
359
+ callback.code,
360
+ redirectUri,
361
+ pkce.verifier
362
+ );
363
+ const now = (deps.now ?? Date.now)();
364
+ return tokenToCredential(token, now);
365
+ } finally {
366
+ await handle.close();
367
+ }
368
+ }
369
+ async function runXaiOAuthDeviceCode(deps = {}) {
370
+ const fetchImpl = deps.fetchImpl ?? fetch;
371
+ const output = deps.output ?? console;
372
+ const error = deps.error ?? console;
373
+ const sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
374
+ const now = deps.now ?? Date.now;
375
+ const discovery = await xaiOAuthDiscovery(fetchImpl, {
376
+ requireDeviceAuthorization: true
377
+ });
378
+ const deviceAuthorizationEndpoint = discovery.device_authorization_endpoint;
379
+ if (!deviceAuthorizationEndpoint) {
380
+ throw new Error("xAI OIDC discovery missing device_authorization_endpoint");
381
+ }
382
+ const start = await requestXaiDeviceCode(fetchImpl, deviceAuthorizationEndpoint);
383
+ const browserUrl = start.verificationUriComplete ?? start.verificationUri;
384
+ const expiresMinutes = Math.max(1, Math.round(start.expiresInMs / 6e4));
385
+ output.log("Authorize nolo-cli for xAI Grok (device code / headless):");
386
+ output.log(` URL: ${start.verificationUri}`);
387
+ if (start.verificationUriComplete) {
388
+ output.log(` Complete URL: ${start.verificationUriComplete}`);
389
+ }
390
+ output.log(` Code: ${start.userCode}`);
391
+ output.log(
392
+ ` Code expires in ~${expiresMinutes} minute(s). Never share the code or tokens.`
393
+ );
394
+ if (deps.openBrowser) {
395
+ try {
396
+ const opened = await deps.openBrowser(browserUrl);
397
+ if (!opened) {
398
+ output.log("Could not open a browser automatically. Open the URL above.");
399
+ }
400
+ } catch (err) {
401
+ error.error(
402
+ `Failed to open browser automatically: ${toErrorMessage(err)}`
403
+ );
404
+ }
405
+ } else {
406
+ output.log("Open the URL above in a browser and enter the code to approve.");
407
+ }
408
+ const token = await pollXaiDeviceCodeToken({
409
+ fetchImpl,
410
+ tokenEndpoint: discovery.token_endpoint,
411
+ deviceCode: start.deviceCode,
412
+ expiresInMs: start.expiresInMs,
413
+ intervalMs: start.intervalMs,
414
+ sleep,
415
+ now
416
+ });
417
+ return tokenToCredential(token, now());
418
+ }
419
+
54
420
  // packages/cli/oauth/flows/anthropic.ts
55
421
  import { randomBytes } from "node:crypto";
56
422
  import { createInterface } from "node:readline/promises";
57
- var ANTHROPIC_OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
58
- var ANTHROPIC_OAUTH_AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
59
- var ANTHROPIC_OAUTH_TOKEN_URL = "https://api.anthropic.com/v1/oauth/token";
60
- var ANTHROPIC_OAUTH_CALLBACK_PORT = 54545;
61
- var ANTHROPIC_OAUTH_REDIRECT_URI = `http://localhost:${ANTHROPIC_OAUTH_CALLBACK_PORT}/callback`;
62
- var ANTHROPIC_OAUTH_SCOPES = [
63
- "org:create_api_key",
64
- "user:profile",
65
- "user:inference",
66
- "user:sessions:claude_code",
67
- "user:mcp_servers",
68
- "user:file_upload"
69
- ];
70
- var ACCESS_TOKEN_CLIENT_SKEW_MS = 5 * 60 * 1e3;
71
423
  function buildAnthropicAuthorizeUrl(args) {
72
424
  const params = new URLSearchParams({
73
425
  code: "true",
@@ -125,25 +477,14 @@ async function exchangeAnthropicAuthorizationCode(args) {
125
477
  });
126
478
  const payload = await response.json().catch(() => ({}));
127
479
  if (!response.ok) throw new Error(tokenError(payload, response.status));
128
- if (typeof payload.access_token !== "string" || !payload.access_token) {
480
+ if (typeof payload.access_token !== "string" || !payload.access_token.trim()) {
129
481
  throw new Error("Claude OAuth token exchange response missing access_token.");
130
482
  }
131
483
  const now = args.now?.() ?? Date.now();
132
- const expiresIn = typeof payload.expires_in === "number" && Number.isFinite(payload.expires_in) ? payload.expires_in : void 0;
133
- const accountId = typeof payload.account?.uuid === "string" ? payload.account.uuid : void 0;
134
- const email = typeof payload.account?.email_address === "string" ? payload.account.email_address : void 0;
135
- const organizationId = typeof payload.organization?.uuid === "string" ? payload.organization.uuid : void 0;
136
- const organizationName = typeof payload.organization?.name === "string" ? payload.organization.name : void 0;
137
- return {
138
- provider: "claude",
139
- accessToken: payload.access_token,
140
- ...typeof payload.refresh_token === "string" && payload.refresh_token ? { refreshToken: payload.refresh_token } : {},
141
- ...expiresIn !== void 0 ? { expiresAt: now + expiresIn * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS } : {},
142
- ...typeof payload.scope === "string" ? { scope: payload.scope } : {},
143
- ...accountId ? { accountId } : {},
144
- ...email || organizationId || organizationName ? { metadata: { email, organizationId, organizationName } } : {},
145
- obtainedAt: now
146
- };
484
+ return normalizeAnthropicTokenPayload({
485
+ payload,
486
+ now
487
+ });
147
488
  }
148
489
  async function defaultReadLine(prompt) {
149
490
  const terminal = createInterface({ input: process.stdin, output: process.stdout });
@@ -218,6 +559,95 @@ async function runAnthropicOAuthLogin(deps = {}) {
218
559
  });
219
560
  }
220
561
 
562
+ // packages/cli/oauth/flows/cursor.ts
563
+ var POLL_MAX_ATTEMPTS = CURSOR_POLL_MAX_ATTEMPTS;
564
+ var POLL_BASE_DELAY_MS = CURSOR_POLL_BASE_DELAY_MS;
565
+ var POLL_MAX_DELAY_MS = CURSOR_POLL_MAX_DELAY_MS;
566
+ var POLL_BACKOFF_MULTIPLIER = CURSOR_POLL_BACKOFF_MULTIPLIER;
567
+ var POLL_MAX_CONSECUTIVE_ERRORS = CURSOR_POLL_MAX_CONSECUTIVE_ERRORS;
568
+ var ACCESS_TOKEN_CLIENT_SKEW_MS = CURSOR_ACCESS_TOKEN_CLIENT_SKEW_MS;
569
+ var pollCursorAuth = async (uuid, verifier, deps = {}) => {
570
+ const fetchImpl = deps.fetchImpl ?? fetch;
571
+ const sleepFn = deps.sleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
572
+ let delay = POLL_BASE_DELAY_MS;
573
+ let consecutiveErrors = 0;
574
+ for (let attempt = 0; attempt < POLL_MAX_ATTEMPTS; attempt++) {
575
+ try {
576
+ const url = `${CURSOR_POLL_URL}?uuid=${encodeURIComponent(uuid)}&verifier=${encodeURIComponent(verifier)}`;
577
+ const response = await fetchImpl(url, {
578
+ signal: AbortSignal.timeout(1e4)
579
+ });
580
+ if (response.status === 200) {
581
+ const data = await response.json();
582
+ if (typeof data.accessToken === "string" && data.accessToken && typeof data.refreshToken === "string" && data.refreshToken) {
583
+ return data;
584
+ }
585
+ }
586
+ if (response.status === 404) {
587
+ consecutiveErrors = 0;
588
+ } else {
589
+ consecutiveErrors++;
590
+ if (consecutiveErrors >= POLL_MAX_CONSECUTIVE_ERRORS) {
591
+ throw new Error(`Cursor auth poll failed: ${response.status}`);
592
+ }
593
+ }
594
+ } catch (err) {
595
+ if (err instanceof Error && err.message.includes("Cursor auth poll failed")) {
596
+ throw err;
597
+ }
598
+ consecutiveErrors++;
599
+ if (consecutiveErrors >= POLL_MAX_CONSECUTIVE_ERRORS) {
600
+ throw new Error(
601
+ `Cursor auth poll failed: ${err instanceof Error ? err.message : String(err)}`
602
+ );
603
+ }
604
+ }
605
+ await sleepFn(delay);
606
+ delay = Math.min(delay * POLL_BACKOFF_MULTIPLIER, POLL_MAX_DELAY_MS);
607
+ }
608
+ throw new Error("Cursor auth timed out. Please try again.");
609
+ };
610
+ async function runCursorOAuthLogin(deps = {}) {
611
+ const output = deps.output ?? console;
612
+ const fetchImpl = deps.fetchImpl ?? fetch;
613
+ const pkce = await generatePkcePair();
614
+ const uuid = crypto.randomUUID();
615
+ const params = new URLSearchParams({
616
+ challenge: pkce.challenge,
617
+ uuid,
618
+ mode: "login",
619
+ redirectTarget: "cli"
620
+ });
621
+ const loginUrl = `${CURSOR_LOGIN_URL}?${params.toString()}`;
622
+ output.log(
623
+ `
624
+ Open this URL in your browser to authorize Cursor:
625
+ ${loginUrl}
626
+
627
+ Waiting for browser authentication... (polling ${CURSOR_POLL_URL})`
628
+ );
629
+ const openBrowser = deps.openBrowser;
630
+ if (openBrowser) {
631
+ try {
632
+ await openBrowser(loginUrl);
633
+ } catch {
634
+ }
635
+ }
636
+ const { accessToken, refreshToken } = await pollCursorAuth(uuid, pkce.verifier, {
637
+ fetchImpl,
638
+ ...deps.sleep ? { sleepFn: deps.sleep } : {}
639
+ });
640
+ const now = deps.now?.() ?? Date.now();
641
+ const expiresAt = decodeJwtExp(accessToken) ?? now + 3600 * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS;
642
+ return {
643
+ provider: "cursor",
644
+ accessToken,
645
+ refreshToken,
646
+ expiresAt,
647
+ obtainedAt: now
648
+ };
649
+ }
650
+
221
651
  // packages/cli/oauth/authCommand.ts
222
652
  var SYNC_HELP_LINE = ` --sync-to-server After local save, push the credential to your nolo server
223
653
  (default when NOLO_SERVER + AUTH_TOKEN / profile are set).