@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.
- package/dist/index.js +153 -15
- 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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
6129
|
-
|
|
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
|