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