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.
Files changed (3) hide show
  1. package/README.md +4 -2
  2. package/dist/index.js +744 -528
  3. 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 `nexrall-code login` first.");
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 `nexrall-code login` first." };
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 `nexrall-code login` first." };
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 `nexrall-code login` first." };
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 import_code_core = __toESM(require_dist2(), 1);
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
- } else if (char === "\x7F" || char === "\b") {
79787
- if (value.length > 0) {
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, import_code_core.isAuthenticated)()) {
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, import_code_core.login)(email, password);
79822
- (0, import_code_core.saveAuth)(authConfig);
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
- console.log();
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.exit(1);
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, import_code_core.isAuthenticated)()) {
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 (0, import_code_core.revokeRefreshToken)();
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 import_code_core2 = __toESM(require_dist2(), 1);
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, import_code_core2.isAuthenticated)())
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, import_code_core2.getOrgs)();
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, import_code_core2.billingOverrideFields)();
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, import_code_core2.setBillingTarget)(teamId);
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 import_code_core4 = __toESM(require_dist2(), 1);
80581
+ var import_code_core5 = __toESM(require_dist2(), 1);
79966
80582
 
79967
80583
  // src/ui/scheduleLines.ts
79968
- var import_code_core3 = __toESM(require_dist2(), 1);
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, import_code_core4.isAuthenticated)())
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, import_code_core4.listCloudSchedules)();
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, import_code_core3.scheduleListLines)(list, { cols: cols() }))
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, import_code_core3.parseScheduleEvery)(everyRaw);
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, import_code_core4.createCloudSchedule)({
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, import_code_core3.formatEvery)(s2.intervalMinutes)}${s2.enabled ? "" : paint.yellow(" (paused)")}`
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, import_code_core3.fmtScheduleWhen)(s2.nextRunAt)} \u2014 on Nexrall's servers; your laptop can be closed.`)
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, import_code_core4.updateCloudSchedule)(id, { enabled });
80061
- const next = s2.nextRunAt ? paint.dim(` \u2014 next run ${(0, import_code_core3.fmtScheduleWhen)(s2.nextRunAt)}`) : "";
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, import_code_core4.runCloudScheduleNow)(clean);
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, import_code_core4.deleteCloudSchedule)(clean);
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, import_code_core4.getCloudScheduleRuns)(clean);
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, import_code_core3.scheduleRunsLines)(runs2, { cols: cols() }))
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 import_proc = __toESM(require_proc(), 1);
80155
- var import_code_core5 = __toESM(require_dist2(), 1);
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, import_code_core5.isAuthenticated)())
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, import_proc.spawn)(cmd, args, { stdio: "ignore", detached: !win });
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, import_code_core5.createCloudSession)({ repo: opts.repo, task, baseBranch: opts.base, pat: pat || void 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, import_code_core5.listCloudSessions)();
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, import_code_core5.cloudListLines)(list, { cols: cols2() }))
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, import_code_core5.getCloudSessionLogs)(id, tail ?? 300);
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, import_code_core5.getCloudSessionLogs)(id, tail ?? 1e3);
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, import_code_core5.stopCloudSession)(id);
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, import_code_core5.getCloudSession)(id);
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 fs2 from "node:fs";
80528
- import path from "node:path";
81143
+ import fs3 from "node:fs";
81144
+ import path2 from "node:path";
80529
81145
 
80530
81146
  // src/sessionEvents.ts
80531
- var import_code_core6 = __toESM(require_dist2(), 1);
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 = fs2.readdirSync(dir);
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 = fs2.readFileSync(file, "utf8");
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, import_code_core6.eventsDir)(deps.env ?? process.env);
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, import_code_core6.safeId)(opts.session);
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 fs2.statSync(path.join(dir, `${id}.jsonl`)).mtimeMs >= since;
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: fs2.statSync(path.join(dir, `${id}.jsonl`)).mtimeMs };
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(path.join(dir, `${id}.jsonl`)));
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
- fs2.writeFileSync(opts.out, content);
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 import_code_core7 = __toESM(require_dist2(), 1);
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 `nexrall-code login` to sign in again", "Your session keeps its place: /resume picks it back up"]
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, import_code_core6.eventsDir)(), ".jsonl");
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, import_code_core6.eventsPath)(r2.id));
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, import_code_core6.readEvents)(r2.id)
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, import_code_core6.writeEvents)(id, bundle.events);
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, import_code_core6.readEvents)("", { file: full }));
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, import_code_core6.eventsEnabled)() ? new import_code_core6.EventLog(id) : null;
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, import_code_core6.toolTarget)(name, input)
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, import_code_core6.MAX_FILE_EVENTS))
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, import_code_core6.toolTarget)(name, input)}`);
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, import_code_core6.readEvents)(stored.id, { last: import_code_core33.RECAP_EVENT_WINDOW }), { now: Date.now() });
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, import_code_core6.eventsEnabled)()) {
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, import_code_core6.readEvents)(sessionId, { last: import_code_core33.RECAP_EVENT_WINDOW }), { now: Date.now() });
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 login`) \u2014 watching uses your Nexrall account."));
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, import_code_core6.eventsEnabled)()) {
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 login`) \u2014 pushes find this session through your account.")
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, import_code_core3.parseScheduleChatArg)(arg);
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, import_code_core3.scheduleListLines)(list, { cols: panelCols }))
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, import_code_core3.formatEvery)(s2.intervalMinutes)}${s2.nextRunAt ? `, next run ${(0, import_code_core3.fmtScheduleWhen)(s2.nextRunAt)}` : ""}.`
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, import_code_core3.scheduleRunsLines)(runs2, { cols: panelCols }))
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, import_code_core7.findFiles)(files, want);
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, import_code_core7.capLines)((0, import_code_core7.renderFile)(hit[0], { foldHunks: false })));
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, import_code_core7.summaryLines)(files));
110306
+ out((0, import_code_core8.summaryLines)(files));
110091
110307
  console.log();
110092
110308
  verification.noteDiffReviewed();
110093
- out((0, import_code_core7.renderAll)(files));
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, import_code_core7.findFiles)(files, want);
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, import_code_core8.isTestPath)(x2.path)) };
110123
- for (const line of (0, import_code_core8.formatReviewDetail)((0, import_code_core8.classifyFile)(hit[0], ctx), cols3))
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, import_code_core8.buildReviewQueue)(files, all ? Number.POSITIVE_INFINITY : import_code_core8.DEFAULT_PER_GROUP);
110129
- for (const line of (0, import_code_core8.formatReviewQueue)(q, cols3))
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, import_code_core6.eventsDir)()));
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, import_code_core6.eventsPath)(id, env3);
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("Login to your Nexrall account").action(authCommand);
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, import_code_core6.readEvents)(hit.id, { last: Number.isFinite(want) && want > 0 ? want : 20 });
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, import_code_core6.summarizeEvent)(ev)));
114787
+ console.log(" " + paint.dim((0, import_code_core7.summarizeEvent)(ev)));
114572
114788
  console.log(paint.dim(`
114573
- Raw JSONL: ${(0, import_code_core6.eventsPath)(hit.id)}`));
114789
+ Raw JSONL: ${(0, import_code_core7.eventsPath)(hit.id)}`));
114574
114790
  console.log();
114575
114791
  return;
114576
114792
  }