nolo-cli 0.8.0 → 0.9.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 (183) hide show
  1. package/_executeModel-UD44KGZ4.js +203 -0
  2. package/action-ZJWE3Y5S.js +28 -0
  3. package/addContentAction-TVDBNJM5.js +26 -0
  4. package/addDialogAgentAction-EKH774VT.js +68 -0
  5. package/agentDeleteCommand-TCPEFJTF.js +327 -0
  6. package/agentEmailCommands-CZ3UILZ4.js +626 -0
  7. package/agentListCommands-SJKO2FFB.js +223 -0
  8. package/agentMachineCommands-PSNABISZ.js +425 -0
  9. package/agentPullCommand-O2MTUAFR.js +122 -0
  10. package/agentRecordCommands-MH2JKFHS.js +94 -0
  11. package/agentRunCommand-4KH6MIZR.js +110 -0
  12. package/agentRunControl-K4VKNEHF.js +57 -0
  13. package/agentRuntimeLocal-SKHJUWSS.js +104 -0
  14. package/agentSlice-3WXXN4VK.js +42 -0
  15. package/authCommand-PXFX6JDT.js +581 -0
  16. package/authCommands-356AYWEK.js +35 -0
  17. package/chunk-2EISRLRC.js +238 -0
  18. package/chunk-2FTTDCD4.js +675 -0
  19. package/chunk-2K2ECKSG.js +222 -0
  20. package/chunk-2PUGVR6E.js +19 -0
  21. package/chunk-3ASDTTEI.js +1774 -0
  22. package/chunk-3EUW3WEO.js +8 -0
  23. package/chunk-3LY4F7SV.js +9293 -0
  24. package/chunk-3TZDXYSF.js +71 -0
  25. package/chunk-3WYQEUBI.js +34 -0
  26. package/chunk-4CQLVGRV.js +50 -0
  27. package/chunk-4IJYA27C.js +342 -0
  28. package/chunk-4K2KKCSD.js +577 -0
  29. package/chunk-4SN6J65J.js +38 -0
  30. package/chunk-4VNS5WPM.js +42 -0
  31. package/chunk-4ZDM73SD.js +34 -0
  32. package/chunk-5CS33IBL.js +301 -0
  33. package/chunk-5KLGBAAR.js +386 -0
  34. package/chunk-5MIDPSSK.js +206 -0
  35. package/chunk-5MMAK75Q.js +15 -0
  36. package/chunk-5PJ3VWCS.js +360 -0
  37. package/chunk-63SLEOLK.js +175 -0
  38. package/chunk-63W5N6P3.js +150 -0
  39. package/chunk-6GANEXVO.js +231 -0
  40. package/chunk-6HYVJV7B.js +102 -0
  41. package/chunk-6WD3OOJS.js +20 -0
  42. package/chunk-7OOXPE3N.js +628 -0
  43. package/chunk-7QRKCBOP.js +1078 -0
  44. package/chunk-ATNTHIZI.js +13 -0
  45. package/chunk-AUQ747NZ.js +902 -0
  46. package/chunk-AVMKZHBC.js +85 -0
  47. package/chunk-AZPCASRE.js +36 -0
  48. package/chunk-BBYVOSU2.js +792 -0
  49. package/chunk-C3RUNRHT.js +51 -0
  50. package/chunk-CF2WZF4N.js +134 -0
  51. package/chunk-CF7PE763.js +10 -0
  52. package/chunk-CKOJQDKR.js +198 -0
  53. package/chunk-COMXDWNM.js +807 -0
  54. package/chunk-CPB2YM4D.js +171 -0
  55. package/chunk-CWMSB67P.js +34 -0
  56. package/chunk-DCOA4QSN.js +105 -0
  57. package/chunk-DFFIZ25G.js +30 -0
  58. package/chunk-DWLEOOGR.js +8 -0
  59. package/chunk-DZ3XJVET.js +231 -0
  60. package/chunk-EHLHSX7R.js +1433 -0
  61. package/chunk-ELRRLDWI.js +478 -0
  62. package/chunk-EXTIQUCI.js +482 -0
  63. package/chunk-EYEEHCU4.js +3066 -0
  64. package/chunk-G3SUCT4W.js +8 -0
  65. package/chunk-G43KEVSC.js +1447 -0
  66. package/chunk-GAKEBQD6.js +30 -0
  67. package/chunk-GBRG75BQ.js +86 -0
  68. package/chunk-GG4AJPSR.js +191 -0
  69. package/chunk-H33743XH.js +25 -0
  70. package/chunk-HNMXYYJR.js +12 -0
  71. package/chunk-I42VC5KR.js +15 -0
  72. package/chunk-IDZTNRP2.js +85 -0
  73. package/chunk-ISNCLGUZ.js +146 -0
  74. package/chunk-IWX4UW2E.js +548 -0
  75. package/chunk-JBTJAPUF.js +35 -0
  76. package/chunk-JEXVIMYZ.js +55 -0
  77. package/chunk-JRKKFRAV.js +537 -0
  78. package/chunk-JZPBVBIY.js +94 -0
  79. package/chunk-KKY4B2Q2.js +21 -0
  80. package/chunk-KOCA5FCS.js +4750 -0
  81. package/chunk-KQHGZWSV.js +63 -0
  82. package/chunk-KTVVPRJS.js +138 -0
  83. package/chunk-KUG52IAP.js +8 -0
  84. package/chunk-KX6YBIHG.js +8 -0
  85. package/chunk-LARL2V6G.js +9017 -0
  86. package/chunk-LCBAMY2M.js +60 -0
  87. package/chunk-LM6IYQ3F.js +16 -0
  88. package/chunk-M76ECQRD.js +13658 -0
  89. package/chunk-MGOJ5R6J.js +113 -0
  90. package/chunk-MKMWT7SA.js +27196 -0
  91. package/chunk-MRC4Q34M.js +188 -0
  92. package/chunk-OLZBQWZU.js +6 -0
  93. package/chunk-OPZYGHUC.js +89 -0
  94. package/chunk-OWOHNGCN.js +50 -0
  95. package/chunk-PAKN26FP.js +239 -0
  96. package/chunk-PCWC3JTN.js +91 -0
  97. package/chunk-PG5PNA7Y.js +939 -0
  98. package/chunk-Q2YZULBH.js +104 -0
  99. package/chunk-QSJ3HFEF.js +8 -0
  100. package/chunk-QYXLLLGL.js +4519 -0
  101. package/chunk-ROWQEJRP.js +139 -0
  102. package/chunk-S7Z2HMCN.js +25 -0
  103. package/chunk-SBR32ZZS.js +1901 -0
  104. package/chunk-SH2CFQOH.js +3391 -0
  105. package/chunk-SL35K7AH.js +226 -0
  106. package/chunk-TILFE6LV.js +381 -0
  107. package/chunk-TSS3XJY4.js +2052 -0
  108. package/chunk-U4TRT5V4.js +246 -0
  109. package/chunk-U6VC4MU4.js +8 -0
  110. package/chunk-UAD2WT54.js +64 -0
  111. package/chunk-UUJDGGAK.js +421 -0
  112. package/chunk-VF4K2QE6.js +21 -0
  113. package/chunk-VHBWHNVT.js +59 -0
  114. package/chunk-VIDPVFP3.js +20 -0
  115. package/chunk-VP4WJWKW.js +8882 -0
  116. package/chunk-W4NVOSFF.js +37 -0
  117. package/chunk-WKM6FH35.js +190 -0
  118. package/chunk-WWC7I6TA.js +8 -0
  119. package/chunk-X7W4TNB4.js +1550 -0
  120. package/chunk-XE635NI4.js +1948 -0
  121. package/chunk-XKIMRZO2.js +221 -0
  122. package/chunk-XS466O7S.js +933 -0
  123. package/chunk-Y5GCAMGP.js +9 -0
  124. package/chunk-YBWOP46I.js +3272 -0
  125. package/chunk-YPRZSPF3.js +148 -0
  126. package/chunk-YYUCRDOK.js +69 -0
  127. package/chunk-Z4NR32XR.js +50 -0
  128. package/chunk-ZIG5LJ7X.js +24765 -0
  129. package/chunk-ZNFJWEN4.js +56 -0
  130. package/chunk-ZQ2UM4KG.js +2878 -0
  131. package/cliExecutor-DNE7TCC4.js +36 -0
  132. package/clipboardy-BVE3LVEH.js +12001 -0
  133. package/createAgentAutomationAction-63OIP6X5.js +866 -0
  134. package/createDialogAction-ZLYZDYBB.js +54 -0
  135. package/createPageAction-AL5FSDCK.js +171 -0
  136. package/db-HCJ5PRB5.js +271 -0
  137. package/dbSlice-PR44QPXY.js +60 -0
  138. package/deleteFile-4SKGPHOZ.js +41 -0
  139. package/dialogCommands-F6YI5742.js +63 -0
  140. package/dialogSlice-JSBNZRLR.js +148 -0
  141. package/docCreateCommands-4CTO7ERT.js +219 -0
  142. package/docDeleteCommands-VBWERDRG.js +180 -0
  143. package/docReadCommands-KNP4MFYB.js +133 -0
  144. package/docStore-ADP5VYY4.js +98 -0
  145. package/docUpdateCommands-4BCBJQHA.js +317 -0
  146. package/fetchMessages-U6ZHN56A.js +17 -0
  147. package/getPricing-3E6UNPKL.js +30 -0
  148. package/handleSendMessageAction-IJDQ6ARA.js +185 -0
  149. package/index.js +188 -112943
  150. package/lib-FYYBQIXS.js +19839 -0
  151. package/localDialogRead-Z5UTAJPU.js +26 -0
  152. package/localFirstLog-DHNOY2NA.js +19 -0
  153. package/localRuntimeAuthority-TEGCOWX4.js +471 -0
  154. package/localRuntimeDb-LGDF6FQP.js +12 -0
  155. package/machineCommands-EA3M4XTN.js +779 -0
  156. package/machineInfo-M6KB6HZN.js +10 -0
  157. package/machineWsRunDispatch-NAPKE5ZA.js +110 -0
  158. package/memoryCommands-O4MIPIWQ.js +476 -0
  159. package/models-4OPOOLFS.js +9 -0
  160. package/offlineMarxistsAgentCommand-BCPJITJL.js +242 -0
  161. package/package.json +3 -25
  162. package/read-RIJCIVKX.js +17 -0
  163. package/readDocTool-GZD2N3LO.js +16 -0
  164. package/readlineWorkspace-KMWT4T4S.js +5509 -0
  165. package/removeDialogAgentAction-GJQJNDW6.js +73 -0
  166. package/runAgentClientLoop-U5Q6NDTJ.js +291 -0
  167. package/runtimeDoctorCommands-LRHLPDRC.js +289 -0
  168. package/setDialogExtraReferencesAction-M7OQG54N.js +60 -0
  169. package/setPrimaryDialogAgentAction-46UNFJDL.js +80 -0
  170. package/settingSlice-FVUUH4JA.js +199 -0
  171. package/skillSummaryMarker-42HKSXYY.js +9 -0
  172. package/slateToRenderMarkdown-Y5IYN73U.js +7 -0
  173. package/slateUtils-2M7MUTWX.js +19 -0
  174. package/spaceCommands-E2CFRPZJ.js +454 -0
  175. package/spaceSlice-L3R6IMXE.js +147 -0
  176. package/streamAgentChatTurn-UBDAOQWE.js +4528 -0
  177. package/tableCommands-GOFP7QKA.js +1286 -0
  178. package/toolApiClient-QI3YBNWB.js +26 -0
  179. package/tools-TMJXZTCA.js +85 -0
  180. package/tuiSummaryLlmCaller-RV3THLZF.js +78 -0
  181. package/understanding-GQKNDDWM.js +271 -0
  182. package/updateCommands-VOSPKXFF.js +31 -0
  183. package/updateDialogTitleAction-ZTWRDXI2.js +188 -0
@@ -0,0 +1,1433 @@
1
+ import {
2
+ writeOAuthCredential
3
+ } from "./chunk-JZPBVBIY.js";
4
+ import {
5
+ toErrorMessage
6
+ } from "./chunk-HNMXYYJR.js";
7
+ import {
8
+ asOptionalFiniteNumber
9
+ } from "./chunk-WWC7I6TA.js";
10
+ import {
11
+ asTrimmedString
12
+ } from "./chunk-3EUW3WEO.js";
13
+ import {
14
+ isRecord
15
+ } from "./chunk-KUG52IAP.js";
16
+
17
+ // packages/cli/oauth/callback-server.ts
18
+ import { createServer } from "node:http";
19
+ var DEFAULT_CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
20
+ function parseQuery(url) {
21
+ const questionIndex = url.indexOf("?");
22
+ if (questionIndex < 0) return {};
23
+ const search = url.slice(questionIndex + 1);
24
+ const params = new URLSearchParams(search);
25
+ const result = {};
26
+ for (const [key, value] of params) {
27
+ result[key] = value;
28
+ }
29
+ return result;
30
+ }
31
+ var SUCCESS_HTML = `<!doctype html>
32
+ <html><head><meta charset="utf-8"><title>Authorization complete</title></head>
33
+ <body><h2>Authorization complete</h2><p>You can close this tab and return to the terminal.</p></body></html>`;
34
+ var ERROR_HTML = `<!doctype html>
35
+ <html><head><meta charset="utf-8"><title>Authorization error</title></head>
36
+ <body><h2>Authorization error</h2><p>No authorization code was received. Please try again.</p></body></html>`;
37
+ function startCallbackServerOnPort(options) {
38
+ const hostname = options.hostname ?? "localhost";
39
+ const timeoutMs = options.timeoutMs ?? DEFAULT_CALLBACK_TIMEOUT_MS;
40
+ const now = options.now ?? Date.now;
41
+ return new Promise((resolve, reject) => {
42
+ const deadline = now() + timeoutMs;
43
+ let result = null;
44
+ let resultResolve = null;
45
+ let resultReject = null;
46
+ let settled = false;
47
+ let listening = false;
48
+ const codePromise = new Promise((res, rej) => {
49
+ resultResolve = res;
50
+ resultReject = rej;
51
+ });
52
+ const timeoutTimer = setTimeout(() => {
53
+ if (settled) return;
54
+ settled = true;
55
+ const err = new Error(`OAuth callback timed out after ${Math.round(timeoutMs / 1e3)}s`);
56
+ server.close();
57
+ resultReject?.(err);
58
+ }, Math.max(0, deadline - now()));
59
+ const server = createServer((req, res) => {
60
+ if (!req.url) {
61
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
62
+ res.end(ERROR_HTML);
63
+ return;
64
+ }
65
+ const query = parseQuery(req.url);
66
+ const code = query.code;
67
+ if (!code) {
68
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
69
+ res.end(ERROR_HTML);
70
+ return;
71
+ }
72
+ result = { code, ...query.state ? { state: query.state } : {} };
73
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
74
+ res.end(SUCCESS_HTML);
75
+ if (!settled) {
76
+ settled = true;
77
+ clearTimeout(timeoutTimer);
78
+ server.close();
79
+ resultResolve?.(result);
80
+ }
81
+ });
82
+ server.on("error", (err) => {
83
+ if (settled) return;
84
+ settled = true;
85
+ clearTimeout(timeoutTimer);
86
+ server.close();
87
+ if (listening) {
88
+ resultReject?.(err);
89
+ } else {
90
+ reject(err);
91
+ }
92
+ });
93
+ server.listen(options.port, hostname, () => {
94
+ listening = true;
95
+ const address = server.address();
96
+ if (!address || typeof address === "string") {
97
+ const err = new Error("OAuth callback server did not bind a TCP port.");
98
+ settled = true;
99
+ clearTimeout(timeoutTimer);
100
+ server.close();
101
+ reject(err);
102
+ return;
103
+ }
104
+ resolve({
105
+ server,
106
+ port: address.port,
107
+ waitForCode: () => codePromise,
108
+ close: () => {
109
+ clearTimeout(timeoutTimer);
110
+ settled = true;
111
+ return new Promise((res) => server.close(() => res()));
112
+ }
113
+ });
114
+ });
115
+ });
116
+ }
117
+ function isAddressInUse(error) {
118
+ return !!error && typeof error === "object" && "code" in error && error.code === "EADDRINUSE";
119
+ }
120
+ async function startCallbackServer(options) {
121
+ try {
122
+ return await startCallbackServerOnPort(options);
123
+ } catch (error) {
124
+ if (!options.fallbackToRandomPort || options.port === 0 || !isAddressInUse(error)) {
125
+ throw error;
126
+ }
127
+ return startCallbackServerOnPort({ ...options, port: 0 });
128
+ }
129
+ }
130
+
131
+ // packages/cli/oauth/pkce.ts
132
+ import { createHash, randomBytes } from "node:crypto";
133
+ var PKCE_VERIFIER_LENGTH = 64;
134
+ function generatePkceVerifier(length = PKCE_VERIFIER_LENGTH) {
135
+ if (length < 43 || length > 128) {
136
+ throw new Error("PKCE verifier length must be between 43 and 128 characters.");
137
+ }
138
+ return randomBytes(length).toString("base64url").slice(0, length);
139
+ }
140
+ function computePkceChallenge(verifier) {
141
+ return createHash("sha256").update(verifier).digest("base64url");
142
+ }
143
+ function generatePkcePair(length = PKCE_VERIFIER_LENGTH) {
144
+ const verifier = generatePkceVerifier(length);
145
+ return {
146
+ verifier,
147
+ challenge: computePkceChallenge(verifier),
148
+ method: "S256"
149
+ };
150
+ }
151
+
152
+ // packages/cli/oauth/flows/antigravity.ts
153
+ var decode = (s) => Buffer.from(s, "base64").toString("utf8");
154
+ var CLIENT_ID = decode(
155
+ "MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ=="
156
+ );
157
+ var CLIENT_SECRET = decode("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=");
158
+ var CALLBACK_PORT = 51121;
159
+ var CALLBACK_PATH = "/oauth-callback";
160
+ var SCOPES = [
161
+ "https://www.googleapis.com/auth/cloud-platform",
162
+ "https://www.googleapis.com/auth/userinfo.email",
163
+ "https://www.googleapis.com/auth/userinfo.profile",
164
+ "https://www.googleapis.com/auth/cclog",
165
+ "https://www.googleapis.com/auth/experimentsandconfigs"
166
+ ];
167
+ var AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
168
+ var TOKEN_URL = "https://oauth2.googleapis.com/token";
169
+ var USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json";
170
+ var CLOUD_CODE_ENDPOINT = "https://cloudcode-pa.googleapis.com";
171
+ var TIER_LEGACY = "legacy-tier";
172
+ var PROJECT_ONBOARD_MAX_ATTEMPTS = 5;
173
+ var PROJECT_ONBOARD_INTERVAL_MS = 2e3;
174
+ var ACCESS_TOKEN_CLIENT_SKEW_MS = 5 * 60 * 1e3;
175
+ var ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA = Object.freeze({
176
+ ideType: "ANTIGRAVITY",
177
+ platform: "PLATFORM_UNSPECIFIED",
178
+ pluginType: "GEMINI"
179
+ });
180
+ function readProjectId(value) {
181
+ if (typeof value === "string" && value.length > 0) {
182
+ return value;
183
+ }
184
+ if (value && typeof value === "object" && typeof value.id === "string" && value.id.length > 0) {
185
+ return value.id;
186
+ }
187
+ return void 0;
188
+ }
189
+ function getDefaultTierId(allowedTiers) {
190
+ if (!allowedTiers || allowedTiers.length === 0) {
191
+ return TIER_LEGACY;
192
+ }
193
+ const defaultTier = allowedTiers.find(
194
+ (tier) => tier.isDefault && typeof tier.id === "string" && tier.id.length > 0
195
+ );
196
+ if (defaultTier?.id) {
197
+ return defaultTier.id;
198
+ }
199
+ return TIER_LEGACY;
200
+ }
201
+ async function sleep(ms) {
202
+ const { promise, resolve } = Promise.withResolvers();
203
+ setTimeout(resolve, ms);
204
+ return promise;
205
+ }
206
+ async function onboardProjectWithRetries(fetchImpl, endpoint, headers, onboardBody, onProgress) {
207
+ for (let attempt = 1; attempt <= PROJECT_ONBOARD_MAX_ATTEMPTS; attempt += 1) {
208
+ if (attempt > 1) {
209
+ onProgress?.(
210
+ `Waiting for project provisioning (attempt ${attempt}/${PROJECT_ONBOARD_MAX_ATTEMPTS})...`
211
+ );
212
+ await sleep(PROJECT_ONBOARD_INTERVAL_MS);
213
+ }
214
+ const onboardResponse = await fetchImpl(
215
+ `${endpoint}/v1internal:onboardUser`,
216
+ {
217
+ method: "POST",
218
+ headers,
219
+ body: JSON.stringify(onboardBody)
220
+ }
221
+ );
222
+ if (!onboardResponse.ok) {
223
+ const errorText = await onboardResponse.text();
224
+ throw new Error(
225
+ `onboardUser failed: ${onboardResponse.status} ${onboardResponse.statusText}: ${errorText}`
226
+ );
227
+ }
228
+ const operation = await onboardResponse.json();
229
+ if (!operation.done) {
230
+ continue;
231
+ }
232
+ const projectId = readProjectId(operation.response?.cloudaicompanionProject);
233
+ if (projectId) {
234
+ return projectId;
235
+ }
236
+ }
237
+ throw new Error(
238
+ `onboardUser did not return a provisioned project id after ${PROJECT_ONBOARD_MAX_ATTEMPTS} attempts`
239
+ );
240
+ }
241
+ async function discoverProject(fetchImpl, accessToken, onProgress) {
242
+ const headers = {
243
+ Authorization: `Bearer ${accessToken}`,
244
+ "Content-Type": "application/json"
245
+ };
246
+ onProgress?.("Checking for existing project...");
247
+ const endpoint = CLOUD_CODE_ENDPOINT;
248
+ const loadResponse = await fetchImpl(`${endpoint}/v1internal:loadCodeAssist`, {
249
+ method: "POST",
250
+ headers,
251
+ body: JSON.stringify({
252
+ metadata: ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA
253
+ })
254
+ });
255
+ if (!loadResponse.ok) {
256
+ const errorText = await loadResponse.text();
257
+ throw new Error(
258
+ `loadCodeAssist failed: ${loadResponse.status} ${loadResponse.statusText}: ${errorText}`
259
+ );
260
+ }
261
+ const loadPayload = await loadResponse.json();
262
+ const existingProject = readProjectId(loadPayload.cloudaicompanionProject);
263
+ if (existingProject) {
264
+ return existingProject;
265
+ }
266
+ const tierId = getDefaultTierId(loadPayload.allowedTiers);
267
+ onProgress?.("Provisioning project...");
268
+ const onboardBody = {
269
+ tierId,
270
+ metadata: ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA
271
+ };
272
+ return onboardProjectWithRetries(
273
+ fetchImpl,
274
+ endpoint,
275
+ headers,
276
+ onboardBody,
277
+ onProgress
278
+ );
279
+ }
280
+ async function getUserEmail(fetchImpl, accessToken) {
281
+ try {
282
+ const response = await fetchImpl(USERINFO_URL, {
283
+ headers: { Authorization: `Bearer ${accessToken}` }
284
+ });
285
+ if (!response.ok) return void 0;
286
+ const data = await response.json();
287
+ return typeof data.email === "string" ? data.email : void 0;
288
+ } catch {
289
+ return void 0;
290
+ }
291
+ }
292
+ function generateState() {
293
+ const bytes = new Uint8Array(16);
294
+ crypto.getRandomValues(bytes);
295
+ return Array.from(bytes).map((value) => value.toString(16).padStart(2, "0")).join("");
296
+ }
297
+ async function runAntigravityOAuthLogin(deps = {}) {
298
+ const fetchImpl = deps.fetchImpl ?? fetch;
299
+ const output = deps.output ?? console;
300
+ const error = deps.error ?? console;
301
+ const redirectUri = `http://127.0.0.1:${CALLBACK_PORT}${CALLBACK_PATH}`;
302
+ let handle;
303
+ try {
304
+ handle = await startCallbackServer({
305
+ port: CALLBACK_PORT,
306
+ hostname: "127.0.0.1",
307
+ timeoutMs: 5 * 6e4
308
+ });
309
+ } catch (err) {
310
+ throw new Error(
311
+ `Failed to start Antigravity OAuth callback server on ${CALLBACK_PORT}: ${toErrorMessage(err)}`
312
+ );
313
+ }
314
+ try {
315
+ const state = generateState();
316
+ const pkce = await generatePkcePair();
317
+ const authParams = new URLSearchParams({
318
+ client_id: CLIENT_ID,
319
+ response_type: "code",
320
+ redirect_uri: redirectUri,
321
+ scope: SCOPES.join(" "),
322
+ state,
323
+ access_type: "offline",
324
+ prompt: "consent"
325
+ });
326
+ const authUrl = `${AUTH_URL}?${authParams.toString()}`;
327
+ output.log(
328
+ `Open the following URL in your browser to log in to Antigravity:
329
+ ${authUrl}`
330
+ );
331
+ if (deps.openBrowser) {
332
+ try {
333
+ await deps.openBrowser(authUrl);
334
+ } catch (err) {
335
+ error.error(
336
+ `Failed to open browser automatically: ${toErrorMessage(err)}`
337
+ );
338
+ }
339
+ }
340
+ const callback = await handle.waitForCode();
341
+ const tokenResponse = await fetchImpl(TOKEN_URL, {
342
+ method: "POST",
343
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
344
+ body: new URLSearchParams({
345
+ client_id: CLIENT_ID,
346
+ client_secret: CLIENT_SECRET,
347
+ code: callback.code,
348
+ grant_type: "authorization_code",
349
+ redirect_uri: redirectUri
350
+ })
351
+ });
352
+ if (!tokenResponse.ok) {
353
+ const body = await tokenResponse.text();
354
+ throw new Error(`Antigravity token exchange failed: ${body}`);
355
+ }
356
+ const tokenData = await tokenResponse.json();
357
+ if (!tokenData.refresh_token) {
358
+ throw new Error("No refresh token received. Please try again.");
359
+ }
360
+ const email = await getUserEmail(fetchImpl, tokenData.access_token);
361
+ const projectId = await discoverProject(
362
+ fetchImpl,
363
+ tokenData.access_token,
364
+ (message) => output.log(message)
365
+ );
366
+ const now = (deps.now ?? Date.now)();
367
+ return {
368
+ provider: "antigravity",
369
+ accessToken: tokenData.access_token,
370
+ refreshToken: tokenData.refresh_token,
371
+ expiresAt: now + tokenData.expires_in * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS,
372
+ scope: tokenData.scope,
373
+ obtainedAt: now,
374
+ metadata: {
375
+ projectId,
376
+ email
377
+ }
378
+ };
379
+ } finally {
380
+ await handle.close();
381
+ }
382
+ }
383
+ async function refreshAntigravityOAuthToken(credential) {
384
+ if (!credential.refreshToken) {
385
+ throw new Error("Antigravity credential has no refresh_token");
386
+ }
387
+ const fetchImpl = fetch;
388
+ const projectId = credential.metadata?.projectId ?? void 0;
389
+ const response = await fetchImpl(TOKEN_URL, {
390
+ method: "POST",
391
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
392
+ body: new URLSearchParams({
393
+ client_id: CLIENT_ID,
394
+ client_secret: CLIENT_SECRET,
395
+ refresh_token: credential.refreshToken,
396
+ grant_type: "refresh_token"
397
+ })
398
+ });
399
+ if (!response.ok) {
400
+ const body = await response.text();
401
+ throw new Error(`Antigravity token refresh failed: ${body}`);
402
+ }
403
+ const data = await response.json();
404
+ const now = Date.now();
405
+ return {
406
+ ...credential,
407
+ accessToken: data.access_token,
408
+ refreshToken: data.refresh_token || credential.refreshToken,
409
+ expiresAt: now + data.expires_in * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS,
410
+ scope: data.scope ?? credential.scope,
411
+ obtainedAt: now
412
+ };
413
+ }
414
+
415
+ // packages/cli/oauth/flows/openai-codex.ts
416
+ import { createHash as createHash2 } from "node:crypto";
417
+ var OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
418
+ var OPENAI_CODEX_SCOPES = [
419
+ "openid",
420
+ "profile",
421
+ "email",
422
+ "offline_access",
423
+ "api.connectors.read",
424
+ "api.connectors.invoke"
425
+ ];
426
+ var OPENAI_CODEX_DEVICE_AUTH_URL = "https://auth.openai.com/api/accounts/deviceauth/usercode";
427
+ var OPENAI_CODEX_DEVICE_TOKEN_URL = "https://auth.openai.com/api/accounts/deviceauth/token";
428
+ var OPENAI_CODEX_DEVICE_VERIFICATION_URL = "https://auth.openai.com/codex/device";
429
+ var OPENAI_CODEX_DEVICE_REDIRECT_URI = "https://auth.openai.com/deviceauth/callback";
430
+ var OPENAI_CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token";
431
+ var OPENAI_CODEX_AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
432
+ var OPENAI_CODEX_ORIGINATOR = "codex_cli_rs";
433
+ var OPENAI_CODEX_CALLBACK_PORT = 1455;
434
+ var OPENAI_CODEX_CALLBACK_PATH = "/auth/callback";
435
+ var OPENAI_CODEX_CALLBACK_URL = `http://localhost:${OPENAI_CODEX_CALLBACK_PORT}${OPENAI_CODEX_CALLBACK_PATH}`;
436
+ var AUTHORIZATION_CODE_GRANT_TYPE = "authorization_code";
437
+ var REFRESH_TOKEN_GRANT_TYPE = "refresh_token";
438
+ var DEVICE_CODE_DEFAULT_INTERVAL_MS = 5e3;
439
+ var DEVICE_POLL_SAFETY_MARGIN_MS = 3e3;
440
+ var DEVICE_MAX_POLLS = 120;
441
+ async function postJson(fetchImpl, url, body, deps) {
442
+ const response = await fetchImpl(url, {
443
+ method: "POST",
444
+ headers: {
445
+ "Content-Type": "application/json",
446
+ Accept: "application/json"
447
+ },
448
+ body: JSON.stringify(body)
449
+ });
450
+ deps?.output?.log?.(`[oauth:openai-codex] POST ${url} -> ${response.status}`);
451
+ return response;
452
+ }
453
+ async function postForm(fetchImpl, url, body, deps) {
454
+ const response = await fetchImpl(url, {
455
+ method: "POST",
456
+ headers: {
457
+ "Content-Type": "application/x-www-form-urlencoded",
458
+ Accept: "application/json"
459
+ },
460
+ body: new URLSearchParams(body).toString()
461
+ });
462
+ deps?.output?.log?.(`[oauth:openai-codex] POST ${url} -> ${response.status}`);
463
+ return response;
464
+ }
465
+ async function parseJsonResponse(response) {
466
+ const text = await response.text().catch(() => "");
467
+ if (!text) return {};
468
+ try {
469
+ return JSON.parse(text);
470
+ } catch {
471
+ return {};
472
+ }
473
+ }
474
+ function scopeString(scopes = OPENAI_CODEX_SCOPES) {
475
+ return scopes.join(" ");
476
+ }
477
+ function base64UrlDecode(value) {
478
+ const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
479
+ const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
480
+ return Buffer.from(padded, "base64").toString("utf8");
481
+ }
482
+ var OPENAI_AUTH_CLAIM = "https://api.openai.com/auth";
483
+ var OPENAI_PROFILE_CLAIM = "https://api.openai.com/profile";
484
+ function readClaimString(claim, key) {
485
+ if (!claim || typeof claim !== "object") return void 0;
486
+ const value = claim[key];
487
+ return typeof value === "string" && value ? value : void 0;
488
+ }
489
+ function decodeOpenAiIdToken(idToken) {
490
+ if (!idToken) return {};
491
+ const parts = idToken.split(".");
492
+ if (parts.length < 2) return {};
493
+ try {
494
+ const payload = JSON.parse(base64UrlDecode(parts[1]));
495
+ const authClaim = payload[OPENAI_AUTH_CLAIM];
496
+ const profileClaim = payload[OPENAI_PROFILE_CLAIM];
497
+ return {
498
+ chatgptAccountId: readClaimString(authClaim, "chatgpt_account_id") ?? (typeof payload["chatgpt_account_id"] === "string" ? payload["chatgpt_account_id"] : void 0),
499
+ email: readClaimString(profileClaim, "email") ?? (typeof payload["email"] === "string" ? payload["email"] : void 0),
500
+ payload
501
+ };
502
+ } catch {
503
+ return {};
504
+ }
505
+ }
506
+ function tokenResponseToCredential(token, now) {
507
+ const fromAccess = decodeOpenAiIdToken(token.accessToken);
508
+ const fromId = decodeOpenAiIdToken(token.idToken);
509
+ const decoded = {
510
+ chatgptAccountId: fromAccess.chatgptAccountId ?? fromId.chatgptAccountId,
511
+ email: fromAccess.email ?? fromId.email
512
+ };
513
+ const expiresAt = typeof token.expiresIn === "number" && token.expiresIn > 0 ? now + token.expiresIn * 1e3 : void 0;
514
+ return {
515
+ provider: "chatgpt",
516
+ accessToken: token.accessToken,
517
+ ...token.refreshToken ? { refreshToken: token.refreshToken } : {},
518
+ ...expiresAt ? { expiresAt } : {},
519
+ ...token.scope ? { scope: token.scope } : {},
520
+ ...token.idToken ? { idToken: token.idToken } : {},
521
+ ...decoded.chatgptAccountId ? { accountId: decoded.chatgptAccountId } : {},
522
+ obtainedAt: now
523
+ };
524
+ }
525
+ function extractTokenResponse(data) {
526
+ const accessToken = data["access_token"];
527
+ if (typeof accessToken !== "string" || !accessToken) return null;
528
+ const expiresInRaw = data["expires_in"];
529
+ return {
530
+ accessToken,
531
+ ...typeof data["refresh_token"] === "string" ? { refreshToken: data["refresh_token"] } : {},
532
+ ...typeof expiresInRaw === "number" ? { expiresIn: expiresInRaw } : {},
533
+ ...typeof data["scope"] === "string" ? { scope: data["scope"] } : {},
534
+ ...typeof data["id_token"] === "string" ? { idToken: data["id_token"] } : {}
535
+ };
536
+ }
537
+ async function startDeviceCodeFlow(deps) {
538
+ const fetchImpl = deps.fetchImpl ?? fetch;
539
+ const response = await postJson(
540
+ fetchImpl,
541
+ OPENAI_CODEX_DEVICE_AUTH_URL,
542
+ { client_id: OPENAI_CODEX_CLIENT_ID },
543
+ deps
544
+ );
545
+ if (!response.ok) {
546
+ const data2 = await parseJsonResponse(response);
547
+ throw new Error(
548
+ `OpenAI device-code start failed (HTTP ${response.status}): ${JSON.stringify(data2)}`
549
+ );
550
+ }
551
+ const data = await parseJsonResponse(response);
552
+ const deviceAuthId = data["device_auth_id"];
553
+ const userCode = data["user_code"];
554
+ if (typeof deviceAuthId !== "string" || !deviceAuthId) {
555
+ throw new Error("OpenAI device-code start response did not include a device_auth_id.");
556
+ }
557
+ if (typeof userCode !== "string" || !userCode) {
558
+ throw new Error("OpenAI device-code start response did not include a user_code.");
559
+ }
560
+ const intervalRaw = data["interval"];
561
+ const intervalSec = typeof intervalRaw === "number" ? intervalRaw : Number.parseInt(String(intervalRaw ?? "5"), 10) || 5;
562
+ return {
563
+ deviceAuthId,
564
+ userCode,
565
+ intervalMs: Math.max(1, intervalSec) * 1e3 + DEVICE_POLL_SAFETY_MARGIN_MS
566
+ };
567
+ }
568
+ async function pollDeviceCodeToken(args) {
569
+ const fetchImpl = args.deps?.fetchImpl ?? fetch;
570
+ const sleep3 = args.sleep ?? args.deps?.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
571
+ const intervalMs = args.intervalMs ?? DEVICE_CODE_DEFAULT_INTERVAL_MS;
572
+ const maxPolls = args.maxPolls ?? DEVICE_MAX_POLLS;
573
+ for (let poll = 0; poll < maxPolls; poll++) {
574
+ await sleep3(poll === 0 ? Math.min(intervalMs, DEVICE_CODE_DEFAULT_INTERVAL_MS) : intervalMs);
575
+ const response = await postJson(
576
+ fetchImpl,
577
+ OPENAI_CODEX_DEVICE_TOKEN_URL,
578
+ { device_auth_id: args.deviceAuthId, user_code: args.userCode },
579
+ args.deps
580
+ );
581
+ if (response.status === 403 || response.status === 404) {
582
+ args.onPending?.();
583
+ continue;
584
+ }
585
+ const data = await parseJsonResponse(response);
586
+ if (!response.ok) {
587
+ throw new Error(
588
+ `OpenAI device-code poll failed (HTTP ${response.status}): ${JSON.stringify(data)}`
589
+ );
590
+ }
591
+ const authorizationCode = data["authorization_code"];
592
+ const codeVerifier = data["code_verifier"];
593
+ if (typeof authorizationCode !== "string" || !authorizationCode) {
594
+ throw new Error("OpenAI device-code poll response missing authorization_code.");
595
+ }
596
+ if (typeof codeVerifier !== "string" || !codeVerifier) {
597
+ throw new Error("OpenAI device-code poll response missing code_verifier.");
598
+ }
599
+ return { authorizationCode, codeVerifier };
600
+ }
601
+ throw new Error("OpenAI device-code flow timed out waiting for user authorization.");
602
+ }
603
+ async function runOpenAiCodexDeviceCode(deps = {}) {
604
+ const now = deps.now ?? Date.now;
605
+ const start = await startDeviceCodeFlow(deps);
606
+ deps.output?.log?.("Authorize nolo-cli for ChatGPT / OpenAI Codex:");
607
+ deps.output?.log?.(OPENAI_CODEX_DEVICE_VERIFICATION_URL);
608
+ deps.output?.log?.(`Enter code: ${start.userCode}`);
609
+ if (deps.openBrowser) {
610
+ const opened = await deps.openBrowser(OPENAI_CODEX_DEVICE_VERIFICATION_URL);
611
+ if (!opened) deps.output?.log?.("Could not open a browser automatically. Open the URL above.");
612
+ } else {
613
+ deps.output?.log?.("Open the URL above in a browser and enter the code to approve.");
614
+ }
615
+ const { authorizationCode, codeVerifier } = await pollDeviceCodeToken({
616
+ deviceAuthId: start.deviceAuthId,
617
+ userCode: start.userCode,
618
+ deps,
619
+ intervalMs: start.intervalMs,
620
+ sleep: deps.sleep,
621
+ onPending: () => {
622
+ deps.output?.log?.("[oauth:openai-codex] Waiting for authorization...");
623
+ }
624
+ });
625
+ const credential = await exchangeCodexAuthorizationCode({
626
+ code: authorizationCode,
627
+ codeVerifier,
628
+ redirectUri: OPENAI_CODEX_DEVICE_REDIRECT_URI,
629
+ deps,
630
+ now
631
+ });
632
+ writeOAuthCredential("chatgpt", credential);
633
+ deps.output?.log?.(
634
+ `Saved ChatGPT OAuth credential (account=${credential.accountId ?? "unknown"}).`
635
+ );
636
+ return credential;
637
+ }
638
+ async function exchangeCodexAuthorizationCode(args) {
639
+ const fetchImpl = args.deps?.fetchImpl ?? fetch;
640
+ const now = args.now ?? Date.now;
641
+ const response = await postForm(
642
+ fetchImpl,
643
+ OPENAI_CODEX_TOKEN_URL,
644
+ {
645
+ grant_type: AUTHORIZATION_CODE_GRANT_TYPE,
646
+ code: args.code,
647
+ redirect_uri: args.redirectUri,
648
+ client_id: OPENAI_CODEX_CLIENT_ID,
649
+ code_verifier: args.codeVerifier
650
+ },
651
+ args.deps
652
+ );
653
+ const data = await parseJsonResponse(response);
654
+ if (!response.ok) {
655
+ throw new Error(
656
+ `OpenAI authorization-code exchange failed (HTTP ${response.status}): ${JSON.stringify(data)}`
657
+ );
658
+ }
659
+ const token = extractTokenResponse(data);
660
+ if (!token) {
661
+ throw new Error(`OpenAI authorization-code exchange returned no token: ${JSON.stringify(data)}`);
662
+ }
663
+ return tokenResponseToCredential(token, now());
664
+ }
665
+ async function exchangeAuthorizationCode(args) {
666
+ return exchangeCodexAuthorizationCode({
667
+ code: args.code,
668
+ codeVerifier: args.pkce.verifier,
669
+ redirectUri: OPENAI_CODEX_CALLBACK_URL,
670
+ deps: args.deps,
671
+ now: args.now
672
+ });
673
+ }
674
+ async function runOpenAiCodexBrowserPkce(deps = {}) {
675
+ const now = deps.now ?? Date.now;
676
+ const pkce = generatePkcePair();
677
+ const state = createHash2("sha256").update(`${now()}-${Math.random()}`).digest("base64url");
678
+ const callback = await startCallbackServer({
679
+ port: OPENAI_CODEX_CALLBACK_PORT,
680
+ now
681
+ });
682
+ try {
683
+ const authorizeUrl = new URL(OPENAI_CODEX_AUTHORIZE_URL);
684
+ authorizeUrl.searchParams.set("response_type", "code");
685
+ authorizeUrl.searchParams.set("client_id", OPENAI_CODEX_CLIENT_ID);
686
+ authorizeUrl.searchParams.set("redirect_uri", OPENAI_CODEX_CALLBACK_URL);
687
+ authorizeUrl.searchParams.set("scope", scopeString());
688
+ authorizeUrl.searchParams.set("code_challenge", pkce.challenge);
689
+ authorizeUrl.searchParams.set("code_challenge_method", pkce.method);
690
+ authorizeUrl.searchParams.set("id_token_add_organizations", "true");
691
+ authorizeUrl.searchParams.set("codex_cli_simplified_flow", "true");
692
+ authorizeUrl.searchParams.set("originator", OPENAI_CODEX_ORIGINATOR);
693
+ authorizeUrl.searchParams.set("state", state);
694
+ const authorizeUrlString = authorizeUrl.toString();
695
+ deps.output?.log?.("Authorize nolo-cli for ChatGPT / OpenAI Codex:");
696
+ deps.output?.log?.(authorizeUrlString);
697
+ if (deps.openBrowser) {
698
+ const opened = await deps.openBrowser(authorizeUrlString);
699
+ if (!opened) deps.output?.log?.("Could not open a browser automatically. Open the URL above.");
700
+ }
701
+ const result = await callback.waitForCode();
702
+ if (result.state && result.state !== state) {
703
+ throw new Error("OpenAI browser PKCE flow state mismatch.");
704
+ }
705
+ const credential = await exchangeAuthorizationCode({
706
+ code: result.code,
707
+ state,
708
+ pkce,
709
+ deps,
710
+ now
711
+ });
712
+ writeOAuthCredential("chatgpt", credential);
713
+ deps.output?.log?.(
714
+ `Saved ChatGPT OAuth credential (account=${credential.accountId ?? "unknown"}).`
715
+ );
716
+ return credential;
717
+ } finally {
718
+ await callback.close().catch(() => {
719
+ });
720
+ }
721
+ }
722
+ var refreshOpenAiCodexToken = async (credential, deps = {}) => {
723
+ if (!credential.refreshToken) {
724
+ throw new Error("Cannot refresh ChatGPT OAuth token without a refresh_token.");
725
+ }
726
+ const fetchImpl = deps.fetchImpl ?? fetch;
727
+ const now = deps.now ?? Date.now;
728
+ const response = await postForm(
729
+ fetchImpl,
730
+ OPENAI_CODEX_TOKEN_URL,
731
+ {
732
+ grant_type: REFRESH_TOKEN_GRANT_TYPE,
733
+ refresh_token: credential.refreshToken,
734
+ client_id: OPENAI_CODEX_CLIENT_ID,
735
+ scope: scopeString()
736
+ },
737
+ deps
738
+ );
739
+ const data = await parseJsonResponse(response);
740
+ if (!response.ok) {
741
+ throw new Error(
742
+ `OpenAI token refresh failed (HTTP ${response.status}): ${JSON.stringify(data)}`
743
+ );
744
+ }
745
+ const token = extractTokenResponse(data);
746
+ if (!token) {
747
+ throw new Error(`OpenAI token refresh returned no token: ${JSON.stringify(data)}`);
748
+ }
749
+ const refreshed = tokenResponseToCredential(token, now());
750
+ return {
751
+ ...refreshed,
752
+ refreshToken: refreshed.refreshToken ?? credential.refreshToken,
753
+ accountId: refreshed.accountId ?? credential.accountId
754
+ };
755
+ };
756
+
757
+ // packages/cli/oauth/flows/xai.ts
758
+ var XAI_OAUTH_ISSUER = "https://auth.x.ai";
759
+ var XAI_OAUTH_DISCOVERY_URL = `${XAI_OAUTH_ISSUER}/.well-known/openid-configuration`;
760
+ var XAI_OAUTH_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
761
+ var XAI_OAUTH_SCOPE = "openid profile email offline_access grok-cli:access api:access";
762
+ var XAI_OAUTH_REDIRECT_HOST = "127.0.0.1";
763
+ var XAI_OAUTH_REDIRECT_PORT = 56121;
764
+ var XAI_OAUTH_REDIRECT_PATH = "/callback";
765
+ var XAI_OAUTH_DOCS_URL = "https://hermes-agent.nousresearch.com/docs/guides/xai-grok-oauth";
766
+ var XAI_DEVICE_CODE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
767
+ var XAI_DEVICE_CODE_DEFAULT_INTERVAL_MS = 5e3;
768
+ var XAI_DEVICE_CODE_MIN_INTERVAL_MS = 1e3;
769
+ var XAI_DEVICE_CODE_SLOW_DOWN_INCREMENT_MS = 5e3;
770
+ var XAI_DEVICE_CODE_DEFAULT_EXPIRES_MS = 5 * 6e4;
771
+ var ACCESS_TOKEN_CLIENT_SKEW_MS2 = 5 * 60 * 1e3;
772
+ var DISCOVERY_TIMEOUT_MS = 15e3;
773
+ var TOKEN_REQUEST_TIMEOUT_MS = 2e4;
774
+ function validateXAIEndpoint(url, field) {
775
+ let parsed;
776
+ try {
777
+ parsed = new URL(url);
778
+ } catch {
779
+ throw new Error(`Invalid xAI ${field}: ${url}`);
780
+ }
781
+ if (parsed.protocol !== "https:") {
782
+ throw new Error(`Invalid xAI ${field}: ${url}`);
783
+ }
784
+ const host = parsed.hostname.toLowerCase();
785
+ if (!host || host !== "x.ai" && !host.endsWith(".x.ai")) {
786
+ throw new Error(`Invalid xAI ${field}: ${url}`);
787
+ }
788
+ return url;
789
+ }
790
+ function isLikelyHtmlOrCloudflareChallenge(response, bodyText) {
791
+ const contentType = response.headers.get("content-type") ?? "";
792
+ return response.headers.get("cf-mitigated") === "challenge" || /text\/html/i.test(contentType) || /<!doctype html|<html\b/i.test(bodyText) || /\b(?:cloudflare|attention required|just a moment|enable javascript and cookies|challenge-platform)\b/i.test(
793
+ bodyText
794
+ );
795
+ }
796
+ async function readJsonBody(response, context) {
797
+ let text = "";
798
+ try {
799
+ text = await response.text();
800
+ } catch (error) {
801
+ throw new Error(
802
+ `${context}: failed to read response body: ${toErrorMessage(error)}`
803
+ );
804
+ }
805
+ if (!text.trim()) {
806
+ if (!response.ok) {
807
+ throw new Error(`${context} failed (${response.status}): empty response body`);
808
+ }
809
+ return {};
810
+ }
811
+ try {
812
+ const parsed = JSON.parse(text);
813
+ if (!isRecord(parsed)) {
814
+ throw new Error(`${context} returned non-object JSON`);
815
+ }
816
+ return parsed;
817
+ } catch (error) {
818
+ if (error instanceof Error && error.message.startsWith(context)) {
819
+ throw error;
820
+ }
821
+ if (isLikelyHtmlOrCloudflareChallenge(response, text)) {
822
+ throw new Error(
823
+ `${context} failed (${response.status}): xAI returned an HTML/Cloudflare challenge instead of OAuth JSON`
824
+ );
825
+ }
826
+ throw new Error(
827
+ `${context} returned invalid JSON: ${toErrorMessage(error)}`
828
+ );
829
+ }
830
+ }
831
+ async function xaiOAuthDiscovery(fetchImpl, options = {}) {
832
+ const timeoutMs = options.timeoutMs ?? DISCOVERY_TIMEOUT_MS;
833
+ let response;
834
+ try {
835
+ response = await fetchImpl(XAI_OAUTH_DISCOVERY_URL, {
836
+ method: "GET",
837
+ headers: { Accept: "application/json" },
838
+ signal: AbortSignal.timeout(timeoutMs)
839
+ });
840
+ } catch (error) {
841
+ throw new Error(
842
+ `xAI OIDC discovery failed: ${toErrorMessage(error)}`
843
+ );
844
+ }
845
+ if (response.status !== 200) {
846
+ throw new Error(`xAI OIDC discovery returned status ${response.status}.`);
847
+ }
848
+ const obj = await readJsonBody(response, "xAI OIDC discovery");
849
+ const authorizationEndpoint = asTrimmedString(obj.authorization_endpoint);
850
+ const tokenEndpoint = asTrimmedString(obj.token_endpoint);
851
+ if (!authorizationEndpoint || !tokenEndpoint) {
852
+ throw new Error("xAI OIDC discovery response was missing required endpoints.");
853
+ }
854
+ validateXAIEndpoint(authorizationEndpoint, "authorization_endpoint");
855
+ validateXAIEndpoint(tokenEndpoint, "token_endpoint");
856
+ const deviceAuthorizationEndpoint = asTrimmedString(
857
+ obj.device_authorization_endpoint
858
+ );
859
+ if (options.requireDeviceAuthorization) {
860
+ if (!deviceAuthorizationEndpoint) {
861
+ throw new Error(
862
+ "xAI OIDC discovery response was missing device_authorization_endpoint."
863
+ );
864
+ }
865
+ validateXAIEndpoint(deviceAuthorizationEndpoint, "device_authorization_endpoint");
866
+ } else if (deviceAuthorizationEndpoint) {
867
+ validateXAIEndpoint(deviceAuthorizationEndpoint, "device_authorization_endpoint");
868
+ }
869
+ return {
870
+ authorization_endpoint: authorizationEndpoint,
871
+ token_endpoint: tokenEndpoint,
872
+ ...deviceAuthorizationEndpoint ? { device_authorization_endpoint: deviceAuthorizationEndpoint } : {}
873
+ };
874
+ }
875
+ function buildXAIAuthorizeUrl(opts) {
876
+ const params = new URLSearchParams({
877
+ response_type: "code",
878
+ client_id: XAI_OAUTH_CLIENT_ID,
879
+ redirect_uri: opts.redirectUri,
880
+ scope: XAI_OAUTH_SCOPE,
881
+ code_challenge: opts.codeChallenge,
882
+ code_challenge_method: "S256",
883
+ state: opts.state,
884
+ nonce: opts.nonce,
885
+ plan: "generic",
886
+ referrer: "nolo-cli"
887
+ });
888
+ return `${opts.authorizationEndpoint}?${params.toString()}`;
889
+ }
890
+ function generateState2() {
891
+ const bytes = new Uint8Array(16);
892
+ crypto.getRandomValues(bytes);
893
+ return Array.from(bytes).map((value) => value.toString(16).padStart(2, "0")).join("");
894
+ }
895
+ function parsePositiveSecondsToMs(value, fallbackMs) {
896
+ const sec = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : NaN;
897
+ if (!Number.isFinite(sec) || sec <= 0) return fallbackMs;
898
+ return Math.max(1, Math.floor(sec * 1e3));
899
+ }
900
+ function parseOAuthTokenPayload(data, context, options = {}) {
901
+ if (typeof data.access_token !== "string" || !data.access_token) {
902
+ throw new Error(`${context} missing access_token`);
903
+ }
904
+ const refreshToken = typeof data.refresh_token === "string" && data.refresh_token ? data.refresh_token : void 0;
905
+ if (options.requireRefreshToken && !refreshToken) {
906
+ throw new Error(`${context} missing refresh_token`);
907
+ }
908
+ const expiresIn = asOptionalFiniteNumber(data.expires_in);
909
+ if (expiresIn === void 0) {
910
+ throw new Error(`${context} missing expires_in`);
911
+ }
912
+ return {
913
+ accessToken: data.access_token,
914
+ ...refreshToken ? { refreshToken } : {},
915
+ expiresIn,
916
+ scope: typeof data.scope === "string" ? data.scope : void 0,
917
+ idToken: typeof data.id_token === "string" ? data.id_token : void 0
918
+ };
919
+ }
920
+ function tokenToCredential(token, now) {
921
+ const expiresIn = token.expiresIn;
922
+ return {
923
+ provider: "xai",
924
+ accessToken: token.accessToken,
925
+ refreshToken: token.refreshToken,
926
+ expiresAt: now + expiresIn * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS2,
927
+ scope: token.scope,
928
+ idToken: token.idToken,
929
+ obtainedAt: now
930
+ };
931
+ }
932
+ async function exchangeXAIToken(fetchImpl, code, redirectUri, verifier) {
933
+ const discovery = await xaiOAuthDiscovery(fetchImpl);
934
+ const tokenEndpoint = validateXAIEndpoint(discovery.token_endpoint, "token_endpoint");
935
+ const body = new URLSearchParams({
936
+ grant_type: "authorization_code",
937
+ client_id: XAI_OAUTH_CLIENT_ID,
938
+ code,
939
+ redirect_uri: redirectUri,
940
+ code_verifier: verifier
941
+ });
942
+ let response;
943
+ try {
944
+ response = await fetchImpl(tokenEndpoint, {
945
+ method: "POST",
946
+ headers: {
947
+ "Content-Type": "application/x-www-form-urlencoded",
948
+ Accept: "application/json"
949
+ },
950
+ body,
951
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS)
952
+ });
953
+ } catch (error) {
954
+ throw new Error(
955
+ `xAI token exchange failed: ${toErrorMessage(error)}`
956
+ );
957
+ }
958
+ const data = await readJsonBody(response, "xAI token exchange");
959
+ if (!response.ok) {
960
+ const detail = typeof data.error_description === "string" && data.error_description || typeof data.error === "string" && data.error || "";
961
+ throw new Error(
962
+ `xAI token exchange failed: ${response.status}${detail ? ` ${detail}` : ""}`
963
+ );
964
+ }
965
+ return parseOAuthTokenPayload(data, "xAI token exchange response", {
966
+ requireRefreshToken: true
967
+ });
968
+ }
969
+ async function requestXaiDeviceCode(fetchImpl, deviceAuthorizationEndpoint) {
970
+ const endpoint = validateXAIEndpoint(
971
+ deviceAuthorizationEndpoint,
972
+ "device_authorization_endpoint"
973
+ );
974
+ let response;
975
+ try {
976
+ response = await fetchImpl(endpoint, {
977
+ method: "POST",
978
+ headers: {
979
+ "Content-Type": "application/x-www-form-urlencoded",
980
+ Accept: "application/json"
981
+ },
982
+ body: new URLSearchParams({
983
+ client_id: XAI_OAUTH_CLIENT_ID,
984
+ scope: XAI_OAUTH_SCOPE
985
+ }),
986
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS)
987
+ });
988
+ } catch (error) {
989
+ throw new Error(
990
+ `xAI device code request failed: ${toErrorMessage(error)}`
991
+ );
992
+ }
993
+ const data = await readJsonBody(response, "xAI device code request");
994
+ if (!response.ok) {
995
+ const detail = typeof data.error_description === "string" && data.error_description || typeof data.error === "string" && data.error || "";
996
+ throw new Error(
997
+ `xAI device code request failed (${response.status})${detail ? `: ${detail}` : ""}`
998
+ );
999
+ }
1000
+ const deviceCode = asTrimmedString(data.device_code);
1001
+ const userCode = asTrimmedString(data.user_code);
1002
+ const verificationUri = asTrimmedString(data.verification_uri);
1003
+ const verificationUriComplete = asTrimmedString(
1004
+ data.verification_uri_complete
1005
+ );
1006
+ if (!deviceCode || !userCode || !verificationUri) {
1007
+ throw new Error(
1008
+ "xAI device code response is missing device_code, user_code, or verification_uri"
1009
+ );
1010
+ }
1011
+ validateXAIEndpoint(verificationUri, "verification_uri");
1012
+ if (verificationUriComplete) {
1013
+ validateXAIEndpoint(verificationUriComplete, "verification_uri_complete");
1014
+ }
1015
+ return {
1016
+ deviceCode,
1017
+ userCode,
1018
+ verificationUri,
1019
+ ...verificationUriComplete ? { verificationUriComplete } : {},
1020
+ expiresInMs: parsePositiveSecondsToMs(
1021
+ data.expires_in,
1022
+ XAI_DEVICE_CODE_DEFAULT_EXPIRES_MS
1023
+ ),
1024
+ intervalMs: Math.max(
1025
+ parsePositiveSecondsToMs(data.interval, XAI_DEVICE_CODE_DEFAULT_INTERVAL_MS),
1026
+ XAI_DEVICE_CODE_MIN_INTERVAL_MS
1027
+ )
1028
+ };
1029
+ }
1030
+ async function pollXaiDeviceCodeToken(args) {
1031
+ const tokenEndpoint = validateXAIEndpoint(args.tokenEndpoint, "token_endpoint");
1032
+ const deadlineMs = args.now() + args.expiresInMs;
1033
+ let intervalMs = args.intervalMs;
1034
+ while (args.now() < deadlineMs) {
1035
+ const remainingMs = Math.max(0, deadlineMs - args.now());
1036
+ const delayMs = Math.min(Math.max(intervalMs, XAI_DEVICE_CODE_MIN_INTERVAL_MS), remainingMs);
1037
+ if (delayMs > 0) {
1038
+ await args.sleep(delayMs);
1039
+ }
1040
+ if (args.now() >= deadlineMs) break;
1041
+ let response;
1042
+ try {
1043
+ response = await args.fetchImpl(tokenEndpoint, {
1044
+ method: "POST",
1045
+ headers: {
1046
+ "Content-Type": "application/x-www-form-urlencoded",
1047
+ Accept: "application/json"
1048
+ },
1049
+ body: new URLSearchParams({
1050
+ grant_type: XAI_DEVICE_CODE_GRANT_TYPE,
1051
+ client_id: XAI_OAUTH_CLIENT_ID,
1052
+ device_code: args.deviceCode
1053
+ }),
1054
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS)
1055
+ });
1056
+ } catch (error) {
1057
+ const message = toErrorMessage(error);
1058
+ if (/abort|timeout/i.test(message)) {
1059
+ throw new Error(`xAI device token poll aborted or timed out: ${message}`);
1060
+ }
1061
+ throw new Error(`xAI device token poll failed: ${message}`);
1062
+ }
1063
+ let data;
1064
+ try {
1065
+ data = await readJsonBody(response, "xAI device token exchange");
1066
+ } catch (error) {
1067
+ throw error;
1068
+ }
1069
+ if (response.ok) {
1070
+ return parseOAuthTokenPayload(data, "xAI device token exchange response", {
1071
+ requireRefreshToken: true
1072
+ });
1073
+ }
1074
+ const oauthError = typeof data.error === "string" ? data.error : "";
1075
+ if (oauthError === "authorization_pending") {
1076
+ continue;
1077
+ }
1078
+ if (oauthError === "slow_down") {
1079
+ intervalMs += XAI_DEVICE_CODE_SLOW_DOWN_INCREMENT_MS;
1080
+ continue;
1081
+ }
1082
+ if (oauthError === "access_denied" || oauthError === "authorization_denied") {
1083
+ throw new Error("xAI device authorization was denied");
1084
+ }
1085
+ if (oauthError === "expired_token") {
1086
+ throw new Error("xAI device code expired. Re-run the login.");
1087
+ }
1088
+ const detail = typeof data.error_description === "string" && data.error_description || oauthError || "";
1089
+ throw new Error(
1090
+ `xAI device token exchange failed (${response.status})${detail ? `: ${detail}` : ""}`
1091
+ );
1092
+ }
1093
+ throw new Error("xAI device authorization timed out");
1094
+ }
1095
+ async function runXaiOAuthLogin(deps = {}) {
1096
+ const fetchImpl = deps.fetchImpl ?? fetch;
1097
+ const output = deps.output ?? console;
1098
+ const error = deps.error ?? console;
1099
+ const redirectUri = `http://${XAI_OAUTH_REDIRECT_HOST}:${XAI_OAUTH_REDIRECT_PORT}${XAI_OAUTH_REDIRECT_PATH}`;
1100
+ let handle;
1101
+ try {
1102
+ handle = await startCallbackServer({
1103
+ port: XAI_OAUTH_REDIRECT_PORT,
1104
+ hostname: XAI_OAUTH_REDIRECT_HOST,
1105
+ timeoutMs: 5 * 6e4
1106
+ });
1107
+ } catch (err) {
1108
+ throw new Error(
1109
+ `Failed to start xAI OAuth callback server on ${XAI_OAUTH_REDIRECT_PORT}: ${toErrorMessage(err)}`
1110
+ );
1111
+ }
1112
+ try {
1113
+ const state = generateState2();
1114
+ const pkce = await generatePkcePair();
1115
+ const nonce = crypto.randomUUID().replace(/-/g, "");
1116
+ const discovery = await xaiOAuthDiscovery(fetchImpl);
1117
+ const authUrl = buildXAIAuthorizeUrl({
1118
+ authorizationEndpoint: discovery.authorization_endpoint,
1119
+ redirectUri,
1120
+ codeChallenge: pkce.challenge,
1121
+ state,
1122
+ nonce
1123
+ });
1124
+ output.log(
1125
+ `Open the following URL in your browser to log in to xAI Grok (SuperGrok):
1126
+ ${authUrl}
1127
+
1128
+ Docs: ${XAI_OAUTH_DOCS_URL}`
1129
+ );
1130
+ if (deps.openBrowser) {
1131
+ try {
1132
+ await deps.openBrowser(authUrl);
1133
+ } catch (err) {
1134
+ error.error(
1135
+ `Failed to open browser automatically: ${toErrorMessage(err)}`
1136
+ );
1137
+ }
1138
+ }
1139
+ const callback = await handle.waitForCode();
1140
+ const token = await exchangeXAIToken(
1141
+ fetchImpl,
1142
+ callback.code,
1143
+ redirectUri,
1144
+ pkce.verifier
1145
+ );
1146
+ const now = (deps.now ?? Date.now)();
1147
+ return tokenToCredential(token, now);
1148
+ } finally {
1149
+ await handle.close();
1150
+ }
1151
+ }
1152
+ async function runXaiOAuthDeviceCode(deps = {}) {
1153
+ const fetchImpl = deps.fetchImpl ?? fetch;
1154
+ const output = deps.output ?? console;
1155
+ const error = deps.error ?? console;
1156
+ const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1157
+ const now = deps.now ?? Date.now;
1158
+ const discovery = await xaiOAuthDiscovery(fetchImpl, {
1159
+ requireDeviceAuthorization: true
1160
+ });
1161
+ const deviceAuthorizationEndpoint = discovery.device_authorization_endpoint;
1162
+ if (!deviceAuthorizationEndpoint) {
1163
+ throw new Error("xAI OIDC discovery missing device_authorization_endpoint");
1164
+ }
1165
+ const start = await requestXaiDeviceCode(fetchImpl, deviceAuthorizationEndpoint);
1166
+ const browserUrl = start.verificationUriComplete ?? start.verificationUri;
1167
+ const expiresMinutes = Math.max(1, Math.round(start.expiresInMs / 6e4));
1168
+ output.log("Authorize nolo-cli for xAI Grok (device code / headless):");
1169
+ output.log(` URL: ${start.verificationUri}`);
1170
+ if (start.verificationUriComplete) {
1171
+ output.log(` Complete URL: ${start.verificationUriComplete}`);
1172
+ }
1173
+ output.log(` Code: ${start.userCode}`);
1174
+ output.log(
1175
+ ` Code expires in ~${expiresMinutes} minute(s). Never share the code or tokens.`
1176
+ );
1177
+ if (deps.openBrowser) {
1178
+ try {
1179
+ const opened = await deps.openBrowser(browserUrl);
1180
+ if (!opened) {
1181
+ output.log("Could not open a browser automatically. Open the URL above.");
1182
+ }
1183
+ } catch (err) {
1184
+ error.error(
1185
+ `Failed to open browser automatically: ${toErrorMessage(err)}`
1186
+ );
1187
+ }
1188
+ } else {
1189
+ output.log("Open the URL above in a browser and enter the code to approve.");
1190
+ }
1191
+ const token = await pollXaiDeviceCodeToken({
1192
+ fetchImpl,
1193
+ tokenEndpoint: discovery.token_endpoint,
1194
+ deviceCode: start.deviceCode,
1195
+ expiresInMs: start.expiresInMs,
1196
+ intervalMs: start.intervalMs,
1197
+ sleep: sleep3,
1198
+ now
1199
+ });
1200
+ return tokenToCredential(token, now());
1201
+ }
1202
+ async function refreshXaiOAuthToken(credential) {
1203
+ if (!credential.refreshToken) {
1204
+ throw new Error("xAI credential has no refresh_token");
1205
+ }
1206
+ const fetchImpl = fetch;
1207
+ const discovery = await xaiOAuthDiscovery(fetchImpl);
1208
+ const tokenEndpoint = validateXAIEndpoint(discovery.token_endpoint, "token_endpoint");
1209
+ const body = new URLSearchParams({
1210
+ grant_type: "refresh_token",
1211
+ client_id: XAI_OAUTH_CLIENT_ID,
1212
+ refresh_token: credential.refreshToken
1213
+ });
1214
+ let response;
1215
+ try {
1216
+ response = await fetchImpl(tokenEndpoint, {
1217
+ method: "POST",
1218
+ headers: {
1219
+ "Content-Type": "application/x-www-form-urlencoded",
1220
+ Accept: "application/json"
1221
+ },
1222
+ body,
1223
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS)
1224
+ });
1225
+ } catch (error) {
1226
+ throw new Error(
1227
+ `xAI token refresh failed: ${toErrorMessage(error)}`
1228
+ );
1229
+ }
1230
+ const data = await readJsonBody(response, "xAI token refresh");
1231
+ if (!response.ok) {
1232
+ const detail = typeof data.error_description === "string" && data.error_description || typeof data.error === "string" && data.error || "";
1233
+ throw new Error(
1234
+ `xAI token refresh failed: ${response.status}${detail ? ` ${detail}` : ""}`
1235
+ );
1236
+ }
1237
+ if (typeof data.access_token !== "string" || !data.access_token) {
1238
+ throw new Error("xAI token refresh response missing access_token");
1239
+ }
1240
+ const expiresIn = asOptionalFiniteNumber(data.expires_in);
1241
+ if (expiresIn === void 0) {
1242
+ throw new Error("xAI token refresh response missing expires_in");
1243
+ }
1244
+ const newRefresh = typeof data.refresh_token === "string" && data.refresh_token ? data.refresh_token : credential.refreshToken;
1245
+ const now = Date.now();
1246
+ return {
1247
+ ...credential,
1248
+ accessToken: data.access_token,
1249
+ refreshToken: newRefresh,
1250
+ expiresAt: now + expiresIn * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS2,
1251
+ obtainedAt: now
1252
+ };
1253
+ }
1254
+
1255
+ // packages/cli/oauth/flows/cursor.ts
1256
+ var CURSOR_LOGIN_URL = "https://cursor.com/loginDeepControl";
1257
+ var CURSOR_POLL_URL = "https://api2.cursor.sh/auth/poll";
1258
+ var CURSOR_REFRESH_URL = "https://api2.cursor.sh/auth/exchange_user_api_key";
1259
+ var POLL_MAX_ATTEMPTS = 150;
1260
+ var POLL_BASE_DELAY_MS = 1e3;
1261
+ var POLL_MAX_DELAY_MS = 1e4;
1262
+ var POLL_BACKOFF_MULTIPLIER = 1.2;
1263
+ var POLL_MAX_CONSECUTIVE_ERRORS = 3;
1264
+ var ACCESS_TOKEN_CLIENT_SKEW_MS3 = 5 * 60 * 1e3;
1265
+ function decodeJwtExp(token) {
1266
+ try {
1267
+ const parts = token.split(".");
1268
+ if (parts.length !== 3) return void 0;
1269
+ const payload = parts[1];
1270
+ if (!payload) return void 0;
1271
+ const decoded = JSON.parse(
1272
+ Buffer.from(payload.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf8")
1273
+ );
1274
+ if (decoded && typeof decoded === "object" && typeof decoded.exp === "number") {
1275
+ return decoded.exp * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS3;
1276
+ }
1277
+ } catch {
1278
+ }
1279
+ return void 0;
1280
+ }
1281
+ function sleep2(ms, signal) {
1282
+ if (signal) {
1283
+ return new Promise((resolve, reject) => {
1284
+ if (signal.aborted) {
1285
+ reject(new Error("Aborted"));
1286
+ return;
1287
+ }
1288
+ const timer = setTimeout(resolve, ms);
1289
+ signal.addEventListener(
1290
+ "abort",
1291
+ () => {
1292
+ clearTimeout(timer);
1293
+ reject(new Error("Aborted"));
1294
+ },
1295
+ { once: true }
1296
+ );
1297
+ });
1298
+ }
1299
+ return new Promise((resolve) => setTimeout(resolve, ms));
1300
+ }
1301
+ async function pollCursorAuth(uuid, verifier, deps = {}) {
1302
+ const fetchImpl = deps.fetchImpl ?? fetch;
1303
+ const sleepFn = deps.sleepFn ?? ((ms) => sleep2(ms));
1304
+ let delay = POLL_BASE_DELAY_MS;
1305
+ let consecutiveErrors = 0;
1306
+ for (let attempt = 0; attempt < POLL_MAX_ATTEMPTS; attempt++) {
1307
+ await sleepFn(delay);
1308
+ let response;
1309
+ try {
1310
+ response = await fetchImpl(
1311
+ `${CURSOR_POLL_URL}?uuid=${encodeURIComponent(uuid)}&verifier=${encodeURIComponent(verifier)}`
1312
+ );
1313
+ } catch {
1314
+ consecutiveErrors++;
1315
+ if (consecutiveErrors >= POLL_MAX_CONSECUTIVE_ERRORS) {
1316
+ throw new Error("Too many consecutive network errors during Cursor auth polling");
1317
+ }
1318
+ continue;
1319
+ }
1320
+ if (response.status === 404) {
1321
+ consecutiveErrors = 0;
1322
+ delay = Math.min(delay * POLL_BACKOFF_MULTIPLIER, POLL_MAX_DELAY_MS);
1323
+ continue;
1324
+ }
1325
+ if (response.ok) {
1326
+ const data = await response.json();
1327
+ if (typeof data.accessToken !== "string" || typeof data.refreshToken !== "string") {
1328
+ throw new Error("Cursor poll response missing accessToken or refreshToken");
1329
+ }
1330
+ return { accessToken: data.accessToken, refreshToken: data.refreshToken };
1331
+ }
1332
+ consecutiveErrors++;
1333
+ if (consecutiveErrors >= POLL_MAX_CONSECUTIVE_ERRORS) {
1334
+ throw new Error(`Cursor auth poll failed: ${response.status}`);
1335
+ }
1336
+ }
1337
+ throw new Error("Cursor authentication polling timed out");
1338
+ }
1339
+ async function runCursorOAuthLogin(deps = {}) {
1340
+ const output = deps.output ?? console;
1341
+ const fetchImpl = deps.fetchImpl ?? fetch;
1342
+ const pkce = generatePkcePair();
1343
+ const uuid = crypto.randomUUID();
1344
+ const params = new URLSearchParams({
1345
+ challenge: pkce.challenge,
1346
+ uuid,
1347
+ mode: "login",
1348
+ redirectTarget: "cli"
1349
+ });
1350
+ const loginUrl = `${CURSOR_LOGIN_URL}?${params.toString()}`;
1351
+ output.log(
1352
+ `
1353
+ Open this URL in your browser to authorize Cursor:
1354
+ ${loginUrl}
1355
+
1356
+ Waiting for browser authentication... (polling ${CURSOR_POLL_URL})`
1357
+ );
1358
+ const openBrowser = deps.openBrowser;
1359
+ if (openBrowser) {
1360
+ try {
1361
+ await openBrowser(loginUrl);
1362
+ } catch {
1363
+ }
1364
+ }
1365
+ const { accessToken, refreshToken } = await pollCursorAuth(uuid, pkce.verifier, {
1366
+ fetchImpl,
1367
+ ...deps.sleep ? { sleepFn: deps.sleep } : {}
1368
+ });
1369
+ const now = deps.now?.() ?? Date.now();
1370
+ const expiresAt = decodeJwtExp(accessToken) ?? now + 3600 * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS3;
1371
+ return {
1372
+ provider: "cursor",
1373
+ accessToken,
1374
+ refreshToken,
1375
+ expiresAt,
1376
+ obtainedAt: now
1377
+ };
1378
+ }
1379
+ var cursorRefresh = async (credential, deps = {}) => {
1380
+ if (!credential.refreshToken) {
1381
+ throw new Error("Cursor credential has no refresh_token");
1382
+ }
1383
+ const fetchImpl = deps.fetchImpl ?? fetch;
1384
+ const response = await fetchImpl(CURSOR_REFRESH_URL, {
1385
+ method: "POST",
1386
+ headers: {
1387
+ Authorization: `Bearer ${credential.refreshToken}`,
1388
+ "Content-Type": "application/json"
1389
+ },
1390
+ body: "{}",
1391
+ signal: AbortSignal.timeout(3e4)
1392
+ });
1393
+ if (!response.ok) {
1394
+ let detail = "";
1395
+ try {
1396
+ detail = (await response.text()).trim();
1397
+ } catch {
1398
+ }
1399
+ throw new Error(
1400
+ `Cursor token refresh failed: ${response.status}${detail ? ` ${detail}` : ""}`
1401
+ );
1402
+ }
1403
+ const data = await response.json();
1404
+ if (typeof data.accessToken !== "string" || !data.accessToken) {
1405
+ throw new Error("Cursor token refresh response missing accessToken");
1406
+ }
1407
+ const now = deps.now?.() ?? Date.now();
1408
+ const expiresAt = decodeJwtExp(data.accessToken) ?? now + 3600 * 1e3 - ACCESS_TOKEN_CLIENT_SKEW_MS3;
1409
+ const newRefresh = typeof data.refreshToken === "string" && data.refreshToken ? data.refreshToken : credential.refreshToken;
1410
+ return {
1411
+ ...credential,
1412
+ provider: "cursor",
1413
+ accessToken: data.accessToken,
1414
+ refreshToken: newRefresh,
1415
+ expiresAt,
1416
+ obtainedAt: now
1417
+ };
1418
+ };
1419
+
1420
+ export {
1421
+ startCallbackServer,
1422
+ generatePkcePair,
1423
+ runAntigravityOAuthLogin,
1424
+ refreshAntigravityOAuthToken,
1425
+ runOpenAiCodexDeviceCode,
1426
+ runOpenAiCodexBrowserPkce,
1427
+ refreshOpenAiCodexToken,
1428
+ runXaiOAuthLogin,
1429
+ runXaiOAuthDeviceCode,
1430
+ refreshXaiOAuthToken,
1431
+ runCursorOAuthLogin,
1432
+ cursorRefresh
1433
+ };