@tryarcanist/cli 0.1.207 → 0.1.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 (2) hide show
  1. package/dist/index.js +153 -15
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8,8 +8,9 @@ import { Command } from "commander";
8
8
  import { execFileSync, spawn } from "child_process";
9
9
  import { existsSync as existsSync2, rmSync } from "fs";
10
10
  import { mkdtemp, readFile, rm } from "fs/promises";
11
- import { tmpdir } from "os";
11
+ import { homedir as homedir2, tmpdir } from "os";
12
12
  import { join as join2 } from "path";
13
+ import { createInterface as createInterface2 } from "readline/promises";
13
14
 
14
15
  // src/api.ts
15
16
  import { createRequire } from "module";
@@ -648,7 +649,19 @@ var GROK_SUBSCRIPTION_CLI_CONFIG = {
648
649
  // "…"}} — so the credential fields live one level down and the token field
649
650
  // is named `key`. Token detection scans nested objects.
650
651
  tokenFields: ["access_token", "refresh_token", "key"],
651
- installHint: "Install the Grok CLI (https://x.ai/cli), or point at it with --grok-path <path> or ARCANIST_GROK_BIN."
652
+ installHint: "Install the Grok CLI (https://x.ai/cli), or point at it with --grok-path <path> or ARCANIST_GROK_BIN.",
653
+ // Official Grok Build installer: installs `grok` (plus an `agent` alias)
654
+ // under ~/.grok/bin, symlinks into ~/.local/bin or /usr/local/bin when
655
+ // writable, and appends PATH exports to shell rc files itself.
656
+ installer: {
657
+ command: "curl -fsSL https://x.ai/cli/install.sh | bash",
658
+ binCandidates: (home) => [
659
+ join2(home, ".grok", "bin", "grok"),
660
+ join2(home, ".local", "bin", "grok"),
661
+ "/usr/local/bin/grok"
662
+ ],
663
+ postInstallNote: "Grok CLI installed. Restart your shell if `grok` is not found on PATH later."
664
+ }
652
665
  };
653
666
  var CURSOR_SUBSCRIPTION_CLI_CONFIG = {
654
667
  harness: "cursor",
@@ -679,6 +692,15 @@ var CURSOR_SUBSCRIPTION_CLI_CONFIG = {
679
692
  { service: "cursor-access-token", field: "accessToken", required: true },
680
693
  { service: "cursor-refresh-token", field: "refreshToken", required: false }
681
694
  ]
695
+ },
696
+ // Official Cursor CLI installer: installs to ~/.local/bin. Installers have
697
+ // shipped the binary as both `cursor-agent` and `agent` (the 2026-07
698
+ // installer's success message says `agent`), so probe both — unambiguous
699
+ // name first, since `agent` is also Grok's alias.
700
+ installer: {
701
+ command: "curl https://cursor.com/install -fsS | bash",
702
+ binCandidates: (home) => [join2(home, ".local", "bin", "cursor-agent"), join2(home, ".local", "bin", "agent")],
703
+ postInstallNote: 'Cursor CLI installed to ~/.local/bin. Add it to your PATH for future shells: export PATH="$HOME/.local/bin:$PATH"'
682
704
  }
683
705
  };
684
706
  function subscriptionBasePath(harness) {
@@ -687,6 +709,69 @@ function subscriptionBasePath(harness) {
687
709
  function resolveVendorBin(config, optionPath) {
688
710
  return optionPath?.trim() || process.env[config.binEnvVar]?.trim() || config.defaultBin;
689
711
  }
712
+ var VendorBinaryMissingError = class extends CliError {
713
+ constructor(config, binPath) {
714
+ super("user", `Could not find the \`${binPath}\` executable.`, { hint: config.installHint });
715
+ this.name = "VendorBinaryMissingError";
716
+ }
717
+ };
718
+ function runVendorInstall(config, command) {
719
+ return new Promise((resolve2, reject) => {
720
+ const child = spawn("bash", ["-c", command], { stdio: ["inherit", 2, "inherit"] });
721
+ child.on("error", (err) => {
722
+ reject(
723
+ new CliError("user", `Failed to run the ${config.displayName} CLI installer: ${err.message}`, {
724
+ hint: config.installHint
725
+ })
726
+ );
727
+ });
728
+ child.on("close", (code) => {
729
+ if (code === 0) {
730
+ resolve2();
731
+ return;
732
+ }
733
+ reject(
734
+ new CliError("user", `The ${config.displayName} CLI installer exited with code ${code ?? "unknown"}.`, {
735
+ hint: config.installHint
736
+ })
737
+ );
738
+ });
739
+ });
740
+ }
741
+ async function offerVendorInstall(config, explicitBinPath) {
742
+ const installer = config.installer;
743
+ if (!installer || explicitBinPath) return null;
744
+ if (process.platform !== "linux" && process.platform !== "darwin") return null;
745
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return null;
746
+ console.error(`Could not find the \`${config.defaultBin}\` executable.`);
747
+ console.error(`Install it now by running: ${installer.command}`);
748
+ const rl = createInterface2({ input: process.stdin, output: process.stderr });
749
+ let answer;
750
+ try {
751
+ answer = (await rl.question("Proceed? [y/N] ")).trim().toLowerCase();
752
+ } finally {
753
+ rl.close();
754
+ }
755
+ if (answer !== "y" && answer !== "yes") return null;
756
+ await runVendorInstall(config, installer.command);
757
+ for (const candidate of installer.binCandidates(homedir2())) {
758
+ if (existsSync2(candidate)) {
759
+ if (installer.postInstallNote) console.error(installer.postInstallNote);
760
+ return candidate;
761
+ }
762
+ }
763
+ return config.defaultBin;
764
+ }
765
+ async function runVendorLoginWithInstallOffer(config, binPath, tempHome, explicitBinPath) {
766
+ try {
767
+ await runVendorLogin(config, binPath, tempHome);
768
+ } catch (err) {
769
+ if (!(err instanceof VendorBinaryMissingError)) throw err;
770
+ const installedBin = await offerVendorInstall(config, explicitBinPath);
771
+ if (!installedBin) throw err;
772
+ await runVendorLogin(config, installedBin, tempHome);
773
+ }
774
+ }
690
775
  function runVendorLogin(config, binPath, tempHome) {
691
776
  return new Promise((resolve2, reject) => {
692
777
  const child = spawn(binPath, config.loginArgs, {
@@ -695,7 +780,7 @@ function runVendorLogin(config, binPath, tempHome) {
695
780
  });
696
781
  child.on("error", (err) => {
697
782
  if (err.code === "ENOENT") {
698
- reject(new CliError("user", `Could not find the \`${binPath}\` executable.`, { hint: config.installHint }));
783
+ reject(new VendorBinaryMissingError(config, binPath));
699
784
  return;
700
785
  }
701
786
  reject(new CliError("user", `Failed to launch \`${binPath} ${config.loginArgs.join(" ")}\`: ${err.message}`));
@@ -773,6 +858,7 @@ async function setSubscriptionEnabled(apiConfig, harness, enabled) {
773
858
  async function agentSubscriptionLoginCommand(config, options, command) {
774
859
  const { config: apiConfig } = resolveBusinessContext(command, options);
775
860
  const binPath = resolveVendorBin(config, options.binPath);
861
+ const explicitBinPath = Boolean(options.binPath?.trim() || process.env[config.binEnvVar]?.trim());
776
862
  let tempHome;
777
863
  const handleSigint = () => {
778
864
  if (tempHome) {
@@ -788,11 +874,11 @@ async function agentSubscriptionLoginCommand(config, options, command) {
788
874
  const captureViaKeychain = process.platform === "darwin" && config.darwinKeychain !== void 0;
789
875
  let authJson;
790
876
  if (captureViaKeychain) {
791
- await runVendorLogin(config, binPath, null);
877
+ await runVendorLoginWithInstallOffer(config, binPath, null, explicitBinPath);
792
878
  authJson = readDarwinKeychainAuthJson(config);
793
879
  } else {
794
880
  tempHome = await mkdtemp(join2(tmpdir(), `arcanist-${config.harness}-`));
795
- await runVendorLogin(config, binPath, tempHome);
881
+ await runVendorLoginWithInstallOffer(config, binPath, tempHome, explicitBinPath);
796
882
  authJson = await readLoginAuthJson(config, tempHome);
797
883
  }
798
884
  const state = await apiFetch(
@@ -821,7 +907,7 @@ async function agentSubscriptionLoginCommand(config, options, command) {
821
907
  console.log(`Run \`arcanist ${config.harness} use on\` to start using it.`);
822
908
  }
823
909
  console.log(
824
- `Note: ${config.displayName} BYOS session execution is not live yet; the credential is stored for when it ships.`
910
+ `${config.displayName} sessions will use your subscription auth while the selector is on; run \`arcanist ${config.harness} use off\` to switch back.`
825
911
  );
826
912
  if (captureViaKeychain) {
827
913
  console.log(
@@ -899,18 +985,21 @@ var CODEX_AGENT_RUNTIME_BACKEND = "codex";
899
985
  var CLAUDE_CODE_AGENT_RUNTIME_BACKEND = "claude_code";
900
986
  var OPENCODE_AGENT_RUNTIME_BACKEND = "opencode";
901
987
  var CURSOR_AGENT_RUNTIME_BACKEND = "cursor";
988
+ var GROK_AGENT_RUNTIME_BACKEND = "grok";
902
989
  var AGENT_RUNTIME_BACKENDS = [
903
990
  CODEX_AGENT_RUNTIME_BACKEND,
904
991
  CLAUDE_CODE_AGENT_RUNTIME_BACKEND,
905
992
  OPENCODE_AGENT_RUNTIME_BACKEND,
906
- CURSOR_AGENT_RUNTIME_BACKEND
993
+ CURSOR_AGENT_RUNTIME_BACKEND,
994
+ GROK_AGENT_RUNTIME_BACKEND
907
995
  ];
908
996
  var AGENT_RUNTIME_BACKEND_NAMES = {
909
997
  [CODEX_AGENT_RUNTIME_BACKEND]: "Codex",
910
998
  [CLAUDE_CODE_AGENT_RUNTIME_BACKEND]: "Claude Code",
911
999
  // "opencode" is intentionally lowercase to match the project's brand name.
912
1000
  [OPENCODE_AGENT_RUNTIME_BACKEND]: "opencode",
913
- [CURSOR_AGENT_RUNTIME_BACKEND]: "Cursor"
1001
+ [CURSOR_AGENT_RUNTIME_BACKEND]: "Cursor",
1002
+ [GROK_AGENT_RUNTIME_BACKEND]: "Grok Build"
914
1003
  };
915
1004
  function isAgentRuntimeBackend(value) {
916
1005
  return AGENT_RUNTIME_BACKENDS.includes(value);
@@ -963,12 +1052,17 @@ var XaiModel = {
963
1052
  var CursorModel = {
964
1053
  Composer25: "composer-2.5"
965
1054
  };
1055
+ var GrokBuildModel = {
1056
+ Grok45Byos: "grok-4.5-byos",
1057
+ GrokComposer25Fast: "grok-composer-2.5-fast"
1058
+ };
966
1059
  var MODEL_PROVIDERS_SET = /* @__PURE__ */ new Set([
967
1060
  "openai",
968
1061
  "anthropic",
969
1062
  "baseten",
970
1063
  "xai",
971
- "cursor"
1064
+ "cursor",
1065
+ "grok"
972
1066
  ]);
973
1067
  var BACKEND_DESKTOP_IMAGE_FEEDBACK_CONFIGS = {
974
1068
  [CODEX_AGENT_RUNTIME_BACKEND]: {
@@ -1419,6 +1513,42 @@ var MODEL_REGISTRY = [
1419
1513
  // so none is recorded.
1420
1514
  costTracked: false,
1421
1515
  sessionStart: { eligible: true, isDefault: true }
1516
+ },
1517
+ {
1518
+ id: GrokBuildModel.Grok45Byos,
1519
+ name: "Grok 4.5 (Subscription)",
1520
+ provider: "grok",
1521
+ backends: [GROK_AGENT_RUNTIME_BACKEND],
1522
+ // Wire id `grok-4.5` (the CLI's subscription default) collides with the
1523
+ // xai/opencode registry id, so the Arcanist id is suffixed and the wire id
1524
+ // rides providerModelId. Billed through the user's Grok subscription via
1525
+ // the Grok Build CLI (cli-chat-proxy.grok.com); Arcanist does not meter it
1526
+ // (costTracked: false requires pricing to stay undefined). Context window
1527
+ // verified 2026-07-15 against https://docs.x.ai/docs/models (grok-4.5,
1528
+ // 500k). No reasoning config: the CLI's --reasoning-effort is documented
1529
+ // for reasoning models only and grok-4.5 is classified no-reasoning per
1530
+ // the xai registry entries above.
1531
+ providerModelId: "grok-4.5",
1532
+ contextWindow: 5e5,
1533
+ costTracked: false,
1534
+ sessionStart: { eligible: true, isDefault: true },
1535
+ // Internal probe (ARC-1704): hidden from the public model picker;
1536
+ // CLI/API-selectable by internal Arcanist businesses only.
1537
+ visibility: "internal_probe"
1538
+ },
1539
+ {
1540
+ id: GrokBuildModel.GrokComposer25Fast,
1541
+ name: "Grok Composer 2.5 Fast",
1542
+ provider: "grok",
1543
+ backends: [GROK_AGENT_RUNTIME_BACKEND],
1544
+ // Cursor's Composer 2.5 Fast served through Grok Build (verified in
1545
+ // `grok models` 0.2.101 under a live subscription, 2026-07-15). Wire id
1546
+ // matches the registry id, so no providerModelId. Context window is not
1547
+ // published for Composer variants (cursor.com/docs/models), so none is
1548
+ // recorded. Subscription-billed, not metered by Arcanist.
1549
+ costTracked: false,
1550
+ sessionStart: { eligible: true },
1551
+ visibility: "internal_probe"
1422
1552
  }
1423
1553
  ];
1424
1554
  var MODEL_PROVIDER_NAMES = {
@@ -1426,7 +1556,8 @@ var MODEL_PROVIDER_NAMES = {
1426
1556
  anthropic: "Anthropic",
1427
1557
  baseten: "Baseten",
1428
1558
  xai: "xAI",
1429
- cursor: "Cursor"
1559
+ cursor: "Cursor",
1560
+ grok: "Grok Build"
1430
1561
  };
1431
1562
  function buildSessionStartModelIdsByBackend() {
1432
1563
  const byBackend = Object.fromEntries(AGENT_RUNTIME_BACKENDS.map((backend) => [backend, []]));
@@ -1466,7 +1597,8 @@ var VALID_SESSION_START_MODEL_IDS_BY_BACKEND = {
1466
1597
  SESSION_START_MODEL_IDS_BY_BACKEND[CLAUDE_CODE_AGENT_RUNTIME_BACKEND]
1467
1598
  ),
1468
1599
  [OPENCODE_AGENT_RUNTIME_BACKEND]: new Set(SESSION_START_MODEL_IDS_BY_BACKEND[OPENCODE_AGENT_RUNTIME_BACKEND]),
1469
- [CURSOR_AGENT_RUNTIME_BACKEND]: new Set(SESSION_START_MODEL_IDS_BY_BACKEND[CURSOR_AGENT_RUNTIME_BACKEND])
1600
+ [CURSOR_AGENT_RUNTIME_BACKEND]: new Set(SESSION_START_MODEL_IDS_BY_BACKEND[CURSOR_AGENT_RUNTIME_BACKEND]),
1601
+ [GROK_AGENT_RUNTIME_BACKEND]: new Set(SESSION_START_MODEL_IDS_BY_BACKEND[GROK_AGENT_RUNTIME_BACKEND])
1470
1602
  };
1471
1603
  var MODEL_CONTEXT_WINDOWS = {
1472
1604
  ...Object.fromEntries(
@@ -1935,6 +2067,7 @@ var PHASES = [
1935
2067
  "review_listening",
1936
2068
  "completed",
1937
2069
  "superseded",
2070
+ "needs_you",
1938
2071
  "blocked",
1939
2072
  "failed",
1940
2073
  "stopped",
@@ -1943,6 +2076,7 @@ var PHASES = [
1943
2076
  var TERMINAL_PHASES_ARRAY = [
1944
2077
  "completed",
1945
2078
  "superseded",
2079
+ "needs_you",
1946
2080
  "blocked",
1947
2081
  "failed",
1948
2082
  "stopped",
@@ -1956,7 +2090,7 @@ var CHILD_SLOT_RELEASE_PHASES = new Set(
1956
2090
  TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "stopped")
1957
2091
  );
1958
2092
  var ARCHIVABLE_STALE_TERMINAL_PHASES_ARRAY = TERMINAL_PHASES_ARRAY.filter(
1959
- (phase) => phase !== "archived" && phase !== "blocked" && phase !== "stopped"
2093
+ (phase) => phase !== "archived" && phase !== "needs_you" && phase !== "blocked" && phase !== "stopped"
1960
2094
  );
1961
2095
  function isTerminalPhase(phase, _sessionKind) {
1962
2096
  return TERMINAL_PHASES.has(phase);
@@ -6101,7 +6235,9 @@ grok.command("login").description("Authenticate a Grok (xAI) subscription and st
6101
6235
  Runs the Grok CLI device-authorization login locally under a temporary HOME, then uploads the
6102
6236
  resulting auth.json to Arcanist (encrypted, per user) and activates the selector. Your workspace
6103
6237
  must have Grok subscription auth enabled. The credential is never written to your default ~/.grok.
6104
- Grok BYOS session execution is not live yet; the credential is stored for when it ships.
6238
+ While the selector is on, Grok Build sessions run on your subscription auth.
6239
+ If the grok CLI is not installed, you will be offered its official install script ([y/N] prompt,
6240
+ interactive terminals only).
6105
6241
 
6106
6242
  Examples:
6107
6243
  arcanist grok login
@@ -6125,8 +6261,10 @@ cursor.command("login").description("Authenticate a Cursor subscription and stor
6125
6261
  Runs the Cursor CLI login locally under a temporary HOME/CURSOR_CONFIG_DIR, then uploads the
6126
6262
  captured auth credential to Arcanist (encrypted, per user) and activates the selector. Your
6127
6263
  workspace must have Cursor subscription auth enabled. Cursor's login-token location is not
6128
- formally documented; if capture fails, use a Cursor API key in Settings instead. Cursor BYOS
6129
- session execution is not live yet; the credential is stored for when it ships.
6264
+ formally documented; if capture fails, use a Cursor API key in Settings instead. While the
6265
+ selector is on, Cursor sessions run on your subscription auth instead of a Cursor API key.
6266
+ If the cursor-agent CLI is not installed, you will be offered its official install script
6267
+ ([y/N] prompt, interactive terminals only).
6130
6268
 
6131
6269
  Examples:
6132
6270
  arcanist cursor login
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.207",
3
+ "version": "0.1.209",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {