nolo-cli 0.27.1 → 0.28.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/{_executeModel-M6KLIXW5.js → _executeModel-SUMILI7E.js} +13 -13
- package/{agentDeleteCommand-VFHDZJG3.js → agentDeleteCommand-GEHAXWM6.js} +7 -7
- package/{agentEmailCommands-NQ3SJXZX.js → agentEmailCommands-PQXAKGK2.js} +17 -17
- package/{agentListCommands-BDDUYHLP.js → agentListCommands-V2IIWNTX.js} +8 -8
- package/{agentMachineCommands-5WU7B2UY.js → agentMachineCommands-Y47CABE2.js} +7 -7
- package/{agentRecordCommands-FFFARZTU.js → agentRecordCommands-UYUJUT4K.js} +16 -16
- package/{agentRunCommand-GZYQTIYF.js → agentRunCommand-3P6CCJQK.js} +17 -17
- package/{agentRuntimeLocal-552R4CZA.js → agentRuntimeLocal-ZALQDCZ3.js} +4 -4
- package/{agentSlice-N7E7SG25.js → agentSlice-LH2YKWE7.js} +1 -1
- package/{appDeleteCommands-IPJT6IYW.js → appDeleteCommands-XELAKISE.js} +8 -8
- package/{appDeployCommands-24P4CADQ.js → appDeployCommands-PFHVHXJ3.js} +8 -8
- package/{appDeployStatusCommands-3DRZOUAG.js → appDeployStatusCommands-RULB3DGE.js} +8 -8
- package/{appGetCommands-JEOSBLUL.js → appGetCommands-A3NLQ2U5.js} +8 -8
- package/{appListCommands-EIBM54WR.js → appListCommands-NQLJ7RHY.js} +8 -8
- package/{authCommand-MKMYHD52.js → authCommand-XMQHBK5L.js} +470 -40
- package/chunk-2FX23RGA.js +162 -0
- package/{chunk-6MBOZ4UP.js → chunk-2TYLTEAH.js} +1 -1
- package/{chunk-NWDOO373.js → chunk-4O2L6YR5.js} +3 -3
- package/{chunk-X43U4JUJ.js → chunk-4UYKPT74.js} +1 -1
- package/{chunk-TTPVLL5F.js → chunk-7IX2Z2IF.js} +8 -8
- package/{chunk-ESKT7YVG.js → chunk-7OYY7W4W.js} +2 -2
- package/{chunk-ZNFZGHKL.js → chunk-7ZKK4F22.js} +1 -1
- package/{chunk-YZV4676I.js → chunk-A6WDXLAG.js} +2 -2
- package/{chunk-KXEWHQAW.js → chunk-ATGTWONB.js} +1 -1
- package/{chunk-PPAJNYFB.js → chunk-BTA2CW6H.js} +6 -6
- package/{chunk-FL42U2NZ.js → chunk-D36OKPFG.js} +1 -1
- package/{chunk-MAFUFDFV.js → chunk-D5D3XTWF.js} +30 -35
- package/{chunk-BAKXXTAC.js → chunk-E4XWECLB.js} +2 -2
- package/{chunk-J2GZXLI7.js → chunk-HTLBU4ZY.js} +5 -5
- package/{chunk-4RXTDRTJ.js → chunk-IVMPWUAR.js} +1 -1
- package/{chunk-O5VFD7V6.js → chunk-MSXDPLDQ.js} +1 -1
- package/{chunk-NCRWA6ZB.js → chunk-MY54LNPC.js} +2 -2
- package/{chunk-6V6MYXPV.js → chunk-OMN2LILO.js} +1 -1
- package/{chunk-C4KV25UH.js → chunk-PASYOZYI.js} +22 -65
- package/{chunk-AL2V7U3E.js → chunk-PJY5AH5A.js} +25 -8
- package/chunk-PSK2RUHV.js +529 -0
- package/{chunk-4OHJCHIL.js → chunk-QUXMWSCQ.js} +21 -47
- package/{chunk-PVWFM7JP.js → chunk-RRWRVIQU.js} +1 -1
- package/{chunk-7ORW7XI4.js → chunk-SFFG3HJH.js} +3 -3
- package/{chunk-7WMLPRXP.js → chunk-UP7UFQOH.js} +1 -1
- package/{chunk-VV4TYWNV.js → chunk-WX3ZOSTL.js} +376 -656
- package/{chunk-AYQYOZYH.js → chunk-XMAELUQW.js} +11 -11
- package/{chunk-7OBMZCRD.js → chunk-ZVZMT3EA.js} +7 -5
- package/{createAgentAutomationAction-GU766YRL.js → createAgentAutomationAction-FAYQK5ME.js} +2 -2
- package/{createDialogAction-ADMDJITX.js → createDialogAction-NDBK6IQG.js} +3 -3
- package/{createPageAction-N7EZJVO5.js → createPageAction-CQTLV7FP.js} +2 -2
- package/{dialogCommands-O4CJJRTQ.js → dialogCommands-ZR42K2M5.js} +8 -8
- package/{dialogSlice-JB5YIV5K.js → dialogSlice-ZLGEYKLO.js} +2 -2
- package/{docCreateCommands-2U5AIF3O.js → docCreateCommands-2WXA3BCQ.js} +7 -7
- package/{docDeleteCommands-EDCAUQEF.js → docDeleteCommands-HSL7CD32.js} +8 -8
- package/{docReadCommands-KFMBFGPY.js → docReadCommands-YSPUU2DP.js} +7 -7
- package/{docStore-EXPYXKAO.js → docStore-LJQIY2ZU.js} +3 -3
- package/{docUpdateCommands-57M4XOT7.js → docUpdateCommands-EFYOIVM7.js} +7 -7
- package/{handleSendMessageAction-5E3MBI4X.js → handleSendMessageAction-JL6GGVNW.js} +2 -2
- package/index.js +46 -46
- package/{machineCommands-UPDXXKGX.js → machineCommands-VAANUFNC.js} +15 -15
- package/{machineWsRunDispatch-M43IR6SD.js → machineWsRunDispatch-ABWV2WZO.js} +15 -15
- package/package.json +1 -1
- package/{readlineWorkspace-NEADCABU.js → readlineWorkspace-GQI3XP5E.js} +41 -44
- package/{runAgentClientLoop-NYY3IGNZ.js → runAgentClientLoop-U632N2KO.js} +9 -9
- package/{skillDiscovery-KJWT7JBU.js → skillDiscovery-ITA7BI7A.js} +4 -2
- package/{spaceCommands-XK46FRYG.js → spaceCommands-DE6KNJIV.js} +7 -7
- package/{spaceSlice-S6HBDF4N.js → spaceSlice-QTMWWQHQ.js} +2 -2
- package/{streamAgentChatTurn-2NP4RW7O.js → streamAgentChatTurn-VU7BM3K3.js} +12 -12
- package/{tools-2HPUZOTG.js → tools-LSIS6NY6.js} +5 -5
- package/{updateDialogTitleAction-27EH4CEC.js → updateDialogTitleAction-T77A54DE.js} +2 -2
- package/chunk-UTYJ47H2.js +0 -51
- package/chunk-ZJ7WLPDP.js +0 -553
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
ANTIGRAVITY_AUTH_URL,
|
|
3
|
+
ANTIGRAVITY_CALLBACK_PATH,
|
|
4
|
+
ANTIGRAVITY_CALLBACK_PORT,
|
|
5
|
+
ANTIGRAVITY_CLIENT_ID,
|
|
6
|
+
ANTIGRAVITY_CLIENT_SECRET,
|
|
7
|
+
ANTIGRAVITY_CLOUD_CODE_BASE_URL,
|
|
8
|
+
ANTIGRAVITY_SCOPES,
|
|
9
|
+
ANTIGRAVITY_TOKEN_URL,
|
|
10
|
+
ANTIGRAVITY_USERINFO_URL,
|
|
11
|
+
getAntigravityUserAgent,
|
|
12
|
+
normalizeAntigravityTokenPayload,
|
|
13
|
+
refreshAntigravityToken
|
|
14
|
+
} from "./chunk-2FX23RGA.js";
|
|
4
15
|
import {
|
|
5
16
|
writeOAuthCredential
|
|
6
17
|
} from "./chunk-JZPBVBIY.js";
|
|
@@ -153,28 +164,9 @@ function generatePkcePair(length = PKCE_VERIFIER_LENGTH) {
|
|
|
153
164
|
}
|
|
154
165
|
|
|
155
166
|
// packages/cli/oauth/flows/antigravity.ts
|
|
156
|
-
var decode = (s) => Buffer.from(s, "base64").toString("utf8");
|
|
157
|
-
var CLIENT_ID = decode(
|
|
158
|
-
"MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ=="
|
|
159
|
-
);
|
|
160
|
-
var CLIENT_SECRET = decode("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=");
|
|
161
|
-
var CALLBACK_PORT = 51121;
|
|
162
|
-
var CALLBACK_PATH = "/oauth-callback";
|
|
163
|
-
var SCOPES = [
|
|
164
|
-
"https://www.googleapis.com/auth/cloud-platform",
|
|
165
|
-
"https://www.googleapis.com/auth/userinfo.email",
|
|
166
|
-
"https://www.googleapis.com/auth/userinfo.profile",
|
|
167
|
-
"https://www.googleapis.com/auth/cclog",
|
|
168
|
-
"https://www.googleapis.com/auth/experimentsandconfigs"
|
|
169
|
-
];
|
|
170
|
-
var AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
171
|
-
var TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
172
|
-
var USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json";
|
|
173
|
-
var CLOUD_CODE_ENDPOINT = "https://daily-cloudcode-pa.googleapis.com";
|
|
174
167
|
var TIER_LEGACY = "legacy-tier";
|
|
175
168
|
var PROJECT_ONBOARD_MAX_ATTEMPTS = 5;
|
|
176
169
|
var PROJECT_ONBOARD_INTERVAL_MS = 2e3;
|
|
177
|
-
var ACCESS_TOKEN_CLIENT_SKEW_MS = 5 * 60 * 1e3;
|
|
178
170
|
var ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA = Object.freeze({
|
|
179
171
|
ideType: "ANTIGRAVITY",
|
|
180
172
|
platform: "PLATFORM_UNSPECIFIED",
|
|
@@ -251,7 +243,7 @@ async function discoverProject(fetchImpl, accessToken, onProgress) {
|
|
|
251
243
|
"User-Agent": getAntigravityUserAgent()
|
|
252
244
|
};
|
|
253
245
|
onProgress?.("Checking for existing project...");
|
|
254
|
-
const endpoint =
|
|
246
|
+
const endpoint = ANTIGRAVITY_CLOUD_CODE_BASE_URL;
|
|
255
247
|
const loadResponse = await fetchImpl(`${endpoint}/v1internal:loadCodeAssist`, {
|
|
256
248
|
method: "POST",
|
|
257
249
|
headers,
|
|
@@ -286,7 +278,7 @@ async function discoverProject(fetchImpl, accessToken, onProgress) {
|
|
|
286
278
|
}
|
|
287
279
|
async function getUserEmail(fetchImpl, accessToken) {
|
|
288
280
|
try {
|
|
289
|
-
const response = await fetchImpl(
|
|
281
|
+
const response = await fetchImpl(ANTIGRAVITY_USERINFO_URL, {
|
|
290
282
|
headers: { Authorization: `Bearer ${accessToken}` }
|
|
291
283
|
});
|
|
292
284
|
if (!response.ok) return void 0;
|
|
@@ -305,32 +297,32 @@ async function runAntigravityOAuthLogin(deps = {}) {
|
|
|
305
297
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
306
298
|
const output = deps.output ?? console;
|
|
307
299
|
const error = deps.error ?? console;
|
|
308
|
-
const redirectUri = `http://127.0.0.1:${
|
|
300
|
+
const redirectUri = `http://127.0.0.1:${ANTIGRAVITY_CALLBACK_PORT}${ANTIGRAVITY_CALLBACK_PATH}`;
|
|
309
301
|
let handle;
|
|
310
302
|
try {
|
|
311
303
|
handle = await startCallbackServer({
|
|
312
|
-
port:
|
|
304
|
+
port: ANTIGRAVITY_CALLBACK_PORT,
|
|
313
305
|
hostname: "127.0.0.1",
|
|
314
306
|
timeoutMs: 5 * 6e4
|
|
315
307
|
});
|
|
316
308
|
} catch (err) {
|
|
317
309
|
throw new Error(
|
|
318
|
-
`Failed to start Antigravity OAuth callback server on ${
|
|
310
|
+
`Failed to start Antigravity OAuth callback server on ${ANTIGRAVITY_CALLBACK_PORT}: ${toErrorMessage(err)}`
|
|
319
311
|
);
|
|
320
312
|
}
|
|
321
313
|
try {
|
|
322
314
|
const state = generateState();
|
|
323
315
|
const pkce = await generatePkcePair();
|
|
324
316
|
const authParams = new URLSearchParams({
|
|
325
|
-
client_id:
|
|
317
|
+
client_id: ANTIGRAVITY_CLIENT_ID,
|
|
326
318
|
response_type: "code",
|
|
327
319
|
redirect_uri: redirectUri,
|
|
328
|
-
scope:
|
|
320
|
+
scope: ANTIGRAVITY_SCOPES.join(" "),
|
|
329
321
|
state,
|
|
330
322
|
access_type: "offline",
|
|
331
323
|
prompt: "consent"
|
|
332
324
|
});
|
|
333
|
-
const authUrl = `${
|
|
325
|
+
const authUrl = `${ANTIGRAVITY_AUTH_URL}?${authParams.toString()}`;
|
|
334
326
|
output.log(
|
|
335
327
|
`Open the following URL in your browser to log in to Antigravity:
|
|
336
328
|
${authUrl}`
|
|
@@ -345,15 +337,16 @@ async function runAntigravityOAuthLogin(deps = {}) {
|
|
|
345
337
|
}
|
|
346
338
|
}
|
|
347
339
|
const callback = await handle.waitForCode();
|
|
348
|
-
const tokenResponse = await fetchImpl(
|
|
340
|
+
const tokenResponse = await fetchImpl(ANTIGRAVITY_TOKEN_URL, {
|
|
349
341
|
method: "POST",
|
|
350
342
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
351
343
|
body: new URLSearchParams({
|
|
352
|
-
client_id:
|
|
353
|
-
client_secret:
|
|
344
|
+
client_id: ANTIGRAVITY_CLIENT_ID,
|
|
345
|
+
client_secret: ANTIGRAVITY_CLIENT_SECRET,
|
|
354
346
|
code: callback.code,
|
|
355
347
|
grant_type: "authorization_code",
|
|
356
|
-
redirect_uri: redirectUri
|
|
348
|
+
redirect_uri: redirectUri,
|
|
349
|
+
code_verifier: pkce.verifier
|
|
357
350
|
})
|
|
358
351
|
});
|
|
359
352
|
if (!tokenResponse.ok) {
|
|
@@ -361,66 +354,43 @@ async function runAntigravityOAuthLogin(deps = {}) {
|
|
|
361
354
|
throw new Error(`Antigravity token exchange failed: ${body}`);
|
|
362
355
|
}
|
|
363
356
|
const tokenData = await tokenResponse.json();
|
|
364
|
-
if (!tokenData.refresh_token) {
|
|
357
|
+
if (typeof tokenData.refresh_token !== "string" || !tokenData.refresh_token.trim()) {
|
|
365
358
|
throw new Error("No refresh token received. Please try again.");
|
|
366
359
|
}
|
|
367
|
-
|
|
360
|
+
if (typeof tokenData.access_token !== "string" || !tokenData.access_token.trim()) {
|
|
361
|
+
throw new Error("No access token received. Please try again.");
|
|
362
|
+
}
|
|
363
|
+
const email = await getUserEmail(fetchImpl, tokenData.access_token.trim());
|
|
368
364
|
const projectId = await discoverProject(
|
|
369
365
|
fetchImpl,
|
|
370
|
-
tokenData.access_token,
|
|
366
|
+
tokenData.access_token.trim(),
|
|
371
367
|
(message) => output.log(message)
|
|
372
368
|
);
|
|
373
369
|
const now = (deps.now ?? Date.now)();
|
|
374
|
-
return {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
};
|
|
370
|
+
return normalizeAntigravityTokenPayload({
|
|
371
|
+
payload: tokenData,
|
|
372
|
+
baseCredential: {
|
|
373
|
+
provider: "antigravity",
|
|
374
|
+
accountId: email,
|
|
375
|
+
metadata: {
|
|
376
|
+
projectId,
|
|
377
|
+
email
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
now
|
|
381
|
+
});
|
|
386
382
|
} finally {
|
|
387
383
|
await handle.close();
|
|
388
384
|
}
|
|
389
385
|
}
|
|
390
|
-
async function refreshAntigravityOAuthToken(credential) {
|
|
391
|
-
|
|
392
|
-
throw new Error("Antigravity credential has no refresh_token");
|
|
393
|
-
}
|
|
394
|
-
const fetchImpl = fetch;
|
|
395
|
-
const projectId = credential.metadata?.projectId ?? void 0;
|
|
396
|
-
const response = await fetchImpl(TOKEN_URL, {
|
|
397
|
-
method: "POST",
|
|
398
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
399
|
-
body: new URLSearchParams({
|
|
400
|
-
client_id: CLIENT_ID,
|
|
401
|
-
client_secret: CLIENT_SECRET,
|
|
402
|
-
refresh_token: credential.refreshToken,
|
|
403
|
-
grant_type: "refresh_token"
|
|
404
|
-
})
|
|
405
|
-
});
|
|
406
|
-
if (!response.ok) {
|
|
407
|
-
const body = await response.text();
|
|
408
|
-
throw new Error(`Antigravity token refresh failed: ${body}`);
|
|
409
|
-
}
|
|
410
|
-
const data = await response.json();
|
|
411
|
-
const now = Date.now();
|
|
412
|
-
return {
|
|
413
|
-
...credential,
|
|
414
|
-
accessToken: data.access_token,
|
|
415
|
-
refreshToken: data.refresh_token || credential.refreshToken,
|
|
416
|
-
expiresAt: now + data.expires_in * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS,
|
|
417
|
-
scope: data.scope ?? credential.scope,
|
|
418
|
-
obtainedAt: now
|
|
419
|
-
};
|
|
386
|
+
async function refreshAntigravityOAuthToken(credential, deps = {}) {
|
|
387
|
+
return refreshAntigravityToken(credential, deps);
|
|
420
388
|
}
|
|
421
389
|
|
|
422
390
|
// packages/cli/oauth/flows/openai-codex.ts
|
|
423
391
|
import { createHash as createHash2 } from "node:crypto";
|
|
392
|
+
|
|
393
|
+
// packages/agent-runtime/openaiCodexOAuth.ts
|
|
424
394
|
var OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
425
395
|
var OPENAI_CODEX_SCOPES = [
|
|
426
396
|
"openid",
|
|
@@ -440,8 +410,104 @@ var OPENAI_CODEX_ORIGINATOR = "codex_cli_rs";
|
|
|
440
410
|
var OPENAI_CODEX_CALLBACK_PORT = 1455;
|
|
441
411
|
var OPENAI_CODEX_CALLBACK_PATH = "/auth/callback";
|
|
442
412
|
var OPENAI_CODEX_CALLBACK_URL = `http://localhost:${OPENAI_CODEX_CALLBACK_PORT}${OPENAI_CODEX_CALLBACK_PATH}`;
|
|
413
|
+
var OPENAI_CODEX_ACCESS_TOKEN_CLIENT_SKEW_MS = 5 * 60 * 1e3;
|
|
414
|
+
var OPENAI_CODEX_TOKEN_REQUEST_TIMEOUT_MS = 3e4;
|
|
415
|
+
function decodeOpenAiIdToken(idToken) {
|
|
416
|
+
if (!idToken || typeof idToken !== "string") return {};
|
|
417
|
+
const parts = idToken.split(".");
|
|
418
|
+
if (parts.length < 2) return {};
|
|
419
|
+
try {
|
|
420
|
+
const payload = JSON.parse(
|
|
421
|
+
Buffer.from(parts[1], "base64url").toString("utf-8")
|
|
422
|
+
);
|
|
423
|
+
const authClaim = payload["https://api.openai.com/auth"];
|
|
424
|
+
const nestedAccountId = authClaim && typeof authClaim === "object" ? authClaim.chatgpt_account_id : void 0;
|
|
425
|
+
const accountId = nestedAccountId ?? payload["chatgpt_account_id"] ?? payload["account_id"];
|
|
426
|
+
const email = payload["email"];
|
|
427
|
+
return {
|
|
428
|
+
...typeof accountId === "string" && accountId.trim() ? { accountId: accountId.trim() } : {},
|
|
429
|
+
...typeof email === "string" && email.trim() ? { email: email.trim() } : {}
|
|
430
|
+
};
|
|
431
|
+
} catch {
|
|
432
|
+
return {};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function normalizeOpenAiCodexTokenPayload(args) {
|
|
436
|
+
const { payload, baseCredential, now = Date.now() } = args;
|
|
437
|
+
if (typeof payload.access_token !== "string" || !payload.access_token.trim()) {
|
|
438
|
+
throw new Error("OpenAI Codex token response missing access_token");
|
|
439
|
+
}
|
|
440
|
+
const accessToken = payload.access_token.trim();
|
|
441
|
+
const expiresIn = asOptionalFiniteNumber(payload.expires_in);
|
|
442
|
+
const clientSkewMs = args.clientSkewMs ?? OPENAI_CODEX_ACCESS_TOKEN_CLIENT_SKEW_MS;
|
|
443
|
+
const expiresAt = expiresIn !== void 0 ? now + expiresIn * 1e3 - clientSkewMs : baseCredential?.expiresAt;
|
|
444
|
+
const idTokenStr = typeof payload.id_token === "string" && payload.id_token.trim() ? payload.id_token.trim() : void 0;
|
|
445
|
+
const fromAccess = decodeOpenAiIdToken(accessToken);
|
|
446
|
+
const fromId = idTokenStr ? decodeOpenAiIdToken(idTokenStr) : {};
|
|
447
|
+
const accountId = fromAccess.accountId ?? fromId.accountId ?? baseCredential?.accountId;
|
|
448
|
+
const email = fromAccess.email ?? fromId.email ?? baseCredential?.metadata?.email;
|
|
449
|
+
const refreshToken = typeof payload.refresh_token === "string" && payload.refresh_token.trim() ? payload.refresh_token.trim() : baseCredential?.refreshToken;
|
|
450
|
+
const scope = typeof payload.scope === "string" && payload.scope.trim() ? payload.scope.trim() : baseCredential?.scope;
|
|
451
|
+
const metadata = {
|
|
452
|
+
...baseCredential?.metadata ?? {},
|
|
453
|
+
...email ? { email } : {}
|
|
454
|
+
};
|
|
455
|
+
return {
|
|
456
|
+
...baseCredential ?? {},
|
|
457
|
+
provider: "chatgpt",
|
|
458
|
+
accessToken,
|
|
459
|
+
...refreshToken ? { refreshToken } : {},
|
|
460
|
+
...expiresAt !== void 0 ? { expiresAt } : {},
|
|
461
|
+
obtainedAt: now,
|
|
462
|
+
...scope ? { scope } : {},
|
|
463
|
+
...accountId ? { accountId } : {},
|
|
464
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
async function refreshOpenAiCodexToken(credential, deps = {}) {
|
|
468
|
+
if (!credential.refreshToken) {
|
|
469
|
+
throw new Error("Cannot refresh OpenAI Codex token without a refresh token.");
|
|
470
|
+
}
|
|
471
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
472
|
+
const body = new URLSearchParams({
|
|
473
|
+
client_id: OPENAI_CODEX_CLIENT_ID,
|
|
474
|
+
grant_type: "refresh_token",
|
|
475
|
+
refresh_token: credential.refreshToken
|
|
476
|
+
});
|
|
477
|
+
let response;
|
|
478
|
+
try {
|
|
479
|
+
response = await fetchImpl(OPENAI_CODEX_TOKEN_URL, {
|
|
480
|
+
method: "POST",
|
|
481
|
+
headers: {
|
|
482
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
483
|
+
Accept: "application/json"
|
|
484
|
+
},
|
|
485
|
+
body: body.toString(),
|
|
486
|
+
signal: AbortSignal.timeout(OPENAI_CODEX_TOKEN_REQUEST_TIMEOUT_MS)
|
|
487
|
+
});
|
|
488
|
+
} catch (error) {
|
|
489
|
+
throw new Error(
|
|
490
|
+
`OpenAI Codex token refresh failed: ${toErrorMessage(error)}`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
const payload = await response.json().catch(() => ({}));
|
|
494
|
+
if (!response.ok) {
|
|
495
|
+
const detail = typeof payload.error_description === "string" && payload.error_description ? payload.error_description : typeof payload.error === "string" && payload.error ? payload.error : `HTTP ${response.status}`;
|
|
496
|
+
throw new Error(`OpenAI Codex token refresh failed: ${detail}`);
|
|
497
|
+
}
|
|
498
|
+
if (typeof payload.access_token !== "string" || !payload.access_token.trim()) {
|
|
499
|
+
throw new Error("OpenAI Codex token refresh response missing access_token");
|
|
500
|
+
}
|
|
501
|
+
const now = deps.now?.() ?? Date.now();
|
|
502
|
+
return normalizeOpenAiCodexTokenPayload({
|
|
503
|
+
payload,
|
|
504
|
+
baseCredential: credential,
|
|
505
|
+
now
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// packages/cli/oauth/flows/openai-codex.ts
|
|
443
510
|
var AUTHORIZATION_CODE_GRANT_TYPE = "authorization_code";
|
|
444
|
-
var REFRESH_TOKEN_GRANT_TYPE = "refresh_token";
|
|
445
511
|
var DEVICE_CODE_DEFAULT_INTERVAL_MS = 5e3;
|
|
446
512
|
var DEVICE_POLL_SAFETY_MARGIN_MS = 3e3;
|
|
447
513
|
var DEVICE_MAX_POLLS = 120;
|
|
@@ -470,76 +536,14 @@ async function postForm(fetchImpl, url, body, deps) {
|
|
|
470
536
|
return response;
|
|
471
537
|
}
|
|
472
538
|
async function parseJsonResponse(response) {
|
|
473
|
-
const text = await response.text().catch(() => "");
|
|
474
|
-
if (!text) return {};
|
|
475
539
|
try {
|
|
476
|
-
return
|
|
540
|
+
return await response.json();
|
|
477
541
|
} catch {
|
|
478
542
|
return {};
|
|
479
543
|
}
|
|
480
544
|
}
|
|
481
|
-
function scopeString(
|
|
482
|
-
return
|
|
483
|
-
}
|
|
484
|
-
function base64UrlDecode(value) {
|
|
485
|
-
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
486
|
-
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
487
|
-
return Buffer.from(padded, "base64").toString("utf8");
|
|
488
|
-
}
|
|
489
|
-
var OPENAI_AUTH_CLAIM = "https://api.openai.com/auth";
|
|
490
|
-
var OPENAI_PROFILE_CLAIM = "https://api.openai.com/profile";
|
|
491
|
-
function readClaimString(claim, key) {
|
|
492
|
-
if (!claim || typeof claim !== "object") return void 0;
|
|
493
|
-
const value = claim[key];
|
|
494
|
-
return typeof value === "string" && value ? value : void 0;
|
|
495
|
-
}
|
|
496
|
-
function decodeOpenAiIdToken(idToken) {
|
|
497
|
-
if (!idToken) return {};
|
|
498
|
-
const parts = idToken.split(".");
|
|
499
|
-
if (parts.length < 2) return {};
|
|
500
|
-
try {
|
|
501
|
-
const payload = JSON.parse(base64UrlDecode(parts[1]));
|
|
502
|
-
const authClaim = payload[OPENAI_AUTH_CLAIM];
|
|
503
|
-
const profileClaim = payload[OPENAI_PROFILE_CLAIM];
|
|
504
|
-
return {
|
|
505
|
-
chatgptAccountId: readClaimString(authClaim, "chatgpt_account_id") ?? (typeof payload["chatgpt_account_id"] === "string" ? payload["chatgpt_account_id"] : void 0),
|
|
506
|
-
email: readClaimString(profileClaim, "email") ?? (typeof payload["email"] === "string" ? payload["email"] : void 0),
|
|
507
|
-
payload
|
|
508
|
-
};
|
|
509
|
-
} catch {
|
|
510
|
-
return {};
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
function tokenResponseToCredential(token, now) {
|
|
514
|
-
const fromAccess = decodeOpenAiIdToken(token.accessToken);
|
|
515
|
-
const fromId = decodeOpenAiIdToken(token.idToken);
|
|
516
|
-
const decoded = {
|
|
517
|
-
chatgptAccountId: fromAccess.chatgptAccountId ?? fromId.chatgptAccountId,
|
|
518
|
-
email: fromAccess.email ?? fromId.email
|
|
519
|
-
};
|
|
520
|
-
const expiresAt = typeof token.expiresIn === "number" && token.expiresIn > 0 ? now + token.expiresIn * 1e3 : void 0;
|
|
521
|
-
return {
|
|
522
|
-
provider: "chatgpt",
|
|
523
|
-
accessToken: token.accessToken,
|
|
524
|
-
...token.refreshToken ? { refreshToken: token.refreshToken } : {},
|
|
525
|
-
...expiresAt ? { expiresAt } : {},
|
|
526
|
-
...token.scope ? { scope: token.scope } : {},
|
|
527
|
-
...token.idToken ? { idToken: token.idToken } : {},
|
|
528
|
-
...decoded.chatgptAccountId ? { accountId: decoded.chatgptAccountId } : {},
|
|
529
|
-
obtainedAt: now
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
function extractTokenResponse(data) {
|
|
533
|
-
const accessToken = data["access_token"];
|
|
534
|
-
if (typeof accessToken !== "string" || !accessToken) return null;
|
|
535
|
-
const expiresInRaw = data["expires_in"];
|
|
536
|
-
return {
|
|
537
|
-
accessToken,
|
|
538
|
-
...typeof data["refresh_token"] === "string" ? { refreshToken: data["refresh_token"] } : {},
|
|
539
|
-
...typeof expiresInRaw === "number" ? { expiresIn: expiresInRaw } : {},
|
|
540
|
-
...typeof data["scope"] === "string" ? { scope: data["scope"] } : {},
|
|
541
|
-
...typeof data["id_token"] === "string" ? { idToken: data["id_token"] } : {}
|
|
542
|
-
};
|
|
545
|
+
function scopeString() {
|
|
546
|
+
return OPENAI_CODEX_SCOPES.join(" ");
|
|
543
547
|
}
|
|
544
548
|
async function startDeviceCodeFlow(deps) {
|
|
545
549
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
@@ -574,11 +578,11 @@ async function startDeviceCodeFlow(deps) {
|
|
|
574
578
|
}
|
|
575
579
|
async function pollDeviceCodeToken(args) {
|
|
576
580
|
const fetchImpl = args.deps?.fetchImpl ?? fetch;
|
|
577
|
-
const
|
|
581
|
+
const sleep2 = args.sleep ?? args.deps?.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
578
582
|
const intervalMs = args.intervalMs ?? DEVICE_CODE_DEFAULT_INTERVAL_MS;
|
|
579
583
|
const maxPolls = args.maxPolls ?? DEVICE_MAX_POLLS;
|
|
580
584
|
for (let poll = 0; poll < maxPolls; poll++) {
|
|
581
|
-
await
|
|
585
|
+
await sleep2(poll === 0 ? Math.min(intervalMs, DEVICE_CODE_DEFAULT_INTERVAL_MS) : intervalMs);
|
|
582
586
|
const response = await postJson(
|
|
583
587
|
fetchImpl,
|
|
584
588
|
OPENAI_CODEX_DEVICE_TOKEN_URL,
|
|
@@ -644,7 +648,7 @@ async function runOpenAiCodexDeviceCode(deps = {}) {
|
|
|
644
648
|
}
|
|
645
649
|
async function exchangeCodexAuthorizationCode(args) {
|
|
646
650
|
const fetchImpl = args.deps?.fetchImpl ?? fetch;
|
|
647
|
-
const now = args.now ?? Date.now;
|
|
651
|
+
const now = (args.now ?? Date.now)();
|
|
648
652
|
const response = await postForm(
|
|
649
653
|
fetchImpl,
|
|
650
654
|
OPENAI_CODEX_TOKEN_URL,
|
|
@@ -663,11 +667,10 @@ async function exchangeCodexAuthorizationCode(args) {
|
|
|
663
667
|
`OpenAI authorization-code exchange failed (HTTP ${response.status}): ${JSON.stringify(data)}`
|
|
664
668
|
);
|
|
665
669
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
}
|
|
670
|
-
return tokenResponseToCredential(token, now());
|
|
670
|
+
return normalizeOpenAiCodexTokenPayload({
|
|
671
|
+
payload: data,
|
|
672
|
+
now
|
|
673
|
+
});
|
|
671
674
|
}
|
|
672
675
|
async function exchangeAuthorizationCode(args) {
|
|
673
676
|
return exchangeCodexAuthorizationCode({
|
|
@@ -726,42 +729,14 @@ async function runOpenAiCodexBrowserPkce(deps = {}) {
|
|
|
726
729
|
});
|
|
727
730
|
}
|
|
728
731
|
}
|
|
729
|
-
var
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
const now = deps.now ?? Date.now;
|
|
735
|
-
const response = await postForm(
|
|
736
|
-
fetchImpl,
|
|
737
|
-
OPENAI_CODEX_TOKEN_URL,
|
|
738
|
-
{
|
|
739
|
-
grant_type: REFRESH_TOKEN_GRANT_TYPE,
|
|
740
|
-
refresh_token: credential.refreshToken,
|
|
741
|
-
client_id: OPENAI_CODEX_CLIENT_ID,
|
|
742
|
-
scope: scopeString()
|
|
743
|
-
},
|
|
744
|
-
deps
|
|
745
|
-
);
|
|
746
|
-
const data = await parseJsonResponse(response);
|
|
747
|
-
if (!response.ok) {
|
|
748
|
-
throw new Error(
|
|
749
|
-
`OpenAI token refresh failed (HTTP ${response.status}): ${JSON.stringify(data)}`
|
|
750
|
-
);
|
|
751
|
-
}
|
|
752
|
-
const token = extractTokenResponse(data);
|
|
753
|
-
if (!token) {
|
|
754
|
-
throw new Error(`OpenAI token refresh returned no token: ${JSON.stringify(data)}`);
|
|
755
|
-
}
|
|
756
|
-
const refreshed = tokenResponseToCredential(token, now());
|
|
757
|
-
return {
|
|
758
|
-
...refreshed,
|
|
759
|
-
refreshToken: refreshed.refreshToken ?? credential.refreshToken,
|
|
760
|
-
accountId: refreshed.accountId ?? credential.accountId
|
|
761
|
-
};
|
|
732
|
+
var refreshOpenAiCodexToken2 = async (credential, deps = {}) => {
|
|
733
|
+
return refreshOpenAiCodexToken(credential, {
|
|
734
|
+
fetchImpl: deps.fetchImpl,
|
|
735
|
+
now: deps.now
|
|
736
|
+
});
|
|
762
737
|
};
|
|
763
738
|
|
|
764
|
-
// packages/
|
|
739
|
+
// packages/agent-runtime/xaiOAuth.ts
|
|
765
740
|
var XAI_OAUTH_ISSUER = "https://auth.x.ai";
|
|
766
741
|
var XAI_OAUTH_DISCOVERY_URL = `${XAI_OAUTH_ISSUER}/.well-known/openid-configuration`;
|
|
767
742
|
var XAI_OAUTH_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
|
|
@@ -769,15 +744,16 @@ var XAI_OAUTH_SCOPE = "openid profile email offline_access grok-cli:access api:a
|
|
|
769
744
|
var XAI_OAUTH_REDIRECT_HOST = "127.0.0.1";
|
|
770
745
|
var XAI_OAUTH_REDIRECT_PORT = 56121;
|
|
771
746
|
var XAI_OAUTH_REDIRECT_PATH = "/callback";
|
|
747
|
+
var XAI_OAUTH_REDIRECT_URI = `http://${XAI_OAUTH_REDIRECT_HOST}:${XAI_OAUTH_REDIRECT_PORT}${XAI_OAUTH_REDIRECT_PATH}`;
|
|
772
748
|
var XAI_OAUTH_DOCS_URL = "https://hermes-agent.nousresearch.com/docs/guides/xai-grok-oauth";
|
|
773
749
|
var XAI_DEVICE_CODE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
774
750
|
var XAI_DEVICE_CODE_DEFAULT_INTERVAL_MS = 5e3;
|
|
775
751
|
var XAI_DEVICE_CODE_MIN_INTERVAL_MS = 1e3;
|
|
776
752
|
var XAI_DEVICE_CODE_SLOW_DOWN_INCREMENT_MS = 5e3;
|
|
777
753
|
var XAI_DEVICE_CODE_DEFAULT_EXPIRES_MS = 5 * 6e4;
|
|
778
|
-
var
|
|
779
|
-
var
|
|
780
|
-
var
|
|
754
|
+
var XAI_ACCESS_TOKEN_CLIENT_SKEW_MS = 5 * 60 * 1e3;
|
|
755
|
+
var XAI_DISCOVERY_TIMEOUT_MS = 15e3;
|
|
756
|
+
var XAI_TOKEN_REQUEST_TIMEOUT_MS = 2e4;
|
|
781
757
|
function validateXAIEndpoint(url, field) {
|
|
782
758
|
let parsed;
|
|
783
759
|
try {
|
|
@@ -835,8 +811,8 @@ async function readJsonBody(response, context) {
|
|
|
835
811
|
);
|
|
836
812
|
}
|
|
837
813
|
}
|
|
838
|
-
async function xaiOAuthDiscovery(fetchImpl, options = {}) {
|
|
839
|
-
const timeoutMs = options.timeoutMs ??
|
|
814
|
+
async function xaiOAuthDiscovery(fetchImpl = fetch, options = {}) {
|
|
815
|
+
const timeoutMs = options.timeoutMs ?? XAI_DISCOVERY_TIMEOUT_MS;
|
|
840
816
|
let response;
|
|
841
817
|
try {
|
|
842
818
|
response = await fetchImpl(XAI_OAUTH_DISCOVERY_URL, {
|
|
@@ -850,6 +826,14 @@ async function xaiOAuthDiscovery(fetchImpl, options = {}) {
|
|
|
850
826
|
);
|
|
851
827
|
}
|
|
852
828
|
if (response.status !== 200) {
|
|
829
|
+
if (isLikelyHtmlOrCloudflareChallenge(
|
|
830
|
+
response,
|
|
831
|
+
await response.clone().text().catch(() => "")
|
|
832
|
+
)) {
|
|
833
|
+
throw new Error(
|
|
834
|
+
`xAI OIDC discovery failed (${response.status}): xAI returned an HTML/Cloudflare challenge instead of OAuth JSON`
|
|
835
|
+
);
|
|
836
|
+
}
|
|
853
837
|
throw new Error(`xAI OIDC discovery returned status ${response.status}.`);
|
|
854
838
|
}
|
|
855
839
|
const obj = await readJsonBody(response, "xAI OIDC discovery");
|
|
@@ -879,72 +863,53 @@ async function xaiOAuthDiscovery(fetchImpl, options = {}) {
|
|
|
879
863
|
...deviceAuthorizationEndpoint ? { device_authorization_endpoint: deviceAuthorizationEndpoint } : {}
|
|
880
864
|
};
|
|
881
865
|
}
|
|
882
|
-
function
|
|
883
|
-
const
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
plan: "generic",
|
|
893
|
-
referrer: "nolo-cli"
|
|
894
|
-
});
|
|
895
|
-
return `${opts.authorizationEndpoint}?${params.toString()}`;
|
|
896
|
-
}
|
|
897
|
-
function generateState2() {
|
|
898
|
-
const bytes = new Uint8Array(16);
|
|
899
|
-
crypto.getRandomValues(bytes);
|
|
900
|
-
return Array.from(bytes).map((value) => value.toString(16).padStart(2, "0")).join("");
|
|
901
|
-
}
|
|
902
|
-
function parsePositiveSecondsToMs(value, fallbackMs) {
|
|
903
|
-
const sec = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : NaN;
|
|
904
|
-
if (!Number.isFinite(sec) || sec <= 0) return fallbackMs;
|
|
905
|
-
return Math.max(1, Math.floor(sec * 1e3));
|
|
906
|
-
}
|
|
907
|
-
function parseOAuthTokenPayload(data, context, options = {}) {
|
|
908
|
-
if (typeof data.access_token !== "string" || !data.access_token) {
|
|
866
|
+
function normalizeXaiTokenPayload(args) {
|
|
867
|
+
const {
|
|
868
|
+
payload,
|
|
869
|
+
baseCredential,
|
|
870
|
+
now = Date.now(),
|
|
871
|
+
requireRefreshToken = false,
|
|
872
|
+
context = "xAI OAuth token response"
|
|
873
|
+
} = args;
|
|
874
|
+
const accessToken = typeof payload.access_token === "string" ? payload.access_token.trim() : "";
|
|
875
|
+
if (!accessToken) {
|
|
909
876
|
throw new Error(`${context} missing access_token`);
|
|
910
877
|
}
|
|
911
|
-
const
|
|
912
|
-
if (
|
|
878
|
+
const rawRefreshToken = typeof payload.refresh_token === "string" && payload.refresh_token.trim() ? payload.refresh_token.trim() : void 0;
|
|
879
|
+
if (requireRefreshToken && !rawRefreshToken) {
|
|
913
880
|
throw new Error(`${context} missing refresh_token`);
|
|
914
881
|
}
|
|
915
|
-
const expiresIn = asOptionalFiniteNumber(
|
|
882
|
+
const expiresIn = asOptionalFiniteNumber(payload.expires_in);
|
|
916
883
|
if (expiresIn === void 0) {
|
|
917
884
|
throw new Error(`${context} missing expires_in`);
|
|
918
885
|
}
|
|
886
|
+
const refreshToken = rawRefreshToken ?? baseCredential?.refreshToken;
|
|
887
|
+
const scope = typeof payload.scope === "string" && payload.scope.trim() ? payload.scope.trim() : baseCredential?.scope;
|
|
888
|
+
const idToken = typeof payload.id_token === "string" && payload.id_token.trim() ? payload.id_token.trim() : baseCredential?.idToken;
|
|
889
|
+
const accountId = typeof payload.account_id === "string" && payload.account_id ? payload.account_id : baseCredential?.accountId;
|
|
919
890
|
return {
|
|
920
|
-
|
|
921
|
-
...refreshToken ? { refreshToken } : {},
|
|
922
|
-
expiresIn,
|
|
923
|
-
scope: typeof data.scope === "string" ? data.scope : void 0,
|
|
924
|
-
idToken: typeof data.id_token === "string" ? data.id_token : void 0
|
|
925
|
-
};
|
|
926
|
-
}
|
|
927
|
-
function tokenToCredential(token, now) {
|
|
928
|
-
const expiresIn = token.expiresIn;
|
|
929
|
-
return {
|
|
891
|
+
...baseCredential,
|
|
930
892
|
provider: "xai",
|
|
931
|
-
accessToken
|
|
932
|
-
refreshToken:
|
|
933
|
-
expiresAt: now + expiresIn * 1e3 -
|
|
934
|
-
scope:
|
|
935
|
-
idToken:
|
|
893
|
+
accessToken,
|
|
894
|
+
...refreshToken ? { refreshToken } : {},
|
|
895
|
+
expiresAt: now + expiresIn * 1e3 - XAI_ACCESS_TOKEN_CLIENT_SKEW_MS,
|
|
896
|
+
...scope ? { scope } : {},
|
|
897
|
+
...idToken ? { idToken } : {},
|
|
898
|
+
...accountId ? { accountId } : {},
|
|
936
899
|
obtainedAt: now
|
|
937
900
|
};
|
|
938
901
|
}
|
|
939
|
-
async function
|
|
902
|
+
async function refreshXaiToken(credential, deps = {}) {
|
|
903
|
+
if (!credential.refreshToken) {
|
|
904
|
+
throw new Error("xAI credential has no refresh_token");
|
|
905
|
+
}
|
|
906
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
940
907
|
const discovery = await xaiOAuthDiscovery(fetchImpl);
|
|
941
908
|
const tokenEndpoint = validateXAIEndpoint(discovery.token_endpoint, "token_endpoint");
|
|
942
909
|
const body = new URLSearchParams({
|
|
943
|
-
grant_type: "
|
|
910
|
+
grant_type: "refresh_token",
|
|
944
911
|
client_id: XAI_OAUTH_CLIENT_ID,
|
|
945
|
-
|
|
946
|
-
redirect_uri: redirectUri,
|
|
947
|
-
code_verifier: verifier
|
|
912
|
+
refresh_token: credential.refreshToken
|
|
948
913
|
});
|
|
949
914
|
let response;
|
|
950
915
|
try {
|
|
@@ -955,448 +920,175 @@ async function exchangeXAIToken(fetchImpl, code, redirectUri, verifier) {
|
|
|
955
920
|
Accept: "application/json"
|
|
956
921
|
},
|
|
957
922
|
body,
|
|
958
|
-
signal: AbortSignal.timeout(
|
|
923
|
+
signal: AbortSignal.timeout(XAI_TOKEN_REQUEST_TIMEOUT_MS)
|
|
959
924
|
});
|
|
960
925
|
} catch (error) {
|
|
961
926
|
throw new Error(
|
|
962
|
-
`xAI token
|
|
927
|
+
`xAI token refresh failed: ${toErrorMessage(error)}`
|
|
963
928
|
);
|
|
964
929
|
}
|
|
965
|
-
const data = await readJsonBody(response, "xAI token
|
|
930
|
+
const data = await readJsonBody(response, "xAI token refresh");
|
|
966
931
|
if (!response.ok) {
|
|
967
932
|
const detail = typeof data.error_description === "string" && data.error_description || typeof data.error === "string" && data.error || "";
|
|
968
933
|
throw new Error(
|
|
969
|
-
`xAI token
|
|
934
|
+
`xAI token refresh failed: ${response.status}${detail ? ` ${detail}` : ""}`
|
|
970
935
|
);
|
|
971
936
|
}
|
|
972
|
-
|
|
973
|
-
|
|
937
|
+
const now = deps.now?.() ?? Date.now();
|
|
938
|
+
return normalizeXaiTokenPayload({
|
|
939
|
+
payload: data,
|
|
940
|
+
baseCredential: credential,
|
|
941
|
+
now,
|
|
942
|
+
context: "xAI token refresh response"
|
|
974
943
|
});
|
|
975
944
|
}
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
);
|
|
1013
|
-
if (!deviceCode || !userCode || !verificationUri) {
|
|
1014
|
-
throw new Error(
|
|
1015
|
-
"xAI device code response is missing device_code, user_code, or verification_uri"
|
|
1016
|
-
);
|
|
1017
|
-
}
|
|
1018
|
-
validateXAIEndpoint(verificationUri, "verification_uri");
|
|
1019
|
-
if (verificationUriComplete) {
|
|
1020
|
-
validateXAIEndpoint(verificationUriComplete, "verification_uri_complete");
|
|
1021
|
-
}
|
|
945
|
+
var xaiRefresh = refreshXaiToken;
|
|
946
|
+
|
|
947
|
+
// packages/agent-runtime/anthropicOAuth.ts
|
|
948
|
+
var ANTHROPIC_OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
949
|
+
var ANTHROPIC_OAUTH_AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
|
|
950
|
+
var ANTHROPIC_OAUTH_TOKEN_URL = "https://api.anthropic.com/v1/oauth/token";
|
|
951
|
+
var ANTHROPIC_OAUTH_CALLBACK_PORT = 54545;
|
|
952
|
+
var ANTHROPIC_OAUTH_REDIRECT_URI = `http://localhost:${ANTHROPIC_OAUTH_CALLBACK_PORT}/callback`;
|
|
953
|
+
var ANTHROPIC_OAUTH_SCOPES = [
|
|
954
|
+
"org:create_api_key",
|
|
955
|
+
"user:profile",
|
|
956
|
+
"user:inference",
|
|
957
|
+
"user:sessions:claude_code",
|
|
958
|
+
"user:mcp_servers",
|
|
959
|
+
"user:file_upload"
|
|
960
|
+
];
|
|
961
|
+
var ANTHROPIC_ACCESS_TOKEN_CLIENT_SKEW_MS = 5 * 60 * 1e3;
|
|
962
|
+
var ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
|
|
963
|
+
function normalizeAnthropicTokenPayload(args) {
|
|
964
|
+
const { payload, baseCredential, now = Date.now() } = args;
|
|
965
|
+
if (typeof payload.access_token !== "string" || !payload.access_token.trim()) {
|
|
966
|
+
throw new Error("Claude OAuth token response missing access_token");
|
|
967
|
+
}
|
|
968
|
+
const expiresIn = asOptionalFiniteNumber(payload.expires_in);
|
|
969
|
+
const accountId = typeof payload.account?.uuid === "string" && payload.account.uuid ? payload.account.uuid : baseCredential?.accountId;
|
|
970
|
+
const email = typeof payload.account?.email_address === "string" && payload.account.email_address ? payload.account.email_address : baseCredential?.metadata?.email;
|
|
971
|
+
const organizationId = typeof payload.organization?.uuid === "string" && payload.organization.uuid ? payload.organization.uuid : baseCredential?.metadata?.organizationId;
|
|
972
|
+
const organizationName = typeof payload.organization?.name === "string" && payload.organization.name ? payload.organization.name : baseCredential?.metadata?.organizationName;
|
|
973
|
+
const refreshToken = typeof payload.refresh_token === "string" && payload.refresh_token.trim() ? payload.refresh_token.trim() : baseCredential?.refreshToken;
|
|
974
|
+
const scope = typeof payload.scope === "string" && payload.scope ? payload.scope : baseCredential?.scope;
|
|
975
|
+
const metadata = {
|
|
976
|
+
...baseCredential?.metadata ?? {},
|
|
977
|
+
...email ? { email } : {},
|
|
978
|
+
...organizationId ? { organizationId } : {},
|
|
979
|
+
...organizationName ? { organizationName } : {}
|
|
980
|
+
};
|
|
1022
981
|
return {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
...
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
),
|
|
1031
|
-
|
|
1032
|
-
parsePositiveSecondsToMs(data.interval, XAI_DEVICE_CODE_DEFAULT_INTERVAL_MS),
|
|
1033
|
-
XAI_DEVICE_CODE_MIN_INTERVAL_MS
|
|
1034
|
-
)
|
|
982
|
+
...baseCredential,
|
|
983
|
+
provider: "claude",
|
|
984
|
+
accessToken: payload.access_token.trim(),
|
|
985
|
+
...refreshToken ? { refreshToken } : {},
|
|
986
|
+
...expiresIn !== void 0 ? { expiresAt: now + expiresIn * 1e3 - ANTHROPIC_ACCESS_TOKEN_CLIENT_SKEW_MS } : baseCredential?.expiresAt !== void 0 ? { expiresAt: baseCredential.expiresAt } : {},
|
|
987
|
+
...scope ? { scope } : {},
|
|
988
|
+
...accountId ? { accountId } : {},
|
|
989
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {},
|
|
990
|
+
obtainedAt: now
|
|
1035
991
|
};
|
|
1036
992
|
}
|
|
1037
|
-
async function
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
let intervalMs = args.intervalMs;
|
|
1041
|
-
while (args.now() < deadlineMs) {
|
|
1042
|
-
const remainingMs = Math.max(0, deadlineMs - args.now());
|
|
1043
|
-
const delayMs = Math.min(Math.max(intervalMs, XAI_DEVICE_CODE_MIN_INTERVAL_MS), remainingMs);
|
|
1044
|
-
if (delayMs > 0) {
|
|
1045
|
-
await args.sleep(delayMs);
|
|
1046
|
-
}
|
|
1047
|
-
if (args.now() >= deadlineMs) break;
|
|
1048
|
-
let response;
|
|
1049
|
-
try {
|
|
1050
|
-
response = await args.fetchImpl(tokenEndpoint, {
|
|
1051
|
-
method: "POST",
|
|
1052
|
-
headers: {
|
|
1053
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
1054
|
-
Accept: "application/json"
|
|
1055
|
-
},
|
|
1056
|
-
body: new URLSearchParams({
|
|
1057
|
-
grant_type: XAI_DEVICE_CODE_GRANT_TYPE,
|
|
1058
|
-
client_id: XAI_OAUTH_CLIENT_ID,
|
|
1059
|
-
device_code: args.deviceCode
|
|
1060
|
-
}),
|
|
1061
|
-
signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS)
|
|
1062
|
-
});
|
|
1063
|
-
} catch (error) {
|
|
1064
|
-
const message = toErrorMessage(error);
|
|
1065
|
-
if (/abort|timeout/i.test(message)) {
|
|
1066
|
-
throw new Error(`xAI device token poll aborted or timed out: ${message}`);
|
|
1067
|
-
}
|
|
1068
|
-
throw new Error(`xAI device token poll failed: ${message}`);
|
|
1069
|
-
}
|
|
1070
|
-
let data;
|
|
1071
|
-
try {
|
|
1072
|
-
data = await readJsonBody(response, "xAI device token exchange");
|
|
1073
|
-
} catch (error) {
|
|
1074
|
-
throw error;
|
|
1075
|
-
}
|
|
1076
|
-
if (response.ok) {
|
|
1077
|
-
return parseOAuthTokenPayload(data, "xAI device token exchange response", {
|
|
1078
|
-
requireRefreshToken: true
|
|
1079
|
-
});
|
|
1080
|
-
}
|
|
1081
|
-
const oauthError = typeof data.error === "string" ? data.error : "";
|
|
1082
|
-
if (oauthError === "authorization_pending") {
|
|
1083
|
-
continue;
|
|
1084
|
-
}
|
|
1085
|
-
if (oauthError === "slow_down") {
|
|
1086
|
-
intervalMs += XAI_DEVICE_CODE_SLOW_DOWN_INCREMENT_MS;
|
|
1087
|
-
continue;
|
|
1088
|
-
}
|
|
1089
|
-
if (oauthError === "access_denied" || oauthError === "authorization_denied") {
|
|
1090
|
-
throw new Error("xAI device authorization was denied");
|
|
1091
|
-
}
|
|
1092
|
-
if (oauthError === "expired_token") {
|
|
1093
|
-
throw new Error("xAI device code expired. Re-run the login.");
|
|
1094
|
-
}
|
|
1095
|
-
const detail = typeof data.error_description === "string" && data.error_description || oauthError || "";
|
|
1096
|
-
throw new Error(
|
|
1097
|
-
`xAI device token exchange failed (${response.status})${detail ? `: ${detail}` : ""}`
|
|
1098
|
-
);
|
|
1099
|
-
}
|
|
1100
|
-
throw new Error("xAI device authorization timed out");
|
|
1101
|
-
}
|
|
1102
|
-
async function runXaiOAuthLogin(deps = {}) {
|
|
1103
|
-
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
1104
|
-
const output = deps.output ?? console;
|
|
1105
|
-
const error = deps.error ?? console;
|
|
1106
|
-
const redirectUri = `http://${XAI_OAUTH_REDIRECT_HOST}:${XAI_OAUTH_REDIRECT_PORT}${XAI_OAUTH_REDIRECT_PATH}`;
|
|
1107
|
-
let handle;
|
|
1108
|
-
try {
|
|
1109
|
-
handle = await startCallbackServer({
|
|
1110
|
-
port: XAI_OAUTH_REDIRECT_PORT,
|
|
1111
|
-
hostname: XAI_OAUTH_REDIRECT_HOST,
|
|
1112
|
-
timeoutMs: 5 * 6e4
|
|
1113
|
-
});
|
|
1114
|
-
} catch (err) {
|
|
1115
|
-
throw new Error(
|
|
1116
|
-
`Failed to start xAI OAuth callback server on ${XAI_OAUTH_REDIRECT_PORT}: ${toErrorMessage(err)}`
|
|
1117
|
-
);
|
|
1118
|
-
}
|
|
1119
|
-
try {
|
|
1120
|
-
const state = generateState2();
|
|
1121
|
-
const pkce = await generatePkcePair();
|
|
1122
|
-
const nonce = crypto.randomUUID().replace(/-/g, "");
|
|
1123
|
-
const discovery = await xaiOAuthDiscovery(fetchImpl);
|
|
1124
|
-
const authUrl = buildXAIAuthorizeUrl({
|
|
1125
|
-
authorizationEndpoint: discovery.authorization_endpoint,
|
|
1126
|
-
redirectUri,
|
|
1127
|
-
codeChallenge: pkce.challenge,
|
|
1128
|
-
state,
|
|
1129
|
-
nonce
|
|
1130
|
-
});
|
|
1131
|
-
output.log(
|
|
1132
|
-
`Open the following URL in your browser to log in to xAI Grok (SuperGrok):
|
|
1133
|
-
${authUrl}
|
|
1134
|
-
|
|
1135
|
-
Docs: ${XAI_OAUTH_DOCS_URL}`
|
|
1136
|
-
);
|
|
1137
|
-
if (deps.openBrowser) {
|
|
1138
|
-
try {
|
|
1139
|
-
await deps.openBrowser(authUrl);
|
|
1140
|
-
} catch (err) {
|
|
1141
|
-
error.error(
|
|
1142
|
-
`Failed to open browser automatically: ${toErrorMessage(err)}`
|
|
1143
|
-
);
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
const callback = await handle.waitForCode();
|
|
1147
|
-
const token = await exchangeXAIToken(
|
|
1148
|
-
fetchImpl,
|
|
1149
|
-
callback.code,
|
|
1150
|
-
redirectUri,
|
|
1151
|
-
pkce.verifier
|
|
1152
|
-
);
|
|
1153
|
-
const now = (deps.now ?? Date.now)();
|
|
1154
|
-
return tokenToCredential(token, now);
|
|
1155
|
-
} finally {
|
|
1156
|
-
await handle.close();
|
|
993
|
+
async function refreshAnthropicToken(credential, deps = {}) {
|
|
994
|
+
if (!credential.refreshToken) {
|
|
995
|
+
throw new Error("Claude credential has no refresh_token");
|
|
1157
996
|
}
|
|
1158
|
-
}
|
|
1159
|
-
async function runXaiOAuthDeviceCode(deps = {}) {
|
|
1160
997
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
1161
|
-
const
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
998
|
+
const response = await fetchImpl(ANTHROPIC_OAUTH_TOKEN_URL, {
|
|
999
|
+
method: "POST",
|
|
1000
|
+
headers: {
|
|
1001
|
+
"Content-Type": "application/json",
|
|
1002
|
+
"anthropic-beta": ANTHROPIC_OAUTH_BETA,
|
|
1003
|
+
"User-Agent": "nolo-cli userOAuthProvider"
|
|
1004
|
+
},
|
|
1005
|
+
body: JSON.stringify({
|
|
1006
|
+
grant_type: "refresh_token",
|
|
1007
|
+
client_id: ANTHROPIC_OAUTH_CLIENT_ID,
|
|
1008
|
+
refresh_token: credential.refreshToken
|
|
1009
|
+
}),
|
|
1010
|
+
signal: AbortSignal.timeout(3e4)
|
|
1167
1011
|
});
|
|
1168
|
-
const
|
|
1169
|
-
if (!
|
|
1170
|
-
|
|
1012
|
+
const payload = await response.json().catch(() => ({}));
|
|
1013
|
+
if (!response.ok) {
|
|
1014
|
+
const detail = typeof payload.error_description === "string" ? payload.error_description : typeof payload.error === "string" ? payload.error : `HTTP ${response.status}`;
|
|
1015
|
+
throw new Error(`Claude token refresh failed: ${detail}`);
|
|
1171
1016
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
const expiresMinutes = Math.max(1, Math.round(start.expiresInMs / 6e4));
|
|
1175
|
-
output.log("Authorize nolo-cli for xAI Grok (device code / headless):");
|
|
1176
|
-
output.log(` URL: ${start.verificationUri}`);
|
|
1177
|
-
if (start.verificationUriComplete) {
|
|
1178
|
-
output.log(` Complete URL: ${start.verificationUriComplete}`);
|
|
1017
|
+
if (typeof payload.access_token !== "string" || !payload.access_token.trim()) {
|
|
1018
|
+
throw new Error("Claude token refresh response missing access_token");
|
|
1179
1019
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
if (deps.openBrowser) {
|
|
1185
|
-
try {
|
|
1186
|
-
const opened = await deps.openBrowser(browserUrl);
|
|
1187
|
-
if (!opened) {
|
|
1188
|
-
output.log("Could not open a browser automatically. Open the URL above.");
|
|
1189
|
-
}
|
|
1190
|
-
} catch (err) {
|
|
1191
|
-
error.error(
|
|
1192
|
-
`Failed to open browser automatically: ${toErrorMessage(err)}`
|
|
1193
|
-
);
|
|
1194
|
-
}
|
|
1195
|
-
} else {
|
|
1196
|
-
output.log("Open the URL above in a browser and enter the code to approve.");
|
|
1197
|
-
}
|
|
1198
|
-
const token = await pollXaiDeviceCodeToken({
|
|
1199
|
-
fetchImpl,
|
|
1200
|
-
tokenEndpoint: discovery.token_endpoint,
|
|
1201
|
-
deviceCode: start.deviceCode,
|
|
1202
|
-
expiresInMs: start.expiresInMs,
|
|
1203
|
-
intervalMs: start.intervalMs,
|
|
1204
|
-
sleep: sleep3,
|
|
1020
|
+
const now = deps.now?.() ?? Date.now();
|
|
1021
|
+
return normalizeAnthropicTokenPayload({
|
|
1022
|
+
payload,
|
|
1023
|
+
baseCredential: credential,
|
|
1205
1024
|
now
|
|
1206
1025
|
});
|
|
1207
|
-
return tokenToCredential(token, now());
|
|
1208
|
-
}
|
|
1209
|
-
async function refreshXaiOAuthToken(credential) {
|
|
1210
|
-
if (!credential.refreshToken) {
|
|
1211
|
-
throw new Error("xAI credential has no refresh_token");
|
|
1212
|
-
}
|
|
1213
|
-
const fetchImpl = fetch;
|
|
1214
|
-
const discovery = await xaiOAuthDiscovery(fetchImpl);
|
|
1215
|
-
const tokenEndpoint = validateXAIEndpoint(discovery.token_endpoint, "token_endpoint");
|
|
1216
|
-
const body = new URLSearchParams({
|
|
1217
|
-
grant_type: "refresh_token",
|
|
1218
|
-
client_id: XAI_OAUTH_CLIENT_ID,
|
|
1219
|
-
refresh_token: credential.refreshToken
|
|
1220
|
-
});
|
|
1221
|
-
let response;
|
|
1222
|
-
try {
|
|
1223
|
-
response = await fetchImpl(tokenEndpoint, {
|
|
1224
|
-
method: "POST",
|
|
1225
|
-
headers: {
|
|
1226
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
1227
|
-
Accept: "application/json"
|
|
1228
|
-
},
|
|
1229
|
-
body,
|
|
1230
|
-
signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS)
|
|
1231
|
-
});
|
|
1232
|
-
} catch (error) {
|
|
1233
|
-
throw new Error(
|
|
1234
|
-
`xAI token refresh failed: ${toErrorMessage(error)}`
|
|
1235
|
-
);
|
|
1236
|
-
}
|
|
1237
|
-
const data = await readJsonBody(response, "xAI token refresh");
|
|
1238
|
-
if (!response.ok) {
|
|
1239
|
-
const detail = typeof data.error_description === "string" && data.error_description || typeof data.error === "string" && data.error || "";
|
|
1240
|
-
throw new Error(
|
|
1241
|
-
`xAI token refresh failed: ${response.status}${detail ? ` ${detail}` : ""}`
|
|
1242
|
-
);
|
|
1243
|
-
}
|
|
1244
|
-
if (typeof data.access_token !== "string" || !data.access_token) {
|
|
1245
|
-
throw new Error("xAI token refresh response missing access_token");
|
|
1246
|
-
}
|
|
1247
|
-
const expiresIn = asOptionalFiniteNumber(data.expires_in);
|
|
1248
|
-
if (expiresIn === void 0) {
|
|
1249
|
-
throw new Error("xAI token refresh response missing expires_in");
|
|
1250
|
-
}
|
|
1251
|
-
const newRefresh = typeof data.refresh_token === "string" && data.refresh_token ? data.refresh_token : credential.refreshToken;
|
|
1252
|
-
const now = Date.now();
|
|
1253
|
-
return {
|
|
1254
|
-
...credential,
|
|
1255
|
-
accessToken: data.access_token,
|
|
1256
|
-
refreshToken: newRefresh,
|
|
1257
|
-
expiresAt: now + expiresIn * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS2,
|
|
1258
|
-
obtainedAt: now
|
|
1259
|
-
};
|
|
1260
1026
|
}
|
|
1027
|
+
var anthropicRefresh = refreshAnthropicToken;
|
|
1261
1028
|
|
|
1262
|
-
// packages/
|
|
1029
|
+
// packages/agent-runtime/cursorOAuth.ts
|
|
1263
1030
|
var CURSOR_LOGIN_URL = "https://cursor.com/loginDeepControl";
|
|
1264
1031
|
var CURSOR_POLL_URL = "https://api2.cursor.sh/auth/poll";
|
|
1265
1032
|
var CURSOR_REFRESH_URL = "https://api2.cursor.sh/auth/exchange_user_api_key";
|
|
1266
|
-
var
|
|
1267
|
-
var
|
|
1268
|
-
var
|
|
1269
|
-
var
|
|
1270
|
-
var
|
|
1271
|
-
var
|
|
1033
|
+
var CURSOR_POLL_MAX_ATTEMPTS = 150;
|
|
1034
|
+
var CURSOR_POLL_BASE_DELAY_MS = 1e3;
|
|
1035
|
+
var CURSOR_POLL_MAX_DELAY_MS = 1e4;
|
|
1036
|
+
var CURSOR_POLL_BACKOFF_MULTIPLIER = 1.2;
|
|
1037
|
+
var CURSOR_POLL_MAX_CONSECUTIVE_ERRORS = 3;
|
|
1038
|
+
var CURSOR_ACCESS_TOKEN_CLIENT_SKEW_MS = 5 * 60 * 1e3;
|
|
1039
|
+
var CURSOR_TOKEN_REQUEST_TIMEOUT_MS = 3e4;
|
|
1272
1040
|
function decodeJwtExp(token) {
|
|
1273
1041
|
try {
|
|
1274
1042
|
const parts = token.split(".");
|
|
1275
|
-
if (parts.length
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
);
|
|
1281
|
-
if (decoded && typeof decoded === "object" && typeof decoded.exp === "number") {
|
|
1282
|
-
return decoded.exp * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS3;
|
|
1043
|
+
if (parts.length < 2) return null;
|
|
1044
|
+
const payloadJson = Buffer.from(parts[1], "base64url").toString("utf-8");
|
|
1045
|
+
const payload = JSON.parse(payloadJson);
|
|
1046
|
+
if (typeof payload.exp === "number" && Number.isFinite(payload.exp)) {
|
|
1047
|
+
return payload.exp * 1e3;
|
|
1283
1048
|
}
|
|
1049
|
+
return null;
|
|
1284
1050
|
} catch {
|
|
1051
|
+
return null;
|
|
1285
1052
|
}
|
|
1286
|
-
return void 0;
|
|
1287
|
-
}
|
|
1288
|
-
function sleep2(ms, signal) {
|
|
1289
|
-
if (signal) {
|
|
1290
|
-
return new Promise((resolve, reject) => {
|
|
1291
|
-
if (signal.aborted) {
|
|
1292
|
-
reject(new Error("Aborted"));
|
|
1293
|
-
return;
|
|
1294
|
-
}
|
|
1295
|
-
const timer = setTimeout(resolve, ms);
|
|
1296
|
-
signal.addEventListener(
|
|
1297
|
-
"abort",
|
|
1298
|
-
() => {
|
|
1299
|
-
clearTimeout(timer);
|
|
1300
|
-
reject(new Error("Aborted"));
|
|
1301
|
-
},
|
|
1302
|
-
{ once: true }
|
|
1303
|
-
);
|
|
1304
|
-
});
|
|
1305
|
-
}
|
|
1306
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1307
1053
|
}
|
|
1308
|
-
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
let consecutiveErrors = 0;
|
|
1313
|
-
for (let attempt = 0; attempt < POLL_MAX_ATTEMPTS; attempt++) {
|
|
1314
|
-
await sleepFn(delay);
|
|
1315
|
-
let response;
|
|
1316
|
-
try {
|
|
1317
|
-
response = await fetchImpl(
|
|
1318
|
-
`${CURSOR_POLL_URL}?uuid=${encodeURIComponent(uuid)}&verifier=${encodeURIComponent(verifier)}`
|
|
1319
|
-
);
|
|
1320
|
-
} catch {
|
|
1321
|
-
consecutiveErrors++;
|
|
1322
|
-
if (consecutiveErrors >= POLL_MAX_CONSECUTIVE_ERRORS) {
|
|
1323
|
-
throw new Error("Too many consecutive network errors during Cursor auth polling");
|
|
1324
|
-
}
|
|
1325
|
-
continue;
|
|
1326
|
-
}
|
|
1327
|
-
if (response.status === 404) {
|
|
1328
|
-
consecutiveErrors = 0;
|
|
1329
|
-
delay = Math.min(delay * POLL_BACKOFF_MULTIPLIER, POLL_MAX_DELAY_MS);
|
|
1330
|
-
continue;
|
|
1331
|
-
}
|
|
1332
|
-
if (response.ok) {
|
|
1333
|
-
const data = await response.json();
|
|
1334
|
-
if (typeof data.accessToken !== "string" || typeof data.refreshToken !== "string") {
|
|
1335
|
-
throw new Error("Cursor poll response missing accessToken or refreshToken");
|
|
1336
|
-
}
|
|
1337
|
-
return { accessToken: data.accessToken, refreshToken: data.refreshToken };
|
|
1338
|
-
}
|
|
1339
|
-
consecutiveErrors++;
|
|
1340
|
-
if (consecutiveErrors >= POLL_MAX_CONSECUTIVE_ERRORS) {
|
|
1341
|
-
throw new Error(`Cursor auth poll failed: ${response.status}`);
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
throw new Error("Cursor authentication polling timed out");
|
|
1345
|
-
}
|
|
1346
|
-
async function runCursorOAuthLogin(deps = {}) {
|
|
1347
|
-
const output = deps.output ?? console;
|
|
1348
|
-
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
1349
|
-
const pkce = generatePkcePair();
|
|
1350
|
-
const uuid = crypto.randomUUID();
|
|
1351
|
-
const params = new URLSearchParams({
|
|
1352
|
-
challenge: pkce.challenge,
|
|
1353
|
-
uuid,
|
|
1354
|
-
mode: "login",
|
|
1355
|
-
redirectTarget: "cli"
|
|
1356
|
-
});
|
|
1357
|
-
const loginUrl = `${CURSOR_LOGIN_URL}?${params.toString()}`;
|
|
1358
|
-
output.log(
|
|
1359
|
-
`
|
|
1360
|
-
Open this URL in your browser to authorize Cursor:
|
|
1361
|
-
${loginUrl}
|
|
1362
|
-
|
|
1363
|
-
Waiting for browser authentication... (polling ${CURSOR_POLL_URL})`
|
|
1364
|
-
);
|
|
1365
|
-
const openBrowser = deps.openBrowser;
|
|
1366
|
-
if (openBrowser) {
|
|
1367
|
-
try {
|
|
1368
|
-
await openBrowser(loginUrl);
|
|
1369
|
-
} catch {
|
|
1370
|
-
}
|
|
1054
|
+
function normalizeCursorTokenPayload(args) {
|
|
1055
|
+
const { payload, baseCredential, now } = args;
|
|
1056
|
+
if (typeof payload.accessToken !== "string" || !payload.accessToken.trim()) {
|
|
1057
|
+
throw new Error("Cursor token refresh response missing accessToken");
|
|
1371
1058
|
}
|
|
1372
|
-
const
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
});
|
|
1376
|
-
const now = deps.now?.() ?? Date.now();
|
|
1377
|
-
const expiresAt = decodeJwtExp(accessToken) ?? now + 3600 * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS3;
|
|
1059
|
+
const accessToken = payload.accessToken.trim();
|
|
1060
|
+
const expiresAt = decodeJwtExp(accessToken) ?? now + 3600 * 1e3 - CURSOR_ACCESS_TOKEN_CLIENT_SKEW_MS;
|
|
1061
|
+
const newRefresh = typeof payload.refreshToken === "string" && payload.refreshToken.trim() ? payload.refreshToken.trim() : baseCredential?.refreshToken;
|
|
1378
1062
|
return {
|
|
1063
|
+
...baseCredential ?? {},
|
|
1379
1064
|
provider: "cursor",
|
|
1380
1065
|
accessToken,
|
|
1381
|
-
refreshToken,
|
|
1066
|
+
...newRefresh ? { refreshToken: newRefresh } : {},
|
|
1382
1067
|
expiresAt,
|
|
1383
1068
|
obtainedAt: now
|
|
1384
1069
|
};
|
|
1385
1070
|
}
|
|
1386
|
-
|
|
1071
|
+
async function refreshCursorToken(credential, deps = {}) {
|
|
1387
1072
|
if (!credential.refreshToken) {
|
|
1388
1073
|
throw new Error("Cursor credential has no refresh_token");
|
|
1389
1074
|
}
|
|
1390
1075
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1076
|
+
let response;
|
|
1077
|
+
try {
|
|
1078
|
+
response = await fetchImpl(CURSOR_REFRESH_URL, {
|
|
1079
|
+
method: "POST",
|
|
1080
|
+
headers: {
|
|
1081
|
+
Authorization: `Bearer ${credential.refreshToken}`,
|
|
1082
|
+
"Content-Type": "application/json"
|
|
1083
|
+
},
|
|
1084
|
+
body: "{}",
|
|
1085
|
+
signal: AbortSignal.timeout(CURSOR_TOKEN_REQUEST_TIMEOUT_MS)
|
|
1086
|
+
});
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
throw new Error(
|
|
1089
|
+
`Cursor token refresh failed: ${toErrorMessage(error)}`
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1400
1092
|
if (!response.ok) {
|
|
1401
1093
|
let detail = "";
|
|
1402
1094
|
try {
|
|
@@ -1407,22 +1099,20 @@ var cursorRefresh = async (credential, deps = {}) => {
|
|
|
1407
1099
|
`Cursor token refresh failed: ${response.status}${detail ? ` ${detail}` : ""}`
|
|
1408
1100
|
);
|
|
1409
1101
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1102
|
+
let data;
|
|
1103
|
+
try {
|
|
1104
|
+
data = await response.json();
|
|
1105
|
+
} catch {
|
|
1106
|
+
throw new Error("Cursor token refresh response is not valid JSON");
|
|
1413
1107
|
}
|
|
1414
1108
|
const now = deps.now?.() ?? Date.now();
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
expiresAt,
|
|
1423
|
-
obtainedAt: now
|
|
1424
|
-
};
|
|
1425
|
-
};
|
|
1109
|
+
return normalizeCursorTokenPayload({
|
|
1110
|
+
payload: data,
|
|
1111
|
+
baseCredential: credential,
|
|
1112
|
+
now
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
var cursorRefresh = refreshCursorToken;
|
|
1426
1116
|
|
|
1427
1117
|
export {
|
|
1428
1118
|
startCallbackServer,
|
|
@@ -1431,10 +1121,40 @@ export {
|
|
|
1431
1121
|
refreshAntigravityOAuthToken,
|
|
1432
1122
|
runOpenAiCodexDeviceCode,
|
|
1433
1123
|
runOpenAiCodexBrowserPkce,
|
|
1434
|
-
refreshOpenAiCodexToken,
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1124
|
+
refreshOpenAiCodexToken2 as refreshOpenAiCodexToken,
|
|
1125
|
+
XAI_OAUTH_CLIENT_ID,
|
|
1126
|
+
XAI_OAUTH_SCOPE,
|
|
1127
|
+
XAI_OAUTH_REDIRECT_HOST,
|
|
1128
|
+
XAI_OAUTH_REDIRECT_PORT,
|
|
1129
|
+
XAI_OAUTH_REDIRECT_PATH,
|
|
1130
|
+
XAI_OAUTH_DOCS_URL,
|
|
1131
|
+
XAI_DEVICE_CODE_GRANT_TYPE,
|
|
1132
|
+
XAI_DEVICE_CODE_DEFAULT_INTERVAL_MS,
|
|
1133
|
+
XAI_DEVICE_CODE_MIN_INTERVAL_MS,
|
|
1134
|
+
XAI_DEVICE_CODE_SLOW_DOWN_INCREMENT_MS,
|
|
1135
|
+
XAI_DEVICE_CODE_DEFAULT_EXPIRES_MS,
|
|
1136
|
+
XAI_ACCESS_TOKEN_CLIENT_SKEW_MS,
|
|
1137
|
+
XAI_TOKEN_REQUEST_TIMEOUT_MS,
|
|
1138
|
+
validateXAIEndpoint,
|
|
1139
|
+
readJsonBody,
|
|
1140
|
+
xaiOAuthDiscovery,
|
|
1141
|
+
xaiRefresh,
|
|
1142
|
+
ANTHROPIC_OAUTH_CLIENT_ID,
|
|
1143
|
+
ANTHROPIC_OAUTH_AUTHORIZE_URL,
|
|
1144
|
+
ANTHROPIC_OAUTH_TOKEN_URL,
|
|
1145
|
+
ANTHROPIC_OAUTH_CALLBACK_PORT,
|
|
1146
|
+
ANTHROPIC_OAUTH_REDIRECT_URI,
|
|
1147
|
+
ANTHROPIC_OAUTH_SCOPES,
|
|
1148
|
+
normalizeAnthropicTokenPayload,
|
|
1149
|
+
anthropicRefresh,
|
|
1150
|
+
CURSOR_LOGIN_URL,
|
|
1151
|
+
CURSOR_POLL_URL,
|
|
1152
|
+
CURSOR_POLL_MAX_ATTEMPTS,
|
|
1153
|
+
CURSOR_POLL_BASE_DELAY_MS,
|
|
1154
|
+
CURSOR_POLL_MAX_DELAY_MS,
|
|
1155
|
+
CURSOR_POLL_BACKOFF_MULTIPLIER,
|
|
1156
|
+
CURSOR_POLL_MAX_CONSECUTIVE_ERRORS,
|
|
1157
|
+
CURSOR_ACCESS_TOKEN_CLIENT_SKEW_MS,
|
|
1158
|
+
decodeJwtExp,
|
|
1439
1159
|
cursorRefresh
|
|
1440
1160
|
};
|