nexrall-code 0.5.207 → 0.5.209
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/README.md +5 -3
- package/dist/index.js +779 -540
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -10114,6 +10114,8 @@ var require_client = __commonJS({
|
|
|
10114
10114
|
exports.getUsageDaily = getUsageDaily;
|
|
10115
10115
|
exports.exchangeVscodeCode = exchangeVscodeCode;
|
|
10116
10116
|
exports.login = login2;
|
|
10117
|
+
exports.exchangeAuthCode = exchangeAuthCode2;
|
|
10118
|
+
exports.checkToken = checkToken2;
|
|
10117
10119
|
exports.getUserProfile = getUserProfile;
|
|
10118
10120
|
exports.clampScheduleMinutes = clampScheduleMinutes;
|
|
10119
10121
|
exports.listCloudSchedules = listCloudSchedules3;
|
|
@@ -10173,7 +10175,7 @@ var require_client = __commonJS({
|
|
|
10173
10175
|
function authHeaders() {
|
|
10174
10176
|
const token = (0, index_1.getToken)();
|
|
10175
10177
|
if (!token) {
|
|
10176
|
-
throw new Error("Not authenticated. Run `
|
|
10178
|
+
throw new Error("Not authenticated. Run `nex auth` first.");
|
|
10177
10179
|
}
|
|
10178
10180
|
const headers = {
|
|
10179
10181
|
"Content-Type": "application/json",
|
|
@@ -11159,6 +11161,42 @@ var require_client = __commonJS({
|
|
|
11159
11161
|
email: data.email ?? email
|
|
11160
11162
|
};
|
|
11161
11163
|
}
|
|
11164
|
+
async function exchangeAuthCode2(code) {
|
|
11165
|
+
const response = await (0, node_fetch_1.default)(`${exports.API_BASE}/api/auth/vscode-exchange`, {
|
|
11166
|
+
method: "POST",
|
|
11167
|
+
headers: { "Content-Type": "application/json", ...deviceHeader() },
|
|
11168
|
+
body: JSON.stringify({ code })
|
|
11169
|
+
});
|
|
11170
|
+
if (response.status === 401 || response.status === 400) {
|
|
11171
|
+
throw new Error("That sign-in code is invalid or has expired (codes last 5 minutes). Run `nex auth --browser` again.");
|
|
11172
|
+
}
|
|
11173
|
+
if (!response.ok) {
|
|
11174
|
+
const errText3 = await response.text().catch(() => "");
|
|
11175
|
+
throw new Error(`Sign-in failed (${response.status}): ${errText3}`);
|
|
11176
|
+
}
|
|
11177
|
+
const data = await response.json();
|
|
11178
|
+
if (!data.token)
|
|
11179
|
+
throw new Error("Sign-in response missing token");
|
|
11180
|
+
return {
|
|
11181
|
+
token: data.token,
|
|
11182
|
+
...data.refreshToken ? { refreshToken: data.refreshToken } : {},
|
|
11183
|
+
email: data.user?.email ?? ""
|
|
11184
|
+
};
|
|
11185
|
+
}
|
|
11186
|
+
async function checkToken2(token) {
|
|
11187
|
+
try {
|
|
11188
|
+
const response = await (0, node_fetch_1.default)(`${exports.API_BASE}/api/user/profile`, {
|
|
11189
|
+
method: "GET",
|
|
11190
|
+
headers: { Authorization: `Bearer ${token}`, ...deviceHeader() }
|
|
11191
|
+
});
|
|
11192
|
+
if (!response.ok)
|
|
11193
|
+
return { ok: false, status: response.status };
|
|
11194
|
+
const data = await response.json().catch(() => ({}));
|
|
11195
|
+
return { ok: true, status: response.status, ...data.email ? { email: data.email } : {} };
|
|
11196
|
+
} catch {
|
|
11197
|
+
return { ok: false, status: 0 };
|
|
11198
|
+
}
|
|
11199
|
+
}
|
|
11162
11200
|
async function getUserProfile() {
|
|
11163
11201
|
const fetchOnce = () => (0, node_fetch_1.default)(`${exports.API_BASE}/api/user/profile`, { method: "GET", headers: authHeaders() });
|
|
11164
11202
|
let response = await fetchOnce();
|
|
@@ -21082,7 +21120,7 @@ ${lines.join("\n")}`;
|
|
|
21082
21120
|
return { error: "Missing required parameter: path" };
|
|
21083
21121
|
const token = (0, auth_1.getToken)();
|
|
21084
21122
|
if (!token)
|
|
21085
|
-
return { error: "Not authenticated. Run `
|
|
21123
|
+
return { error: "Not authenticated. Run `nex auth` first." };
|
|
21086
21124
|
let imageUrl;
|
|
21087
21125
|
let cost;
|
|
21088
21126
|
let balance;
|
|
@@ -21136,7 +21174,7 @@ ${lines.join("\n")}`;
|
|
|
21136
21174
|
return { error: "Missing required parameter: query" };
|
|
21137
21175
|
const token = (0, auth_1.getToken)();
|
|
21138
21176
|
if (!token)
|
|
21139
|
-
return { error: "Not authenticated. Run `
|
|
21177
|
+
return { error: "Not authenticated. Run `nex auth` first." };
|
|
21140
21178
|
let photos;
|
|
21141
21179
|
try {
|
|
21142
21180
|
const u = new URL(`${client_2.API_BASE}/api/code/assets/stock`);
|
|
@@ -21193,7 +21231,7 @@ ${lines.join("\n")}` };
|
|
|
21193
21231
|
return { error: "Missing required parameter: query" };
|
|
21194
21232
|
const token = (0, auth_1.getToken)();
|
|
21195
21233
|
if (!token)
|
|
21196
|
-
return { error: "Not authenticated. Run `
|
|
21234
|
+
return { error: "Not authenticated. Run `nex auth` first." };
|
|
21197
21235
|
try {
|
|
21198
21236
|
const r2 = await _httpsPost(`${client_2.API_BASE}/api/code/tools/web_search`, JSON.stringify({ query, ...(0, client_1.billingOverrideFields)() }), { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, 3e4, abortSignal);
|
|
21199
21237
|
let json = {};
|
|
@@ -78440,7 +78478,7 @@ import * as fs32 from "fs";
|
|
|
78440
78478
|
import { createRequire as createRequire2 } from "module";
|
|
78441
78479
|
|
|
78442
78480
|
// src/commands/auth.ts
|
|
78443
|
-
var
|
|
78481
|
+
var import_code_core2 = __toESM(require_dist2(), 1);
|
|
78444
78482
|
import * as readline from "readline";
|
|
78445
78483
|
|
|
78446
78484
|
// ../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
@@ -79756,7 +79794,458 @@ function formatUsage(outputTokens) {
|
|
|
79756
79794
|
return paint.dim(`\u2193 ${tok} tokens`);
|
|
79757
79795
|
}
|
|
79758
79796
|
|
|
79797
|
+
// src/mcp/oauth.ts
|
|
79798
|
+
var import_proc = __toESM(require_proc(), 1);
|
|
79799
|
+
var import_code_core = __toESM(require_dist2(), 1);
|
|
79800
|
+
import * as fs2 from "fs";
|
|
79801
|
+
import * as os2 from "os";
|
|
79802
|
+
import * as path from "path";
|
|
79803
|
+
import * as http3 from "http";
|
|
79804
|
+
import * as crypto from "crypto";
|
|
79805
|
+
var CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
79806
|
+
function tokensFile(env3 = process.env) {
|
|
79807
|
+
return env3.NEXRALL_MCP_AUTH_FILE || path.join(os2.homedir(), ".nexrall", "mcp-auth.json");
|
|
79808
|
+
}
|
|
79809
|
+
function readStore(file) {
|
|
79810
|
+
try {
|
|
79811
|
+
const parsed = JSON.parse(fs2.readFileSync(file, "utf-8"));
|
|
79812
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
79813
|
+
return {};
|
|
79814
|
+
const out = {};
|
|
79815
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
79816
|
+
const b = v;
|
|
79817
|
+
if (b && typeof b.access_token === "string" && typeof b.token_endpoint === "string")
|
|
79818
|
+
out[k] = b;
|
|
79819
|
+
}
|
|
79820
|
+
return out;
|
|
79821
|
+
} catch {
|
|
79822
|
+
return {};
|
|
79823
|
+
}
|
|
79824
|
+
}
|
|
79825
|
+
function writeStore(file, store) {
|
|
79826
|
+
fs2.mkdirSync(path.dirname(file), { recursive: true });
|
|
79827
|
+
fs2.writeFileSync(file, JSON.stringify(store, null, 2), { mode: 384 });
|
|
79828
|
+
try {
|
|
79829
|
+
fs2.chmodSync(file, 384);
|
|
79830
|
+
} catch {
|
|
79831
|
+
}
|
|
79832
|
+
}
|
|
79833
|
+
function loadToken(server, file = tokensFile()) {
|
|
79834
|
+
return readStore(file)[server] ?? null;
|
|
79835
|
+
}
|
|
79836
|
+
function saveToken(server, bundle, file = tokensFile()) {
|
|
79837
|
+
const store = readStore(file);
|
|
79838
|
+
store[server] = bundle;
|
|
79839
|
+
writeStore(file, store);
|
|
79840
|
+
}
|
|
79841
|
+
function clearToken(server, file = tokensFile()) {
|
|
79842
|
+
const store = readStore(file);
|
|
79843
|
+
if (!(server in store))
|
|
79844
|
+
return false;
|
|
79845
|
+
delete store[server];
|
|
79846
|
+
writeStore(file, store);
|
|
79847
|
+
return true;
|
|
79848
|
+
}
|
|
79849
|
+
function defaultFetch() {
|
|
79850
|
+
const f3 = globalThis.fetch;
|
|
79851
|
+
if (typeof f3 !== "function")
|
|
79852
|
+
throw new Error("global fetch unavailable \u2014 Node 18+ is required for MCP OAuth");
|
|
79853
|
+
return f3;
|
|
79854
|
+
}
|
|
79855
|
+
async function getValidAccessToken(server, opts = {}) {
|
|
79856
|
+
const file = opts.file ?? tokensFile();
|
|
79857
|
+
const bundle = loadToken(server, file);
|
|
79858
|
+
if (!bundle)
|
|
79859
|
+
return null;
|
|
79860
|
+
const fresh = !bundle.expires_at || bundle.expires_at - Date.now() > 6e4;
|
|
79861
|
+
if (fresh || !bundle.refresh_token)
|
|
79862
|
+
return bundle.access_token;
|
|
79863
|
+
try {
|
|
79864
|
+
const refreshed = await refreshAccessToken(bundle, opts.fetchImpl ?? defaultFetch());
|
|
79865
|
+
saveToken(server, refreshed, file);
|
|
79866
|
+
return refreshed.access_token;
|
|
79867
|
+
} catch {
|
|
79868
|
+
return bundle.access_token;
|
|
79869
|
+
}
|
|
79870
|
+
}
|
|
79871
|
+
function parseResourceMetadataUrl(wwwAuthenticate) {
|
|
79872
|
+
const m2 = /resource_metadata\s*=\s*"([^"]+)"/i.exec(wwwAuthenticate || "");
|
|
79873
|
+
return m2 ? m2[1] : null;
|
|
79874
|
+
}
|
|
79875
|
+
function protectedResourceCandidates(serverUrl) {
|
|
79876
|
+
const u = new URL(serverUrl);
|
|
79877
|
+
const base = u.origin;
|
|
79878
|
+
const p = u.pathname.replace(/\/$/, "");
|
|
79879
|
+
return [
|
|
79880
|
+
...p ? [`${base}/.well-known/oauth-protected-resource${p}`] : [],
|
|
79881
|
+
`${base}/.well-known/oauth-protected-resource`
|
|
79882
|
+
];
|
|
79883
|
+
}
|
|
79884
|
+
function authServerCandidates(authServer) {
|
|
79885
|
+
const u = new URL(authServer);
|
|
79886
|
+
const base = u.origin;
|
|
79887
|
+
const p = u.pathname.replace(/\/$/, "");
|
|
79888
|
+
const root = authServer.replace(/\/$/, "");
|
|
79889
|
+
return [
|
|
79890
|
+
`${base}/.well-known/oauth-authorization-server${p}`,
|
|
79891
|
+
`${base}/.well-known/openid-configuration${p}`,
|
|
79892
|
+
`${root}/.well-known/oauth-authorization-server`,
|
|
79893
|
+
`${root}/.well-known/openid-configuration`
|
|
79894
|
+
];
|
|
79895
|
+
}
|
|
79896
|
+
function buildAuthorizeUrl(meta, p) {
|
|
79897
|
+
const url = new URL(meta.authorization_endpoint);
|
|
79898
|
+
url.searchParams.set("response_type", "code");
|
|
79899
|
+
url.searchParams.set("client_id", p.clientId);
|
|
79900
|
+
url.searchParams.set("redirect_uri", p.redirectUri);
|
|
79901
|
+
url.searchParams.set("state", p.state);
|
|
79902
|
+
url.searchParams.set("code_challenge", p.codeChallenge);
|
|
79903
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
79904
|
+
url.searchParams.set("resource", meta.resource);
|
|
79905
|
+
if (meta.scope)
|
|
79906
|
+
url.searchParams.set("scope", meta.scope);
|
|
79907
|
+
return url.toString();
|
|
79908
|
+
}
|
|
79909
|
+
function toBundle(data, p) {
|
|
79910
|
+
const accessToken = data.access_token;
|
|
79911
|
+
if (!accessToken)
|
|
79912
|
+
throw new Error("Token response had no access_token");
|
|
79913
|
+
const expiresIn = typeof data.expires_in === "number" ? data.expires_in : void 0;
|
|
79914
|
+
return {
|
|
79915
|
+
access_token: accessToken,
|
|
79916
|
+
refresh_token: data.refresh_token ?? p.prevRefresh,
|
|
79917
|
+
expires_at: expiresIn ? Date.now() + expiresIn * 1e3 : void 0,
|
|
79918
|
+
token_endpoint: p.tokenEndpoint,
|
|
79919
|
+
client_id: p.clientId,
|
|
79920
|
+
client_secret: p.clientSecret,
|
|
79921
|
+
resource: p.resource,
|
|
79922
|
+
scope: data.scope ?? p.scope
|
|
79923
|
+
};
|
|
79924
|
+
}
|
|
79925
|
+
async function fetchJsonSafe(f3, url) {
|
|
79926
|
+
try {
|
|
79927
|
+
const resp = await f3(url, { headers: { Accept: "application/json" } });
|
|
79928
|
+
if (!resp.ok)
|
|
79929
|
+
return null;
|
|
79930
|
+
return await resp.json();
|
|
79931
|
+
} catch {
|
|
79932
|
+
return null;
|
|
79933
|
+
}
|
|
79934
|
+
}
|
|
79935
|
+
async function discover(serverUrl, wwwAuthenticate, f3 = defaultFetch()) {
|
|
79936
|
+
const origin = new URL(serverUrl).origin;
|
|
79937
|
+
let authServer = null;
|
|
79938
|
+
let resource = serverUrl;
|
|
79939
|
+
let scope;
|
|
79940
|
+
const prmCandidates = [parseResourceMetadataUrl(wwwAuthenticate), ...protectedResourceCandidates(serverUrl)].filter(
|
|
79941
|
+
(u) => Boolean(u)
|
|
79942
|
+
);
|
|
79943
|
+
let prm = null;
|
|
79944
|
+
for (const url of new Set(prmCandidates)) {
|
|
79945
|
+
prm = await fetchJsonSafe(f3, url);
|
|
79946
|
+
if (prm)
|
|
79947
|
+
break;
|
|
79948
|
+
}
|
|
79949
|
+
if (prm) {
|
|
79950
|
+
const servers = prm.authorization_servers;
|
|
79951
|
+
if (Array.isArray(servers) && servers.length)
|
|
79952
|
+
authServer = servers[0];
|
|
79953
|
+
if (typeof prm.resource === "string")
|
|
79954
|
+
resource = prm.resource;
|
|
79955
|
+
const scopes = prm.scopes_supported;
|
|
79956
|
+
if (Array.isArray(scopes) && scopes.length)
|
|
79957
|
+
scope = scopes.join(" ");
|
|
79958
|
+
}
|
|
79959
|
+
if (!authServer)
|
|
79960
|
+
authServer = origin;
|
|
79961
|
+
let asm = null;
|
|
79962
|
+
for (const url of authServerCandidates(authServer)) {
|
|
79963
|
+
asm = await fetchJsonSafe(f3, url);
|
|
79964
|
+
if (asm && asm.authorization_endpoint && asm.token_endpoint)
|
|
79965
|
+
break;
|
|
79966
|
+
asm = null;
|
|
79967
|
+
}
|
|
79968
|
+
if (!asm)
|
|
79969
|
+
throw new Error(`No OAuth metadata found for authorization server ${authServer}`);
|
|
79970
|
+
return {
|
|
79971
|
+
authorization_endpoint: asm.authorization_endpoint,
|
|
79972
|
+
token_endpoint: asm.token_endpoint,
|
|
79973
|
+
registration_endpoint: asm.registration_endpoint,
|
|
79974
|
+
resource,
|
|
79975
|
+
scope
|
|
79976
|
+
};
|
|
79977
|
+
}
|
|
79978
|
+
async function registerClient(meta, redirectUri, f3) {
|
|
79979
|
+
if (!meta.registration_endpoint) {
|
|
79980
|
+
throw new Error(
|
|
79981
|
+
'Authorization server does not support dynamic client registration; a client_id must be configured manually \u2014 create an OAuth client for this server, then re-run `nex mcp login <server> --client-id <id> [--client-secret <secret>]` (or set "clientId" / "clientSecret" in its mcp.json entry).'
|
|
79982
|
+
);
|
|
79983
|
+
}
|
|
79984
|
+
const resp = await f3(meta.registration_endpoint, {
|
|
79985
|
+
method: "POST",
|
|
79986
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
79987
|
+
body: JSON.stringify({
|
|
79988
|
+
client_name: "Nexrall Code CLI",
|
|
79989
|
+
redirect_uris: [redirectUri],
|
|
79990
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
79991
|
+
response_types: ["code"],
|
|
79992
|
+
token_endpoint_auth_method: "none",
|
|
79993
|
+
scope: meta.scope
|
|
79994
|
+
})
|
|
79995
|
+
});
|
|
79996
|
+
if (!resp.ok)
|
|
79997
|
+
throw new Error(`Client registration failed (HTTP ${resp.status})`);
|
|
79998
|
+
const data = await resp.json();
|
|
79999
|
+
if (!data.client_id)
|
|
80000
|
+
throw new Error("Client registration returned no client_id");
|
|
80001
|
+
return data.client_id;
|
|
80002
|
+
}
|
|
80003
|
+
async function postToken(tokenEndpoint, body, f3) {
|
|
80004
|
+
const resp = await f3(tokenEndpoint, {
|
|
80005
|
+
method: "POST",
|
|
80006
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
|
|
80007
|
+
body: body.toString()
|
|
80008
|
+
});
|
|
80009
|
+
if (!resp.ok) {
|
|
80010
|
+
const txt = await resp.text().catch(() => "");
|
|
80011
|
+
throw new Error(`Token endpoint error (HTTP ${resp.status})${txt ? `: ${txt.slice(0, 200)}` : ""}`);
|
|
80012
|
+
}
|
|
80013
|
+
return await resp.json();
|
|
80014
|
+
}
|
|
80015
|
+
async function exchangeCode(meta, clientId, clientSecret, code, verifier, redirectUri, f3) {
|
|
80016
|
+
const body = new URLSearchParams({
|
|
80017
|
+
grant_type: "authorization_code",
|
|
80018
|
+
code,
|
|
80019
|
+
redirect_uri: redirectUri,
|
|
80020
|
+
client_id: clientId,
|
|
80021
|
+
code_verifier: verifier,
|
|
80022
|
+
resource: meta.resource
|
|
80023
|
+
});
|
|
80024
|
+
if (clientSecret)
|
|
80025
|
+
body.set("client_secret", clientSecret);
|
|
80026
|
+
const data = await postToken(meta.token_endpoint, body, f3);
|
|
80027
|
+
return toBundle(data, {
|
|
80028
|
+
tokenEndpoint: meta.token_endpoint,
|
|
80029
|
+
clientId,
|
|
80030
|
+
clientSecret,
|
|
80031
|
+
resource: meta.resource,
|
|
80032
|
+
scope: meta.scope
|
|
80033
|
+
});
|
|
80034
|
+
}
|
|
80035
|
+
async function refreshAccessToken(bundle, f3) {
|
|
80036
|
+
const body = new URLSearchParams({
|
|
80037
|
+
grant_type: "refresh_token",
|
|
80038
|
+
refresh_token: bundle.refresh_token,
|
|
80039
|
+
client_id: bundle.client_id,
|
|
80040
|
+
resource: bundle.resource
|
|
80041
|
+
});
|
|
80042
|
+
if (bundle.client_secret)
|
|
80043
|
+
body.set("client_secret", bundle.client_secret);
|
|
80044
|
+
const data = await postToken(bundle.token_endpoint, body, f3);
|
|
80045
|
+
return toBundle(data, {
|
|
80046
|
+
tokenEndpoint: bundle.token_endpoint,
|
|
80047
|
+
clientId: bundle.client_id,
|
|
80048
|
+
clientSecret: bundle.client_secret,
|
|
80049
|
+
resource: bundle.resource,
|
|
80050
|
+
scope: bundle.scope,
|
|
80051
|
+
prevRefresh: bundle.refresh_token
|
|
80052
|
+
});
|
|
80053
|
+
}
|
|
80054
|
+
function startLoopback() {
|
|
80055
|
+
return new Promise((resolve19, reject) => {
|
|
80056
|
+
let resolveCode;
|
|
80057
|
+
let rejectCode;
|
|
80058
|
+
const codePromise = new Promise((res, rej) => {
|
|
80059
|
+
resolveCode = res;
|
|
80060
|
+
rejectCode = rej;
|
|
80061
|
+
});
|
|
80062
|
+
codePromise.catch(() => void 0);
|
|
80063
|
+
const server = http3.createServer((req, res) => {
|
|
80064
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
80065
|
+
if (url.pathname !== "/callback") {
|
|
80066
|
+
res.writeHead(404);
|
|
80067
|
+
res.end();
|
|
80068
|
+
return;
|
|
80069
|
+
}
|
|
80070
|
+
const code = url.searchParams.get("code");
|
|
80071
|
+
const state = url.searchParams.get("state");
|
|
80072
|
+
const error = url.searchParams.get("error");
|
|
80073
|
+
res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
|
|
80074
|
+
res.end(
|
|
80075
|
+
`<!doctype html><html><body style="font-family:system-ui;text-align:center;padding:40px"><h2>${error || !code ? "Authentication failed" : "Authentication complete"}</h2><p>You can close this tab and return to the terminal.</p></body></html>`
|
|
80076
|
+
);
|
|
80077
|
+
if (error) {
|
|
80078
|
+
rejectCode(new Error(`Authorization denied: ${error}`));
|
|
80079
|
+
return;
|
|
80080
|
+
}
|
|
80081
|
+
if (!code) {
|
|
80082
|
+
rejectCode(new Error("No authorization code in redirect"));
|
|
80083
|
+
return;
|
|
80084
|
+
}
|
|
80085
|
+
resolveCode({ code, state });
|
|
80086
|
+
});
|
|
80087
|
+
server.on("error", reject);
|
|
80088
|
+
server.listen(0, "127.0.0.1", () => {
|
|
80089
|
+
const port = server.address().port;
|
|
80090
|
+
resolve19({
|
|
80091
|
+
port,
|
|
80092
|
+
waitForCode(expectedState) {
|
|
80093
|
+
const timeout = setTimeout(
|
|
80094
|
+
() => rejectCode(new Error("Timed out waiting for authorization")),
|
|
80095
|
+
CALLBACK_TIMEOUT_MS
|
|
80096
|
+
);
|
|
80097
|
+
return codePromise.then(
|
|
80098
|
+
({ code, state }) => {
|
|
80099
|
+
clearTimeout(timeout);
|
|
80100
|
+
if (state !== expectedState)
|
|
80101
|
+
throw new Error("OAuth state mismatch \u2014 possible CSRF, aborting");
|
|
80102
|
+
return code;
|
|
80103
|
+
},
|
|
80104
|
+
// A denial must clear the timer too: otherwise `nex mcp login` reports the
|
|
80105
|
+
// failure but the process sits on a 5-minute keep-alive before exiting.
|
|
80106
|
+
(err) => {
|
|
80107
|
+
clearTimeout(timeout);
|
|
80108
|
+
throw err;
|
|
80109
|
+
}
|
|
80110
|
+
);
|
|
80111
|
+
},
|
|
80112
|
+
close() {
|
|
80113
|
+
try {
|
|
80114
|
+
server.closeAllConnections?.();
|
|
80115
|
+
} catch {
|
|
80116
|
+
}
|
|
80117
|
+
try {
|
|
80118
|
+
server.close();
|
|
80119
|
+
} catch {
|
|
80120
|
+
}
|
|
80121
|
+
}
|
|
80122
|
+
});
|
|
80123
|
+
});
|
|
80124
|
+
});
|
|
80125
|
+
}
|
|
80126
|
+
function b64url(buf) {
|
|
80127
|
+
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
80128
|
+
}
|
|
80129
|
+
function openInBrowser(url) {
|
|
80130
|
+
const win = process.platform === "win32";
|
|
80131
|
+
const cmd = process.platform === "darwin" ? "open" : win ? "rundll32" : "xdg-open";
|
|
80132
|
+
const args = win ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
80133
|
+
try {
|
|
80134
|
+
const child = (0, import_proc.spawn)(cmd, args, { stdio: "ignore", detached: !win });
|
|
80135
|
+
child.on("error", () => void 0);
|
|
80136
|
+
child.unref();
|
|
80137
|
+
} catch {
|
|
80138
|
+
}
|
|
80139
|
+
}
|
|
80140
|
+
async function probeAuthChallenge(serverUrl, f3 = defaultFetch()) {
|
|
80141
|
+
const steps = [
|
|
80142
|
+
{
|
|
80143
|
+
method: "initialize",
|
|
80144
|
+
params: {
|
|
80145
|
+
protocolVersion: import_code_core.MCP_PROTOCOL_VERSION,
|
|
80146
|
+
capabilities: {},
|
|
80147
|
+
clientInfo: { name: "nexrall-code", version: "1.0" }
|
|
80148
|
+
}
|
|
80149
|
+
},
|
|
80150
|
+
{ method: "tools/list", params: {} }
|
|
80151
|
+
];
|
|
80152
|
+
for (const { method, params } of steps) {
|
|
80153
|
+
try {
|
|
80154
|
+
const resp = await f3(serverUrl, {
|
|
80155
|
+
method: "POST",
|
|
80156
|
+
headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
|
|
80157
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
|
|
80158
|
+
});
|
|
80159
|
+
const challenge = resp.headers?.get("www-authenticate") ?? "";
|
|
80160
|
+
if (challenge)
|
|
80161
|
+
return challenge;
|
|
80162
|
+
} catch {
|
|
80163
|
+
return "";
|
|
80164
|
+
}
|
|
80165
|
+
}
|
|
80166
|
+
return "";
|
|
80167
|
+
}
|
|
80168
|
+
async function authorizeServer(o) {
|
|
80169
|
+
const f3 = o.fetchImpl ?? defaultFetch();
|
|
80170
|
+
const challenge = o.wwwAuthenticate ?? await probeAuthChallenge(o.serverUrl, f3);
|
|
80171
|
+
const meta = await discover(o.serverUrl, challenge, f3);
|
|
80172
|
+
const scope = [meta.scope, (0, import_code_core.parseOAuthChallenge)(challenge).scope, o.extraScope].map((s2) => s2?.trim()).filter((s2) => Boolean(s2)).join(" ").split(/\s+/).filter((s2, i2, a) => Boolean(s2) && a.indexOf(s2) === i2).join(" ");
|
|
80173
|
+
meta.scope = scope || void 0;
|
|
80174
|
+
const loopback = await startLoopback();
|
|
80175
|
+
const redirectUri = `http://127.0.0.1:${loopback.port}/callback`;
|
|
80176
|
+
try {
|
|
80177
|
+
const creds = o.credentials ?? {};
|
|
80178
|
+
let clientId;
|
|
80179
|
+
if (creds.clientId) {
|
|
80180
|
+
clientId = creds.clientId;
|
|
80181
|
+
o.onStep?.("Using the OAuth client configured for this server\u2026");
|
|
80182
|
+
} else {
|
|
80183
|
+
o.onStep?.(`Registering with ${new URL(meta.token_endpoint).origin}\u2026`);
|
|
80184
|
+
clientId = await registerClient(meta, redirectUri, f3);
|
|
80185
|
+
}
|
|
80186
|
+
const verifier = b64url(crypto.randomBytes(32));
|
|
80187
|
+
const challengeB64 = b64url(crypto.createHash("sha256").update(verifier).digest());
|
|
80188
|
+
const state = b64url(crypto.randomBytes(16));
|
|
80189
|
+
const authUrl = buildAuthorizeUrl(meta, { clientId, redirectUri, codeChallenge: challengeB64, state });
|
|
80190
|
+
o.onStep?.("Opening your browser to authorize\u2026");
|
|
80191
|
+
await o.openUrl(authUrl);
|
|
80192
|
+
const code = await loopback.waitForCode(state);
|
|
80193
|
+
const bundle = await exchangeCode(meta, clientId, creds.clientSecret, code, verifier, redirectUri, f3);
|
|
80194
|
+
saveToken(o.server, bundle, o.file ?? tokensFile());
|
|
80195
|
+
} finally {
|
|
80196
|
+
loopback.close();
|
|
80197
|
+
}
|
|
80198
|
+
}
|
|
80199
|
+
var isRemote = (cfg) => cfg.type === "http" || cfg.type === "sse" || !cfg.command && !!cfg.url;
|
|
80200
|
+
async function injectMcpTokens(config, opts = {}) {
|
|
80201
|
+
const file = opts.file ?? tokensFile();
|
|
80202
|
+
const injected = [];
|
|
80203
|
+
for (const [name, cfg] of Object.entries(config.mcpServers ?? {})) {
|
|
80204
|
+
if (!isRemote(cfg))
|
|
80205
|
+
continue;
|
|
80206
|
+
const hasAuth = cfg.headers && Object.keys(cfg.headers).some((h2) => h2.toLowerCase() === "authorization");
|
|
80207
|
+
if (hasAuth)
|
|
80208
|
+
continue;
|
|
80209
|
+
let token;
|
|
80210
|
+
try {
|
|
80211
|
+
token = await getValidAccessToken(name, { file, fetchImpl: opts.fetchImpl });
|
|
80212
|
+
} catch {
|
|
80213
|
+
token = null;
|
|
80214
|
+
}
|
|
80215
|
+
if (!token)
|
|
80216
|
+
continue;
|
|
80217
|
+
cfg.headers = { ...cfg.headers ?? {}, Authorization: `Bearer ${token}` };
|
|
80218
|
+
injected.push(name);
|
|
80219
|
+
}
|
|
80220
|
+
return injected;
|
|
80221
|
+
}
|
|
80222
|
+
|
|
79759
80223
|
// src/commands/auth.ts
|
|
80224
|
+
var AUTH_APP_URL = "https://app.nexrall.com";
|
|
80225
|
+
function browserAuthUrl() {
|
|
80226
|
+
return `${AUTH_APP_URL}/auth/vscode?cli=1`;
|
|
80227
|
+
}
|
|
80228
|
+
function normalizeAuthCode(raw) {
|
|
80229
|
+
const cleaned = raw.trim().replace(/^[`'"“”‘’]+|[`'"“”‘’]+$/g, "").trim();
|
|
80230
|
+
const fromUrl = /[?&]code=([^&\s]+)/.exec(cleaned);
|
|
80231
|
+
return fromUrl ? decodeURIComponent(fromUrl[1]) : cleaned.replace(/\s+/g, "");
|
|
80232
|
+
}
|
|
80233
|
+
function parseAuthMethod(input) {
|
|
80234
|
+
const v = input.trim().toLowerCase();
|
|
80235
|
+
if (v === "" || v === "1" || v === "b" || v === "browser")
|
|
80236
|
+
return "browser";
|
|
80237
|
+
if (v === "2" || v === "p" || v === "e" || v === "email" || v === "password")
|
|
80238
|
+
return "password";
|
|
80239
|
+
if (v === "3" || v === "t" || v === "token")
|
|
80240
|
+
return "token";
|
|
80241
|
+
return null;
|
|
80242
|
+
}
|
|
80243
|
+
function shouldOfferLogin(ctx) {
|
|
80244
|
+
return ctx.isTTY && !ctx.headless;
|
|
80245
|
+
}
|
|
80246
|
+
function stripPasteMarkers(raw) {
|
|
80247
|
+
return raw.replace(/\u001b\[[0-9;?]*[~A-Za-z]/g, "").replace(/\u001b/g, "");
|
|
80248
|
+
}
|
|
79760
80249
|
function prompt(question, hidden = false) {
|
|
79761
80250
|
return new Promise((resolve19) => {
|
|
79762
80251
|
const rl = readline.createInterface({
|
|
@@ -79767,27 +80256,36 @@ function prompt(question, hidden = false) {
|
|
|
79767
80256
|
if (hidden) {
|
|
79768
80257
|
process.stdout.write(question);
|
|
79769
80258
|
let value = "";
|
|
80259
|
+
let finished = false;
|
|
79770
80260
|
const { stdin } = process;
|
|
79771
80261
|
const wasRaw = stdin.isRaw;
|
|
79772
80262
|
stdin.setRawMode(true);
|
|
79773
80263
|
stdin.resume();
|
|
79774
80264
|
stdin.setEncoding("utf8");
|
|
79775
|
-
const
|
|
79776
|
-
if (
|
|
79777
|
-
|
|
79778
|
-
|
|
79779
|
-
|
|
79780
|
-
|
|
79781
|
-
|
|
79782
|
-
|
|
79783
|
-
|
|
79784
|
-
|
|
79785
|
-
|
|
79786
|
-
|
|
79787
|
-
|
|
79788
|
-
|
|
80265
|
+
const finish = (submit) => {
|
|
80266
|
+
if (finished)
|
|
80267
|
+
return;
|
|
80268
|
+
finished = true;
|
|
80269
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
80270
|
+
stdin.pause();
|
|
80271
|
+
stdin.removeListener("data", onData);
|
|
80272
|
+
rl.close();
|
|
80273
|
+
process.stdout.write("\n");
|
|
80274
|
+
if (!submit)
|
|
80275
|
+
process.exit(0);
|
|
80276
|
+
resolve19(stripPasteMarkers(value));
|
|
80277
|
+
};
|
|
80278
|
+
const onData = (chunk) => {
|
|
80279
|
+
for (const char of chunk) {
|
|
80280
|
+
if (char === "\n" || char === "\r")
|
|
80281
|
+
return finish(true);
|
|
80282
|
+
if (char === "")
|
|
80283
|
+
return finish(false);
|
|
80284
|
+
if (char === "\x7F" || char === "\b") {
|
|
80285
|
+
if (value.length > 0)
|
|
80286
|
+
value = value.slice(0, -1);
|
|
80287
|
+
continue;
|
|
79789
80288
|
}
|
|
79790
|
-
} else {
|
|
79791
80289
|
value += char;
|
|
79792
80290
|
}
|
|
79793
80291
|
};
|
|
@@ -79800,12 +80298,12 @@ function prompt(question, hidden = false) {
|
|
|
79800
80298
|
}
|
|
79801
80299
|
});
|
|
79802
80300
|
}
|
|
79803
|
-
async function authCommand() {
|
|
80301
|
+
async function authCommand(opts = {}) {
|
|
79804
80302
|
console.log();
|
|
79805
80303
|
console.log(paint.boldBlue(" Nexrall Code \u2014 Login"));
|
|
79806
80304
|
console.log(paint.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
79807
80305
|
console.log();
|
|
79808
|
-
if ((0,
|
|
80306
|
+
if ((0, import_code_core2.isAuthenticated)() && !opts.token && !opts.code) {
|
|
79809
80307
|
const overwrite = await prompt(paint.yellow(" Already logged in. Login again? ") + paint.dim("[y/n] > "));
|
|
79810
80308
|
if (overwrite !== "y" && overwrite !== "yes") {
|
|
79811
80309
|
console.log(paint.dim(" Cancelled."));
|
|
@@ -79813,21 +80311,90 @@ async function authCommand() {
|
|
|
79813
80311
|
}
|
|
79814
80312
|
console.log();
|
|
79815
80313
|
}
|
|
80314
|
+
if (opts.token)
|
|
80315
|
+
return loginWithToken(opts.token);
|
|
80316
|
+
if (opts.browser || opts.code)
|
|
80317
|
+
return loginWithBrowser(opts.code);
|
|
80318
|
+
if (opts.password)
|
|
80319
|
+
return loginWithPassword();
|
|
80320
|
+
if (!process.stdin.isTTY)
|
|
80321
|
+
return loginWithPassword();
|
|
80322
|
+
const method = await chooseMethod();
|
|
80323
|
+
if (method === null) {
|
|
80324
|
+
console.log(paint.dim(" Cancelled."));
|
|
80325
|
+
return;
|
|
80326
|
+
}
|
|
80327
|
+
if (method === "browser")
|
|
80328
|
+
return loginWithBrowser();
|
|
80329
|
+
if (method === "token")
|
|
80330
|
+
return loginWithTokenPrompt();
|
|
80331
|
+
return loginWithPassword();
|
|
80332
|
+
}
|
|
80333
|
+
async function chooseMethod() {
|
|
80334
|
+
console.log(" How do you want to sign in?");
|
|
80335
|
+
console.log();
|
|
80336
|
+
console.log(
|
|
80337
|
+
` ${paint.bold("1.")} Browser ${paint.dim("Google, Apple, SSO or email + password \u2014 recommended")}`
|
|
80338
|
+
);
|
|
80339
|
+
console.log(` ${paint.bold("2.")} Email + password ${paint.dim("typed here in the terminal")}`);
|
|
80340
|
+
console.log(` ${paint.bold("3.")} Token ${paint.dim("CI / headless \u2014 paste a token")}`);
|
|
80341
|
+
console.log();
|
|
80342
|
+
for (; ; ) {
|
|
80343
|
+
const answer = await prompt(paint.bold(" Choose ") + paint.dim("[1] > "));
|
|
80344
|
+
const method = parseAuthMethod(answer);
|
|
80345
|
+
if (method)
|
|
80346
|
+
return method;
|
|
80347
|
+
console.log(paint.yellow(" Pick 1, 2 or 3."));
|
|
80348
|
+
}
|
|
80349
|
+
}
|
|
80350
|
+
async function loginWithBrowser(codeArg) {
|
|
80351
|
+
let code = codeArg ? normalizeAuthCode(codeArg) : "";
|
|
80352
|
+
if (!code) {
|
|
80353
|
+
const url = browserAuthUrl();
|
|
80354
|
+
openInBrowser(url);
|
|
80355
|
+
console.log(paint.dim(" Opened: ") + paint.cyan(url));
|
|
80356
|
+
console.log();
|
|
80357
|
+
console.log(" Sign in there \u2014 Google, Apple, SSO or your password all work.");
|
|
80358
|
+
console.log(paint.dim(" Copy the code the page shows, then paste it here."));
|
|
80359
|
+
console.log();
|
|
80360
|
+
code = normalizeAuthCode(await prompt(paint.bold(" Code ") + paint.dim("> ")));
|
|
80361
|
+
}
|
|
80362
|
+
if (!code) {
|
|
80363
|
+
console.log(paint.red(" \u2717 No code entered."));
|
|
80364
|
+
console.log();
|
|
80365
|
+
process.exitCode = 1;
|
|
80366
|
+
return;
|
|
80367
|
+
}
|
|
80368
|
+
console.log();
|
|
80369
|
+
const spinner = startSpinner("Signing in\u2026");
|
|
80370
|
+
try {
|
|
80371
|
+
const authConfig = await (0, import_code_core2.exchangeAuthCode)(code);
|
|
80372
|
+
(0, import_code_core2.saveAuth)(authConfig);
|
|
80373
|
+
stopSpinner(spinner);
|
|
80374
|
+
console.log(paint.green(" \u2713 Logged in as ") + paint.bold(authConfig.email || "your account"));
|
|
80375
|
+
await showBalance();
|
|
80376
|
+
console.log();
|
|
80377
|
+
console.log(paint.dim(" Run ") + paint.bold("nex") + paint.dim(" to start coding."));
|
|
80378
|
+
console.log();
|
|
80379
|
+
} catch (err) {
|
|
80380
|
+
stopSpinner(spinner);
|
|
80381
|
+
console.log();
|
|
80382
|
+
console.log(paint.red(" \u2717 Login failed: ") + String(err.message));
|
|
80383
|
+
console.log();
|
|
80384
|
+
process.exitCode = 1;
|
|
80385
|
+
}
|
|
80386
|
+
}
|
|
80387
|
+
async function loginWithPassword() {
|
|
79816
80388
|
const email = await prompt(paint.bold(" Email ") + paint.dim("> "));
|
|
79817
80389
|
const password = await prompt(paint.bold(" Password ") + paint.dim("> "), true);
|
|
79818
80390
|
console.log();
|
|
79819
80391
|
const spinner = startSpinner("Logging in\u2026");
|
|
79820
80392
|
try {
|
|
79821
|
-
const authConfig = await (0,
|
|
79822
|
-
(0,
|
|
80393
|
+
const authConfig = await (0, import_code_core2.login)(email, password);
|
|
80394
|
+
(0, import_code_core2.saveAuth)(authConfig);
|
|
79823
80395
|
stopSpinner(spinner);
|
|
79824
80396
|
console.log(paint.green(" \u2713 Logged in as ") + paint.bold(authConfig.email ?? email));
|
|
79825
|
-
|
|
79826
|
-
try {
|
|
79827
|
-
const balance = await (0, import_code_core.getBalance)();
|
|
79828
|
-
console.log(paint.dim(" Wallet balance: ") + paint.cyan(`$${balance.toFixed(4)}`));
|
|
79829
|
-
} catch {
|
|
79830
|
-
}
|
|
80397
|
+
await showBalance();
|
|
79831
80398
|
console.log();
|
|
79832
80399
|
console.log(paint.dim(" Run ") + paint.bold("nex") + paint.dim(" to start coding."));
|
|
79833
80400
|
console.log();
|
|
@@ -79836,12 +80403,71 @@ async function authCommand() {
|
|
|
79836
80403
|
console.log();
|
|
79837
80404
|
console.log(paint.red(" \u2717 Login failed: ") + String(err.message));
|
|
79838
80405
|
console.log();
|
|
79839
|
-
process.
|
|
80406
|
+
process.exitCode = 1;
|
|
79840
80407
|
}
|
|
79841
80408
|
}
|
|
80409
|
+
async function loginWithTokenPrompt() {
|
|
80410
|
+
console.log(paint.dim(" Paste a token from the Nexrall dashboard."));
|
|
80411
|
+
const token = await prompt(paint.bold(" Token ") + paint.dim("> "), true);
|
|
80412
|
+
if (!token) {
|
|
80413
|
+
console.log(paint.red(" \u2717 No token entered."));
|
|
80414
|
+
console.log();
|
|
80415
|
+
process.exitCode = 1;
|
|
80416
|
+
return;
|
|
80417
|
+
}
|
|
80418
|
+
await loginWithToken(token);
|
|
80419
|
+
}
|
|
80420
|
+
async function loginWithToken(rawToken) {
|
|
80421
|
+
const token = rawToken.trim();
|
|
80422
|
+
if (!token) {
|
|
80423
|
+
console.log(paint.red(" \u2717 Empty token."));
|
|
80424
|
+
console.log();
|
|
80425
|
+
process.exitCode = 1;
|
|
80426
|
+
return;
|
|
80427
|
+
}
|
|
80428
|
+
console.log();
|
|
80429
|
+
const spinner = startSpinner("Checking the token\u2026");
|
|
80430
|
+
const check2 = await (0, import_code_core2.checkToken)(token);
|
|
80431
|
+
if (!check2.ok && check2.status !== 0) {
|
|
80432
|
+
stopSpinner(spinner);
|
|
80433
|
+
console.log();
|
|
80434
|
+
console.log(paint.red(` \u2717 The API refused this token (HTTP ${check2.status}).`) + paint.dim(" Nothing was saved."));
|
|
80435
|
+
console.log();
|
|
80436
|
+
process.exitCode = 1;
|
|
80437
|
+
return;
|
|
80438
|
+
}
|
|
80439
|
+
(0, import_code_core2.saveAuth)({ token, ...check2.email ? { email: check2.email } : {} });
|
|
80440
|
+
stopSpinner(spinner);
|
|
80441
|
+
console.log(paint.green(" \u2713 Token saved") + (check2.email ? paint.dim(` for ${check2.email}`) : ""));
|
|
80442
|
+
if (check2.status === 0) {
|
|
80443
|
+
console.log(paint.yellow(" Could not reach the API to verify it \u2014 check the token once you are online."));
|
|
80444
|
+
}
|
|
80445
|
+
warnIfEnvTokenWins();
|
|
80446
|
+
await showBalance();
|
|
80447
|
+
console.log();
|
|
80448
|
+
}
|
|
80449
|
+
function warnIfEnvTokenWins() {
|
|
80450
|
+
const envToken = process.env.NEXRALL_TOKEN?.trim();
|
|
80451
|
+
if (!envToken)
|
|
80452
|
+
return;
|
|
80453
|
+
console.log(
|
|
80454
|
+
paint.yellow(" NEXRALL_TOKEN is set in this shell and takes precedence over the saved login.") + paint.dim(" Unset it to use what you just saved.")
|
|
80455
|
+
);
|
|
80456
|
+
}
|
|
80457
|
+
async function showBalance() {
|
|
80458
|
+
try {
|
|
80459
|
+
const balance = await (0, import_code_core2.getBalance)();
|
|
80460
|
+
console.log(paint.dim(" Wallet balance: ") + paint.cyan(`$${balance.toFixed(4)}`));
|
|
80461
|
+
} catch {
|
|
80462
|
+
}
|
|
80463
|
+
}
|
|
80464
|
+
async function signOut() {
|
|
80465
|
+
await (0, import_code_core2.revokeRefreshToken)();
|
|
80466
|
+
(0, import_code_core2.clearAuth)();
|
|
80467
|
+
}
|
|
79842
80468
|
async function logoutCommand() {
|
|
79843
80469
|
console.log();
|
|
79844
|
-
if (!(0,
|
|
80470
|
+
if (!(0, import_code_core2.isAuthenticated)()) {
|
|
79845
80471
|
console.log(paint.dim(" Not logged in."));
|
|
79846
80472
|
console.log();
|
|
79847
80473
|
return;
|
|
@@ -79852,9 +80478,13 @@ async function logoutCommand() {
|
|
|
79852
80478
|
console.log();
|
|
79853
80479
|
return;
|
|
79854
80480
|
}
|
|
79855
|
-
await (
|
|
79856
|
-
(0, import_code_core.clearAuth)();
|
|
80481
|
+
await signOut();
|
|
79857
80482
|
console.log(paint.green(" \u2713 Logged out."));
|
|
80483
|
+
if (process.env.NEXRALL_TOKEN?.trim()) {
|
|
80484
|
+
console.log(
|
|
80485
|
+
paint.yellow(" NEXRALL_TOKEN is still set in this shell") + paint.dim(" \u2014 unset it, or the CLI stays signed in with that token.")
|
|
80486
|
+
);
|
|
80487
|
+
}
|
|
79858
80488
|
console.log();
|
|
79859
80489
|
}
|
|
79860
80490
|
function startSpinner(text) {
|
|
@@ -79873,12 +80503,12 @@ function stopSpinner(handle2) {
|
|
|
79873
80503
|
}
|
|
79874
80504
|
|
|
79875
80505
|
// src/commands/billing.ts
|
|
79876
|
-
var
|
|
80506
|
+
var import_code_core3 = __toESM(require_dist2(), 1);
|
|
79877
80507
|
function money(usd) {
|
|
79878
80508
|
return "$" + usd.toFixed(2);
|
|
79879
80509
|
}
|
|
79880
80510
|
async function requireAuth() {
|
|
79881
|
-
if ((0,
|
|
80511
|
+
if ((0, import_code_core3.isAuthenticated)())
|
|
79882
80512
|
return true;
|
|
79883
80513
|
console.log(paint.red("Not logged in. Run `nex auth` first."));
|
|
79884
80514
|
return false;
|
|
@@ -79888,7 +80518,7 @@ async function billingStatusCommand() {
|
|
|
79888
80518
|
return;
|
|
79889
80519
|
let orgs;
|
|
79890
80520
|
try {
|
|
79891
|
-
orgs = await (0,
|
|
80521
|
+
orgs = await (0, import_code_core3.getOrgs)();
|
|
79892
80522
|
} catch {
|
|
79893
80523
|
console.log(paint.yellow("Could not read your billing target (offline, or a server without team support)."));
|
|
79894
80524
|
return;
|
|
@@ -79920,7 +80550,7 @@ async function billingStatusCommand() {
|
|
|
79920
80550
|
console.log("");
|
|
79921
80551
|
console.log(paint.gray("Change it with `nex billing use <id>` or `nex billing use personal`."));
|
|
79922
80552
|
}
|
|
79923
|
-
const override = (0,
|
|
80553
|
+
const override = (0, import_code_core3.billingOverrideFields)();
|
|
79924
80554
|
if ("teamId" in override) {
|
|
79925
80555
|
console.log("");
|
|
79926
80556
|
console.log(
|
|
@@ -79948,7 +80578,7 @@ async function billingUseCommand(target) {
|
|
|
79948
80578
|
}
|
|
79949
80579
|
async function applyTarget(teamId, label) {
|
|
79950
80580
|
try {
|
|
79951
|
-
await (0,
|
|
80581
|
+
await (0, import_code_core3.setBillingTarget)(teamId);
|
|
79952
80582
|
} catch (e2) {
|
|
79953
80583
|
console.log(paint.red(e2.message));
|
|
79954
80584
|
process.exitCode = 1;
|
|
@@ -79962,14 +80592,14 @@ async function applyTarget(teamId, label) {
|
|
|
79962
80592
|
|
|
79963
80593
|
// src/commands/schedule.ts
|
|
79964
80594
|
var import_prompts = __toESM(require_prompts3(), 1);
|
|
79965
|
-
var
|
|
80595
|
+
var import_code_core5 = __toESM(require_dist2(), 1);
|
|
79966
80596
|
|
|
79967
80597
|
// src/ui/scheduleLines.ts
|
|
79968
|
-
var
|
|
80598
|
+
var import_code_core4 = __toESM(require_dist2(), 1);
|
|
79969
80599
|
|
|
79970
80600
|
// src/commands/schedule.ts
|
|
79971
80601
|
async function requireAuth2() {
|
|
79972
|
-
if ((0,
|
|
80602
|
+
if ((0, import_code_core5.isAuthenticated)())
|
|
79973
80603
|
return true;
|
|
79974
80604
|
console.log(paint.red("Not logged in. Run `nex auth` first."));
|
|
79975
80605
|
process.exitCode = 1;
|
|
@@ -79990,7 +80620,7 @@ async function scheduleListCommand(opts = {}) {
|
|
|
79990
80620
|
return;
|
|
79991
80621
|
let list;
|
|
79992
80622
|
try {
|
|
79993
|
-
list = await (0,
|
|
80623
|
+
list = await (0, import_code_core5.listCloudSchedules)();
|
|
79994
80624
|
} catch (e2) {
|
|
79995
80625
|
fail(errText(e2));
|
|
79996
80626
|
return;
|
|
@@ -79999,7 +80629,7 @@ async function scheduleListCommand(opts = {}) {
|
|
|
79999
80629
|
console.log(JSON.stringify(list, null, 2));
|
|
80000
80630
|
return;
|
|
80001
80631
|
}
|
|
80002
|
-
for (const line of (0,
|
|
80632
|
+
for (const line of (0, import_code_core4.scheduleListLines)(list, { cols: cols() }))
|
|
80003
80633
|
console.log(line);
|
|
80004
80634
|
if (list.length) {
|
|
80005
80635
|
console.log(paint.dim(`
|
|
@@ -80019,7 +80649,7 @@ async function scheduleAddCommand(taskParts, opts) {
|
|
|
80019
80649
|
fail("Missing --every. Usage: nex schedule add --every 1d <task> (e.g. --every 2h)");
|
|
80020
80650
|
return;
|
|
80021
80651
|
}
|
|
80022
|
-
const every = (0,
|
|
80652
|
+
const every = (0, import_code_core4.parseScheduleEvery)(everyRaw);
|
|
80023
80653
|
if ("error" in every) {
|
|
80024
80654
|
fail(every.error);
|
|
80025
80655
|
return;
|
|
@@ -80027,7 +80657,7 @@ async function scheduleAddCommand(taskParts, opts) {
|
|
|
80027
80657
|
const domains = (opts.domains ?? "").split(",").map((d) => d.trim()).filter(Boolean);
|
|
80028
80658
|
let s2;
|
|
80029
80659
|
try {
|
|
80030
|
-
s2 = await (0,
|
|
80660
|
+
s2 = await (0, import_code_core5.createCloudSchedule)({
|
|
80031
80661
|
task,
|
|
80032
80662
|
intervalMinutes: every.minutes,
|
|
80033
80663
|
startUrl: opts.startUrl,
|
|
@@ -80039,12 +80669,12 @@ async function scheduleAddCommand(taskParts, opts) {
|
|
|
80039
80669
|
return;
|
|
80040
80670
|
}
|
|
80041
80671
|
console.log(
|
|
80042
|
-
`\u2714 Scheduled ${paint.bold(`#${s2.id}`)} \u2014 every ${(0,
|
|
80672
|
+
`\u2714 Scheduled ${paint.bold(`#${s2.id}`)} \u2014 every ${(0, import_code_core4.formatEvery)(s2.intervalMinutes)}${s2.enabled ? "" : paint.yellow(" (paused)")}`
|
|
80043
80673
|
);
|
|
80044
80674
|
console.log(` ${s2.task}`);
|
|
80045
80675
|
if (s2.nextRunAt) {
|
|
80046
80676
|
console.log(
|
|
80047
|
-
paint.dim(` Next run ${(0,
|
|
80677
|
+
paint.dim(` Next run ${(0, import_code_core4.fmtScheduleWhen)(s2.nextRunAt)} \u2014 on Nexrall's servers; your laptop can be closed.`)
|
|
80048
80678
|
);
|
|
80049
80679
|
}
|
|
80050
80680
|
console.log(paint.dim(` History: nex schedule runs ${s2.id} \xB7 Pause: nex schedule pause ${s2.id}`));
|
|
@@ -80057,8 +80687,8 @@ async function scheduleSetEnabled(id, enabled) {
|
|
|
80057
80687
|
return;
|
|
80058
80688
|
}
|
|
80059
80689
|
try {
|
|
80060
|
-
const s2 = await (0,
|
|
80061
|
-
const next = s2.nextRunAt ? paint.dim(` \u2014 next run ${(0,
|
|
80690
|
+
const s2 = await (0, import_code_core5.updateCloudSchedule)(id, { enabled });
|
|
80691
|
+
const next = s2.nextRunAt ? paint.dim(` \u2014 next run ${(0, import_code_core4.fmtScheduleWhen)(s2.nextRunAt)}`) : "";
|
|
80062
80692
|
console.log(`\u2714 Schedule #${s2.id} is now ${s2.enabled ? "on" : "paused"}${next}`);
|
|
80063
80693
|
} catch (e2) {
|
|
80064
80694
|
fail(errText(e2));
|
|
@@ -80079,7 +80709,7 @@ async function scheduleRunCommand(id) {
|
|
|
80079
80709
|
return;
|
|
80080
80710
|
}
|
|
80081
80711
|
try {
|
|
80082
|
-
const s2 = await (0,
|
|
80712
|
+
const s2 = await (0, import_code_core5.runCloudScheduleNow)(clean);
|
|
80083
80713
|
console.log(`\u2714 Schedule #${s2.id} armed \u2014 the cloud runner picks it up within ~60s.`);
|
|
80084
80714
|
console.log(paint.dim(` Result: nex schedule runs ${s2.id}`));
|
|
80085
80715
|
} catch (e2) {
|
|
@@ -80113,7 +80743,7 @@ async function scheduleRemoveCommand(id, opts = {}) {
|
|
|
80113
80743
|
}
|
|
80114
80744
|
}
|
|
80115
80745
|
try {
|
|
80116
|
-
await (0,
|
|
80746
|
+
await (0, import_code_core5.deleteCloudSchedule)(clean);
|
|
80117
80747
|
console.log(`\u2714 Removed schedule #${clean}`);
|
|
80118
80748
|
} catch (e2) {
|
|
80119
80749
|
fail(errText(e2));
|
|
@@ -80129,7 +80759,7 @@ async function scheduleRunsCommand(id, opts = {}) {
|
|
|
80129
80759
|
}
|
|
80130
80760
|
let runs2;
|
|
80131
80761
|
try {
|
|
80132
|
-
runs2 = await (0,
|
|
80762
|
+
runs2 = await (0, import_code_core5.getCloudScheduleRuns)(clean);
|
|
80133
80763
|
} catch (e2) {
|
|
80134
80764
|
fail(errText(e2));
|
|
80135
80765
|
return;
|
|
@@ -80138,7 +80768,7 @@ async function scheduleRunsCommand(id, opts = {}) {
|
|
|
80138
80768
|
console.log(JSON.stringify(runs2, null, 2));
|
|
80139
80769
|
return;
|
|
80140
80770
|
}
|
|
80141
|
-
for (const line of (0,
|
|
80771
|
+
for (const line of (0, import_code_core4.scheduleRunsLines)(runs2, { cols: cols() }))
|
|
80142
80772
|
console.log(line);
|
|
80143
80773
|
if (runs2.length) {
|
|
80144
80774
|
console.log(
|
|
@@ -80151,10 +80781,10 @@ async function scheduleRunsCommand(id, opts = {}) {
|
|
|
80151
80781
|
}
|
|
80152
80782
|
|
|
80153
80783
|
// src/commands/cloud.ts
|
|
80154
|
-
var
|
|
80155
|
-
var
|
|
80784
|
+
var import_proc2 = __toESM(require_proc(), 1);
|
|
80785
|
+
var import_code_core6 = __toESM(require_dist2(), 1);
|
|
80156
80786
|
async function requireAuth3() {
|
|
80157
|
-
if ((0,
|
|
80787
|
+
if ((0, import_code_core6.isAuthenticated)())
|
|
80158
80788
|
return true;
|
|
80159
80789
|
console.log(paint.red("Not logged in. Run `nex auth` first."));
|
|
80160
80790
|
process.exitCode = 1;
|
|
@@ -80192,7 +80822,7 @@ function openUrl(url) {
|
|
|
80192
80822
|
const cmd = process.platform === "darwin" ? "open" : win ? "rundll32" : "xdg-open";
|
|
80193
80823
|
const args = win ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
80194
80824
|
try {
|
|
80195
|
-
const child = (0,
|
|
80825
|
+
const child = (0, import_proc2.spawn)(cmd, args, { stdio: "ignore", detached: !win });
|
|
80196
80826
|
child.on("error", () => {
|
|
80197
80827
|
});
|
|
80198
80828
|
child.unref();
|
|
@@ -80224,7 +80854,7 @@ async function cloudRunCommand(taskParts, opts) {
|
|
|
80224
80854
|
}
|
|
80225
80855
|
let created;
|
|
80226
80856
|
try {
|
|
80227
|
-
created = await (0,
|
|
80857
|
+
created = await (0, import_code_core6.createCloudSession)({ repo: opts.repo, task, baseBranch: opts.base, pat: pat || void 0 });
|
|
80228
80858
|
} catch (e2) {
|
|
80229
80859
|
return fail2(errText2(e2));
|
|
80230
80860
|
}
|
|
@@ -80243,7 +80873,7 @@ async function cloudListCommand(opts = {}) {
|
|
|
80243
80873
|
return;
|
|
80244
80874
|
let list;
|
|
80245
80875
|
try {
|
|
80246
|
-
list = await (0,
|
|
80876
|
+
list = await (0, import_code_core6.listCloudSessions)();
|
|
80247
80877
|
} catch (e2) {
|
|
80248
80878
|
return fail2(errText2(e2));
|
|
80249
80879
|
}
|
|
@@ -80251,7 +80881,7 @@ async function cloudListCommand(opts = {}) {
|
|
|
80251
80881
|
console.log(JSON.stringify({ sessions: list }, null, 2));
|
|
80252
80882
|
return;
|
|
80253
80883
|
}
|
|
80254
|
-
for (const line of (0,
|
|
80884
|
+
for (const line of (0, import_code_core6.cloudListLines)(list, { cols: cols2() }))
|
|
80255
80885
|
console.log(` ${line}`);
|
|
80256
80886
|
}
|
|
80257
80887
|
async function cloudLogsCommand(id, opts) {
|
|
@@ -80264,7 +80894,7 @@ async function cloudLogsCommand(id, opts) {
|
|
|
80264
80894
|
if (!opts.follow) {
|
|
80265
80895
|
let r2;
|
|
80266
80896
|
try {
|
|
80267
|
-
r2 = await (0,
|
|
80897
|
+
r2 = await (0, import_code_core6.getCloudSessionLogs)(id, tail ?? 300);
|
|
80268
80898
|
} catch (e2) {
|
|
80269
80899
|
return fail2(errText2(e2));
|
|
80270
80900
|
}
|
|
@@ -80290,7 +80920,7 @@ async function cloudLogsCommand(id, opts) {
|
|
|
80290
80920
|
for (; ; ) {
|
|
80291
80921
|
let r2;
|
|
80292
80922
|
try {
|
|
80293
|
-
r2 = await (0,
|
|
80923
|
+
r2 = await (0, import_code_core6.getCloudSessionLogs)(id, tail ?? 1e3);
|
|
80294
80924
|
consecutiveErrors = 0;
|
|
80295
80925
|
} catch (e2) {
|
|
80296
80926
|
const msg = errText2(e2);
|
|
@@ -80339,7 +80969,7 @@ async function cloudStopCommand(id) {
|
|
|
80339
80969
|
return;
|
|
80340
80970
|
let s2;
|
|
80341
80971
|
try {
|
|
80342
|
-
s2 = await (0,
|
|
80972
|
+
s2 = await (0, import_code_core6.stopCloudSession)(id);
|
|
80343
80973
|
} catch (e2) {
|
|
80344
80974
|
return fail2(errText2(e2));
|
|
80345
80975
|
}
|
|
@@ -80354,7 +80984,7 @@ async function cloudWatchCommand(id, opts = {}) {
|
|
|
80354
80984
|
return;
|
|
80355
80985
|
let s2;
|
|
80356
80986
|
try {
|
|
80357
|
-
s2 = await (0,
|
|
80987
|
+
s2 = await (0, import_code_core6.getCloudSession)(id);
|
|
80358
80988
|
} catch (e2) {
|
|
80359
80989
|
return fail2(errText2(e2));
|
|
80360
80990
|
}
|
|
@@ -80524,11 +81154,11 @@ async function channelPushCommand(textParts, opts, deps = {}) {
|
|
|
80524
81154
|
}
|
|
80525
81155
|
|
|
80526
81156
|
// src/commands/usageCmd.ts
|
|
80527
|
-
import
|
|
80528
|
-
import
|
|
81157
|
+
import fs3 from "node:fs";
|
|
81158
|
+
import path2 from "node:path";
|
|
80529
81159
|
|
|
80530
81160
|
// src/sessionEvents.ts
|
|
80531
|
-
var
|
|
81161
|
+
var import_code_core7 = __toESM(require_dist2(), 1);
|
|
80532
81162
|
|
|
80533
81163
|
// src/usageExport.ts
|
|
80534
81164
|
var DURATION_RE = /^(\d+)([mhdw])$/;
|
|
@@ -80825,7 +81455,7 @@ var USAGE_FORMATS = ["json", "jsonl", "csv"];
|
|
|
80825
81455
|
function logFileIds(dir) {
|
|
80826
81456
|
let names;
|
|
80827
81457
|
try {
|
|
80828
|
-
names =
|
|
81458
|
+
names = fs3.readdirSync(dir);
|
|
80829
81459
|
} catch {
|
|
80830
81460
|
return [];
|
|
80831
81461
|
}
|
|
@@ -80835,7 +81465,7 @@ function readRows(file) {
|
|
|
80835
81465
|
const rows = [];
|
|
80836
81466
|
let text;
|
|
80837
81467
|
try {
|
|
80838
|
-
text =
|
|
81468
|
+
text = fs3.readFileSync(file, "utf8");
|
|
80839
81469
|
} catch {
|
|
80840
81470
|
return rows;
|
|
80841
81471
|
}
|
|
@@ -80883,13 +81513,13 @@ function usageExportCommand(opts, deps = {}) {
|
|
|
80883
81513
|
}
|
|
80884
81514
|
limit = n;
|
|
80885
81515
|
}
|
|
80886
|
-
const dir = deps.eventsDirPath ?? (0,
|
|
81516
|
+
const dir = deps.eventsDirPath ?? (0, import_code_core7.eventsDir)(deps.env ?? process.env);
|
|
80887
81517
|
const ids = logFileIds(dir);
|
|
80888
81518
|
const acc = createAccumulator({ since, until });
|
|
80889
81519
|
acc.filesTotal = ids.length;
|
|
80890
81520
|
let candidates = ids;
|
|
80891
81521
|
if (opts.session) {
|
|
80892
|
-
const wanted = (0,
|
|
81522
|
+
const wanted = (0, import_code_core7.safeId)(opts.session);
|
|
80893
81523
|
if (!candidates.includes(wanted)) {
|
|
80894
81524
|
stderr(paint.red(`Session not found: ${opts.session}`));
|
|
80895
81525
|
stderr(paint.dim(` Logs live in ${dir} (one .jsonl per session).`));
|
|
@@ -80900,7 +81530,7 @@ function usageExportCommand(opts, deps = {}) {
|
|
|
80900
81530
|
if (since !== void 0) {
|
|
80901
81531
|
candidates = candidates.filter((id) => {
|
|
80902
81532
|
try {
|
|
80903
|
-
return
|
|
81533
|
+
return fs3.statSync(path2.join(dir, `${id}.jsonl`)).mtimeMs >= since;
|
|
80904
81534
|
} catch {
|
|
80905
81535
|
return false;
|
|
80906
81536
|
}
|
|
@@ -80908,7 +81538,7 @@ function usageExportCommand(opts, deps = {}) {
|
|
|
80908
81538
|
}
|
|
80909
81539
|
const byNewest = candidates.map((id) => {
|
|
80910
81540
|
try {
|
|
80911
|
-
return { id, mtime:
|
|
81541
|
+
return { id, mtime: fs3.statSync(path2.join(dir, `${id}.jsonl`)).mtimeMs };
|
|
80912
81542
|
} catch {
|
|
80913
81543
|
return { id, mtime: 0 };
|
|
80914
81544
|
}
|
|
@@ -80917,7 +81547,7 @@ function usageExportCommand(opts, deps = {}) {
|
|
|
80917
81547
|
acc.truncated = taken.length < byNewest.length;
|
|
80918
81548
|
for (const { id } of taken) {
|
|
80919
81549
|
acc.filesRead += 1;
|
|
80920
|
-
addSessionFile(acc, id, readRows(
|
|
81550
|
+
addSessionFile(acc, id, readRows(path2.join(dir, `${id}.jsonl`)));
|
|
80921
81551
|
}
|
|
80922
81552
|
const report = finalizeUsage(acc, now);
|
|
80923
81553
|
let content;
|
|
@@ -80929,7 +81559,7 @@ function usageExportCommand(opts, deps = {}) {
|
|
|
80929
81559
|
content = formatUsageJson(report);
|
|
80930
81560
|
if (opts.out) {
|
|
80931
81561
|
try {
|
|
80932
|
-
|
|
81562
|
+
fs3.writeFileSync(opts.out, content);
|
|
80933
81563
|
} catch (e2) {
|
|
80934
81564
|
stderr(paint.red(`Could not write ${opts.out}: ${e2.message}`));
|
|
80935
81565
|
return USAGE_EXIT.FAIL;
|
|
@@ -80963,7 +81593,7 @@ import * as path27 from "path";
|
|
|
80963
81593
|
import * as fs26 from "fs";
|
|
80964
81594
|
|
|
80965
81595
|
// src/ui/diffView.ts
|
|
80966
|
-
var
|
|
81596
|
+
var import_code_core8 = __toESM(require_dist2(), 1);
|
|
80967
81597
|
function paintLine(l) {
|
|
80968
81598
|
switch (l.kind) {
|
|
80969
81599
|
case "file":
|
|
@@ -80984,433 +81614,7 @@ function paintLine(l) {
|
|
|
80984
81614
|
}
|
|
80985
81615
|
|
|
80986
81616
|
// src/ui/reviewQueue.ts
|
|
80987
|
-
var import_code_core8 = __toESM(require_dist2(), 1);
|
|
80988
|
-
|
|
80989
|
-
// src/mcp/oauth.ts
|
|
80990
|
-
var import_proc2 = __toESM(require_proc(), 1);
|
|
80991
81617
|
var import_code_core9 = __toESM(require_dist2(), 1);
|
|
80992
|
-
import * as fs3 from "fs";
|
|
80993
|
-
import * as os2 from "os";
|
|
80994
|
-
import * as path2 from "path";
|
|
80995
|
-
import * as http3 from "http";
|
|
80996
|
-
import * as crypto from "crypto";
|
|
80997
|
-
var CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
80998
|
-
function tokensFile(env3 = process.env) {
|
|
80999
|
-
return env3.NEXRALL_MCP_AUTH_FILE || path2.join(os2.homedir(), ".nexrall", "mcp-auth.json");
|
|
81000
|
-
}
|
|
81001
|
-
function readStore(file) {
|
|
81002
|
-
try {
|
|
81003
|
-
const parsed = JSON.parse(fs3.readFileSync(file, "utf-8"));
|
|
81004
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
81005
|
-
return {};
|
|
81006
|
-
const out = {};
|
|
81007
|
-
for (const [k, v] of Object.entries(parsed)) {
|
|
81008
|
-
const b = v;
|
|
81009
|
-
if (b && typeof b.access_token === "string" && typeof b.token_endpoint === "string")
|
|
81010
|
-
out[k] = b;
|
|
81011
|
-
}
|
|
81012
|
-
return out;
|
|
81013
|
-
} catch {
|
|
81014
|
-
return {};
|
|
81015
|
-
}
|
|
81016
|
-
}
|
|
81017
|
-
function writeStore(file, store) {
|
|
81018
|
-
fs3.mkdirSync(path2.dirname(file), { recursive: true });
|
|
81019
|
-
fs3.writeFileSync(file, JSON.stringify(store, null, 2), { mode: 384 });
|
|
81020
|
-
try {
|
|
81021
|
-
fs3.chmodSync(file, 384);
|
|
81022
|
-
} catch {
|
|
81023
|
-
}
|
|
81024
|
-
}
|
|
81025
|
-
function loadToken(server, file = tokensFile()) {
|
|
81026
|
-
return readStore(file)[server] ?? null;
|
|
81027
|
-
}
|
|
81028
|
-
function saveToken(server, bundle, file = tokensFile()) {
|
|
81029
|
-
const store = readStore(file);
|
|
81030
|
-
store[server] = bundle;
|
|
81031
|
-
writeStore(file, store);
|
|
81032
|
-
}
|
|
81033
|
-
function clearToken(server, file = tokensFile()) {
|
|
81034
|
-
const store = readStore(file);
|
|
81035
|
-
if (!(server in store))
|
|
81036
|
-
return false;
|
|
81037
|
-
delete store[server];
|
|
81038
|
-
writeStore(file, store);
|
|
81039
|
-
return true;
|
|
81040
|
-
}
|
|
81041
|
-
function defaultFetch() {
|
|
81042
|
-
const f3 = globalThis.fetch;
|
|
81043
|
-
if (typeof f3 !== "function")
|
|
81044
|
-
throw new Error("global fetch unavailable \u2014 Node 18+ is required for MCP OAuth");
|
|
81045
|
-
return f3;
|
|
81046
|
-
}
|
|
81047
|
-
async function getValidAccessToken(server, opts = {}) {
|
|
81048
|
-
const file = opts.file ?? tokensFile();
|
|
81049
|
-
const bundle = loadToken(server, file);
|
|
81050
|
-
if (!bundle)
|
|
81051
|
-
return null;
|
|
81052
|
-
const fresh = !bundle.expires_at || bundle.expires_at - Date.now() > 6e4;
|
|
81053
|
-
if (fresh || !bundle.refresh_token)
|
|
81054
|
-
return bundle.access_token;
|
|
81055
|
-
try {
|
|
81056
|
-
const refreshed = await refreshAccessToken(bundle, opts.fetchImpl ?? defaultFetch());
|
|
81057
|
-
saveToken(server, refreshed, file);
|
|
81058
|
-
return refreshed.access_token;
|
|
81059
|
-
} catch {
|
|
81060
|
-
return bundle.access_token;
|
|
81061
|
-
}
|
|
81062
|
-
}
|
|
81063
|
-
function parseResourceMetadataUrl(wwwAuthenticate) {
|
|
81064
|
-
const m2 = /resource_metadata\s*=\s*"([^"]+)"/i.exec(wwwAuthenticate || "");
|
|
81065
|
-
return m2 ? m2[1] : null;
|
|
81066
|
-
}
|
|
81067
|
-
function protectedResourceCandidates(serverUrl) {
|
|
81068
|
-
const u = new URL(serverUrl);
|
|
81069
|
-
const base = u.origin;
|
|
81070
|
-
const p = u.pathname.replace(/\/$/, "");
|
|
81071
|
-
return [
|
|
81072
|
-
...p ? [`${base}/.well-known/oauth-protected-resource${p}`] : [],
|
|
81073
|
-
`${base}/.well-known/oauth-protected-resource`
|
|
81074
|
-
];
|
|
81075
|
-
}
|
|
81076
|
-
function authServerCandidates(authServer) {
|
|
81077
|
-
const u = new URL(authServer);
|
|
81078
|
-
const base = u.origin;
|
|
81079
|
-
const p = u.pathname.replace(/\/$/, "");
|
|
81080
|
-
const root = authServer.replace(/\/$/, "");
|
|
81081
|
-
return [
|
|
81082
|
-
`${base}/.well-known/oauth-authorization-server${p}`,
|
|
81083
|
-
`${base}/.well-known/openid-configuration${p}`,
|
|
81084
|
-
`${root}/.well-known/oauth-authorization-server`,
|
|
81085
|
-
`${root}/.well-known/openid-configuration`
|
|
81086
|
-
];
|
|
81087
|
-
}
|
|
81088
|
-
function buildAuthorizeUrl(meta, p) {
|
|
81089
|
-
const url = new URL(meta.authorization_endpoint);
|
|
81090
|
-
url.searchParams.set("response_type", "code");
|
|
81091
|
-
url.searchParams.set("client_id", p.clientId);
|
|
81092
|
-
url.searchParams.set("redirect_uri", p.redirectUri);
|
|
81093
|
-
url.searchParams.set("state", p.state);
|
|
81094
|
-
url.searchParams.set("code_challenge", p.codeChallenge);
|
|
81095
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
81096
|
-
url.searchParams.set("resource", meta.resource);
|
|
81097
|
-
if (meta.scope)
|
|
81098
|
-
url.searchParams.set("scope", meta.scope);
|
|
81099
|
-
return url.toString();
|
|
81100
|
-
}
|
|
81101
|
-
function toBundle(data, p) {
|
|
81102
|
-
const accessToken = data.access_token;
|
|
81103
|
-
if (!accessToken)
|
|
81104
|
-
throw new Error("Token response had no access_token");
|
|
81105
|
-
const expiresIn = typeof data.expires_in === "number" ? data.expires_in : void 0;
|
|
81106
|
-
return {
|
|
81107
|
-
access_token: accessToken,
|
|
81108
|
-
refresh_token: data.refresh_token ?? p.prevRefresh,
|
|
81109
|
-
expires_at: expiresIn ? Date.now() + expiresIn * 1e3 : void 0,
|
|
81110
|
-
token_endpoint: p.tokenEndpoint,
|
|
81111
|
-
client_id: p.clientId,
|
|
81112
|
-
client_secret: p.clientSecret,
|
|
81113
|
-
resource: p.resource,
|
|
81114
|
-
scope: data.scope ?? p.scope
|
|
81115
|
-
};
|
|
81116
|
-
}
|
|
81117
|
-
async function fetchJsonSafe(f3, url) {
|
|
81118
|
-
try {
|
|
81119
|
-
const resp = await f3(url, { headers: { Accept: "application/json" } });
|
|
81120
|
-
if (!resp.ok)
|
|
81121
|
-
return null;
|
|
81122
|
-
return await resp.json();
|
|
81123
|
-
} catch {
|
|
81124
|
-
return null;
|
|
81125
|
-
}
|
|
81126
|
-
}
|
|
81127
|
-
async function discover(serverUrl, wwwAuthenticate, f3 = defaultFetch()) {
|
|
81128
|
-
const origin = new URL(serverUrl).origin;
|
|
81129
|
-
let authServer = null;
|
|
81130
|
-
let resource = serverUrl;
|
|
81131
|
-
let scope;
|
|
81132
|
-
const prmCandidates = [parseResourceMetadataUrl(wwwAuthenticate), ...protectedResourceCandidates(serverUrl)].filter(
|
|
81133
|
-
(u) => Boolean(u)
|
|
81134
|
-
);
|
|
81135
|
-
let prm = null;
|
|
81136
|
-
for (const url of new Set(prmCandidates)) {
|
|
81137
|
-
prm = await fetchJsonSafe(f3, url);
|
|
81138
|
-
if (prm)
|
|
81139
|
-
break;
|
|
81140
|
-
}
|
|
81141
|
-
if (prm) {
|
|
81142
|
-
const servers = prm.authorization_servers;
|
|
81143
|
-
if (Array.isArray(servers) && servers.length)
|
|
81144
|
-
authServer = servers[0];
|
|
81145
|
-
if (typeof prm.resource === "string")
|
|
81146
|
-
resource = prm.resource;
|
|
81147
|
-
const scopes = prm.scopes_supported;
|
|
81148
|
-
if (Array.isArray(scopes) && scopes.length)
|
|
81149
|
-
scope = scopes.join(" ");
|
|
81150
|
-
}
|
|
81151
|
-
if (!authServer)
|
|
81152
|
-
authServer = origin;
|
|
81153
|
-
let asm = null;
|
|
81154
|
-
for (const url of authServerCandidates(authServer)) {
|
|
81155
|
-
asm = await fetchJsonSafe(f3, url);
|
|
81156
|
-
if (asm && asm.authorization_endpoint && asm.token_endpoint)
|
|
81157
|
-
break;
|
|
81158
|
-
asm = null;
|
|
81159
|
-
}
|
|
81160
|
-
if (!asm)
|
|
81161
|
-
throw new Error(`No OAuth metadata found for authorization server ${authServer}`);
|
|
81162
|
-
return {
|
|
81163
|
-
authorization_endpoint: asm.authorization_endpoint,
|
|
81164
|
-
token_endpoint: asm.token_endpoint,
|
|
81165
|
-
registration_endpoint: asm.registration_endpoint,
|
|
81166
|
-
resource,
|
|
81167
|
-
scope
|
|
81168
|
-
};
|
|
81169
|
-
}
|
|
81170
|
-
async function registerClient(meta, redirectUri, f3) {
|
|
81171
|
-
if (!meta.registration_endpoint) {
|
|
81172
|
-
throw new Error(
|
|
81173
|
-
'Authorization server does not support dynamic client registration; a client_id must be configured manually \u2014 create an OAuth client for this server, then re-run `nex mcp login <server> --client-id <id> [--client-secret <secret>]` (or set "clientId" / "clientSecret" in its mcp.json entry).'
|
|
81174
|
-
);
|
|
81175
|
-
}
|
|
81176
|
-
const resp = await f3(meta.registration_endpoint, {
|
|
81177
|
-
method: "POST",
|
|
81178
|
-
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
81179
|
-
body: JSON.stringify({
|
|
81180
|
-
client_name: "Nexrall Code CLI",
|
|
81181
|
-
redirect_uris: [redirectUri],
|
|
81182
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
81183
|
-
response_types: ["code"],
|
|
81184
|
-
token_endpoint_auth_method: "none",
|
|
81185
|
-
scope: meta.scope
|
|
81186
|
-
})
|
|
81187
|
-
});
|
|
81188
|
-
if (!resp.ok)
|
|
81189
|
-
throw new Error(`Client registration failed (HTTP ${resp.status})`);
|
|
81190
|
-
const data = await resp.json();
|
|
81191
|
-
if (!data.client_id)
|
|
81192
|
-
throw new Error("Client registration returned no client_id");
|
|
81193
|
-
return data.client_id;
|
|
81194
|
-
}
|
|
81195
|
-
async function postToken(tokenEndpoint, body, f3) {
|
|
81196
|
-
const resp = await f3(tokenEndpoint, {
|
|
81197
|
-
method: "POST",
|
|
81198
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
|
|
81199
|
-
body: body.toString()
|
|
81200
|
-
});
|
|
81201
|
-
if (!resp.ok) {
|
|
81202
|
-
const txt = await resp.text().catch(() => "");
|
|
81203
|
-
throw new Error(`Token endpoint error (HTTP ${resp.status})${txt ? `: ${txt.slice(0, 200)}` : ""}`);
|
|
81204
|
-
}
|
|
81205
|
-
return await resp.json();
|
|
81206
|
-
}
|
|
81207
|
-
async function exchangeCode(meta, clientId, clientSecret, code, verifier, redirectUri, f3) {
|
|
81208
|
-
const body = new URLSearchParams({
|
|
81209
|
-
grant_type: "authorization_code",
|
|
81210
|
-
code,
|
|
81211
|
-
redirect_uri: redirectUri,
|
|
81212
|
-
client_id: clientId,
|
|
81213
|
-
code_verifier: verifier,
|
|
81214
|
-
resource: meta.resource
|
|
81215
|
-
});
|
|
81216
|
-
if (clientSecret)
|
|
81217
|
-
body.set("client_secret", clientSecret);
|
|
81218
|
-
const data = await postToken(meta.token_endpoint, body, f3);
|
|
81219
|
-
return toBundle(data, {
|
|
81220
|
-
tokenEndpoint: meta.token_endpoint,
|
|
81221
|
-
clientId,
|
|
81222
|
-
clientSecret,
|
|
81223
|
-
resource: meta.resource,
|
|
81224
|
-
scope: meta.scope
|
|
81225
|
-
});
|
|
81226
|
-
}
|
|
81227
|
-
async function refreshAccessToken(bundle, f3) {
|
|
81228
|
-
const body = new URLSearchParams({
|
|
81229
|
-
grant_type: "refresh_token",
|
|
81230
|
-
refresh_token: bundle.refresh_token,
|
|
81231
|
-
client_id: bundle.client_id,
|
|
81232
|
-
resource: bundle.resource
|
|
81233
|
-
});
|
|
81234
|
-
if (bundle.client_secret)
|
|
81235
|
-
body.set("client_secret", bundle.client_secret);
|
|
81236
|
-
const data = await postToken(bundle.token_endpoint, body, f3);
|
|
81237
|
-
return toBundle(data, {
|
|
81238
|
-
tokenEndpoint: bundle.token_endpoint,
|
|
81239
|
-
clientId: bundle.client_id,
|
|
81240
|
-
clientSecret: bundle.client_secret,
|
|
81241
|
-
resource: bundle.resource,
|
|
81242
|
-
scope: bundle.scope,
|
|
81243
|
-
prevRefresh: bundle.refresh_token
|
|
81244
|
-
});
|
|
81245
|
-
}
|
|
81246
|
-
function startLoopback() {
|
|
81247
|
-
return new Promise((resolve19, reject) => {
|
|
81248
|
-
let resolveCode;
|
|
81249
|
-
let rejectCode;
|
|
81250
|
-
const codePromise = new Promise((res, rej) => {
|
|
81251
|
-
resolveCode = res;
|
|
81252
|
-
rejectCode = rej;
|
|
81253
|
-
});
|
|
81254
|
-
codePromise.catch(() => void 0);
|
|
81255
|
-
const server = http3.createServer((req, res) => {
|
|
81256
|
-
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
81257
|
-
if (url.pathname !== "/callback") {
|
|
81258
|
-
res.writeHead(404);
|
|
81259
|
-
res.end();
|
|
81260
|
-
return;
|
|
81261
|
-
}
|
|
81262
|
-
const code = url.searchParams.get("code");
|
|
81263
|
-
const state = url.searchParams.get("state");
|
|
81264
|
-
const error = url.searchParams.get("error");
|
|
81265
|
-
res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
|
|
81266
|
-
res.end(
|
|
81267
|
-
`<!doctype html><html><body style="font-family:system-ui;text-align:center;padding:40px"><h2>${error || !code ? "Authentication failed" : "Authentication complete"}</h2><p>You can close this tab and return to the terminal.</p></body></html>`
|
|
81268
|
-
);
|
|
81269
|
-
if (error) {
|
|
81270
|
-
rejectCode(new Error(`Authorization denied: ${error}`));
|
|
81271
|
-
return;
|
|
81272
|
-
}
|
|
81273
|
-
if (!code) {
|
|
81274
|
-
rejectCode(new Error("No authorization code in redirect"));
|
|
81275
|
-
return;
|
|
81276
|
-
}
|
|
81277
|
-
resolveCode({ code, state });
|
|
81278
|
-
});
|
|
81279
|
-
server.on("error", reject);
|
|
81280
|
-
server.listen(0, "127.0.0.1", () => {
|
|
81281
|
-
const port = server.address().port;
|
|
81282
|
-
resolve19({
|
|
81283
|
-
port,
|
|
81284
|
-
waitForCode(expectedState) {
|
|
81285
|
-
const timeout = setTimeout(
|
|
81286
|
-
() => rejectCode(new Error("Timed out waiting for authorization")),
|
|
81287
|
-
CALLBACK_TIMEOUT_MS
|
|
81288
|
-
);
|
|
81289
|
-
return codePromise.then(
|
|
81290
|
-
({ code, state }) => {
|
|
81291
|
-
clearTimeout(timeout);
|
|
81292
|
-
if (state !== expectedState)
|
|
81293
|
-
throw new Error("OAuth state mismatch \u2014 possible CSRF, aborting");
|
|
81294
|
-
return code;
|
|
81295
|
-
},
|
|
81296
|
-
// A denial must clear the timer too: otherwise `nex mcp login` reports the
|
|
81297
|
-
// failure but the process sits on a 5-minute keep-alive before exiting.
|
|
81298
|
-
(err) => {
|
|
81299
|
-
clearTimeout(timeout);
|
|
81300
|
-
throw err;
|
|
81301
|
-
}
|
|
81302
|
-
);
|
|
81303
|
-
},
|
|
81304
|
-
close() {
|
|
81305
|
-
try {
|
|
81306
|
-
server.closeAllConnections?.();
|
|
81307
|
-
} catch {
|
|
81308
|
-
}
|
|
81309
|
-
try {
|
|
81310
|
-
server.close();
|
|
81311
|
-
} catch {
|
|
81312
|
-
}
|
|
81313
|
-
}
|
|
81314
|
-
});
|
|
81315
|
-
});
|
|
81316
|
-
});
|
|
81317
|
-
}
|
|
81318
|
-
function b64url(buf) {
|
|
81319
|
-
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
81320
|
-
}
|
|
81321
|
-
function openInBrowser(url) {
|
|
81322
|
-
const win = process.platform === "win32";
|
|
81323
|
-
const cmd = process.platform === "darwin" ? "open" : win ? "rundll32" : "xdg-open";
|
|
81324
|
-
const args = win ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
81325
|
-
try {
|
|
81326
|
-
const child = (0, import_proc2.spawn)(cmd, args, { stdio: "ignore", detached: !win });
|
|
81327
|
-
child.on("error", () => void 0);
|
|
81328
|
-
child.unref();
|
|
81329
|
-
} catch {
|
|
81330
|
-
}
|
|
81331
|
-
}
|
|
81332
|
-
async function probeAuthChallenge(serverUrl, f3 = defaultFetch()) {
|
|
81333
|
-
const steps = [
|
|
81334
|
-
{
|
|
81335
|
-
method: "initialize",
|
|
81336
|
-
params: {
|
|
81337
|
-
protocolVersion: import_code_core9.MCP_PROTOCOL_VERSION,
|
|
81338
|
-
capabilities: {},
|
|
81339
|
-
clientInfo: { name: "nexrall-code", version: "1.0" }
|
|
81340
|
-
}
|
|
81341
|
-
},
|
|
81342
|
-
{ method: "tools/list", params: {} }
|
|
81343
|
-
];
|
|
81344
|
-
for (const { method, params } of steps) {
|
|
81345
|
-
try {
|
|
81346
|
-
const resp = await f3(serverUrl, {
|
|
81347
|
-
method: "POST",
|
|
81348
|
-
headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
|
|
81349
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
|
|
81350
|
-
});
|
|
81351
|
-
const challenge = resp.headers?.get("www-authenticate") ?? "";
|
|
81352
|
-
if (challenge)
|
|
81353
|
-
return challenge;
|
|
81354
|
-
} catch {
|
|
81355
|
-
return "";
|
|
81356
|
-
}
|
|
81357
|
-
}
|
|
81358
|
-
return "";
|
|
81359
|
-
}
|
|
81360
|
-
async function authorizeServer(o) {
|
|
81361
|
-
const f3 = o.fetchImpl ?? defaultFetch();
|
|
81362
|
-
const challenge = o.wwwAuthenticate ?? await probeAuthChallenge(o.serverUrl, f3);
|
|
81363
|
-
const meta = await discover(o.serverUrl, challenge, f3);
|
|
81364
|
-
const scope = [meta.scope, (0, import_code_core9.parseOAuthChallenge)(challenge).scope, o.extraScope].map((s2) => s2?.trim()).filter((s2) => Boolean(s2)).join(" ").split(/\s+/).filter((s2, i2, a) => Boolean(s2) && a.indexOf(s2) === i2).join(" ");
|
|
81365
|
-
meta.scope = scope || void 0;
|
|
81366
|
-
const loopback = await startLoopback();
|
|
81367
|
-
const redirectUri = `http://127.0.0.1:${loopback.port}/callback`;
|
|
81368
|
-
try {
|
|
81369
|
-
const creds = o.credentials ?? {};
|
|
81370
|
-
let clientId;
|
|
81371
|
-
if (creds.clientId) {
|
|
81372
|
-
clientId = creds.clientId;
|
|
81373
|
-
o.onStep?.("Using the OAuth client configured for this server\u2026");
|
|
81374
|
-
} else {
|
|
81375
|
-
o.onStep?.(`Registering with ${new URL(meta.token_endpoint).origin}\u2026`);
|
|
81376
|
-
clientId = await registerClient(meta, redirectUri, f3);
|
|
81377
|
-
}
|
|
81378
|
-
const verifier = b64url(crypto.randomBytes(32));
|
|
81379
|
-
const challengeB64 = b64url(crypto.createHash("sha256").update(verifier).digest());
|
|
81380
|
-
const state = b64url(crypto.randomBytes(16));
|
|
81381
|
-
const authUrl = buildAuthorizeUrl(meta, { clientId, redirectUri, codeChallenge: challengeB64, state });
|
|
81382
|
-
o.onStep?.("Opening your browser to authorize\u2026");
|
|
81383
|
-
await o.openUrl(authUrl);
|
|
81384
|
-
const code = await loopback.waitForCode(state);
|
|
81385
|
-
const bundle = await exchangeCode(meta, clientId, creds.clientSecret, code, verifier, redirectUri, f3);
|
|
81386
|
-
saveToken(o.server, bundle, o.file ?? tokensFile());
|
|
81387
|
-
} finally {
|
|
81388
|
-
loopback.close();
|
|
81389
|
-
}
|
|
81390
|
-
}
|
|
81391
|
-
var isRemote = (cfg) => cfg.type === "http" || cfg.type === "sse" || !cfg.command && !!cfg.url;
|
|
81392
|
-
async function injectMcpTokens(config, opts = {}) {
|
|
81393
|
-
const file = opts.file ?? tokensFile();
|
|
81394
|
-
const injected = [];
|
|
81395
|
-
for (const [name, cfg] of Object.entries(config.mcpServers ?? {})) {
|
|
81396
|
-
if (!isRemote(cfg))
|
|
81397
|
-
continue;
|
|
81398
|
-
const hasAuth = cfg.headers && Object.keys(cfg.headers).some((h2) => h2.toLowerCase() === "authorization");
|
|
81399
|
-
if (hasAuth)
|
|
81400
|
-
continue;
|
|
81401
|
-
let token;
|
|
81402
|
-
try {
|
|
81403
|
-
token = await getValidAccessToken(name, { file, fetchImpl: opts.fetchImpl });
|
|
81404
|
-
} catch {
|
|
81405
|
-
token = null;
|
|
81406
|
-
}
|
|
81407
|
-
if (!token)
|
|
81408
|
-
continue;
|
|
81409
|
-
cfg.headers = { ...cfg.headers ?? {}, Authorization: `Bearer ${token}` };
|
|
81410
|
-
injected.push(name);
|
|
81411
|
-
}
|
|
81412
|
-
return injected;
|
|
81413
|
-
}
|
|
81414
81618
|
|
|
81415
81619
|
// src/commands/mcpCmd.ts
|
|
81416
81620
|
var import_code_core10 = __toESM(require_dist2(), 1);
|
|
@@ -82477,7 +82681,7 @@ function classify(err, opts) {
|
|
|
82477
82681
|
return {
|
|
82478
82682
|
kind: "auth",
|
|
82479
82683
|
title: "Not signed in",
|
|
82480
|
-
hints: ["Run `
|
|
82684
|
+
hints: ["Run `nex auth` to sign in again", "Your session keeps its place: /resume picks it back up"]
|
|
82481
82685
|
};
|
|
82482
82686
|
}
|
|
82483
82687
|
if (status === 429 || RATE_RE.test(msg)) {
|
|
@@ -87980,7 +88184,7 @@ function knownSessionIds() {
|
|
|
87980
88184
|
}
|
|
87981
88185
|
};
|
|
87982
88186
|
add(SESSIONS_DIR, ".json");
|
|
87983
|
-
add((0,
|
|
88187
|
+
add((0, import_code_core7.eventsDir)(), ".jsonl");
|
|
87984
88188
|
return [...ids];
|
|
87985
88189
|
}
|
|
87986
88190
|
function findSessionsByLink(link2) {
|
|
@@ -88053,7 +88257,7 @@ function deleteSession(id) {
|
|
|
88053
88257
|
return false;
|
|
88054
88258
|
}
|
|
88055
88259
|
try {
|
|
88056
|
-
fs12.unlinkSync((0,
|
|
88260
|
+
fs12.unlinkSync((0, import_code_core7.eventsPath)(r2.id));
|
|
88057
88261
|
} catch {
|
|
88058
88262
|
}
|
|
88059
88263
|
return true;
|
|
@@ -88087,7 +88291,7 @@ function exportSession(id, events) {
|
|
|
88087
88291
|
version: 1,
|
|
88088
88292
|
exportedAt: Date.now(),
|
|
88089
88293
|
session,
|
|
88090
|
-
events: events ?? (0,
|
|
88294
|
+
events: events ?? (0, import_code_core7.readEvents)(r2.id)
|
|
88091
88295
|
};
|
|
88092
88296
|
return JSON.stringify(bundle, null, 2);
|
|
88093
88297
|
}
|
|
@@ -88129,7 +88333,7 @@ function importSession(bundle) {
|
|
|
88129
88333
|
renamed = true;
|
|
88130
88334
|
}
|
|
88131
88335
|
saveSession(id, title, bundle.session.workDir || process.cwd(), bundle.session.messages);
|
|
88132
|
-
(0,
|
|
88336
|
+
(0, import_code_core7.writeEvents)(id, bundle.events);
|
|
88133
88337
|
return { id, messages: bundle.session.messages.length, renamed };
|
|
88134
88338
|
}
|
|
88135
88339
|
|
|
@@ -98327,6 +98531,11 @@ var BUILTIN_SLASH_COMMANDS = [
|
|
|
98327
98531
|
kind: "builtin"
|
|
98328
98532
|
},
|
|
98329
98533
|
{ name: "cost", description: "Where the tokens and money went (per model, per agent, per turn)", kind: "builtin" },
|
|
98534
|
+
{
|
|
98535
|
+
name: "logout",
|
|
98536
|
+
description: "Sign out on this machine (revokes the saved refresh token)",
|
|
98537
|
+
kind: "builtin"
|
|
98538
|
+
},
|
|
98330
98539
|
{
|
|
98331
98540
|
name: "stats",
|
|
98332
98541
|
description: "Usage across sessions: activity by day, tokens and models (local event logs)",
|
|
@@ -103881,7 +104090,7 @@ function scanRecentSkillEvents(dir, maxFiles = DOCTOR_SESSION_WINDOW) {
|
|
|
103881
104090
|
const out = [];
|
|
103882
104091
|
for (const { full } of withTime.slice(0, maxFiles)) {
|
|
103883
104092
|
try {
|
|
103884
|
-
out.push((0,
|
|
104093
|
+
out.push((0, import_code_core7.readEvents)("", { file: full }));
|
|
103885
104094
|
} catch {
|
|
103886
104095
|
}
|
|
103887
104096
|
}
|
|
@@ -105429,7 +105638,7 @@ function emitEvent(type, data = {}) {
|
|
|
105429
105638
|
}
|
|
105430
105639
|
function startSessionLog(id, resumed, extra = {}) {
|
|
105431
105640
|
const previous = liveSessionId;
|
|
105432
|
-
sessionLog = (0,
|
|
105641
|
+
sessionLog = (0, import_code_core7.eventsEnabled)() ? new import_code_core7.EventLog(id) : null;
|
|
105433
105642
|
liveSessionId = id;
|
|
105434
105643
|
if (previous && previous !== id)
|
|
105435
105644
|
(0, import_code_core22.rebindSessionShare)(id);
|
|
@@ -105774,7 +105983,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
|
|
|
105774
105983
|
emitEvent("tool.started", {
|
|
105775
105984
|
name,
|
|
105776
105985
|
sub: !!isSubTask,
|
|
105777
|
-
target: name === "use_skill" && typeof input.name === "string" ? input.name : (0,
|
|
105986
|
+
target: name === "use_skill" && typeof input.name === "string" ? input.name : (0, import_code_core7.toolTarget)(name, input)
|
|
105778
105987
|
});
|
|
105779
105988
|
if (!isSubTask)
|
|
105780
105989
|
sessionToolCalls++;
|
|
@@ -105827,7 +106036,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
|
|
|
105827
106036
|
const changed = noteToolResult(turnStats, tracked?.name ?? name, tracked?.input, cmdOk);
|
|
105828
106037
|
for (const t2 of changed)
|
|
105829
106038
|
failureLedger.noteEdit(t2);
|
|
105830
|
-
for (const t2 of changed.slice(0,
|
|
106039
|
+
for (const t2 of changed.slice(0, import_code_core7.MAX_FILE_EVENTS))
|
|
105831
106040
|
emitEvent("file.changed", { path: t2 });
|
|
105832
106041
|
}
|
|
105833
106042
|
if (!isSubTask && tracked?.command)
|
|
@@ -106169,7 +106378,7 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env3
|
|
|
106169
106378
|
onToolUse: (name, input, isSubTask, meta) => {
|
|
106170
106379
|
debugLog("tool_use", { tool: name, sub: !!isSubTask });
|
|
106171
106380
|
emit({ type: "tool_use", tool: name, input, ...toolIds(isSubTask, meta) });
|
|
106172
|
-
verbose(`[verbose] -> ${name}${isSubTask ? " (sub-agent)" : ""} ${(0,
|
|
106381
|
+
verbose(`[verbose] -> ${name}${isSubTask ? " (sub-agent)" : ""} ${(0, import_code_core7.toolTarget)(name, input)}`);
|
|
106173
106382
|
},
|
|
106174
106383
|
// Live bash progress for scripted/CI consumers — lets a wrapper tail a
|
|
106175
106384
|
// long-running build/test command instead of blocking silently until
|
|
@@ -106449,6 +106658,7 @@ function printHelp() {
|
|
|
106449
106658
|
["/yolo", "Auto-approve all permissions"],
|
|
106450
106659
|
["/balance", "Show what is paying (personal wallet or team) and what is left"],
|
|
106451
106660
|
["/team [use <id|personal>]", "List your teams, or switch what usage is billed to"],
|
|
106661
|
+
["/logout", "Sign out on this machine (revokes the saved refresh token)"],
|
|
106452
106662
|
["/cost [--all]", "Cost and tokens: per model, per sub-agent, per turn (/balance shows the wallet)"],
|
|
106453
106663
|
["/stats", "Usage across sessions: activity by day, tokens and models (local event logs)"],
|
|
106454
106664
|
[
|
|
@@ -107165,7 +107375,7 @@ Resume it with: nex --worktree ${worktreeState.name}
|
|
|
107165
107375
|
console.log(paint.green(` Resumed session: ${paint.bold(stored.title.slice(0, 60))}`));
|
|
107166
107376
|
console.log(paint.dim(` ${messages.length} messages restored`));
|
|
107167
107377
|
if (autoRecap) {
|
|
107168
|
-
const recap = (0, import_code_core33.buildRecap)((0,
|
|
107378
|
+
const recap = (0, import_code_core33.buildRecap)((0, import_code_core7.readEvents)(stored.id, { last: import_code_core33.RECAP_EVENT_WINDOW }), { now: Date.now() });
|
|
107169
107379
|
if (recap)
|
|
107170
107380
|
console.log(paint.dim(` Recap: ${recap}`));
|
|
107171
107381
|
}
|
|
@@ -109005,6 +109215,26 @@ ${dirList}`;
|
|
|
109005
109215
|
rl.prompt();
|
|
109006
109216
|
return;
|
|
109007
109217
|
}
|
|
109218
|
+
case "/logout": {
|
|
109219
|
+
const answer = await new Promise(
|
|
109220
|
+
(resolve19) => rl.question(paint.yellow(" Sign out on this machine? ") + paint.dim("[y/n] "), (a) => resolve19(a.trim()))
|
|
109221
|
+
);
|
|
109222
|
+
if (answer !== "y" && answer !== "yes") {
|
|
109223
|
+
console.log(paint.dim(" Cancelled."));
|
|
109224
|
+
rl.prompt();
|
|
109225
|
+
return;
|
|
109226
|
+
}
|
|
109227
|
+
await signOut();
|
|
109228
|
+
console.log(paint.green(" \u2713 Logged out."));
|
|
109229
|
+
if (process.env.NEXRALL_TOKEN?.trim()) {
|
|
109230
|
+
console.log(
|
|
109231
|
+
paint.yellow(" NEXRALL_TOKEN is still set in this shell") + paint.dim(" \u2014 unset it, or the CLI stays signed in with that token.")
|
|
109232
|
+
);
|
|
109233
|
+
}
|
|
109234
|
+
console.log(paint.dim(" Run `nex auth` (in any terminal) to sign in again; this session picks it up."));
|
|
109235
|
+
rl.prompt();
|
|
109236
|
+
return;
|
|
109237
|
+
}
|
|
109008
109238
|
case "/cost": {
|
|
109009
109239
|
const parsed = parseCostArgs(arg);
|
|
109010
109240
|
if ("error" in parsed) {
|
|
@@ -109067,14 +109297,14 @@ ${dirList}`;
|
|
|
109067
109297
|
return;
|
|
109068
109298
|
}
|
|
109069
109299
|
case "/recap": {
|
|
109070
|
-
if (!(0,
|
|
109300
|
+
if (!(0, import_code_core7.eventsEnabled)()) {
|
|
109071
109301
|
console.log(
|
|
109072
109302
|
paint.dim(" Events are off for this run (NEXRALL_EVENTS=off), so there is nothing to read back.")
|
|
109073
109303
|
);
|
|
109074
109304
|
rl.prompt();
|
|
109075
109305
|
return;
|
|
109076
109306
|
}
|
|
109077
|
-
const recap = (0, import_code_core33.buildRecap)((0,
|
|
109307
|
+
const recap = (0, import_code_core33.buildRecap)((0, import_code_core7.readEvents)(sessionId, { last: import_code_core33.RECAP_EVENT_WINDOW }), { now: Date.now() });
|
|
109078
109308
|
console.log();
|
|
109079
109309
|
if (recap)
|
|
109080
109310
|
console.log(` ${paint.bold("Recap: ")}${recap}`);
|
|
@@ -109638,11 +109868,11 @@ ${dirList}`;
|
|
|
109638
109868
|
return;
|
|
109639
109869
|
}
|
|
109640
109870
|
if (!(0, import_code_core64.getToken)()) {
|
|
109641
|
-
console.log(paint.yellow(" Sign in first (`nex
|
|
109871
|
+
console.log(paint.yellow(" Sign in first (`nex auth`) \u2014 watching uses your Nexrall account."));
|
|
109642
109872
|
rl.prompt();
|
|
109643
109873
|
return;
|
|
109644
109874
|
}
|
|
109645
|
-
if (!(0,
|
|
109875
|
+
if (!(0, import_code_core7.eventsEnabled)()) {
|
|
109646
109876
|
console.log(
|
|
109647
109877
|
paint.yellow(" Sharing follows the event stream, and this run has it off (NEXRALL_EVENTS=off).")
|
|
109648
109878
|
);
|
|
@@ -109746,7 +109976,7 @@ ${dirList}`;
|
|
|
109746
109976
|
}
|
|
109747
109977
|
if (!(0, import_code_core64.getToken)()) {
|
|
109748
109978
|
console.log(
|
|
109749
|
-
paint.yellow(" Sign in first (`nex
|
|
109979
|
+
paint.yellow(" Sign in first (`nex auth`) \u2014 pushes find this session through your account.")
|
|
109750
109980
|
);
|
|
109751
109981
|
rl.prompt();
|
|
109752
109982
|
return;
|
|
@@ -109817,7 +110047,7 @@ ${dirList}`;
|
|
|
109817
110047
|
}
|
|
109818
110048
|
case "/schedule":
|
|
109819
110049
|
case "/routines": {
|
|
109820
|
-
const action = (0,
|
|
110050
|
+
const action = (0, import_code_core4.parseScheduleChatArg)(arg);
|
|
109821
110051
|
if (action.kind === "error") {
|
|
109822
110052
|
console.log(paint.yellow(" " + action.message));
|
|
109823
110053
|
rl.prompt();
|
|
@@ -109828,7 +110058,7 @@ ${dirList}`;
|
|
|
109828
110058
|
const panelCols = Math.max(60, (process.stdout.columns || 100) - 2);
|
|
109829
110059
|
if (action.kind === "list") {
|
|
109830
110060
|
const list = await (0, import_code_core63.listCloudSchedules)();
|
|
109831
|
-
for (const l of (0,
|
|
110061
|
+
for (const l of (0, import_code_core4.scheduleListLines)(list, { cols: panelCols }))
|
|
109832
110062
|
console.log(" " + l);
|
|
109833
110063
|
if (list.length)
|
|
109834
110064
|
console.log(
|
|
@@ -109838,7 +110068,7 @@ ${dirList}`;
|
|
|
109838
110068
|
const s2 = await (0, import_code_core63.createCloudSchedule)({ task: action.task, intervalMinutes: action.intervalMinutes });
|
|
109839
110069
|
console.log(
|
|
109840
110070
|
paint.green(
|
|
109841
|
-
` \u2713 Scheduled #${s2.id} \u2014 every ${(0,
|
|
110071
|
+
` \u2713 Scheduled #${s2.id} \u2014 every ${(0, import_code_core4.formatEvery)(s2.intervalMinutes)}${s2.nextRunAt ? `, next run ${(0, import_code_core4.fmtScheduleWhen)(s2.nextRunAt)}` : ""}.`
|
|
109842
110072
|
)
|
|
109843
110073
|
);
|
|
109844
110074
|
console.log(
|
|
@@ -109856,7 +110086,7 @@ ${dirList}`;
|
|
|
109856
110086
|
console.log(paint.dim(` Removed schedule #${action.id} \u2014 no further runs fire.`));
|
|
109857
110087
|
} else {
|
|
109858
110088
|
const runs2 = await (0, import_code_core63.getCloudScheduleRuns)(action.id);
|
|
109859
|
-
for (const l of (0,
|
|
110089
|
+
for (const l of (0, import_code_core4.scheduleRunsLines)(runs2, { cols: panelCols }))
|
|
109860
110090
|
console.log(" " + l);
|
|
109861
110091
|
}
|
|
109862
110092
|
} catch (err) {
|
|
@@ -110074,7 +110304,7 @@ ${dirList}`;
|
|
|
110074
110304
|
};
|
|
110075
110305
|
const want = arg.trim();
|
|
110076
110306
|
if (want) {
|
|
110077
|
-
const hit = (0,
|
|
110307
|
+
const hit = (0, import_code_core8.findFiles)(files, want);
|
|
110078
110308
|
if (!hit.length)
|
|
110079
110309
|
console.log(paint.dim(` No changed file matches "${want}". (/diff lists them all)`));
|
|
110080
110310
|
else if (hit.length > 1) {
|
|
@@ -110083,14 +110313,14 @@ ${dirList}`;
|
|
|
110083
110313
|
console.log(" " + f3.path);
|
|
110084
110314
|
} else {
|
|
110085
110315
|
verification.noteDiffReviewed();
|
|
110086
|
-
out((0,
|
|
110316
|
+
out((0, import_code_core8.capLines)((0, import_code_core8.renderFile)(hit[0], { foldHunks: false })));
|
|
110087
110317
|
}
|
|
110088
110318
|
} else {
|
|
110089
110319
|
console.log(paint.bold(" Uncommitted changes"));
|
|
110090
|
-
out((0,
|
|
110320
|
+
out((0, import_code_core8.summaryLines)(files));
|
|
110091
110321
|
console.log();
|
|
110092
110322
|
verification.noteDiffReviewed();
|
|
110093
|
-
out((0,
|
|
110323
|
+
out((0, import_code_core8.renderAll)(files));
|
|
110094
110324
|
}
|
|
110095
110325
|
rl.prompt();
|
|
110096
110326
|
return;
|
|
@@ -110111,7 +110341,7 @@ ${dirList}`;
|
|
|
110111
110341
|
const emit = (line) => console.log(truncateCells(line, cols3));
|
|
110112
110342
|
const want = arg.replace(/(^|\s)--all(?=\s|$)/, " ").trim();
|
|
110113
110343
|
if (want) {
|
|
110114
|
-
const hit = (0,
|
|
110344
|
+
const hit = (0, import_code_core8.findFiles)(files, want);
|
|
110115
110345
|
if (!hit.length)
|
|
110116
110346
|
emit(paint.dim(` No changed file matches "${want}". (/review lists them all)`));
|
|
110117
110347
|
else if (hit.length > 1) {
|
|
@@ -110119,14 +110349,14 @@ ${dirList}`;
|
|
|
110119
110349
|
for (const f3 of hit.slice(0, 10))
|
|
110120
110350
|
emit(" " + f3.path);
|
|
110121
110351
|
} else {
|
|
110122
|
-
const ctx = { tests: files.filter((x2) => (0,
|
|
110123
|
-
for (const line of (0,
|
|
110352
|
+
const ctx = { tests: files.filter((x2) => (0, import_code_core9.isTestPath)(x2.path)) };
|
|
110353
|
+
for (const line of (0, import_code_core9.formatReviewDetail)((0, import_code_core9.classifyFile)(hit[0], ctx), cols3))
|
|
110124
110354
|
emit(" " + line);
|
|
110125
110355
|
}
|
|
110126
110356
|
} else {
|
|
110127
110357
|
const all = /(^|\s)--all(\s|$)/.test(arg);
|
|
110128
|
-
const q = (0,
|
|
110129
|
-
for (const line of (0,
|
|
110358
|
+
const q = (0, import_code_core9.buildReviewQueue)(files, all ? Number.POSITIVE_INFINITY : import_code_core9.DEFAULT_PER_GROUP);
|
|
110359
|
+
for (const line of (0, import_code_core9.formatReviewQueue)(q, cols3))
|
|
110130
110360
|
emit(" " + line);
|
|
110131
110361
|
emit(paint.dim(" /review <file> for one entry's reasons \xB7 /diff for the change itself"));
|
|
110132
110362
|
}
|
|
@@ -110903,7 +111133,7 @@ ${text}
|
|
|
110903
111133
|
}
|
|
110904
111134
|
case "/skill-doctor": {
|
|
110905
111135
|
const loadedSkills = (0, import_code_core66.loadSkills)(workDir);
|
|
110906
|
-
const usage2 = collectSkillUsage(scanRecentSkillEvents((0,
|
|
111136
|
+
const usage2 = collectSkillUsage(scanRecentSkillEvents((0, import_code_core7.eventsDir)()));
|
|
110907
111137
|
for (const l of buildSkillDoctorReport(loadedSkills, usage2).lines)
|
|
110908
111138
|
console.log(l);
|
|
110909
111139
|
rl.prompt();
|
|
@@ -112811,7 +113041,7 @@ function planProjectPurge(targetDir, env3 = process.env) {
|
|
|
112811
113041
|
}
|
|
112812
113042
|
}
|
|
112813
113043
|
for (const id of sessionIds) {
|
|
112814
|
-
const p = (0,
|
|
113044
|
+
const p = (0, import_code_core7.eventsPath)(id, env3);
|
|
112815
113045
|
if (fs28.existsSync(p))
|
|
112816
113046
|
items.push({ kind: "events", path: p, dir: false });
|
|
112817
113047
|
}
|
|
@@ -114095,7 +114325,7 @@ var NEX_VERSION = require3("../package.json").version;
|
|
|
114095
114325
|
installEpipeGuard();
|
|
114096
114326
|
var program2 = new Command();
|
|
114097
114327
|
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version(NEX_VERSION).enablePositionalOptions();
|
|
114098
|
-
var auth = program2.command("auth").description("
|
|
114328
|
+
var auth = program2.command("auth").alias("login").description("Log in (browser, email + password, or a token)").option("--browser", "Sign in through the browser \u2014 works for Google, Apple and SSO accounts").option("--code <code>", "Finish a browser sign-in with a code you already copied (no browser is opened)").option("--token <token>", "Save a token directly (CI/headless). Prefer NEXRALL_TOKEN where you can").option("--password", "Ask for email and password in this terminal (skips the method menu)").action(authCommand);
|
|
114099
114329
|
auth.command("status").description("Show whether you are logged in (exit 0 = yes, 1 = no). --json for scripts; the token is never printed").option("--json", "Machine-readable output").action((opts) => {
|
|
114100
114330
|
const r2 = authStatusReport((0, import_code_core79.loadAuth)(), process.env);
|
|
114101
114331
|
if (opts.json)
|
|
@@ -114553,7 +114783,7 @@ program2.command("sessions").description("List, export, import, or delete saved
|
|
|
114553
114783
|
process.exitCode = 1;
|
|
114554
114784
|
return;
|
|
114555
114785
|
}
|
|
114556
|
-
const events = (0,
|
|
114786
|
+
const events = (0, import_code_core7.readEvents)(hit.id, { last: Number.isFinite(want) && want > 0 ? want : 20 });
|
|
114557
114787
|
if (!events.length) {
|
|
114558
114788
|
console.log(
|
|
114559
114789
|
paint.dim(` No events recorded for ${hit.id.slice(0, 8)} (NEXRALL_EVENTS=off disables the stream).`)
|
|
@@ -114568,9 +114798,9 @@ program2.command("sessions").description("List, export, import, or delete saved
|
|
|
114568
114798
|
console.log();
|
|
114569
114799
|
console.log(paint.bold(` Events for ${hit.id.slice(0, 8)} `) + paint.dim(`(last ${events.length})`));
|
|
114570
114800
|
for (const ev of events)
|
|
114571
|
-
console.log(" " + paint.dim((0,
|
|
114801
|
+
console.log(" " + paint.dim((0, import_code_core7.summarizeEvent)(ev)));
|
|
114572
114802
|
console.log(paint.dim(`
|
|
114573
|
-
Raw JSONL: ${(0,
|
|
114803
|
+
Raw JSONL: ${(0, import_code_core7.eventsPath)(hit.id)}`));
|
|
114574
114804
|
console.log();
|
|
114575
114805
|
return;
|
|
114576
114806
|
}
|
|
@@ -114684,8 +114914,17 @@ program2.option("-y, --yolo", "Auto-approve all tool calls (no permission prompt
|
|
|
114684
114914
|
"Settings (permissions, hooks\u2026) for this run only: a JSON file or an inline JSON object; merged over the user/project settings"
|
|
114685
114915
|
).option("--debug-file <path>", "Write the --debug diagnostics log to this file (implies --debug)").argument("[prompt...]", "One-shot prompt \u2014 if omitted, starts interactive mode").action(async (promptParts, options) => {
|
|
114686
114916
|
if (!(0, import_code_core78.isAuthenticated)()) {
|
|
114687
|
-
|
|
114688
|
-
|
|
114917
|
+
if (!shouldOfferLogin({
|
|
114918
|
+
isTTY: !!process.stdin.isTTY,
|
|
114919
|
+
headless: !!(options.print || options.outputFormat)
|
|
114920
|
+
})) {
|
|
114921
|
+
console.error("Not logged in. Run: nex auth (or set NEXRALL_TOKEN for CI)");
|
|
114922
|
+
process.exit(1);
|
|
114923
|
+
}
|
|
114924
|
+
console.log(paint.dim(" Not signed in yet \u2014 signing in first (Ctrl+C cancels)."));
|
|
114925
|
+
await authCommand();
|
|
114926
|
+
if (!(0, import_code_core78.isAuthenticated)())
|
|
114927
|
+
process.exit(process.exitCode || 1);
|
|
114689
114928
|
}
|
|
114690
114929
|
const model = options.model ?? "deepseek-flash";
|
|
114691
114930
|
const workDir = options.dir ? path35.resolve(options.dir) : process.cwd();
|