nexrall-code 0.5.207 → 0.5.209

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