nexrall-code 0.5.61 → 0.5.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3160,8 +3160,8 @@ var require_auth = __commonJS({
3160
3160
  exports.updateTokens = updateTokens;
3161
3161
  var fs9 = __importStar(__require("fs"));
3162
3162
  var path6 = __importStar(__require("path"));
3163
- var os6 = __importStar(__require("os"));
3164
- var CONFIG_DIR = path6.join(os6.homedir(), ".nexrall");
3163
+ var os5 = __importStar(__require("os"));
3164
+ var CONFIG_DIR = path6.join(os5.homedir(), ".nexrall");
3165
3165
  var CONFIG_FILE = path6.join(CONFIG_DIR, "config.json");
3166
3166
  function ensureConfigDir() {
3167
3167
  if (!fs9.existsSync(CONFIG_DIR)) {
@@ -10677,7 +10677,7 @@ var require_sandbox = __commonJS({
10677
10677
  exports.parseSandboxConfig = parseSandboxConfig;
10678
10678
  exports.wrapCommandForSandbox = wrapCommandForSandbox;
10679
10679
  var child_process_1 = __require("child_process");
10680
- var os6 = __importStar(__require("os"));
10680
+ var os5 = __importStar(__require("os"));
10681
10681
  function parseSandboxConfig(raw) {
10682
10682
  if (raw === void 0 || raw === null || raw === false || raw === "off")
10683
10683
  return null;
@@ -10722,9 +10722,9 @@ var require_sandbox = __commonJS({
10722
10722
  return lines.join("\n");
10723
10723
  }
10724
10724
  function wrapCommandForSandbox(command, workDir, cfg) {
10725
- const platform3 = os6.platform();
10725
+ const platform3 = os5.platform();
10726
10726
  if (platform3 === "darwin") {
10727
- const writePaths = Array.from(/* @__PURE__ */ new Set([workDir, "/tmp", "/private/tmp", "/private/var/folders", os6.tmpdir(), ...cfg.writePaths]));
10727
+ const writePaths = Array.from(/* @__PURE__ */ new Set([workDir, "/tmp", "/private/tmp", "/private/var/folders", os5.tmpdir(), ...cfg.writePaths]));
10728
10728
  const profile = buildSeatbeltProfile(writePaths, cfg.network);
10729
10729
  return {
10730
10730
  command: `sandbox-exec -p ${shQuote(profile)} /bin/sh -c ${shQuote(command)}`,
@@ -11026,10 +11026,10 @@ var require_memory = __commonJS({
11026
11026
  exports.agentMemoryPreamble = agentMemoryPreamble;
11027
11027
  exports.compactMemoryIfNeeded = compactMemoryIfNeeded2;
11028
11028
  var fs9 = __importStar(__require("fs"));
11029
- var os6 = __importStar(__require("os"));
11029
+ var os5 = __importStar(__require("os"));
11030
11030
  var path6 = __importStar(__require("path"));
11031
11031
  var crypto2 = __importStar(__require("crypto"));
11032
- var MEMORY_ROOT = process.env.NEXRALL_MEMORY_DIR || path6.join(os6.homedir(), ".nexrall", "memory");
11032
+ var MEMORY_ROOT = process.env.NEXRALL_MEMORY_DIR || path6.join(os5.homedir(), ".nexrall", "memory");
11033
11033
  var GLOBAL_FILE = path6.join(MEMORY_ROOT, "global.md");
11034
11034
  function memoryFilePath(scope, workDir) {
11035
11035
  if (scope === "global" || !workDir)
@@ -11285,7 +11285,7 @@ var require_plugins = __commonJS({
11285
11285
  exports.pluginMcpServers = pluginMcpServers;
11286
11286
  var fs9 = __importStar(__require("fs"));
11287
11287
  var path6 = __importStar(__require("path"));
11288
- var os6 = __importStar(__require("os"));
11288
+ var os5 = __importStar(__require("os"));
11289
11289
  exports.FILE_CANDIDATES = {
11290
11290
  manifest: ["plugin.json", path6.join(".claude-plugin", "plugin.json")],
11291
11291
  hooks: ["hooks.json", path6.join("hooks", "hooks.json")],
@@ -11345,7 +11345,7 @@ var require_plugins = __commonJS({
11345
11345
  function loadPlugins3(workDir) {
11346
11346
  const out = /* @__PURE__ */ new Map();
11347
11347
  scanRoot(path6.join(workDir, ".nexrall", "plugins"), "project", out);
11348
- scanRoot(path6.join(os6.homedir(), ".nexrall", "plugins"), "global", out);
11348
+ scanRoot(path6.join(os5.homedir(), ".nexrall", "plugins"), "global", out);
11349
11349
  return [...out.values()];
11350
11350
  }
11351
11351
  function pluginAssetDirs(workDir, kind) {
@@ -11454,7 +11454,7 @@ var require_loader = __commonJS({
11454
11454
  exports.expandCommand = expandCommand;
11455
11455
  var fs9 = __importStar(__require("fs"));
11456
11456
  var path6 = __importStar(__require("path"));
11457
- var os6 = __importStar(__require("os"));
11457
+ var os5 = __importStar(__require("os"));
11458
11458
  var child_process_1 = __require("child_process");
11459
11459
  var index_1 = require_plugins();
11460
11460
  var BUILTIN_COMMANDS = [
@@ -11535,7 +11535,7 @@ var require_loader = __commonJS({
11535
11535
  function loadSlashCommands(workDir) {
11536
11536
  const out = /* @__PURE__ */ new Map();
11537
11537
  loadDir(path6.join(workDir, ".nexrall", "commands"), "project", out);
11538
- loadDir(path6.join(os6.homedir(), ".nexrall", "commands"), "global", out);
11538
+ loadDir(path6.join(os5.homedir(), ".nexrall", "commands"), "global", out);
11539
11539
  for (const dir of (0, index_1.pluginAssetDirs)(workDir, "commands"))
11540
11540
  loadDir(dir, "plugin", out);
11541
11541
  for (const cmd of BUILTIN_COMMANDS) {
@@ -11647,7 +11647,7 @@ var require_skills = __commonJS({
11647
11647
  exports.expandSkill = expandSkill2;
11648
11648
  var fs9 = __importStar(__require("fs"));
11649
11649
  var path6 = __importStar(__require("path"));
11650
- var os6 = __importStar(__require("os"));
11650
+ var os5 = __importStar(__require("os"));
11651
11651
  var index_1 = require_plugins();
11652
11652
  var loader_1 = require_loader();
11653
11653
  function parseFrontmatter(raw) {
@@ -11778,8 +11778,8 @@ var require_skills = __commonJS({
11778
11778
  const out = /* @__PURE__ */ new Map();
11779
11779
  loadFlatCommandDir(path6.join(workDir, ".nexrall", "commands"), "project", out);
11780
11780
  loadSkillDir(path6.join(workDir, ".nexrall", "skills"), "project", out);
11781
- loadFlatCommandDir(path6.join(os6.homedir(), ".nexrall", "commands"), "global", out);
11782
- loadSkillDir(path6.join(os6.homedir(), ".nexrall", "skills"), "global", out);
11781
+ loadFlatCommandDir(path6.join(os5.homedir(), ".nexrall", "commands"), "global", out);
11782
+ loadSkillDir(path6.join(os5.homedir(), ".nexrall", "skills"), "global", out);
11783
11783
  for (const dir of (0, index_1.pluginAssetDirs)(workDir, "commands"))
11784
11784
  loadFlatCommandDir(dir, "plugin", out);
11785
11785
  for (const dir of (0, index_1.pluginAssetDirs)(workDir, "skills"))
@@ -12743,7 +12743,7 @@ var require_executor = __commonJS({
12743
12743
  exports.executeTool = executeTool;
12744
12744
  var fs9 = __importStar(__require("fs"));
12745
12745
  var path6 = __importStar(__require("path"));
12746
- var os6 = __importStar(__require("os"));
12746
+ var os5 = __importStar(__require("os"));
12747
12747
  var https3 = __importStar(__require("https"));
12748
12748
  var http3 = __importStar(__require("http"));
12749
12749
  var dns = __importStar(__require("dns"));
@@ -12769,7 +12769,7 @@ var require_executor = __commonJS({
12769
12769
  var READ_CHARS_PER_TOKEN = 4;
12770
12770
  var MAX_READ_CHARS = MAX_READ_TOKENS * READ_CHARS_PER_TOKEN;
12771
12771
  var MAX_SPILL_BYTES = 20 * 1024 * 1024;
12772
- var SPILL_DIR = path6.join(os6.tmpdir(), "nexrall-code", "bash-output");
12772
+ var SPILL_DIR = path6.join(os5.tmpdir(), "nexrall-code", "bash-output");
12773
12773
  var BLOCKED_REGEXES = [
12774
12774
  // Filesystem destruction — match rm/chmod with any whitespace between flags.
12775
12775
  // Only root itself and BARE top-level system dirs are blocked; ordinary
@@ -13919,6 +13919,19 @@ ${diff3}${xfile}${sec}` };
13919
13919
  return false;
13920
13920
  return m2.slice(1).every((o) => Number(o) <= 255);
13921
13921
  }
13922
+ function fetchBlocklistCheck(parsedUrl) {
13923
+ const raw = process.env.NEXRALL_FETCH_BLOCKLIST;
13924
+ if (!raw)
13925
+ return null;
13926
+ const host = parsedUrl.hostname.toLowerCase().replace(/^\[|\]$/g, "");
13927
+ const entries = raw.split(",").map((s2) => s2.trim().toLowerCase()).filter(Boolean);
13928
+ for (const entry of entries) {
13929
+ if (host === entry || host.endsWith(`.${entry}`)) {
13930
+ return `Refusing to fetch "${host}" \u2014 blocked by NEXRALL_FETCH_BLOCKLIST (matched "${entry}").`;
13931
+ }
13932
+ }
13933
+ return null;
13934
+ }
13922
13935
  async function fetchUrl(input, _workDir2, _redirectCount = 0) {
13923
13936
  const url = typeof input.url === "string" ? input.url : "";
13924
13937
  if (!url)
@@ -13935,6 +13948,9 @@ ${diff3}${xfile}${sec}` };
13935
13948
  if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
13936
13949
  return { error: `Unsupported protocol: ${parsedUrl.protocol}. Only http and https are supported.` };
13937
13950
  }
13951
+ const blocklistError = fetchBlocklistCheck(parsedUrl);
13952
+ if (blocklistError)
13953
+ return { error: blocklistError };
13938
13954
  const ssrfError = await ssrfCheck(parsedUrl);
13939
13955
  if (ssrfError)
13940
13956
  return { error: ssrfError };
@@ -14682,7 +14698,7 @@ var require_agentTypes = __commonJS({
14682
14698
  exports.findAgentType = findAgentType;
14683
14699
  var fs9 = __importStar(__require("fs"));
14684
14700
  var path6 = __importStar(__require("path"));
14685
- var os6 = __importStar(__require("os"));
14701
+ var os5 = __importStar(__require("os"));
14686
14702
  var index_1 = require_plugins();
14687
14703
  var VALID_MEMORY_SCOPES = ["project", "user", "local"];
14688
14704
  function parseMemoryScope(v) {
@@ -15167,7 +15183,7 @@ var require_agentTypes = __commonJS({
15167
15183
  const out = /* @__PURE__ */ new Map();
15168
15184
  const warnings = [];
15169
15185
  loadDir(path6.join(workDir, ".nexrall", "agents"), "project", out, warnings);
15170
- loadDir(path6.join(os6.homedir(), ".nexrall", "agents"), "global", out, warnings);
15186
+ loadDir(path6.join(os5.homedir(), ".nexrall", "agents"), "global", out, warnings);
15171
15187
  for (const dir of (0, index_1.pluginAssetDirs)(workDir, "agents"))
15172
15188
  loadDir(dir, "plugin", out, warnings);
15173
15189
  for (const agent of BUILTIN_AGENTS) {
@@ -15275,7 +15291,7 @@ var require_rules = __commonJS({
15275
15291
  exports.evaluatePermission = evaluatePermission2;
15276
15292
  var fs9 = __importStar(__require("fs"));
15277
15293
  var path6 = __importStar(__require("path"));
15278
- var os6 = __importStar(__require("os"));
15294
+ var os5 = __importStar(__require("os"));
15279
15295
  function managedSettingsPath() {
15280
15296
  if (process.platform === "darwin")
15281
15297
  return "/Library/Application Support/Nexrall/managed-settings.json";
@@ -15298,7 +15314,7 @@ var require_rules = __commonJS({
15298
15314
  function loadSettings2(workDir) {
15299
15315
  const tiers = [
15300
15316
  managedSettingsPath(),
15301
- path6.join(os6.homedir(), ".nexrall", "settings.json"),
15317
+ path6.join(os5.homedir(), ".nexrall", "settings.json"),
15302
15318
  path6.join(workDir, ".nexrall", "settings.json"),
15303
15319
  path6.join(workDir, ".nexrall", "settings.local.json")
15304
15320
  ];
@@ -16879,7 +16895,15 @@ ${partial}` : "",
16879
16895
  "gpt-5.4": 922e3,
16880
16896
  "gpt-5.4-mini": 272e3,
16881
16897
  "gpt-4.1": 1047576,
16882
- "gpt-4o-mini": 128e3
16898
+ "gpt-4o-mini": 128e3,
16899
+ // DeepSeek, by real model id (documented — see backend/services/providers/
16900
+ // modelRegistry.js's own TODO(unverified-by-400): DeepSeek accepts an
16901
+ // oversized max_completion_tokens without rejecting it, so there was no 400
16902
+ // to measure the ceiling from the way the OpenAI rows above were).
16903
+ "deepseek-v4-pro": 1048576,
16904
+ "deepseek-v4-flash": 1048576,
16905
+ // Qwen (DashScope), by real model id (documented max input, same caveat).
16906
+ "qwen3.7-max": 991800
16883
16907
  };
16884
16908
  function contextWindowFor2(model) {
16885
16909
  return MODEL_CONTEXT_TOKENS[model ?? "turbo"] ?? 128e3;
@@ -18268,7 +18292,7 @@ var require_manager = __commonJS({
18268
18292
  exports.McpManager = void 0;
18269
18293
  var fs9 = __importStar(__require("fs"));
18270
18294
  var path6 = __importStar(__require("path"));
18271
- var os6 = __importStar(__require("os"));
18295
+ var os5 = __importStar(__require("os"));
18272
18296
  var client_1 = require_client2();
18273
18297
  var httpClient_1 = require_httpClient();
18274
18298
  var sseClient_1 = require_sseClient();
@@ -18296,7 +18320,7 @@ var require_manager = __commonJS({
18296
18320
  } catch {
18297
18321
  }
18298
18322
  const locations = [
18299
- path6.join(os6.homedir(), ".nexrall", "mcp.json"),
18323
+ path6.join(os5.homedir(), ".nexrall", "mcp.json"),
18300
18324
  path6.join(workDir, ".nexrall", "mcp.json")
18301
18325
  ];
18302
18326
  for (const loc of locations) {
@@ -18401,7 +18425,7 @@ var require_manager = __commonJS({
18401
18425
  // ─── Config file persistence (for the management UI) ──────────────────────────
18402
18426
  /** Path to an mcp.json for the given scope. */
18403
18427
  static configPath(workDir, scope) {
18404
- return scope === "user" ? path6.join(os6.homedir(), ".nexrall", "mcp.json") : path6.join(workDir, ".nexrall", "mcp.json");
18428
+ return scope === "user" ? path6.join(os5.homedir(), ".nexrall", "mcp.json") : path6.join(workDir, ".nexrall", "mcp.json");
18405
18429
  }
18406
18430
  static readConfigFile(p) {
18407
18431
  try {
@@ -18503,7 +18527,7 @@ var require_manager2 = __commonJS({
18503
18527
  exports.isMutatingTool = isMutatingTool;
18504
18528
  exports.isPotentiallyMutatingBash = isPotentiallyMutatingBash;
18505
18529
  var fs9 = __importStar(__require("fs"));
18506
- var os6 = __importStar(__require("os"));
18530
+ var os5 = __importStar(__require("os"));
18507
18531
  var path6 = __importStar(__require("path"));
18508
18532
  var crypto2 = __importStar(__require("crypto"));
18509
18533
  var child_process_1 = __require("child_process");
@@ -18562,7 +18586,7 @@ var require_manager2 = __commonJS({
18562
18586
  this.current = null;
18563
18587
  this.nextId = 1;
18564
18588
  const key = crypto2.createHash("sha1").update(path6.resolve(workDir) + "\0" + (scope ?? "")).digest("hex").slice(0, 16);
18565
- const base = process.env.NEXRALL_CHECKPOINT_DIR || path6.join(os6.homedir(), ".nexrall", "checkpoints");
18589
+ const base = process.env.NEXRALL_CHECKPOINT_DIR || path6.join(os5.homedir(), ".nexrall", "checkpoints");
18566
18590
  this.storeDir = path6.join(base, key);
18567
18591
  this.loadFromDisk();
18568
18592
  }
@@ -18885,13 +18909,13 @@ var require_modePolicy = __commonJS({
18885
18909
  return "confirm";
18886
18910
  if (mode === "plan")
18887
18911
  return "deny";
18888
- if (req.bypass)
18912
+ if (req.bypass || mode === "auto")
18889
18913
  return "allow";
18890
18914
  if (exports.FILE_WRITE_TOOLS.has(tool) || exports.FILE_MUTATE_TOOLS.has(tool)) {
18891
18915
  return mode === "ask" ? "ask" : "allow";
18892
18916
  }
18893
18917
  if (exports.BASH_TOOLS.has(tool)) {
18894
- return mode === "auto" ? "allow" : "ask";
18918
+ return "ask";
18895
18919
  }
18896
18920
  return "ask";
18897
18921
  }
@@ -18904,7 +18928,7 @@ var require_modePolicy = __commonJS({
18904
18928
  case "edit":
18905
18929
  return "edit \u2014 edits files freely; asks before shell commands";
18906
18930
  case "auto":
18907
- return "auto \u2014 edits and runs commands; still confirms irreversible actions";
18931
+ return "auto \u2014 runs every tool on its own; still confirms irreversible actions";
18908
18932
  }
18909
18933
  }
18910
18934
  function parseMode2(raw) {
@@ -19168,7 +19192,7 @@ var require_sources = __commonJS({
19168
19192
  exports.sourcesFileFor = sourcesFileFor2;
19169
19193
  var fs9 = __importStar(__require("fs"));
19170
19194
  var path6 = __importStar(__require("path"));
19171
- var os6 = __importStar(__require("os"));
19195
+ var os5 = __importStar(__require("os"));
19172
19196
  function managedSettingsPath() {
19173
19197
  if (process.platform === "darwin")
19174
19198
  return "/Library/Application Support/Nexrall/managed-settings.json";
@@ -19219,7 +19243,7 @@ var require_sources = __commonJS({
19219
19243
  function loadDeclaredSources2(workDir, opts = {}) {
19220
19244
  const out = /* @__PURE__ */ new Map();
19221
19245
  collect(managedSettingsPath(), "managed", out);
19222
- collect(path6.join(os6.homedir(), ".nexrall", "settings.json"), "user", out);
19246
+ collect(path6.join(os5.homedir(), ".nexrall", "settings.json"), "user", out);
19223
19247
  if (opts.includeProject) {
19224
19248
  collect(path6.join(workDir, ".nexrall", "settings.json"), "project", out);
19225
19249
  collect(path6.join(workDir, ".nexrall", "settings.local.json"), "project", out);
@@ -19274,7 +19298,7 @@ var require_sources = __commonJS({
19274
19298
  return true;
19275
19299
  }
19276
19300
  function sourcesFileFor2(scope, workDir) {
19277
- return scope === "user" ? path6.join(os6.homedir(), ".nexrall", "settings.json") : path6.join(workDir, ".nexrall", "settings.json");
19301
+ return scope === "user" ? path6.join(os5.homedir(), ".nexrall", "settings.json") : path6.join(workDir, ".nexrall", "settings.json");
19278
19302
  }
19279
19303
  }
19280
19304
  });
@@ -19339,7 +19363,7 @@ var require_installer = __commonJS({
19339
19363
  exports.updatePlugin = updatePlugin2;
19340
19364
  var fs9 = __importStar(__require("fs"));
19341
19365
  var path6 = __importStar(__require("path"));
19342
- var os6 = __importStar(__require("os"));
19366
+ var os5 = __importStar(__require("os"));
19343
19367
  var child_process_1 = __require("child_process");
19344
19368
  var client_1 = require_client();
19345
19369
  var index_1 = require_plugins();
@@ -19378,7 +19402,7 @@ var require_installer = __commonJS({
19378
19402
  if (!raw)
19379
19403
  throw new Error("Empty plugin source.");
19380
19404
  if (raw.startsWith("./") || raw.startsWith("../") || raw.startsWith("/") || raw.startsWith("~/")) {
19381
- const abs = path6.resolve(raw.replace(/^~(?=\/)/, os6.homedir()));
19405
+ const abs = path6.resolve(raw.replace(/^~(?=\/)/, os5.homedir()));
19382
19406
  return { kind: "local", repo: abs, raw };
19383
19407
  }
19384
19408
  let rest = raw;
@@ -19506,7 +19530,7 @@ var require_installer = __commonJS({
19506
19530
  }
19507
19531
  }
19508
19532
  function pluginsRoot(scope, workDir) {
19509
- return scope === "project" ? path6.join(workDir || process.cwd(), ".nexrall", "plugins") : path6.join(os6.homedir(), ".nexrall", "plugins");
19533
+ return scope === "project" ? path6.join(workDir || process.cwd(), ".nexrall", "plugins") : path6.join(os5.homedir(), ".nexrall", "plugins");
19510
19534
  }
19511
19535
  async function installPlugin2(spec, opts = {}) {
19512
19536
  const source2 = parsePluginSource2(spec);
@@ -19515,7 +19539,7 @@ var require_installer = __commonJS({
19515
19539
  if (patterns && source2.kind === "github" && !(0, sources_1.isSourceAllowed)(source2.raw, patterns)) {
19516
19540
  throw new Error(`Blocked by your organisation's policy: plugins may only be installed from ${patterns.join(", ")}.`);
19517
19541
  }
19518
- const tmpBase = fs9.mkdtempSync(path6.join(os6.tmpdir(), "nexrall-plugin-"));
19542
+ const tmpBase = fs9.mkdtempSync(path6.join(os5.tmpdir(), "nexrall-plugin-"));
19519
19543
  try {
19520
19544
  let repoRoot;
19521
19545
  if (source2.kind === "local") {
@@ -54131,7 +54155,7 @@ function stopSpinner(handle2) {
54131
54155
  import * as readline3 from "readline";
54132
54156
  import * as path4 from "path";
54133
54157
  import * as fs8 from "fs";
54134
- import * as os5 from "os";
54158
+ import * as os4 from "os";
54135
54159
  import * as crypto from "crypto";
54136
54160
  import { execSync } from "child_process";
54137
54161
  var import_code_core3 = __toESM(require_dist2(), 1);
@@ -55385,51 +55409,28 @@ function lastSession() {
55385
55409
  import * as fs4 from "fs";
55386
55410
  import * as https2 from "https";
55387
55411
  import * as path2 from "path";
55388
- import * as os3 from "os";
55389
- var API_LATEST = "https://api.nexrall.com/api/code/cli/latest";
55390
- var CDN_BASE = "https://cli.nexrall.com";
55412
+ import { fileURLToPath } from "url";
55413
+ var __dirname = path2.dirname(fileURLToPath(import.meta.url));
55414
+ var NPM_REGISTRY_LATEST = "https://registry.npmjs.org/nexrall-code/latest";
55391
55415
  function get2(url) {
55392
55416
  return new Promise((resolve3, reject) => {
55393
- https2.get(url, { headers: { "User-Agent": "nexrall-cli-updater" } }, (res) => {
55417
+ https2.get(url, { headers: { "User-Agent": "nexrall-cli-updater", Accept: "application/json" } }, (res) => {
55394
55418
  if (res.statusCode === 301 || res.statusCode === 302) {
55395
55419
  return get2(res.headers.location).then(resolve3).catch(reject);
55396
55420
  }
55421
+ if (res.statusCode !== 200) {
55422
+ res.resume();
55423
+ return reject(new Error(`npm registry returned HTTP ${res.statusCode}`));
55424
+ }
55397
55425
  let raw = "";
55398
55426
  res.on("data", (c) => raw += c.toString());
55399
55427
  res.on("end", () => resolve3(raw));
55400
55428
  }).on("error", reject);
55401
55429
  });
55402
55430
  }
55403
- function download(url, dest) {
55404
- return new Promise((resolve3, reject) => {
55405
- const doGet = (u) => {
55406
- https2.get(u, { headers: { "User-Agent": "nexrall-cli-updater" } }, (res) => {
55407
- if (res.statusCode === 301 || res.statusCode === 302) {
55408
- return doGet(res.headers.location);
55409
- }
55410
- if (res.statusCode !== 200) {
55411
- return reject(new Error(`Download failed: HTTP ${res.statusCode}`));
55412
- }
55413
- const tmpPath = dest + ".tmp";
55414
- const file = fs4.createWriteStream(tmpPath);
55415
- res.pipe(file);
55416
- file.on("finish", () => {
55417
- file.close();
55418
- fs4.renameSync(tmpPath, dest);
55419
- resolve3();
55420
- });
55421
- file.on("error", (err) => {
55422
- fs4.unlinkSync(tmpPath);
55423
- reject(err);
55424
- });
55425
- }).on("error", reject);
55426
- };
55427
- doGet(url);
55428
- });
55429
- }
55430
55431
  function getCurrentVersion() {
55431
55432
  try {
55432
- const pkg = path2.resolve(__dirname, "../../package.json");
55433
+ const pkg = path2.resolve(__dirname, "../package.json");
55433
55434
  return JSON.parse(fs4.readFileSync(pkg, "utf-8")).version ?? "0.0.0";
55434
55435
  } catch {
55435
55436
  return "0.0.0";
@@ -55446,8 +55447,31 @@ function semverGt(a, b) {
55446
55447
  }
55447
55448
  return false;
55448
55449
  }
55449
- function currentBinaryPath() {
55450
- return process.argv[1] ?? "";
55450
+ async function fetchLatestVersion() {
55451
+ const raw = await get2(NPM_REGISTRY_LATEST);
55452
+ const meta = JSON.parse(raw);
55453
+ return meta.version ?? "0.0.0";
55454
+ }
55455
+ function npmInstallGlobal() {
55456
+ return new Promise((resolve3) => {
55457
+ const { spawn } = __require("child_process");
55458
+ const child = spawn("npm", ["install", "-g", "nexrall-code"], { stdio: "pipe" });
55459
+ let stderr = "";
55460
+ child.stderr?.on("data", (c) => {
55461
+ stderr += c.toString();
55462
+ });
55463
+ child.on("close", (code) => {
55464
+ if (code === 0)
55465
+ return resolve3({ ok: true, usedSudo: false });
55466
+ if (/EACCES|permission denied/i.test(stderr)) {
55467
+ const sudo = spawn("sudo", ["npm", "install", "-g", "nexrall-code"], { stdio: "inherit" });
55468
+ sudo.on("close", (sudoCode) => resolve3({ ok: sudoCode === 0, usedSudo: true }));
55469
+ } else {
55470
+ console.error(source_default.dim(stderr.trim()));
55471
+ resolve3({ ok: false, usedSudo: false });
55472
+ }
55473
+ });
55474
+ });
55451
55475
  }
55452
55476
  async function updateCommand(opts) {
55453
55477
  const current = getCurrentVersion();
@@ -55457,17 +55481,15 @@ async function updateCommand(opts) {
55457
55481
  console.log(source_default.dim(` Current version: v${current}`));
55458
55482
  console.log();
55459
55483
  process.stdout.write(source_default.dim(" \u2192 Checking latest version\u2026"));
55460
- let meta;
55484
+ let latest;
55461
55485
  try {
55462
- meta = JSON.parse(await get2(API_LATEST));
55486
+ latest = await fetchLatestVersion();
55463
55487
  process.stdout.write("\r\x1B[K");
55464
55488
  } catch (e2) {
55465
55489
  process.stdout.write("\r\x1B[K");
55466
- console.error(source_default.red(" \u2717 Could not reach update server: ") + String(e2.message));
55490
+ console.error(source_default.red(" \u2717 Could not reach npm registry: ") + String(e2.message));
55467
55491
  process.exit(1);
55468
55492
  }
55469
- const latest = meta.version ?? "0.0.0";
55470
- const binaryUrl = `${CDN_BASE}/nex`;
55471
55493
  if (!semverGt(latest, current)) {
55472
55494
  console.log(source_default.green(` \u2713 Already up to date \u2014 v${current}`));
55473
55495
  console.log();
@@ -55497,70 +55519,15 @@ async function updateCommand(opts) {
55497
55519
  }
55498
55520
  console.log();
55499
55521
  }
55500
- const tmpFile = path2.join(os3.tmpdir(), `nex_update_${Date.now()}`);
55501
- process.stdout.write(source_default.dim(" \u2192 Downloading\u2026"));
55502
- try {
55503
- await download(binaryUrl, tmpFile);
55504
- fs4.chmodSync(tmpFile, 493);
55505
- process.stdout.write("\r\x1B[K");
55506
- console.log(source_default.dim(` \u2713 Downloaded v${latest}`));
55507
- } catch (e2) {
55508
- process.stdout.write("\r\x1B[K");
55509
- console.error(source_default.red(" \u2717 Download failed: ") + String(e2.message));
55510
- try {
55511
- fs4.unlinkSync(tmpFile);
55512
- } catch {
55513
- }
55522
+ process.stdout.write(source_default.dim(` \u2192 Running npm install -g nexrall-code\u2026`));
55523
+ const { ok, usedSudo } = await npmInstallGlobal();
55524
+ process.stdout.write("\r\x1B[K");
55525
+ if (!ok) {
55526
+ console.error(source_default.red(" \u2717 Update failed. Run manually:"));
55527
+ console.error(source_default.dim(" npm install -g nexrall-code"));
55514
55528
  process.exit(1);
55515
55529
  }
55516
- const binPath = currentBinaryPath();
55517
- if (!binPath || !fs4.existsSync(binPath)) {
55518
- const fallbacks = ["/usr/local/bin/nex", `${os3.homedir()}/.local/bin/nex`];
55519
- const found = fallbacks.find((p) => fs4.existsSync(p));
55520
- if (!found) {
55521
- console.log(source_default.yellow(" \u26A0 Could not locate nex binary to replace."));
55522
- console.log(source_default.dim(` Downloaded binary is at: ${tmpFile}`));
55523
- console.log(source_default.dim(" Move it manually: sudo mv " + tmpFile + " /usr/local/bin/nex"));
55524
- return;
55525
- }
55526
- try {
55527
- fs4.copyFileSync(tmpFile, found);
55528
- fs4.chmodSync(found, 493);
55529
- console.log(source_default.green(` \u2713 Updated \u2192 ${found}`));
55530
- } catch {
55531
- console.log(source_default.yellow(` \u26A0 No write permission to ${found}. Trying sudo\u2026`));
55532
- const { spawnSync } = await import("child_process");
55533
- const r2 = spawnSync("sudo", ["cp", tmpFile, found], { stdio: "inherit" });
55534
- if (r2.status !== 0) {
55535
- console.error(source_default.red(" \u2717 Update failed. Run manually:"));
55536
- console.error(source_default.dim(` sudo cp ${tmpFile} ${found} && sudo chmod +x ${found}`));
55537
- process.exit(1);
55538
- }
55539
- spawnSync("sudo", ["chmod", "+x", found], { stdio: "inherit" });
55540
- console.log(source_default.green(` \u2713 Updated \u2192 ${found}`));
55541
- }
55542
- } else {
55543
- try {
55544
- fs4.copyFileSync(tmpFile, binPath);
55545
- fs4.chmodSync(binPath, 493);
55546
- console.log(source_default.green(` \u2713 Updated \u2192 ${binPath}`));
55547
- } catch {
55548
- const { spawnSync } = await import("child_process");
55549
- console.log(source_default.yellow(` \u26A0 No write permission. Trying sudo\u2026`));
55550
- const r2 = spawnSync("sudo", ["cp", tmpFile, binPath], { stdio: "inherit" });
55551
- if (r2.status !== 0) {
55552
- console.error(source_default.red(" \u2717 Update failed. Run manually:"));
55553
- console.error(source_default.dim(` sudo cp ${tmpFile} ${binPath} && sudo chmod +x ${binPath}`));
55554
- process.exit(1);
55555
- }
55556
- spawnSync("sudo", ["chmod", "+x", binPath], { stdio: "inherit" });
55557
- console.log(source_default.green(` \u2713 Updated \u2192 ${binPath}`));
55558
- }
55559
- }
55560
- try {
55561
- fs4.unlinkSync(tmpFile);
55562
- } catch {
55563
- }
55530
+ console.log(source_default.green(` \u2713 Updated \u2192 v${latest}${usedSudo ? " (via sudo)" : ""}`));
55564
55531
  console.log();
55565
55532
  console.log(source_default.bold(` Nexrall CLI v${latest} installed!`));
55566
55533
  console.log(source_default.dim(" Restart your terminal or run: exec $SHELL"));
@@ -55957,7 +55924,7 @@ __export(base_exports, {
55957
55924
  synchronizedOutput: () => synchronizedOutput
55958
55925
  });
55959
55926
  import process3 from "node:process";
55960
- import os4 from "node:os";
55927
+ import os3 from "node:os";
55961
55928
 
55962
55929
  // ../../node_modules/.pnpm/environment@1.1.0/node_modules/environment/index.js
55963
55930
  var isBrowser = globalThis.window?.document !== void 0;
@@ -56056,7 +56023,7 @@ var isOldWindows = () => {
56056
56023
  if (isBrowser || !isWindows2) {
56057
56024
  return false;
56058
56025
  }
56059
- const parts = os4.release().split(".");
56026
+ const parts = os3.release().split(".");
56060
56027
  const major = Number(parts[0]);
56061
56028
  const build = Number(parts[2] ?? 0);
56062
56029
  if (major < 10) {
@@ -64751,7 +64718,7 @@ var InkReadlineAdapter = class extends EventEmitter3 {
64751
64718
  };
64752
64719
 
64753
64720
  // src/commands/chat.ts
64754
- var CLI_VERSION = "0.5.61";
64721
+ var CLI_VERSION = "0.5.63";
64755
64722
  var MODEL_LABELS = {
64756
64723
  "claude-sonnet-5": "Claude Sonnet 5",
64757
64724
  "claude-opus-5": "Claude Opus 5",
@@ -64759,7 +64726,10 @@ var MODEL_LABELS = {
64759
64726
  "claude-haiku-4-5-20251001": "Claude Haiku 4.5",
64760
64727
  "gpt-5.4": "GPT-5.4",
64761
64728
  "gpt-5.4-mini": "GPT-5.4 Mini",
64762
- "gpt-4.1": "GPT-4.1"
64729
+ "gpt-4.1": "GPT-4.1",
64730
+ "deepseek-v4-pro": "DeepSeek V4 Pro",
64731
+ "deepseek-v4-flash": "DeepSeek V4 Flash",
64732
+ "qwen3.7-max": "Qwen3.7 Max"
64763
64733
  };
64764
64734
  var LEGACY_MODEL_ALIASES = {
64765
64735
  turbo: "claude-sonnet-5",
@@ -64775,7 +64745,10 @@ var SELECTABLE_MODELS = [
64775
64745
  "claude-fable-5",
64776
64746
  "gpt-5.4",
64777
64747
  "gpt-5.4-mini",
64778
- "gpt-4.1"
64748
+ "gpt-4.1",
64749
+ "deepseek-v4-pro",
64750
+ "deepseek-v4-flash",
64751
+ "qwen3.7-max"
64779
64752
  ];
64780
64753
  function normaliseModelId(model) {
64781
64754
  const raw = (model ?? "").trim();
@@ -64813,7 +64786,7 @@ function printBalanceNotice(balance, zero) {
64813
64786
  }
64814
64787
  function currentUserLabel() {
64815
64788
  try {
64816
- const u = os5.userInfo().username;
64789
+ const u = os4.userInfo().username;
64817
64790
  return u ? u.charAt(0).toUpperCase() + u.slice(1) : "there";
64818
64791
  } catch {
64819
64792
  return "there";
@@ -64822,7 +64795,7 @@ function currentUserLabel() {
64822
64795
  function collectEnv(workDir) {
64823
64796
  return {
64824
64797
  cwd: workDir,
64825
- platform: `${os5.platform()} ${os5.release()}`,
64798
+ platform: `${os4.platform()} ${os4.release()}`,
64826
64799
  shell: process.env.SHELL ?? process.env.ComSpec ?? "unknown",
64827
64800
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
64828
64801
  gitBranch: tryExec("git branch --show-current", workDir),
@@ -64849,7 +64822,7 @@ function readNexrallMd(workDir) {
64849
64822
  const memContent = (0, import_code_core3.readAllMemory)(workDir);
64850
64823
  if (memContent)
64851
64824
  parts.push(memContent);
64852
- const globalContent = tryRead(path4.join(os5.homedir(), ".nexrall", "nexrall.md"));
64825
+ const globalContent = tryRead(path4.join(os4.homedir(), ".nexrall", "nexrall.md"));
64853
64826
  if (globalContent)
64854
64827
  parts.push(`[Global instructions]
64855
64828
  ${globalContent}`);
@@ -66454,7 +66427,7 @@ function pluginSourceRemoveCommand(name, opts) {
66454
66427
 
66455
66428
  // src/index.ts
66456
66429
  var program2 = new Command();
66457
- program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.61").enablePositionalOptions();
66430
+ program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.63").enablePositionalOptions();
66458
66431
  program2.command("auth").description("Login to your Nexrall account").action(authCommand);
66459
66432
  program2.command("logout").description("Log out of your Nexrall account").action(logoutCommand);
66460
66433
  program2.command("update").description("Update nex to the latest version").option("-c, --check", "Check for updates without installing").option("-y, --yes", "Skip confirmation prompt").action(async (opts) => {
package/install.ps1 CHANGED
@@ -1,20 +1,20 @@
1
1
  # Nexrall Code CLI — Windows Installer
2
- # Usage: irm https://nexrall.com/install.ps1 | iex
2
+ # Usage: irm https://cli.nexrall.com/install.ps1 | iex
3
3
  #
4
4
  # What it does:
5
- # 1. Check Node.js >= 18
6
- # 2. Download latest nex binary from Nexrall CDN
7
- # 3. Install to %LOCALAPPDATA%\nexrall-cli\nex.js
8
- # 4. Add to PATH via User environment variable
9
- # 5. Create nex.cmd wrapper so "nex" works in CMD + PowerShell
5
+ # 1. Check Node.js >= 18 (npm ships with it)
6
+ # 2. Install the `nex` package globally from the npm registry
7
+ #
8
+ # Why npm and not a hand-rolled CDN/binary download: npm's registry is
9
+ # already the correct source of truth for "what is the latest version"
10
+ # `npm install -g nexrall-code` always resolves to the current `latest`
11
+ # dist-tag with zero extra infrastructure to keep in sync. A previous
12
+ # version of this script downloaded a binary from a custom CDN path whose
13
+ # "latest version" metadata was a hardcoded backend fallback that had
14
+ # drifted 5 months out of date — every install silently got a broken build.
10
15
 
11
16
  $ErrorActionPreference = 'Stop'
12
17
 
13
- $API_LATEST = 'https://api.nexrall.com/api/code/cli/latest'
14
- $INSTALL_DIR = "$env:LOCALAPPDATA\nexrall-cli"
15
- $BINARY_JS = "$INSTALL_DIR\nex.js"
16
- $WRAPPER_CMD = "$INSTALL_DIR\nex.cmd"
17
-
18
18
  Write-Host ""
19
19
  Write-Host " Nexrall Code CLI — Installer" -ForegroundColor Cyan -NoNewline
20
20
  Write-Host ""
@@ -37,63 +37,29 @@ try {
37
37
  exit 1
38
38
  }
39
39
 
40
- # ── Fetch latest version ───────────────────────────────────────────────────────
41
- Write-Host " → Checking latest version…" -ForegroundColor DarkGray
42
-
43
- try {
44
- $meta = Invoke-RestMethod -Uri $API_LATEST -UseBasicParsing
45
- $version = $meta.version
46
- $binaryUrl = $meta.url
47
- } catch {
48
- # Fallback
49
- $version = "latest"
50
- $binaryUrl = "https://media.nexrall.com/cli/nex"
51
- }
52
-
53
- Write-Host " → Version: $version" -ForegroundColor DarkGray
54
- Write-Host " → Download: $binaryUrl" -ForegroundColor DarkGray
55
-
56
- # ── Download binary ───────────────────────────────────────────────────────────
57
- Write-Host " → Downloading…" -ForegroundColor DarkGray
58
-
59
- New-Item -ItemType Directory -Force -Path $INSTALL_DIR | Out-Null
60
- $TMP = "$env:TEMP\nex_tmp.js"
61
-
62
40
  try {
63
- Invoke-WebRequest -Uri $binaryUrl -OutFile $TMP -UseBasicParsing
41
+ npm --version 2>$null | Out-Null
64
42
  } catch {
65
- Write-Host " ✗ Download failed: $_" -ForegroundColor Red
43
+ Write-Host " ✗ npm not found (it normally ships with Node.js). Please reinstall Node.js from https://nodejs.org" -ForegroundColor Red
66
44
  exit 1
67
45
  }
68
46
 
69
- # Sanity check
70
- $checkOutput = node $TMP --version 2>&1
47
+ # ── Install via npm ────────────────────────────────────────────────────────────
48
+ Write-Host " → Installing nexrall-code (latest) from npm…" -ForegroundColor DarkGray
49
+
50
+ npm install -g nexrall-code
71
51
  if ($LASTEXITCODE -ne 0) {
72
- Remove-Item $TMP -Force
73
- Write-Host " ✗ Downloaded binary failed sanity check." -ForegroundColor Red
52
+ Write-Host " ✗ npm install failed." -ForegroundColor Red
74
53
  exit 1
75
54
  }
55
+ Write-Host " ✓ Installed" -ForegroundColor Green
76
56
 
77
- # ── Install ───────────────────────────────────────────────────────────────────
78
- Move-Item $TMP $BINARY_JS -Force
79
-
80
- # Create nex.cmd wrapper so CMD/PowerShell can run "nex" directly
81
- $wrapperContent = "@echo off`r`nnode `"%~dp0nex.js`" %*"
82
- Set-Content -Path $WRAPPER_CMD -Value $wrapperContent -Encoding ASCII
83
-
84
- Write-Host " ✓ Installed to $INSTALL_DIR" -ForegroundColor Green
85
-
86
- # ── Add to PATH ───────────────────────────────────────────────────────────────
87
- $userPath = [System.Environment]::GetEnvironmentVariable('PATH', 'User')
88
- if ($userPath -notlike "*$INSTALL_DIR*") {
89
- [System.Environment]::SetEnvironmentVariable(
90
- 'PATH',
91
- "$INSTALL_DIR;$userPath",
92
- 'User'
93
- )
94
- Write-Host " ✓ Added to PATH (restart your terminal to apply)" -ForegroundColor Green
95
- } else {
96
- Write-Host " ✓ Already in PATH" -ForegroundColor Green
57
+ # ── Sanity check ───────────────────────────────────────────────────────────────
58
+ try {
59
+ $installedVersion = (nex --version 2>$null)
60
+ Write-Host " ✓ $installedVersion" -ForegroundColor Green
61
+ } catch {
62
+ Write-Host " ⚠ 'nex' installed but not found on PATH yet. Open a new terminal window." -ForegroundColor Yellow
97
63
  }
98
64
 
99
65
  # ── Done ──────────────────────────────────────────────────────────────────────
package/install.sh CHANGED
@@ -1,22 +1,22 @@
1
1
  #!/usr/bin/env sh
2
2
  # Nexrall Code CLI — installer
3
- # Usage: curl -fsSL https://nexrall.com/install.sh | sh
3
+ # Usage: curl -fsSL https://cli.nexrall.com/install.sh | sh
4
4
  #
5
5
  # What it does:
6
- # 1. Detect OS / arch
7
- # 2. Check Node.js >= 18
8
- # 3. Download the latest nex binary from Nexrall CDN
9
- # 4. Install to /usr/local/bin/nex (or ~/bin/nex if no write permission)
10
- # 5. Print next steps
6
+ # 1. Check Node.js >= 18 (npm ships with it — no separate check needed)
7
+ # 2. Install the `nex` package globally from the npm registry
8
+ #
9
+ # Why npm and not a hand-rolled CDN/binary download: npm's registry is
10
+ # already the correct source of truth for "what is the latest version" —
11
+ # `npm install -g nexrall-code` always resolves to the current `latest`
12
+ # dist-tag with zero extra infrastructure to keep in sync. A previous
13
+ # version of this script downloaded a binary from a custom CDN path whose
14
+ # "latest version" metadata was a hardcoded fallback in the backend that had
15
+ # drifted 5 months out of date — every install silently got a broken build.
16
+ # Delegating to npm removes that entire class of bug.
11
17
 
12
18
  set -e
13
19
 
14
- BINARY_NAME="nex"
15
- CDN_BASE="https://cli.nexrall.com"
16
- API_LATEST="https://api.nexrall.com/api/code/cli/latest"
17
- INSTALL_DIR="/usr/local/bin"
18
-
19
- # ── Colors ────────────────────────────────────────────────────────────────────
20
20
  bold=""
21
21
  dim=""
22
22
  green=""
@@ -37,29 +37,10 @@ success() { printf "${green} ✓${reset} %s\n" "$*"; }
37
37
  error() { printf "${red} ✗${reset} %s\n" "$*" >&2; }
38
38
  fatal() { error "$*"; exit 1; }
39
39
 
40
- # ── Banner ────────────────────────────────────────────────────────────────────
41
40
  printf "\n"
42
41
  printf "${bold} Nexrall Code CLI — Installer${reset}\n"
43
42
  printf "${dim} ────────────────────────────────────${reset}\n\n"
44
43
 
45
- # ── OS detection ──────────────────────────────────────────────────────────────
46
- OS=$(uname -s | tr '[:upper:]' '[:lower:]')
47
- ARCH=$(uname -m)
48
-
49
- case "$OS" in
50
- darwin) PLATFORM="macos" ;;
51
- linux) PLATFORM="linux" ;;
52
- *) fatal "Unsupported OS: $OS. Please install manually." ;;
53
- esac
54
-
55
- case "$ARCH" in
56
- x86_64|amd64) ARCH_TAG="x64" ;;
57
- arm64|aarch64) ARCH_TAG="arm64" ;;
58
- *) ARCH_TAG="x64" ;; # fallback
59
- esac
60
-
61
- info "Detected: ${PLATFORM} / ${ARCH_TAG}"
62
-
63
44
  # ── Node.js check ─────────────────────────────────────────────────────────────
64
45
  if ! command -v node >/dev/null 2>&1; then
65
46
  printf "\n"
@@ -78,82 +59,37 @@ fi
78
59
 
79
60
  success "Node.js $(node --version)"
80
61
 
81
- # ── Fetch latest version ───────────────────────────────────────────────────────
82
- info "Checking latest version…"
83
-
84
- if command -v curl >/dev/null 2>&1; then
85
- LATEST_JSON=$(curl -fsSL "$API_LATEST" 2>/dev/null || echo '{}')
86
- elif command -v wget >/dev/null 2>&1; then
87
- LATEST_JSON=$(wget -qO- "$API_LATEST" 2>/dev/null || echo '{}')
88
- else
89
- fatal "curl or wget is required to download the installer."
90
- fi
91
-
92
- # Parse version from JSON (simple sed, no jq dependency)
93
- VERSION=$(echo "$LATEST_JSON" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
94
- BINARY_URL=$(echo "$LATEST_JSON" | sed -n 's/.*"url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
95
-
96
- if [ -z "$VERSION" ] || [ -z "$BINARY_URL" ]; then
97
- # Fallback: construct URL directly (single universal Node.js binary)
98
- VERSION="latest"
99
- BINARY_URL="${CDN_BASE}/nex"
100
- fi
101
-
102
- info "Version: ${VERSION}"
103
- info "Download: ${BINARY_URL}"
104
-
105
- # ── Download binary ───────────────────────────────────────────────────────────
106
- TMP_FILE=$(mktemp /tmp/nex_XXXXXX)
107
-
108
- info "Downloading…"
109
- if command -v curl >/dev/null 2>&1; then
110
- curl -fsSL --progress-bar "$BINARY_URL" -o "$TMP_FILE"
111
- elif command -v wget >/dev/null 2>&1; then
112
- wget -q --show-progress "$BINARY_URL" -O "$TMP_FILE"
113
- fi
114
-
115
- chmod +x "$TMP_FILE"
116
-
117
- # Quick sanity check
118
- if ! node "$TMP_FILE" --version >/dev/null 2>&1; then
119
- rm -f "$TMP_FILE"
120
- fatal "Downloaded binary failed sanity check. Please try again."
62
+ if ! command -v npm >/dev/null 2>&1; then
63
+ fatal "npm not found (it normally ships with Node.js). Please reinstall Node.js from https://nodejs.org."
121
64
  fi
122
65
 
123
- # ── Install ───────────────────────────────────────────────────────────────────
124
- TARGET="${INSTALL_DIR}/${BINARY_NAME}"
66
+ # ── Install via npm ────────────────────────────────────────────────────────────
67
+ info "Installing nexrall-code (latest) from npm…"
125
68
 
126
- if [ -w "$INSTALL_DIR" ]; then
127
- mv "$TMP_FILE" "$TARGET"
128
- success "Installed to ${TARGET}"
69
+ if npm install -g nexrall-code; then
70
+ success "Installed"
129
71
  else
130
- # Try sudo
72
+ # Global install can fail on permissions (e.g. system Node without a user
73
+ # prefix configured). Retry with sudo before giving up.
131
74
  if command -v sudo >/dev/null 2>&1; then
132
- info "Need sudo to write to ${INSTALL_DIR}…"
133
- sudo mv "$TMP_FILE" "$TARGET"
134
- sudo chmod +x "$TARGET"
135
- success "Installed to ${TARGET} (via sudo)"
75
+ info "Retrying with sudo…"
76
+ sudo npm install -g nexrall-code || fatal "npm install failed. Try: npm config set prefix ~/.npm-global"
77
+ success "Installed (via sudo)"
136
78
  else
137
- # Fallback: ~/bin
138
- FALLBACK_DIR="$HOME/.local/bin"
139
- mkdir -p "$FALLBACK_DIR"
140
- mv "$TMP_FILE" "${FALLBACK_DIR}/${BINARY_NAME}"
141
- chmod +x "${FALLBACK_DIR}/${BINARY_NAME}"
142
- TARGET="${FALLBACK_DIR}/${BINARY_NAME}"
143
- success "Installed to ${TARGET}"
144
-
145
- # Check if fallback dir is on PATH
146
- case ":$PATH:" in
147
- *":${FALLBACK_DIR}:"*) ;;
148
- *)
149
- printf "\n"
150
- printf " ${dim}Add this to your shell profile (~/.zshrc or ~/.bashrc):${reset}\n"
151
- printf " ${cyan}export PATH=\"\$HOME/.local/bin:\$PATH\"${reset}\n"
152
- ;;
153
- esac
79
+ fatal "npm install failed. Try: npm config set prefix ~/.npm-global && npm install -g nexrall-code"
154
80
  fi
155
81
  fi
156
82
 
83
+ if ! command -v nex >/dev/null 2>&1; then
84
+ printf "\n"
85
+ error "'nex' installed but is not on PATH."
86
+ printf " Find npm's global bin dir with: ${cyan}npm bin -g${reset}\n"
87
+ printf " Then add it to PATH in your shell profile.\n\n"
88
+ exit 1
89
+ fi
90
+
91
+ success "$(nex --version 2>/dev/null | head -1)"
92
+
157
93
  # ── Done ──────────────────────────────────────────────────────────────────────
158
94
  printf "\n"
159
95
  printf "${bold}${green} Nexrall Code CLI installed successfully!${reset}\n\n"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.61",
3
+ "version": "0.5.63",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",
@@ -39,7 +39,7 @@
39
39
  "release": "node build.js && node scripts/upload-release.cjs"
40
40
  },
41
41
  "dependencies": {
42
- "@nexrall/code-core": "^1.4.35",
42
+ "@nexrall/code-core": "^1.4.37",
43
43
  "chalk": "^5.3.0",
44
44
  "commander": "^12.0.0",
45
45
  "diff": "^5.2.0",