nexrall-code 0.5.207 → 0.5.208
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 +4 -2
- package/dist/index.js +744 -528
- 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,452 @@ 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
|
+
}
|
|
79760
80243
|
function prompt(question, hidden = false) {
|
|
79761
80244
|
return new Promise((resolve19) => {
|
|
79762
80245
|
const rl = readline.createInterface({
|
|
@@ -79783,13 +80266,14 @@ function prompt(question, hidden = false) {
|
|
|
79783
80266
|
process.exit(0);
|
|
79784
80267
|
}
|
|
79785
80268
|
resolve19(value);
|
|
79786
|
-
|
|
79787
|
-
|
|
80269
|
+
return;
|
|
80270
|
+
}
|
|
80271
|
+
if (char === "\x7F" || char === "\b") {
|
|
80272
|
+
if (value.length > 0)
|
|
79788
80273
|
value = value.slice(0, -1);
|
|
79789
|
-
|
|
79790
|
-
} else {
|
|
79791
|
-
value += char;
|
|
80274
|
+
return;
|
|
79792
80275
|
}
|
|
80276
|
+
value += char;
|
|
79793
80277
|
};
|
|
79794
80278
|
stdin.on("data", onData);
|
|
79795
80279
|
} else {
|
|
@@ -79800,12 +80284,12 @@ function prompt(question, hidden = false) {
|
|
|
79800
80284
|
}
|
|
79801
80285
|
});
|
|
79802
80286
|
}
|
|
79803
|
-
async function authCommand() {
|
|
80287
|
+
async function authCommand(opts = {}) {
|
|
79804
80288
|
console.log();
|
|
79805
80289
|
console.log(paint.boldBlue(" Nexrall Code \u2014 Login"));
|
|
79806
80290
|
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
80291
|
console.log();
|
|
79808
|
-
if ((0,
|
|
80292
|
+
if ((0, import_code_core2.isAuthenticated)() && !opts.token && !opts.code) {
|
|
79809
80293
|
const overwrite = await prompt(paint.yellow(" Already logged in. Login again? ") + paint.dim("[y/n] > "));
|
|
79810
80294
|
if (overwrite !== "y" && overwrite !== "yes") {
|
|
79811
80295
|
console.log(paint.dim(" Cancelled."));
|
|
@@ -79813,21 +80297,90 @@ async function authCommand() {
|
|
|
79813
80297
|
}
|
|
79814
80298
|
console.log();
|
|
79815
80299
|
}
|
|
80300
|
+
if (opts.token)
|
|
80301
|
+
return loginWithToken(opts.token);
|
|
80302
|
+
if (opts.browser || opts.code)
|
|
80303
|
+
return loginWithBrowser(opts.code);
|
|
80304
|
+
if (opts.password)
|
|
80305
|
+
return loginWithPassword();
|
|
80306
|
+
if (!process.stdin.isTTY)
|
|
80307
|
+
return loginWithPassword();
|
|
80308
|
+
const method = await chooseMethod();
|
|
80309
|
+
if (method === null) {
|
|
80310
|
+
console.log(paint.dim(" Cancelled."));
|
|
80311
|
+
return;
|
|
80312
|
+
}
|
|
80313
|
+
if (method === "browser")
|
|
80314
|
+
return loginWithBrowser();
|
|
80315
|
+
if (method === "token")
|
|
80316
|
+
return loginWithTokenPrompt();
|
|
80317
|
+
return loginWithPassword();
|
|
80318
|
+
}
|
|
80319
|
+
async function chooseMethod() {
|
|
80320
|
+
console.log(" How do you want to sign in?");
|
|
80321
|
+
console.log();
|
|
80322
|
+
console.log(
|
|
80323
|
+
` ${paint.bold("1.")} Browser ${paint.dim("Google, Apple, SSO or email + password \u2014 recommended")}`
|
|
80324
|
+
);
|
|
80325
|
+
console.log(` ${paint.bold("2.")} Email + password ${paint.dim("typed here in the terminal")}`);
|
|
80326
|
+
console.log(` ${paint.bold("3.")} Token ${paint.dim("CI / headless \u2014 paste a token")}`);
|
|
80327
|
+
console.log();
|
|
80328
|
+
for (; ; ) {
|
|
80329
|
+
const answer = await prompt(paint.bold(" Choose ") + paint.dim("[1] > "));
|
|
80330
|
+
const method = parseAuthMethod(answer);
|
|
80331
|
+
if (method)
|
|
80332
|
+
return method;
|
|
80333
|
+
console.log(paint.yellow(" Pick 1, 2 or 3."));
|
|
80334
|
+
}
|
|
80335
|
+
}
|
|
80336
|
+
async function loginWithBrowser(codeArg) {
|
|
80337
|
+
let code = codeArg ? normalizeAuthCode(codeArg) : "";
|
|
80338
|
+
if (!code) {
|
|
80339
|
+
const url = browserAuthUrl();
|
|
80340
|
+
openInBrowser(url);
|
|
80341
|
+
console.log(paint.dim(" Opened: ") + paint.cyan(url));
|
|
80342
|
+
console.log();
|
|
80343
|
+
console.log(" Sign in there \u2014 Google, Apple, SSO or your password all work.");
|
|
80344
|
+
console.log(paint.dim(" Copy the code the page shows, then paste it here."));
|
|
80345
|
+
console.log();
|
|
80346
|
+
code = normalizeAuthCode(await prompt(paint.bold(" Code ") + paint.dim("> ")));
|
|
80347
|
+
}
|
|
80348
|
+
if (!code) {
|
|
80349
|
+
console.log(paint.red(" \u2717 No code entered."));
|
|
80350
|
+
console.log();
|
|
80351
|
+
process.exitCode = 1;
|
|
80352
|
+
return;
|
|
80353
|
+
}
|
|
80354
|
+
console.log();
|
|
80355
|
+
const spinner = startSpinner("Signing in\u2026");
|
|
80356
|
+
try {
|
|
80357
|
+
const authConfig = await (0, import_code_core2.exchangeAuthCode)(code);
|
|
80358
|
+
(0, import_code_core2.saveAuth)(authConfig);
|
|
80359
|
+
stopSpinner(spinner);
|
|
80360
|
+
console.log(paint.green(" \u2713 Logged in as ") + paint.bold(authConfig.email || "your account"));
|
|
80361
|
+
await showBalance();
|
|
80362
|
+
console.log();
|
|
80363
|
+
console.log(paint.dim(" Run ") + paint.bold("nex") + paint.dim(" to start coding."));
|
|
80364
|
+
console.log();
|
|
80365
|
+
} catch (err) {
|
|
80366
|
+
stopSpinner(spinner);
|
|
80367
|
+
console.log();
|
|
80368
|
+
console.log(paint.red(" \u2717 Login failed: ") + String(err.message));
|
|
80369
|
+
console.log();
|
|
80370
|
+
process.exitCode = 1;
|
|
80371
|
+
}
|
|
80372
|
+
}
|
|
80373
|
+
async function loginWithPassword() {
|
|
79816
80374
|
const email = await prompt(paint.bold(" Email ") + paint.dim("> "));
|
|
79817
80375
|
const password = await prompt(paint.bold(" Password ") + paint.dim("> "), true);
|
|
79818
80376
|
console.log();
|
|
79819
80377
|
const spinner = startSpinner("Logging in\u2026");
|
|
79820
80378
|
try {
|
|
79821
|
-
const authConfig = await (0,
|
|
79822
|
-
(0,
|
|
80379
|
+
const authConfig = await (0, import_code_core2.login)(email, password);
|
|
80380
|
+
(0, import_code_core2.saveAuth)(authConfig);
|
|
79823
80381
|
stopSpinner(spinner);
|
|
79824
80382
|
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
|
-
}
|
|
80383
|
+
await showBalance();
|
|
79831
80384
|
console.log();
|
|
79832
80385
|
console.log(paint.dim(" Run ") + paint.bold("nex") + paint.dim(" to start coding."));
|
|
79833
80386
|
console.log();
|
|
@@ -79836,12 +80389,71 @@ async function authCommand() {
|
|
|
79836
80389
|
console.log();
|
|
79837
80390
|
console.log(paint.red(" \u2717 Login failed: ") + String(err.message));
|
|
79838
80391
|
console.log();
|
|
79839
|
-
process.
|
|
80392
|
+
process.exitCode = 1;
|
|
80393
|
+
}
|
|
80394
|
+
}
|
|
80395
|
+
async function loginWithTokenPrompt() {
|
|
80396
|
+
console.log(paint.dim(" Paste a token from the Nexrall dashboard."));
|
|
80397
|
+
const token = await prompt(paint.bold(" Token ") + paint.dim("> "), true);
|
|
80398
|
+
if (!token) {
|
|
80399
|
+
console.log(paint.red(" \u2717 No token entered."));
|
|
80400
|
+
console.log();
|
|
80401
|
+
process.exitCode = 1;
|
|
80402
|
+
return;
|
|
80403
|
+
}
|
|
80404
|
+
await loginWithToken(token);
|
|
80405
|
+
}
|
|
80406
|
+
async function loginWithToken(rawToken) {
|
|
80407
|
+
const token = rawToken.trim();
|
|
80408
|
+
if (!token) {
|
|
80409
|
+
console.log(paint.red(" \u2717 Empty token."));
|
|
80410
|
+
console.log();
|
|
80411
|
+
process.exitCode = 1;
|
|
80412
|
+
return;
|
|
80413
|
+
}
|
|
80414
|
+
console.log();
|
|
80415
|
+
const spinner = startSpinner("Checking the token\u2026");
|
|
80416
|
+
const check2 = await (0, import_code_core2.checkToken)(token);
|
|
80417
|
+
if (!check2.ok && check2.status !== 0) {
|
|
80418
|
+
stopSpinner(spinner);
|
|
80419
|
+
console.log();
|
|
80420
|
+
console.log(paint.red(` \u2717 The API refused this token (HTTP ${check2.status}).`) + paint.dim(" Nothing was saved."));
|
|
80421
|
+
console.log();
|
|
80422
|
+
process.exitCode = 1;
|
|
80423
|
+
return;
|
|
79840
80424
|
}
|
|
80425
|
+
(0, import_code_core2.saveAuth)({ token, ...check2.email ? { email: check2.email } : {} });
|
|
80426
|
+
stopSpinner(spinner);
|
|
80427
|
+
console.log(paint.green(" \u2713 Token saved") + (check2.email ? paint.dim(` for ${check2.email}`) : ""));
|
|
80428
|
+
if (check2.status === 0) {
|
|
80429
|
+
console.log(paint.yellow(" Could not reach the API to verify it \u2014 check the token once you are online."));
|
|
80430
|
+
}
|
|
80431
|
+
warnIfEnvTokenWins();
|
|
80432
|
+
await showBalance();
|
|
80433
|
+
console.log();
|
|
80434
|
+
}
|
|
80435
|
+
function warnIfEnvTokenWins() {
|
|
80436
|
+
const envToken = process.env.NEXRALL_TOKEN?.trim();
|
|
80437
|
+
if (!envToken)
|
|
80438
|
+
return;
|
|
80439
|
+
console.log(
|
|
80440
|
+
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.")
|
|
80441
|
+
);
|
|
80442
|
+
}
|
|
80443
|
+
async function showBalance() {
|
|
80444
|
+
try {
|
|
80445
|
+
const balance = await (0, import_code_core2.getBalance)();
|
|
80446
|
+
console.log(paint.dim(" Wallet balance: ") + paint.cyan(`$${balance.toFixed(4)}`));
|
|
80447
|
+
} catch {
|
|
80448
|
+
}
|
|
80449
|
+
}
|
|
80450
|
+
async function signOut() {
|
|
80451
|
+
await (0, import_code_core2.revokeRefreshToken)();
|
|
80452
|
+
(0, import_code_core2.clearAuth)();
|
|
79841
80453
|
}
|
|
79842
80454
|
async function logoutCommand() {
|
|
79843
80455
|
console.log();
|
|
79844
|
-
if (!(0,
|
|
80456
|
+
if (!(0, import_code_core2.isAuthenticated)()) {
|
|
79845
80457
|
console.log(paint.dim(" Not logged in."));
|
|
79846
80458
|
console.log();
|
|
79847
80459
|
return;
|
|
@@ -79852,9 +80464,13 @@ async function logoutCommand() {
|
|
|
79852
80464
|
console.log();
|
|
79853
80465
|
return;
|
|
79854
80466
|
}
|
|
79855
|
-
await (
|
|
79856
|
-
(0, import_code_core.clearAuth)();
|
|
80467
|
+
await signOut();
|
|
79857
80468
|
console.log(paint.green(" \u2713 Logged out."));
|
|
80469
|
+
if (process.env.NEXRALL_TOKEN?.trim()) {
|
|
80470
|
+
console.log(
|
|
80471
|
+
paint.yellow(" NEXRALL_TOKEN is still set in this shell") + paint.dim(" \u2014 unset it, or the CLI stays signed in with that token.")
|
|
80472
|
+
);
|
|
80473
|
+
}
|
|
79858
80474
|
console.log();
|
|
79859
80475
|
}
|
|
79860
80476
|
function startSpinner(text) {
|
|
@@ -79873,12 +80489,12 @@ function stopSpinner(handle2) {
|
|
|
79873
80489
|
}
|
|
79874
80490
|
|
|
79875
80491
|
// src/commands/billing.ts
|
|
79876
|
-
var
|
|
80492
|
+
var import_code_core3 = __toESM(require_dist2(), 1);
|
|
79877
80493
|
function money(usd) {
|
|
79878
80494
|
return "$" + usd.toFixed(2);
|
|
79879
80495
|
}
|
|
79880
80496
|
async function requireAuth() {
|
|
79881
|
-
if ((0,
|
|
80497
|
+
if ((0, import_code_core3.isAuthenticated)())
|
|
79882
80498
|
return true;
|
|
79883
80499
|
console.log(paint.red("Not logged in. Run `nex auth` first."));
|
|
79884
80500
|
return false;
|
|
@@ -79888,7 +80504,7 @@ async function billingStatusCommand() {
|
|
|
79888
80504
|
return;
|
|
79889
80505
|
let orgs;
|
|
79890
80506
|
try {
|
|
79891
|
-
orgs = await (0,
|
|
80507
|
+
orgs = await (0, import_code_core3.getOrgs)();
|
|
79892
80508
|
} catch {
|
|
79893
80509
|
console.log(paint.yellow("Could not read your billing target (offline, or a server without team support)."));
|
|
79894
80510
|
return;
|
|
@@ -79920,7 +80536,7 @@ async function billingStatusCommand() {
|
|
|
79920
80536
|
console.log("");
|
|
79921
80537
|
console.log(paint.gray("Change it with `nex billing use <id>` or `nex billing use personal`."));
|
|
79922
80538
|
}
|
|
79923
|
-
const override = (0,
|
|
80539
|
+
const override = (0, import_code_core3.billingOverrideFields)();
|
|
79924
80540
|
if ("teamId" in override) {
|
|
79925
80541
|
console.log("");
|
|
79926
80542
|
console.log(
|
|
@@ -79948,7 +80564,7 @@ async function billingUseCommand(target) {
|
|
|
79948
80564
|
}
|
|
79949
80565
|
async function applyTarget(teamId, label) {
|
|
79950
80566
|
try {
|
|
79951
|
-
await (0,
|
|
80567
|
+
await (0, import_code_core3.setBillingTarget)(teamId);
|
|
79952
80568
|
} catch (e2) {
|
|
79953
80569
|
console.log(paint.red(e2.message));
|
|
79954
80570
|
process.exitCode = 1;
|
|
@@ -79962,14 +80578,14 @@ async function applyTarget(teamId, label) {
|
|
|
79962
80578
|
|
|
79963
80579
|
// src/commands/schedule.ts
|
|
79964
80580
|
var import_prompts = __toESM(require_prompts3(), 1);
|
|
79965
|
-
var
|
|
80581
|
+
var import_code_core5 = __toESM(require_dist2(), 1);
|
|
79966
80582
|
|
|
79967
80583
|
// src/ui/scheduleLines.ts
|
|
79968
|
-
var
|
|
80584
|
+
var import_code_core4 = __toESM(require_dist2(), 1);
|
|
79969
80585
|
|
|
79970
80586
|
// src/commands/schedule.ts
|
|
79971
80587
|
async function requireAuth2() {
|
|
79972
|
-
if ((0,
|
|
80588
|
+
if ((0, import_code_core5.isAuthenticated)())
|
|
79973
80589
|
return true;
|
|
79974
80590
|
console.log(paint.red("Not logged in. Run `nex auth` first."));
|
|
79975
80591
|
process.exitCode = 1;
|
|
@@ -79990,7 +80606,7 @@ async function scheduleListCommand(opts = {}) {
|
|
|
79990
80606
|
return;
|
|
79991
80607
|
let list;
|
|
79992
80608
|
try {
|
|
79993
|
-
list = await (0,
|
|
80609
|
+
list = await (0, import_code_core5.listCloudSchedules)();
|
|
79994
80610
|
} catch (e2) {
|
|
79995
80611
|
fail(errText(e2));
|
|
79996
80612
|
return;
|
|
@@ -79999,7 +80615,7 @@ async function scheduleListCommand(opts = {}) {
|
|
|
79999
80615
|
console.log(JSON.stringify(list, null, 2));
|
|
80000
80616
|
return;
|
|
80001
80617
|
}
|
|
80002
|
-
for (const line of (0,
|
|
80618
|
+
for (const line of (0, import_code_core4.scheduleListLines)(list, { cols: cols() }))
|
|
80003
80619
|
console.log(line);
|
|
80004
80620
|
if (list.length) {
|
|
80005
80621
|
console.log(paint.dim(`
|
|
@@ -80019,7 +80635,7 @@ async function scheduleAddCommand(taskParts, opts) {
|
|
|
80019
80635
|
fail("Missing --every. Usage: nex schedule add --every 1d <task> (e.g. --every 2h)");
|
|
80020
80636
|
return;
|
|
80021
80637
|
}
|
|
80022
|
-
const every = (0,
|
|
80638
|
+
const every = (0, import_code_core4.parseScheduleEvery)(everyRaw);
|
|
80023
80639
|
if ("error" in every) {
|
|
80024
80640
|
fail(every.error);
|
|
80025
80641
|
return;
|
|
@@ -80027,7 +80643,7 @@ async function scheduleAddCommand(taskParts, opts) {
|
|
|
80027
80643
|
const domains = (opts.domains ?? "").split(",").map((d) => d.trim()).filter(Boolean);
|
|
80028
80644
|
let s2;
|
|
80029
80645
|
try {
|
|
80030
|
-
s2 = await (0,
|
|
80646
|
+
s2 = await (0, import_code_core5.createCloudSchedule)({
|
|
80031
80647
|
task,
|
|
80032
80648
|
intervalMinutes: every.minutes,
|
|
80033
80649
|
startUrl: opts.startUrl,
|
|
@@ -80039,12 +80655,12 @@ async function scheduleAddCommand(taskParts, opts) {
|
|
|
80039
80655
|
return;
|
|
80040
80656
|
}
|
|
80041
80657
|
console.log(
|
|
80042
|
-
`\u2714 Scheduled ${paint.bold(`#${s2.id}`)} \u2014 every ${(0,
|
|
80658
|
+
`\u2714 Scheduled ${paint.bold(`#${s2.id}`)} \u2014 every ${(0, import_code_core4.formatEvery)(s2.intervalMinutes)}${s2.enabled ? "" : paint.yellow(" (paused)")}`
|
|
80043
80659
|
);
|
|
80044
80660
|
console.log(` ${s2.task}`);
|
|
80045
80661
|
if (s2.nextRunAt) {
|
|
80046
80662
|
console.log(
|
|
80047
|
-
paint.dim(` Next run ${(0,
|
|
80663
|
+
paint.dim(` Next run ${(0, import_code_core4.fmtScheduleWhen)(s2.nextRunAt)} \u2014 on Nexrall's servers; your laptop can be closed.`)
|
|
80048
80664
|
);
|
|
80049
80665
|
}
|
|
80050
80666
|
console.log(paint.dim(` History: nex schedule runs ${s2.id} \xB7 Pause: nex schedule pause ${s2.id}`));
|
|
@@ -80057,8 +80673,8 @@ async function scheduleSetEnabled(id, enabled) {
|
|
|
80057
80673
|
return;
|
|
80058
80674
|
}
|
|
80059
80675
|
try {
|
|
80060
|
-
const s2 = await (0,
|
|
80061
|
-
const next = s2.nextRunAt ? paint.dim(` \u2014 next run ${(0,
|
|
80676
|
+
const s2 = await (0, import_code_core5.updateCloudSchedule)(id, { enabled });
|
|
80677
|
+
const next = s2.nextRunAt ? paint.dim(` \u2014 next run ${(0, import_code_core4.fmtScheduleWhen)(s2.nextRunAt)}`) : "";
|
|
80062
80678
|
console.log(`\u2714 Schedule #${s2.id} is now ${s2.enabled ? "on" : "paused"}${next}`);
|
|
80063
80679
|
} catch (e2) {
|
|
80064
80680
|
fail(errText(e2));
|
|
@@ -80079,7 +80695,7 @@ async function scheduleRunCommand(id) {
|
|
|
80079
80695
|
return;
|
|
80080
80696
|
}
|
|
80081
80697
|
try {
|
|
80082
|
-
const s2 = await (0,
|
|
80698
|
+
const s2 = await (0, import_code_core5.runCloudScheduleNow)(clean);
|
|
80083
80699
|
console.log(`\u2714 Schedule #${s2.id} armed \u2014 the cloud runner picks it up within ~60s.`);
|
|
80084
80700
|
console.log(paint.dim(` Result: nex schedule runs ${s2.id}`));
|
|
80085
80701
|
} catch (e2) {
|
|
@@ -80113,7 +80729,7 @@ async function scheduleRemoveCommand(id, opts = {}) {
|
|
|
80113
80729
|
}
|
|
80114
80730
|
}
|
|
80115
80731
|
try {
|
|
80116
|
-
await (0,
|
|
80732
|
+
await (0, import_code_core5.deleteCloudSchedule)(clean);
|
|
80117
80733
|
console.log(`\u2714 Removed schedule #${clean}`);
|
|
80118
80734
|
} catch (e2) {
|
|
80119
80735
|
fail(errText(e2));
|
|
@@ -80129,7 +80745,7 @@ async function scheduleRunsCommand(id, opts = {}) {
|
|
|
80129
80745
|
}
|
|
80130
80746
|
let runs2;
|
|
80131
80747
|
try {
|
|
80132
|
-
runs2 = await (0,
|
|
80748
|
+
runs2 = await (0, import_code_core5.getCloudScheduleRuns)(clean);
|
|
80133
80749
|
} catch (e2) {
|
|
80134
80750
|
fail(errText(e2));
|
|
80135
80751
|
return;
|
|
@@ -80138,7 +80754,7 @@ async function scheduleRunsCommand(id, opts = {}) {
|
|
|
80138
80754
|
console.log(JSON.stringify(runs2, null, 2));
|
|
80139
80755
|
return;
|
|
80140
80756
|
}
|
|
80141
|
-
for (const line of (0,
|
|
80757
|
+
for (const line of (0, import_code_core4.scheduleRunsLines)(runs2, { cols: cols() }))
|
|
80142
80758
|
console.log(line);
|
|
80143
80759
|
if (runs2.length) {
|
|
80144
80760
|
console.log(
|
|
@@ -80151,10 +80767,10 @@ async function scheduleRunsCommand(id, opts = {}) {
|
|
|
80151
80767
|
}
|
|
80152
80768
|
|
|
80153
80769
|
// src/commands/cloud.ts
|
|
80154
|
-
var
|
|
80155
|
-
var
|
|
80770
|
+
var import_proc2 = __toESM(require_proc(), 1);
|
|
80771
|
+
var import_code_core6 = __toESM(require_dist2(), 1);
|
|
80156
80772
|
async function requireAuth3() {
|
|
80157
|
-
if ((0,
|
|
80773
|
+
if ((0, import_code_core6.isAuthenticated)())
|
|
80158
80774
|
return true;
|
|
80159
80775
|
console.log(paint.red("Not logged in. Run `nex auth` first."));
|
|
80160
80776
|
process.exitCode = 1;
|
|
@@ -80192,7 +80808,7 @@ function openUrl(url) {
|
|
|
80192
80808
|
const cmd = process.platform === "darwin" ? "open" : win ? "rundll32" : "xdg-open";
|
|
80193
80809
|
const args = win ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
80194
80810
|
try {
|
|
80195
|
-
const child = (0,
|
|
80811
|
+
const child = (0, import_proc2.spawn)(cmd, args, { stdio: "ignore", detached: !win });
|
|
80196
80812
|
child.on("error", () => {
|
|
80197
80813
|
});
|
|
80198
80814
|
child.unref();
|
|
@@ -80224,7 +80840,7 @@ async function cloudRunCommand(taskParts, opts) {
|
|
|
80224
80840
|
}
|
|
80225
80841
|
let created;
|
|
80226
80842
|
try {
|
|
80227
|
-
created = await (0,
|
|
80843
|
+
created = await (0, import_code_core6.createCloudSession)({ repo: opts.repo, task, baseBranch: opts.base, pat: pat || void 0 });
|
|
80228
80844
|
} catch (e2) {
|
|
80229
80845
|
return fail2(errText2(e2));
|
|
80230
80846
|
}
|
|
@@ -80243,7 +80859,7 @@ async function cloudListCommand(opts = {}) {
|
|
|
80243
80859
|
return;
|
|
80244
80860
|
let list;
|
|
80245
80861
|
try {
|
|
80246
|
-
list = await (0,
|
|
80862
|
+
list = await (0, import_code_core6.listCloudSessions)();
|
|
80247
80863
|
} catch (e2) {
|
|
80248
80864
|
return fail2(errText2(e2));
|
|
80249
80865
|
}
|
|
@@ -80251,7 +80867,7 @@ async function cloudListCommand(opts = {}) {
|
|
|
80251
80867
|
console.log(JSON.stringify({ sessions: list }, null, 2));
|
|
80252
80868
|
return;
|
|
80253
80869
|
}
|
|
80254
|
-
for (const line of (0,
|
|
80870
|
+
for (const line of (0, import_code_core6.cloudListLines)(list, { cols: cols2() }))
|
|
80255
80871
|
console.log(` ${line}`);
|
|
80256
80872
|
}
|
|
80257
80873
|
async function cloudLogsCommand(id, opts) {
|
|
@@ -80264,7 +80880,7 @@ async function cloudLogsCommand(id, opts) {
|
|
|
80264
80880
|
if (!opts.follow) {
|
|
80265
80881
|
let r2;
|
|
80266
80882
|
try {
|
|
80267
|
-
r2 = await (0,
|
|
80883
|
+
r2 = await (0, import_code_core6.getCloudSessionLogs)(id, tail ?? 300);
|
|
80268
80884
|
} catch (e2) {
|
|
80269
80885
|
return fail2(errText2(e2));
|
|
80270
80886
|
}
|
|
@@ -80290,7 +80906,7 @@ async function cloudLogsCommand(id, opts) {
|
|
|
80290
80906
|
for (; ; ) {
|
|
80291
80907
|
let r2;
|
|
80292
80908
|
try {
|
|
80293
|
-
r2 = await (0,
|
|
80909
|
+
r2 = await (0, import_code_core6.getCloudSessionLogs)(id, tail ?? 1e3);
|
|
80294
80910
|
consecutiveErrors = 0;
|
|
80295
80911
|
} catch (e2) {
|
|
80296
80912
|
const msg = errText2(e2);
|
|
@@ -80339,7 +80955,7 @@ async function cloudStopCommand(id) {
|
|
|
80339
80955
|
return;
|
|
80340
80956
|
let s2;
|
|
80341
80957
|
try {
|
|
80342
|
-
s2 = await (0,
|
|
80958
|
+
s2 = await (0, import_code_core6.stopCloudSession)(id);
|
|
80343
80959
|
} catch (e2) {
|
|
80344
80960
|
return fail2(errText2(e2));
|
|
80345
80961
|
}
|
|
@@ -80354,7 +80970,7 @@ async function cloudWatchCommand(id, opts = {}) {
|
|
|
80354
80970
|
return;
|
|
80355
80971
|
let s2;
|
|
80356
80972
|
try {
|
|
80357
|
-
s2 = await (0,
|
|
80973
|
+
s2 = await (0, import_code_core6.getCloudSession)(id);
|
|
80358
80974
|
} catch (e2) {
|
|
80359
80975
|
return fail2(errText2(e2));
|
|
80360
80976
|
}
|
|
@@ -80524,11 +81140,11 @@ async function channelPushCommand(textParts, opts, deps = {}) {
|
|
|
80524
81140
|
}
|
|
80525
81141
|
|
|
80526
81142
|
// src/commands/usageCmd.ts
|
|
80527
|
-
import
|
|
80528
|
-
import
|
|
81143
|
+
import fs3 from "node:fs";
|
|
81144
|
+
import path2 from "node:path";
|
|
80529
81145
|
|
|
80530
81146
|
// src/sessionEvents.ts
|
|
80531
|
-
var
|
|
81147
|
+
var import_code_core7 = __toESM(require_dist2(), 1);
|
|
80532
81148
|
|
|
80533
81149
|
// src/usageExport.ts
|
|
80534
81150
|
var DURATION_RE = /^(\d+)([mhdw])$/;
|
|
@@ -80825,7 +81441,7 @@ var USAGE_FORMATS = ["json", "jsonl", "csv"];
|
|
|
80825
81441
|
function logFileIds(dir) {
|
|
80826
81442
|
let names;
|
|
80827
81443
|
try {
|
|
80828
|
-
names =
|
|
81444
|
+
names = fs3.readdirSync(dir);
|
|
80829
81445
|
} catch {
|
|
80830
81446
|
return [];
|
|
80831
81447
|
}
|
|
@@ -80835,7 +81451,7 @@ function readRows(file) {
|
|
|
80835
81451
|
const rows = [];
|
|
80836
81452
|
let text;
|
|
80837
81453
|
try {
|
|
80838
|
-
text =
|
|
81454
|
+
text = fs3.readFileSync(file, "utf8");
|
|
80839
81455
|
} catch {
|
|
80840
81456
|
return rows;
|
|
80841
81457
|
}
|
|
@@ -80883,13 +81499,13 @@ function usageExportCommand(opts, deps = {}) {
|
|
|
80883
81499
|
}
|
|
80884
81500
|
limit = n;
|
|
80885
81501
|
}
|
|
80886
|
-
const dir = deps.eventsDirPath ?? (0,
|
|
81502
|
+
const dir = deps.eventsDirPath ?? (0, import_code_core7.eventsDir)(deps.env ?? process.env);
|
|
80887
81503
|
const ids = logFileIds(dir);
|
|
80888
81504
|
const acc = createAccumulator({ since, until });
|
|
80889
81505
|
acc.filesTotal = ids.length;
|
|
80890
81506
|
let candidates = ids;
|
|
80891
81507
|
if (opts.session) {
|
|
80892
|
-
const wanted = (0,
|
|
81508
|
+
const wanted = (0, import_code_core7.safeId)(opts.session);
|
|
80893
81509
|
if (!candidates.includes(wanted)) {
|
|
80894
81510
|
stderr(paint.red(`Session not found: ${opts.session}`));
|
|
80895
81511
|
stderr(paint.dim(` Logs live in ${dir} (one .jsonl per session).`));
|
|
@@ -80900,7 +81516,7 @@ function usageExportCommand(opts, deps = {}) {
|
|
|
80900
81516
|
if (since !== void 0) {
|
|
80901
81517
|
candidates = candidates.filter((id) => {
|
|
80902
81518
|
try {
|
|
80903
|
-
return
|
|
81519
|
+
return fs3.statSync(path2.join(dir, `${id}.jsonl`)).mtimeMs >= since;
|
|
80904
81520
|
} catch {
|
|
80905
81521
|
return false;
|
|
80906
81522
|
}
|
|
@@ -80908,7 +81524,7 @@ function usageExportCommand(opts, deps = {}) {
|
|
|
80908
81524
|
}
|
|
80909
81525
|
const byNewest = candidates.map((id) => {
|
|
80910
81526
|
try {
|
|
80911
|
-
return { id, mtime:
|
|
81527
|
+
return { id, mtime: fs3.statSync(path2.join(dir, `${id}.jsonl`)).mtimeMs };
|
|
80912
81528
|
} catch {
|
|
80913
81529
|
return { id, mtime: 0 };
|
|
80914
81530
|
}
|
|
@@ -80917,7 +81533,7 @@ function usageExportCommand(opts, deps = {}) {
|
|
|
80917
81533
|
acc.truncated = taken.length < byNewest.length;
|
|
80918
81534
|
for (const { id } of taken) {
|
|
80919
81535
|
acc.filesRead += 1;
|
|
80920
|
-
addSessionFile(acc, id, readRows(
|
|
81536
|
+
addSessionFile(acc, id, readRows(path2.join(dir, `${id}.jsonl`)));
|
|
80921
81537
|
}
|
|
80922
81538
|
const report = finalizeUsage(acc, now);
|
|
80923
81539
|
let content;
|
|
@@ -80929,7 +81545,7 @@ function usageExportCommand(opts, deps = {}) {
|
|
|
80929
81545
|
content = formatUsageJson(report);
|
|
80930
81546
|
if (opts.out) {
|
|
80931
81547
|
try {
|
|
80932
|
-
|
|
81548
|
+
fs3.writeFileSync(opts.out, content);
|
|
80933
81549
|
} catch (e2) {
|
|
80934
81550
|
stderr(paint.red(`Could not write ${opts.out}: ${e2.message}`));
|
|
80935
81551
|
return USAGE_EXIT.FAIL;
|
|
@@ -80963,7 +81579,7 @@ import * as path27 from "path";
|
|
|
80963
81579
|
import * as fs26 from "fs";
|
|
80964
81580
|
|
|
80965
81581
|
// src/ui/diffView.ts
|
|
80966
|
-
var
|
|
81582
|
+
var import_code_core8 = __toESM(require_dist2(), 1);
|
|
80967
81583
|
function paintLine(l) {
|
|
80968
81584
|
switch (l.kind) {
|
|
80969
81585
|
case "file":
|
|
@@ -80984,433 +81600,7 @@ function paintLine(l) {
|
|
|
80984
81600
|
}
|
|
80985
81601
|
|
|
80986
81602
|
// 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
81603
|
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
81604
|
|
|
81415
81605
|
// src/commands/mcpCmd.ts
|
|
81416
81606
|
var import_code_core10 = __toESM(require_dist2(), 1);
|
|
@@ -82477,7 +82667,7 @@ function classify(err, opts) {
|
|
|
82477
82667
|
return {
|
|
82478
82668
|
kind: "auth",
|
|
82479
82669
|
title: "Not signed in",
|
|
82480
|
-
hints: ["Run `
|
|
82670
|
+
hints: ["Run `nex auth` to sign in again", "Your session keeps its place: /resume picks it back up"]
|
|
82481
82671
|
};
|
|
82482
82672
|
}
|
|
82483
82673
|
if (status === 429 || RATE_RE.test(msg)) {
|
|
@@ -87980,7 +88170,7 @@ function knownSessionIds() {
|
|
|
87980
88170
|
}
|
|
87981
88171
|
};
|
|
87982
88172
|
add(SESSIONS_DIR, ".json");
|
|
87983
|
-
add((0,
|
|
88173
|
+
add((0, import_code_core7.eventsDir)(), ".jsonl");
|
|
87984
88174
|
return [...ids];
|
|
87985
88175
|
}
|
|
87986
88176
|
function findSessionsByLink(link2) {
|
|
@@ -88053,7 +88243,7 @@ function deleteSession(id) {
|
|
|
88053
88243
|
return false;
|
|
88054
88244
|
}
|
|
88055
88245
|
try {
|
|
88056
|
-
fs12.unlinkSync((0,
|
|
88246
|
+
fs12.unlinkSync((0, import_code_core7.eventsPath)(r2.id));
|
|
88057
88247
|
} catch {
|
|
88058
88248
|
}
|
|
88059
88249
|
return true;
|
|
@@ -88087,7 +88277,7 @@ function exportSession(id, events) {
|
|
|
88087
88277
|
version: 1,
|
|
88088
88278
|
exportedAt: Date.now(),
|
|
88089
88279
|
session,
|
|
88090
|
-
events: events ?? (0,
|
|
88280
|
+
events: events ?? (0, import_code_core7.readEvents)(r2.id)
|
|
88091
88281
|
};
|
|
88092
88282
|
return JSON.stringify(bundle, null, 2);
|
|
88093
88283
|
}
|
|
@@ -88129,7 +88319,7 @@ function importSession(bundle) {
|
|
|
88129
88319
|
renamed = true;
|
|
88130
88320
|
}
|
|
88131
88321
|
saveSession(id, title, bundle.session.workDir || process.cwd(), bundle.session.messages);
|
|
88132
|
-
(0,
|
|
88322
|
+
(0, import_code_core7.writeEvents)(id, bundle.events);
|
|
88133
88323
|
return { id, messages: bundle.session.messages.length, renamed };
|
|
88134
88324
|
}
|
|
88135
88325
|
|
|
@@ -98327,6 +98517,11 @@ var BUILTIN_SLASH_COMMANDS = [
|
|
|
98327
98517
|
kind: "builtin"
|
|
98328
98518
|
},
|
|
98329
98519
|
{ name: "cost", description: "Where the tokens and money went (per model, per agent, per turn)", kind: "builtin" },
|
|
98520
|
+
{
|
|
98521
|
+
name: "logout",
|
|
98522
|
+
description: "Sign out on this machine (revokes the saved refresh token)",
|
|
98523
|
+
kind: "builtin"
|
|
98524
|
+
},
|
|
98330
98525
|
{
|
|
98331
98526
|
name: "stats",
|
|
98332
98527
|
description: "Usage across sessions: activity by day, tokens and models (local event logs)",
|
|
@@ -103881,7 +104076,7 @@ function scanRecentSkillEvents(dir, maxFiles = DOCTOR_SESSION_WINDOW) {
|
|
|
103881
104076
|
const out = [];
|
|
103882
104077
|
for (const { full } of withTime.slice(0, maxFiles)) {
|
|
103883
104078
|
try {
|
|
103884
|
-
out.push((0,
|
|
104079
|
+
out.push((0, import_code_core7.readEvents)("", { file: full }));
|
|
103885
104080
|
} catch {
|
|
103886
104081
|
}
|
|
103887
104082
|
}
|
|
@@ -105429,7 +105624,7 @@ function emitEvent(type, data = {}) {
|
|
|
105429
105624
|
}
|
|
105430
105625
|
function startSessionLog(id, resumed, extra = {}) {
|
|
105431
105626
|
const previous = liveSessionId;
|
|
105432
|
-
sessionLog = (0,
|
|
105627
|
+
sessionLog = (0, import_code_core7.eventsEnabled)() ? new import_code_core7.EventLog(id) : null;
|
|
105433
105628
|
liveSessionId = id;
|
|
105434
105629
|
if (previous && previous !== id)
|
|
105435
105630
|
(0, import_code_core22.rebindSessionShare)(id);
|
|
@@ -105774,7 +105969,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
|
|
|
105774
105969
|
emitEvent("tool.started", {
|
|
105775
105970
|
name,
|
|
105776
105971
|
sub: !!isSubTask,
|
|
105777
|
-
target: name === "use_skill" && typeof input.name === "string" ? input.name : (0,
|
|
105972
|
+
target: name === "use_skill" && typeof input.name === "string" ? input.name : (0, import_code_core7.toolTarget)(name, input)
|
|
105778
105973
|
});
|
|
105779
105974
|
if (!isSubTask)
|
|
105780
105975
|
sessionToolCalls++;
|
|
@@ -105827,7 +106022,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
|
|
|
105827
106022
|
const changed = noteToolResult(turnStats, tracked?.name ?? name, tracked?.input, cmdOk);
|
|
105828
106023
|
for (const t2 of changed)
|
|
105829
106024
|
failureLedger.noteEdit(t2);
|
|
105830
|
-
for (const t2 of changed.slice(0,
|
|
106025
|
+
for (const t2 of changed.slice(0, import_code_core7.MAX_FILE_EVENTS))
|
|
105831
106026
|
emitEvent("file.changed", { path: t2 });
|
|
105832
106027
|
}
|
|
105833
106028
|
if (!isSubTask && tracked?.command)
|
|
@@ -106169,7 +106364,7 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env3
|
|
|
106169
106364
|
onToolUse: (name, input, isSubTask, meta) => {
|
|
106170
106365
|
debugLog("tool_use", { tool: name, sub: !!isSubTask });
|
|
106171
106366
|
emit({ type: "tool_use", tool: name, input, ...toolIds(isSubTask, meta) });
|
|
106172
|
-
verbose(`[verbose] -> ${name}${isSubTask ? " (sub-agent)" : ""} ${(0,
|
|
106367
|
+
verbose(`[verbose] -> ${name}${isSubTask ? " (sub-agent)" : ""} ${(0, import_code_core7.toolTarget)(name, input)}`);
|
|
106173
106368
|
},
|
|
106174
106369
|
// Live bash progress for scripted/CI consumers — lets a wrapper tail a
|
|
106175
106370
|
// long-running build/test command instead of blocking silently until
|
|
@@ -106449,6 +106644,7 @@ function printHelp() {
|
|
|
106449
106644
|
["/yolo", "Auto-approve all permissions"],
|
|
106450
106645
|
["/balance", "Show what is paying (personal wallet or team) and what is left"],
|
|
106451
106646
|
["/team [use <id|personal>]", "List your teams, or switch what usage is billed to"],
|
|
106647
|
+
["/logout", "Sign out on this machine (revokes the saved refresh token)"],
|
|
106452
106648
|
["/cost [--all]", "Cost and tokens: per model, per sub-agent, per turn (/balance shows the wallet)"],
|
|
106453
106649
|
["/stats", "Usage across sessions: activity by day, tokens and models (local event logs)"],
|
|
106454
106650
|
[
|
|
@@ -107165,7 +107361,7 @@ Resume it with: nex --worktree ${worktreeState.name}
|
|
|
107165
107361
|
console.log(paint.green(` Resumed session: ${paint.bold(stored.title.slice(0, 60))}`));
|
|
107166
107362
|
console.log(paint.dim(` ${messages.length} messages restored`));
|
|
107167
107363
|
if (autoRecap) {
|
|
107168
|
-
const recap = (0, import_code_core33.buildRecap)((0,
|
|
107364
|
+
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
107365
|
if (recap)
|
|
107170
107366
|
console.log(paint.dim(` Recap: ${recap}`));
|
|
107171
107367
|
}
|
|
@@ -109005,6 +109201,26 @@ ${dirList}`;
|
|
|
109005
109201
|
rl.prompt();
|
|
109006
109202
|
return;
|
|
109007
109203
|
}
|
|
109204
|
+
case "/logout": {
|
|
109205
|
+
const answer = await new Promise(
|
|
109206
|
+
(resolve19) => rl.question(paint.yellow(" Sign out on this machine? ") + paint.dim("[y/n] "), (a) => resolve19(a.trim()))
|
|
109207
|
+
);
|
|
109208
|
+
if (answer !== "y" && answer !== "yes") {
|
|
109209
|
+
console.log(paint.dim(" Cancelled."));
|
|
109210
|
+
rl.prompt();
|
|
109211
|
+
return;
|
|
109212
|
+
}
|
|
109213
|
+
await signOut();
|
|
109214
|
+
console.log(paint.green(" \u2713 Logged out."));
|
|
109215
|
+
if (process.env.NEXRALL_TOKEN?.trim()) {
|
|
109216
|
+
console.log(
|
|
109217
|
+
paint.yellow(" NEXRALL_TOKEN is still set in this shell") + paint.dim(" \u2014 unset it, or the CLI stays signed in with that token.")
|
|
109218
|
+
);
|
|
109219
|
+
}
|
|
109220
|
+
console.log(paint.dim(" Run `nex auth` (in any terminal) to sign in again; this session picks it up."));
|
|
109221
|
+
rl.prompt();
|
|
109222
|
+
return;
|
|
109223
|
+
}
|
|
109008
109224
|
case "/cost": {
|
|
109009
109225
|
const parsed = parseCostArgs(arg);
|
|
109010
109226
|
if ("error" in parsed) {
|
|
@@ -109067,14 +109283,14 @@ ${dirList}`;
|
|
|
109067
109283
|
return;
|
|
109068
109284
|
}
|
|
109069
109285
|
case "/recap": {
|
|
109070
|
-
if (!(0,
|
|
109286
|
+
if (!(0, import_code_core7.eventsEnabled)()) {
|
|
109071
109287
|
console.log(
|
|
109072
109288
|
paint.dim(" Events are off for this run (NEXRALL_EVENTS=off), so there is nothing to read back.")
|
|
109073
109289
|
);
|
|
109074
109290
|
rl.prompt();
|
|
109075
109291
|
return;
|
|
109076
109292
|
}
|
|
109077
|
-
const recap = (0, import_code_core33.buildRecap)((0,
|
|
109293
|
+
const recap = (0, import_code_core33.buildRecap)((0, import_code_core7.readEvents)(sessionId, { last: import_code_core33.RECAP_EVENT_WINDOW }), { now: Date.now() });
|
|
109078
109294
|
console.log();
|
|
109079
109295
|
if (recap)
|
|
109080
109296
|
console.log(` ${paint.bold("Recap: ")}${recap}`);
|
|
@@ -109638,11 +109854,11 @@ ${dirList}`;
|
|
|
109638
109854
|
return;
|
|
109639
109855
|
}
|
|
109640
109856
|
if (!(0, import_code_core64.getToken)()) {
|
|
109641
|
-
console.log(paint.yellow(" Sign in first (`nex
|
|
109857
|
+
console.log(paint.yellow(" Sign in first (`nex auth`) \u2014 watching uses your Nexrall account."));
|
|
109642
109858
|
rl.prompt();
|
|
109643
109859
|
return;
|
|
109644
109860
|
}
|
|
109645
|
-
if (!(0,
|
|
109861
|
+
if (!(0, import_code_core7.eventsEnabled)()) {
|
|
109646
109862
|
console.log(
|
|
109647
109863
|
paint.yellow(" Sharing follows the event stream, and this run has it off (NEXRALL_EVENTS=off).")
|
|
109648
109864
|
);
|
|
@@ -109746,7 +109962,7 @@ ${dirList}`;
|
|
|
109746
109962
|
}
|
|
109747
109963
|
if (!(0, import_code_core64.getToken)()) {
|
|
109748
109964
|
console.log(
|
|
109749
|
-
paint.yellow(" Sign in first (`nex
|
|
109965
|
+
paint.yellow(" Sign in first (`nex auth`) \u2014 pushes find this session through your account.")
|
|
109750
109966
|
);
|
|
109751
109967
|
rl.prompt();
|
|
109752
109968
|
return;
|
|
@@ -109817,7 +110033,7 @@ ${dirList}`;
|
|
|
109817
110033
|
}
|
|
109818
110034
|
case "/schedule":
|
|
109819
110035
|
case "/routines": {
|
|
109820
|
-
const action = (0,
|
|
110036
|
+
const action = (0, import_code_core4.parseScheduleChatArg)(arg);
|
|
109821
110037
|
if (action.kind === "error") {
|
|
109822
110038
|
console.log(paint.yellow(" " + action.message));
|
|
109823
110039
|
rl.prompt();
|
|
@@ -109828,7 +110044,7 @@ ${dirList}`;
|
|
|
109828
110044
|
const panelCols = Math.max(60, (process.stdout.columns || 100) - 2);
|
|
109829
110045
|
if (action.kind === "list") {
|
|
109830
110046
|
const list = await (0, import_code_core63.listCloudSchedules)();
|
|
109831
|
-
for (const l of (0,
|
|
110047
|
+
for (const l of (0, import_code_core4.scheduleListLines)(list, { cols: panelCols }))
|
|
109832
110048
|
console.log(" " + l);
|
|
109833
110049
|
if (list.length)
|
|
109834
110050
|
console.log(
|
|
@@ -109838,7 +110054,7 @@ ${dirList}`;
|
|
|
109838
110054
|
const s2 = await (0, import_code_core63.createCloudSchedule)({ task: action.task, intervalMinutes: action.intervalMinutes });
|
|
109839
110055
|
console.log(
|
|
109840
110056
|
paint.green(
|
|
109841
|
-
` \u2713 Scheduled #${s2.id} \u2014 every ${(0,
|
|
110057
|
+
` \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
110058
|
)
|
|
109843
110059
|
);
|
|
109844
110060
|
console.log(
|
|
@@ -109856,7 +110072,7 @@ ${dirList}`;
|
|
|
109856
110072
|
console.log(paint.dim(` Removed schedule #${action.id} \u2014 no further runs fire.`));
|
|
109857
110073
|
} else {
|
|
109858
110074
|
const runs2 = await (0, import_code_core63.getCloudScheduleRuns)(action.id);
|
|
109859
|
-
for (const l of (0,
|
|
110075
|
+
for (const l of (0, import_code_core4.scheduleRunsLines)(runs2, { cols: panelCols }))
|
|
109860
110076
|
console.log(" " + l);
|
|
109861
110077
|
}
|
|
109862
110078
|
} catch (err) {
|
|
@@ -110074,7 +110290,7 @@ ${dirList}`;
|
|
|
110074
110290
|
};
|
|
110075
110291
|
const want = arg.trim();
|
|
110076
110292
|
if (want) {
|
|
110077
|
-
const hit = (0,
|
|
110293
|
+
const hit = (0, import_code_core8.findFiles)(files, want);
|
|
110078
110294
|
if (!hit.length)
|
|
110079
110295
|
console.log(paint.dim(` No changed file matches "${want}". (/diff lists them all)`));
|
|
110080
110296
|
else if (hit.length > 1) {
|
|
@@ -110083,14 +110299,14 @@ ${dirList}`;
|
|
|
110083
110299
|
console.log(" " + f3.path);
|
|
110084
110300
|
} else {
|
|
110085
110301
|
verification.noteDiffReviewed();
|
|
110086
|
-
out((0,
|
|
110302
|
+
out((0, import_code_core8.capLines)((0, import_code_core8.renderFile)(hit[0], { foldHunks: false })));
|
|
110087
110303
|
}
|
|
110088
110304
|
} else {
|
|
110089
110305
|
console.log(paint.bold(" Uncommitted changes"));
|
|
110090
|
-
out((0,
|
|
110306
|
+
out((0, import_code_core8.summaryLines)(files));
|
|
110091
110307
|
console.log();
|
|
110092
110308
|
verification.noteDiffReviewed();
|
|
110093
|
-
out((0,
|
|
110309
|
+
out((0, import_code_core8.renderAll)(files));
|
|
110094
110310
|
}
|
|
110095
110311
|
rl.prompt();
|
|
110096
110312
|
return;
|
|
@@ -110111,7 +110327,7 @@ ${dirList}`;
|
|
|
110111
110327
|
const emit = (line) => console.log(truncateCells(line, cols3));
|
|
110112
110328
|
const want = arg.replace(/(^|\s)--all(?=\s|$)/, " ").trim();
|
|
110113
110329
|
if (want) {
|
|
110114
|
-
const hit = (0,
|
|
110330
|
+
const hit = (0, import_code_core8.findFiles)(files, want);
|
|
110115
110331
|
if (!hit.length)
|
|
110116
110332
|
emit(paint.dim(` No changed file matches "${want}". (/review lists them all)`));
|
|
110117
110333
|
else if (hit.length > 1) {
|
|
@@ -110119,14 +110335,14 @@ ${dirList}`;
|
|
|
110119
110335
|
for (const f3 of hit.slice(0, 10))
|
|
110120
110336
|
emit(" " + f3.path);
|
|
110121
110337
|
} else {
|
|
110122
|
-
const ctx = { tests: files.filter((x2) => (0,
|
|
110123
|
-
for (const line of (0,
|
|
110338
|
+
const ctx = { tests: files.filter((x2) => (0, import_code_core9.isTestPath)(x2.path)) };
|
|
110339
|
+
for (const line of (0, import_code_core9.formatReviewDetail)((0, import_code_core9.classifyFile)(hit[0], ctx), cols3))
|
|
110124
110340
|
emit(" " + line);
|
|
110125
110341
|
}
|
|
110126
110342
|
} else {
|
|
110127
110343
|
const all = /(^|\s)--all(\s|$)/.test(arg);
|
|
110128
|
-
const q = (0,
|
|
110129
|
-
for (const line of (0,
|
|
110344
|
+
const q = (0, import_code_core9.buildReviewQueue)(files, all ? Number.POSITIVE_INFINITY : import_code_core9.DEFAULT_PER_GROUP);
|
|
110345
|
+
for (const line of (0, import_code_core9.formatReviewQueue)(q, cols3))
|
|
110130
110346
|
emit(" " + line);
|
|
110131
110347
|
emit(paint.dim(" /review <file> for one entry's reasons \xB7 /diff for the change itself"));
|
|
110132
110348
|
}
|
|
@@ -110903,7 +111119,7 @@ ${text}
|
|
|
110903
111119
|
}
|
|
110904
111120
|
case "/skill-doctor": {
|
|
110905
111121
|
const loadedSkills = (0, import_code_core66.loadSkills)(workDir);
|
|
110906
|
-
const usage2 = collectSkillUsage(scanRecentSkillEvents((0,
|
|
111122
|
+
const usage2 = collectSkillUsage(scanRecentSkillEvents((0, import_code_core7.eventsDir)()));
|
|
110907
111123
|
for (const l of buildSkillDoctorReport(loadedSkills, usage2).lines)
|
|
110908
111124
|
console.log(l);
|
|
110909
111125
|
rl.prompt();
|
|
@@ -112811,7 +113027,7 @@ function planProjectPurge(targetDir, env3 = process.env) {
|
|
|
112811
113027
|
}
|
|
112812
113028
|
}
|
|
112813
113029
|
for (const id of sessionIds) {
|
|
112814
|
-
const p = (0,
|
|
113030
|
+
const p = (0, import_code_core7.eventsPath)(id, env3);
|
|
112815
113031
|
if (fs28.existsSync(p))
|
|
112816
113032
|
items.push({ kind: "events", path: p, dir: false });
|
|
112817
113033
|
}
|
|
@@ -114095,7 +114311,7 @@ var NEX_VERSION = require3("../package.json").version;
|
|
|
114095
114311
|
installEpipeGuard();
|
|
114096
114312
|
var program2 = new Command();
|
|
114097
114313
|
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version(NEX_VERSION).enablePositionalOptions();
|
|
114098
|
-
var auth = program2.command("auth").description("
|
|
114314
|
+
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
114315
|
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
114316
|
const r2 = authStatusReport((0, import_code_core79.loadAuth)(), process.env);
|
|
114101
114317
|
if (opts.json)
|
|
@@ -114553,7 +114769,7 @@ program2.command("sessions").description("List, export, import, or delete saved
|
|
|
114553
114769
|
process.exitCode = 1;
|
|
114554
114770
|
return;
|
|
114555
114771
|
}
|
|
114556
|
-
const events = (0,
|
|
114772
|
+
const events = (0, import_code_core7.readEvents)(hit.id, { last: Number.isFinite(want) && want > 0 ? want : 20 });
|
|
114557
114773
|
if (!events.length) {
|
|
114558
114774
|
console.log(
|
|
114559
114775
|
paint.dim(` No events recorded for ${hit.id.slice(0, 8)} (NEXRALL_EVENTS=off disables the stream).`)
|
|
@@ -114568,9 +114784,9 @@ program2.command("sessions").description("List, export, import, or delete saved
|
|
|
114568
114784
|
console.log();
|
|
114569
114785
|
console.log(paint.bold(` Events for ${hit.id.slice(0, 8)} `) + paint.dim(`(last ${events.length})`));
|
|
114570
114786
|
for (const ev of events)
|
|
114571
|
-
console.log(" " + paint.dim((0,
|
|
114787
|
+
console.log(" " + paint.dim((0, import_code_core7.summarizeEvent)(ev)));
|
|
114572
114788
|
console.log(paint.dim(`
|
|
114573
|
-
Raw JSONL: ${(0,
|
|
114789
|
+
Raw JSONL: ${(0, import_code_core7.eventsPath)(hit.id)}`));
|
|
114574
114790
|
console.log();
|
|
114575
114791
|
return;
|
|
114576
114792
|
}
|