@tryarcanist/cli 0.1.223 → 0.1.225
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/README.md +1 -28
- package/dist/index.js +165 -210
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -194,7 +194,6 @@ arcanist sessions create your-org/your-repo "refactor auth" --reasoning-effort x
|
|
|
194
194
|
arcanist sessions create your-org/your-repo "verify this automatically" --auto-verify
|
|
195
195
|
arcanist sessions create your-org/your-repo "fix release branch" --base-branch release/2026-06
|
|
196
196
|
arcanist sessions create your-org/your-repo "review the trace" --uploaded-file trace.txt
|
|
197
|
-
arcanist sessions create your-org/your-repo "verify the deployed change" --cold
|
|
198
197
|
arcanist sessions create your-org/your-repo "retry-safe create" --idempotency-key 1f0e6f1a-...
|
|
199
198
|
```
|
|
200
199
|
|
|
@@ -217,7 +216,7 @@ The CLI echoes the request value as `jiraIssue` in JSON output; pickup writeback
|
|
|
217
216
|
|
|
218
217
|
Repeatable `--uploaded-file <path>` flags attach local UTF-8 text files to the prompt. Uploaded file names come from the local basename; directory components are not sent.
|
|
219
218
|
|
|
220
|
-
|
|
219
|
+
Sessions always start from a fresh sandbox (the warm sandbox pool was removed).
|
|
221
220
|
|
|
222
221
|
`--idempotency-key <uuid>` is for manually retrying a create request that may have reached the server.
|
|
223
222
|
The CLI derives separate session and prompt idempotency keys from the provided value.
|
|
@@ -322,17 +321,6 @@ JSON mode returns `{sessions, nextCursor}`.
|
|
|
322
321
|
`--all` follows cursors until completion and returns `nextCursor: null`.
|
|
323
322
|
Search is metadata-only: generated titles and repo metadata.
|
|
324
323
|
|
|
325
|
-
### `arcanist sessions search <query>`
|
|
326
|
-
|
|
327
|
-
```bash
|
|
328
|
-
arcanist sessions search "architect agent"
|
|
329
|
-
arcanist sessions search "mcp debugging" --repo your-org/your-repo --json
|
|
330
|
-
arcanist sessions search "repo access" --status idle --scope business --limit 20 --cursor <cursor>
|
|
331
|
-
arcanist sessions search "repo access" --all --json
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
Uses the same metadata-only index and filters as `sessions list`: `--status`, `--scope`, `--repo`, `--limit`, `--cursor`, and `--all`.
|
|
335
|
-
|
|
336
324
|
### `arcanist sessions events <session-id>`
|
|
337
325
|
|
|
338
326
|
Reads canonical session replay events. Cursors are sequence-based.
|
|
@@ -367,15 +355,6 @@ arcanist sessions transcript abc123 --last 20
|
|
|
367
355
|
|
|
368
356
|
`--last <n>` renders only the last `n` stored transcript events after fetching the session export.
|
|
369
357
|
|
|
370
|
-
### `arcanist sessions watch <session-id>`
|
|
371
|
-
|
|
372
|
-
Watches session activity until the session becomes idle. For machine-readable streaming, prefer `arcanist sessions events --follow --json`.
|
|
373
|
-
|
|
374
|
-
```bash
|
|
375
|
-
arcanist sessions watch abc123
|
|
376
|
-
arcanist sessions watch abc123 --poll-interval 500
|
|
377
|
-
```
|
|
378
|
-
|
|
379
358
|
### `arcanist sessions usage <session-id>`
|
|
380
359
|
|
|
381
360
|
```bash
|
|
@@ -510,12 +489,6 @@ SESSION_ID=$(arcanist sessions create your-org/your-repo "audit dependency licen
|
|
|
510
489
|
arcanist sessions events "$SESSION_ID" --follow --json | jq -r 'select(.type == "assistant_message")'
|
|
511
490
|
```
|
|
512
491
|
|
|
513
|
-
Verify a freshly deployed change with a guaranteed-fresh sandbox:
|
|
514
|
-
|
|
515
|
-
```bash
|
|
516
|
-
arcanist sessions create your-org/your-repo "verify the new rate limiter is active" --cold --wait
|
|
517
|
-
```
|
|
518
|
-
|
|
519
492
|
For cron, prefer `ARCANIST_TOKEN` or a logged-in `~/.arcanist/config.json` over passing `--token` on the command line.
|
|
520
493
|
|
|
521
494
|
To capture the PR URL after a successful run, read the best-effort result fields from `create --wait --json`:
|
package/dist/index.js
CHANGED
|
@@ -5,11 +5,11 @@ import { createRequire as createRequire2 } from "module";
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/commands/agent-subscription.ts
|
|
8
|
-
import { execFileSync, spawn } from "child_process";
|
|
9
|
-
import { existsSync as existsSync2
|
|
10
|
-
import {
|
|
11
|
-
import { homedir as homedir2
|
|
12
|
-
import { join as
|
|
8
|
+
import { execFileSync, spawn as spawn2 } from "child_process";
|
|
9
|
+
import { existsSync as existsSync2 } from "fs";
|
|
10
|
+
import { readFile } from "fs/promises";
|
|
11
|
+
import { homedir as homedir2 } from "os";
|
|
12
|
+
import { join as join3 } from "path";
|
|
13
13
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
14
14
|
|
|
15
15
|
// src/api.ts
|
|
@@ -640,6 +640,71 @@ function isControlCharacter(char) {
|
|
|
640
640
|
return code < ASCII_FIRST_PRINTABLE || code === ASCII_DELETE;
|
|
641
641
|
}
|
|
642
642
|
|
|
643
|
+
// src/vendor-login.ts
|
|
644
|
+
import { spawn } from "child_process";
|
|
645
|
+
import { rmSync } from "fs";
|
|
646
|
+
import { mkdtemp, rm } from "fs/promises";
|
|
647
|
+
import { tmpdir } from "os";
|
|
648
|
+
import { join as join2 } from "path";
|
|
649
|
+
var VendorBinaryMissingError = class extends CliError {
|
|
650
|
+
constructor(binPath, hint) {
|
|
651
|
+
super("user", `Could not find the \`${binPath}\` executable.`, { hint });
|
|
652
|
+
this.name = "VendorBinaryMissingError";
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
function runVendorLoginProcess(spec) {
|
|
656
|
+
return new Promise((resolve2, reject) => {
|
|
657
|
+
const child = spawn(spec.binPath, spec.args, {
|
|
658
|
+
stdio: "inherit",
|
|
659
|
+
env: { ...process.env, ...spec.env }
|
|
660
|
+
});
|
|
661
|
+
child.on("error", (err) => {
|
|
662
|
+
if (err.code === "ENOENT") {
|
|
663
|
+
reject(new VendorBinaryMissingError(spec.binPath, spec.missingBinaryHint));
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
reject(new CliError("user", `Failed to launch \`${spec.binPath} ${spec.args.join(" ")}\`: ${err.message}`));
|
|
667
|
+
});
|
|
668
|
+
child.on("close", (code) => {
|
|
669
|
+
if (code === 0) {
|
|
670
|
+
resolve2();
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
reject(
|
|
674
|
+
new CliError("user", `\`${spec.binPath} ${spec.args.join(" ")}\` exited with code ${code ?? "unknown"}.`, {
|
|
675
|
+
hint: `Complete the ${spec.displayName} login, then re-run \`${spec.retryCommand}\`.`
|
|
676
|
+
})
|
|
677
|
+
);
|
|
678
|
+
});
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
async function withIsolatedLoginDir(prefix, fn) {
|
|
682
|
+
let tempDir;
|
|
683
|
+
const handleSigint = () => {
|
|
684
|
+
if (tempDir) {
|
|
685
|
+
try {
|
|
686
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
687
|
+
} catch {
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
process.exit(EXIT_CODE_INTERRUPTED);
|
|
691
|
+
};
|
|
692
|
+
process.on("SIGINT", handleSigint);
|
|
693
|
+
try {
|
|
694
|
+
tempDir = await mkdtemp(join2(tmpdir(), prefix));
|
|
695
|
+
return await fn(tempDir);
|
|
696
|
+
} finally {
|
|
697
|
+
try {
|
|
698
|
+
if (tempDir) {
|
|
699
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => {
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
} finally {
|
|
703
|
+
process.off("SIGINT", handleSigint);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
643
708
|
// src/commands/agent-subscription.ts
|
|
644
709
|
var GROK_SUBSCRIPTION_CLI_CONFIG = {
|
|
645
710
|
harness: "grok",
|
|
@@ -650,7 +715,7 @@ var GROK_SUBSCRIPTION_CLI_CONFIG = {
|
|
|
650
715
|
// terminal without a local browser.
|
|
651
716
|
loginArgs: ["login", "--device-auth"],
|
|
652
717
|
loginEnv: () => ({}),
|
|
653
|
-
authJsonCandidates: [
|
|
718
|
+
authJsonCandidates: [join3(".grok", "auth.json")],
|
|
654
719
|
// Grok Build's auth.json is issuer-keyed (observed 2026-07-15 against the
|
|
655
720
|
// installed CLI's embedded docs): {"https://accounts.x.ai/sign-in": {"key":
|
|
656
721
|
// "…"}} — so the credential fields live one level down and the token field
|
|
@@ -663,8 +728,8 @@ var GROK_SUBSCRIPTION_CLI_CONFIG = {
|
|
|
663
728
|
installer: {
|
|
664
729
|
command: "curl -fsSL https://x.ai/cli/install.sh | bash",
|
|
665
730
|
binCandidates: (home) => [
|
|
666
|
-
|
|
667
|
-
|
|
731
|
+
join3(home, ".grok", "bin", "grok"),
|
|
732
|
+
join3(home, ".local", "bin", "grok"),
|
|
668
733
|
"/usr/local/bin/grok"
|
|
669
734
|
],
|
|
670
735
|
postInstallNote: "Grok CLI installed. Restart your shell if `grok` is not found on PATH later."
|
|
@@ -680,14 +745,14 @@ var CURSOR_SUBSCRIPTION_CLI_CONFIG = {
|
|
|
680
745
|
// formally documented, so pin every documented override at the throwaway
|
|
681
746
|
// HOME and search the known spots afterwards.
|
|
682
747
|
loginEnv: (tempHome) => ({
|
|
683
|
-
CURSOR_CONFIG_DIR:
|
|
684
|
-
XDG_CONFIG_HOME:
|
|
748
|
+
CURSOR_CONFIG_DIR: join3(tempHome, ".cursor"),
|
|
749
|
+
XDG_CONFIG_HOME: join3(tempHome, ".config")
|
|
685
750
|
}),
|
|
686
751
|
authJsonCandidates: [
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
752
|
+
join3(".cursor", "auth.json"),
|
|
753
|
+
join3(".cursor", "cli-config.json"),
|
|
754
|
+
join3(".config", "cursor", "auth.json"),
|
|
755
|
+
join3(".config", "cursor", "cli-config.json")
|
|
691
756
|
],
|
|
692
757
|
tokenFields: ["accessToken", "access_token", "refreshToken", "refresh_token", "token"],
|
|
693
758
|
installHint: "Install the Cursor CLI (https://cursor.com/docs/cli/installation), or point at it with --cursor-path <path> or ARCANIST_CURSOR_AGENT_BIN. If login-token capture keeps failing, use a Cursor API key in Settings instead.",
|
|
@@ -705,7 +770,7 @@ var CURSOR_SUBSCRIPTION_CLI_CONFIG = {
|
|
|
705
770
|
// name first, since `agent` is also Grok's alias.
|
|
706
771
|
installer: {
|
|
707
772
|
command: "curl https://cursor.com/install -fsS | bash",
|
|
708
|
-
binCandidates: (home) => [
|
|
773
|
+
binCandidates: (home) => [join3(home, ".local", "bin", "cursor-agent"), join3(home, ".local", "bin", "agent")],
|
|
709
774
|
postInstallNote: 'Cursor CLI installed to ~/.local/bin. Add it to your PATH for future shells: export PATH="$HOME/.local/bin:$PATH"'
|
|
710
775
|
}
|
|
711
776
|
};
|
|
@@ -715,15 +780,9 @@ function subscriptionBasePath(harness) {
|
|
|
715
780
|
function resolveVendorBin(config, optionPath) {
|
|
716
781
|
return optionPath?.trim() || process.env[config.binEnvVar]?.trim() || config.defaultBin;
|
|
717
782
|
}
|
|
718
|
-
var VendorBinaryMissingError = class extends CliError {
|
|
719
|
-
constructor(config, binPath) {
|
|
720
|
-
super("user", `Could not find the \`${binPath}\` executable.`, { hint: config.installHint });
|
|
721
|
-
this.name = "VendorBinaryMissingError";
|
|
722
|
-
}
|
|
723
|
-
};
|
|
724
783
|
function runVendorInstall(config, command) {
|
|
725
784
|
return new Promise((resolve2, reject) => {
|
|
726
|
-
const child =
|
|
785
|
+
const child = spawn2("bash", ["-c", command], { stdio: ["inherit", 2, "inherit"] });
|
|
727
786
|
child.on("error", (err) => {
|
|
728
787
|
reject(
|
|
729
788
|
new CliError("user", `Failed to run the ${config.displayName} CLI installer: ${err.message}`, {
|
|
@@ -779,29 +838,13 @@ async function runVendorLoginWithInstallOffer(config, binPath, tempHome, explici
|
|
|
779
838
|
}
|
|
780
839
|
}
|
|
781
840
|
function runVendorLogin(config, binPath, tempHome) {
|
|
782
|
-
return
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
reject(new VendorBinaryMissingError(config, binPath));
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
reject(new CliError("user", `Failed to launch \`${binPath} ${config.loginArgs.join(" ")}\`: ${err.message}`));
|
|
793
|
-
});
|
|
794
|
-
child.on("close", (code) => {
|
|
795
|
-
if (code === 0) {
|
|
796
|
-
resolve2();
|
|
797
|
-
return;
|
|
798
|
-
}
|
|
799
|
-
reject(
|
|
800
|
-
new CliError("user", `\`${binPath} ${config.loginArgs.join(" ")}\` exited with code ${code ?? "unknown"}.`, {
|
|
801
|
-
hint: `Complete the ${config.displayName} login, then re-run \`arcanist ${config.harness} login\`.`
|
|
802
|
-
})
|
|
803
|
-
);
|
|
804
|
-
});
|
|
841
|
+
return runVendorLoginProcess({
|
|
842
|
+
displayName: config.displayName,
|
|
843
|
+
binPath,
|
|
844
|
+
args: config.loginArgs,
|
|
845
|
+
env: tempHome ? { HOME: tempHome, ...config.loginEnv(tempHome) } : {},
|
|
846
|
+
missingBinaryHint: config.installHint,
|
|
847
|
+
retryCommand: `arcanist ${config.harness} login`
|
|
805
848
|
});
|
|
806
849
|
}
|
|
807
850
|
function hasTokenField(value, tokenFields, depth = 0) {
|
|
@@ -838,7 +881,7 @@ function readDarwinKeychainAuthJson(config) {
|
|
|
838
881
|
}
|
|
839
882
|
async function readLoginAuthJson(config, tempHome) {
|
|
840
883
|
for (const candidate of config.authJsonCandidates) {
|
|
841
|
-
const path =
|
|
884
|
+
const path = join3(tempHome, candidate);
|
|
842
885
|
if (!existsSync2(path)) continue;
|
|
843
886
|
let raw;
|
|
844
887
|
try {
|
|
@@ -865,72 +908,51 @@ async function agentSubscriptionLoginCommand(config, options, command) {
|
|
|
865
908
|
const { config: apiConfig } = resolveBusinessContext(command, options);
|
|
866
909
|
const binPath = resolveVendorBin(config, options.binPath);
|
|
867
910
|
const explicitBinPath = Boolean(options.binPath?.trim() || process.env[config.binEnvVar]?.trim());
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
}
|
|
876
|
-
process.exit(EXIT_CODE_INTERRUPTED);
|
|
877
|
-
};
|
|
878
|
-
process.on("SIGINT", handleSigint);
|
|
879
|
-
try {
|
|
880
|
-
const captureViaKeychain = process.platform === "darwin" && config.darwinKeychain !== void 0;
|
|
881
|
-
let authJson;
|
|
882
|
-
if (captureViaKeychain) {
|
|
883
|
-
await runVendorLoginWithInstallOffer(config, binPath, null, explicitBinPath);
|
|
884
|
-
authJson = readDarwinKeychainAuthJson(config);
|
|
885
|
-
} else {
|
|
886
|
-
tempHome = await mkdtemp(join2(tmpdir(), `arcanist-${config.harness}-`));
|
|
911
|
+
const captureViaKeychain = process.platform === "darwin" && config.darwinKeychain !== void 0;
|
|
912
|
+
let authJson;
|
|
913
|
+
if (captureViaKeychain) {
|
|
914
|
+
await runVendorLoginWithInstallOffer(config, binPath, null, explicitBinPath);
|
|
915
|
+
authJson = readDarwinKeychainAuthJson(config);
|
|
916
|
+
} else {
|
|
917
|
+
authJson = await withIsolatedLoginDir(`arcanist-${config.harness}-`, async (tempHome) => {
|
|
887
918
|
await runVendorLoginWithInstallOffer(config, binPath, tempHome, explicitBinPath);
|
|
888
|
-
|
|
919
|
+
return readLoginAuthJson(config, tempHome);
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
const state = await apiFetch(
|
|
923
|
+
apiConfig,
|
|
924
|
+
`${subscriptionBasePath(config.harness)}/auth-json`,
|
|
925
|
+
{
|
|
926
|
+
method: "PUT",
|
|
927
|
+
body: JSON.stringify({ authJson })
|
|
889
928
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
929
|
+
);
|
|
930
|
+
let activated = false;
|
|
931
|
+
let activationError;
|
|
932
|
+
try {
|
|
933
|
+
await setSubscriptionEnabled(apiConfig, config.harness, true);
|
|
934
|
+
activated = true;
|
|
935
|
+
} catch (err) {
|
|
936
|
+
activationError = err instanceof Error ? err.message : String(err);
|
|
937
|
+
}
|
|
938
|
+
emit(command, options, { ...state, enabled: activated }, (payload) => {
|
|
939
|
+
console.log(
|
|
940
|
+
activated ? `${config.displayName} subscription auth saved and activated.` : `${config.displayName} subscription auth saved.`
|
|
897
941
|
);
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
activated = true;
|
|
903
|
-
} catch (err) {
|
|
904
|
-
activationError = err instanceof Error ? err.message : String(err);
|
|
942
|
+
if (payload.lastValidationStatus) console.log(`Status: ${payload.lastValidationStatus}`);
|
|
943
|
+
if (!activated) {
|
|
944
|
+
console.log(`Could not activate it automatically${activationError ? ` (${activationError})` : ""}.`);
|
|
945
|
+
console.log(`Run \`arcanist ${config.harness} use on\` to start using it.`);
|
|
905
946
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
if (payload.lastValidationStatus) console.log(`Status: ${payload.lastValidationStatus}`);
|
|
911
|
-
if (!activated) {
|
|
912
|
-
console.log(`Could not activate it automatically${activationError ? ` (${activationError})` : ""}.`);
|
|
913
|
-
console.log(`Run \`arcanist ${config.harness} use on\` to start using it.`);
|
|
914
|
-
}
|
|
947
|
+
console.log(
|
|
948
|
+
`${config.displayName} sessions will use your subscription auth while the selector is on; run \`arcanist ${config.harness} use off\` to switch back.`
|
|
949
|
+
);
|
|
950
|
+
if (captureViaKeychain) {
|
|
915
951
|
console.log(
|
|
916
|
-
|
|
952
|
+
`Your local ${config.defaultBin} login also remains active (Keychain-backed); run \`${config.defaultBin} logout\` to remove it.`
|
|
917
953
|
);
|
|
918
|
-
if (captureViaKeychain) {
|
|
919
|
-
console.log(
|
|
920
|
-
`Your local ${config.defaultBin} login also remains active (Keychain-backed); run \`${config.defaultBin} logout\` to remove it.`
|
|
921
|
-
);
|
|
922
|
-
}
|
|
923
|
-
});
|
|
924
|
-
} finally {
|
|
925
|
-
try {
|
|
926
|
-
if (tempHome) {
|
|
927
|
-
await rm(tempHome, { recursive: true, force: true }).catch(() => {
|
|
928
|
-
});
|
|
929
|
-
}
|
|
930
|
-
} finally {
|
|
931
|
-
process.off("SIGINT", handleSigint);
|
|
932
954
|
}
|
|
933
|
-
}
|
|
955
|
+
});
|
|
934
956
|
}
|
|
935
957
|
async function agentSubscriptionUseCommand(config, state, options, command) {
|
|
936
958
|
const normalized = state.trim().toLowerCase();
|
|
@@ -2108,11 +2130,8 @@ function formatTime(value) {
|
|
|
2108
2130
|
}
|
|
2109
2131
|
|
|
2110
2132
|
// src/commands/codex.ts
|
|
2111
|
-
import {
|
|
2112
|
-
import {
|
|
2113
|
-
import { mkdtemp as mkdtemp2, readFile as readFile2, rm as rm2 } from "fs/promises";
|
|
2114
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
2115
|
-
import { join as join3 } from "path";
|
|
2133
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
2134
|
+
import { join as join4 } from "path";
|
|
2116
2135
|
var CODEX_SUBSCRIPTION_PATH = "/api/settings/codex-subscription";
|
|
2117
2136
|
var CODEX_SUBSCRIPTION_AUTH_JSON_PATH = "/api/settings/codex-subscription/auth-json";
|
|
2118
2137
|
var CODEX_SUBSCRIPTION_ENABLED_PATH = "/api/settings/codex-subscription/enabled";
|
|
@@ -2128,33 +2147,13 @@ function resolveCodexPath(optionPath) {
|
|
|
2128
2147
|
return optionPath?.trim() || process.env.ARCANIST_CODEX_BIN?.trim() || "codex";
|
|
2129
2148
|
}
|
|
2130
2149
|
function runCodexDeviceLogin(codexPath, codexHome) {
|
|
2131
|
-
return
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
reject(
|
|
2139
|
-
new CliError("user", `Could not find the \`${codexPath}\` executable.`, {
|
|
2140
|
-
hint: "Install the Codex CLI, or point at it with --codex-path <path> or ARCANIST_CODEX_BIN."
|
|
2141
|
-
})
|
|
2142
|
-
);
|
|
2143
|
-
return;
|
|
2144
|
-
}
|
|
2145
|
-
reject(new CliError("user", `Failed to launch \`${codexPath} login --device-auth\`: ${err.message}`));
|
|
2146
|
-
});
|
|
2147
|
-
child.on("close", (code) => {
|
|
2148
|
-
if (code === 0) {
|
|
2149
|
-
resolve2();
|
|
2150
|
-
return;
|
|
2151
|
-
}
|
|
2152
|
-
reject(
|
|
2153
|
-
new CliError("user", `\`${codexPath} login --device-auth\` exited with code ${code ?? "unknown"}.`, {
|
|
2154
|
-
hint: "Complete the device approval in your browser, then re-run `arcanist codex login`."
|
|
2155
|
-
})
|
|
2156
|
-
);
|
|
2157
|
-
});
|
|
2150
|
+
return runVendorLoginProcess({
|
|
2151
|
+
displayName: "Codex",
|
|
2152
|
+
binPath: codexPath,
|
|
2153
|
+
args: ["login", "--device-auth"],
|
|
2154
|
+
env: { CODEX_HOME: codexHome },
|
|
2155
|
+
missingBinaryHint: "Install the Codex CLI, or point at it with --codex-path <path> or ARCANIST_CODEX_BIN.",
|
|
2156
|
+
retryCommand: "arcanist codex login"
|
|
2158
2157
|
});
|
|
2159
2158
|
}
|
|
2160
2159
|
async function setCodexSubscriptionEnabled(config, enabled) {
|
|
@@ -2166,64 +2165,44 @@ async function setCodexSubscriptionEnabled(config, enabled) {
|
|
|
2166
2165
|
async function codexLoginCommand(options, command) {
|
|
2167
2166
|
const { config } = resolveBusinessContext(command, options);
|
|
2168
2167
|
const codexPath = resolveCodexPath(options.codexPath);
|
|
2169
|
-
|
|
2170
|
-
const handleSigint = () => {
|
|
2171
|
-
if (codexHome) {
|
|
2172
|
-
try {
|
|
2173
|
-
rmSync2(codexHome, { recursive: true, force: true });
|
|
2174
|
-
} catch {
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
process.exit(EXIT_CODE_INTERRUPTED);
|
|
2178
|
-
};
|
|
2179
|
-
process.on("SIGINT", handleSigint);
|
|
2180
|
-
try {
|
|
2181
|
-
codexHome = await mkdtemp2(join3(tmpdir2(), "arcanist-codex-"));
|
|
2168
|
+
const authJson = await withIsolatedLoginDir("arcanist-codex-", async (codexHome) => {
|
|
2182
2169
|
await runCodexDeviceLogin(codexPath, codexHome);
|
|
2183
|
-
let
|
|
2170
|
+
let raw;
|
|
2184
2171
|
try {
|
|
2185
|
-
|
|
2172
|
+
raw = await readFile2(join4(codexHome, "auth.json"), "utf8");
|
|
2186
2173
|
} catch {
|
|
2187
2174
|
throw new CliError("user", "Codex login completed but no auth.json was written.", {
|
|
2188
2175
|
hint: "Verify `codex login --device-auth` succeeds on its own, then re-run `arcanist codex login`."
|
|
2189
2176
|
});
|
|
2190
2177
|
}
|
|
2191
|
-
if (!
|
|
2178
|
+
if (!raw.trim()) {
|
|
2192
2179
|
throw new CliError("user", "Codex login produced an empty auth.json.");
|
|
2193
2180
|
}
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
console.log(
|
|
2208
|
-
activated ? "Codex subscription auth saved and activated for OpenAI sessions." : "Codex subscription auth saved."
|
|
2209
|
-
);
|
|
2210
|
-
const status = describeCredentialStatus(payload);
|
|
2211
|
-
if (status) console.log(`Status: ${status}`);
|
|
2212
|
-
if (!activated) {
|
|
2213
|
-
console.log(`Could not activate it automatically${activationError ? ` (${activationError})` : ""}.`);
|
|
2214
|
-
console.log("Run `arcanist codex use on` to start using it.");
|
|
2215
|
-
}
|
|
2216
|
-
});
|
|
2217
|
-
} finally {
|
|
2218
|
-
try {
|
|
2219
|
-
if (codexHome) {
|
|
2220
|
-
await rm2(codexHome, { recursive: true, force: true }).catch(() => {
|
|
2221
|
-
});
|
|
2222
|
-
}
|
|
2223
|
-
} finally {
|
|
2224
|
-
process.off("SIGINT", handleSigint);
|
|
2225
|
-
}
|
|
2181
|
+
return raw;
|
|
2182
|
+
});
|
|
2183
|
+
const state = await apiFetch(config, CODEX_SUBSCRIPTION_AUTH_JSON_PATH, {
|
|
2184
|
+
method: "PUT",
|
|
2185
|
+
body: JSON.stringify({ authJson })
|
|
2186
|
+
});
|
|
2187
|
+
let activated = false;
|
|
2188
|
+
let activationError;
|
|
2189
|
+
try {
|
|
2190
|
+
await setCodexSubscriptionEnabled(config, true);
|
|
2191
|
+
activated = true;
|
|
2192
|
+
} catch (err) {
|
|
2193
|
+
activationError = err instanceof Error ? err.message : String(err);
|
|
2226
2194
|
}
|
|
2195
|
+
emit(command, options, { ...state, useCodexSubscription: activated }, (payload) => {
|
|
2196
|
+
console.log(
|
|
2197
|
+
activated ? "Codex subscription auth saved and activated for OpenAI sessions." : "Codex subscription auth saved."
|
|
2198
|
+
);
|
|
2199
|
+
const status = describeCredentialStatus(payload);
|
|
2200
|
+
if (status) console.log(`Status: ${status}`);
|
|
2201
|
+
if (!activated) {
|
|
2202
|
+
console.log(`Could not activate it automatically${activationError ? ` (${activationError})` : ""}.`);
|
|
2203
|
+
console.log("Run `arcanist codex use on` to start using it.");
|
|
2204
|
+
}
|
|
2205
|
+
});
|
|
2227
2206
|
}
|
|
2228
2207
|
async function codexUseCommand(state, options, command) {
|
|
2229
2208
|
const normalized = state.trim().toLowerCase();
|
|
@@ -4206,7 +4185,6 @@ async function createCommand(repoUrl, promptArg, options, command) {
|
|
|
4206
4185
|
if (jiraIssue) body.jiraIssue = jiraIssue;
|
|
4207
4186
|
if (continuePr) body.continuePrUrl = continuePr;
|
|
4208
4187
|
if (continueMode) body.continueMode = continueMode;
|
|
4209
|
-
if (options.cold) body.cold = true;
|
|
4210
4188
|
if (options.onboarding) body.onboarding = true;
|
|
4211
4189
|
const sessionData = await apiFetch(config, "/api/sessions", {
|
|
4212
4190
|
method: "POST",
|
|
@@ -5905,9 +5883,6 @@ async function listSessionsCommand(options, command) {
|
|
|
5905
5883
|
}
|
|
5906
5884
|
if (payload.nextCursor) console.log(`Next cursor: ${payload.nextCursor}`);
|
|
5907
5885
|
}
|
|
5908
|
-
async function searchSessionsCommand(query, options, command) {
|
|
5909
|
-
await listSessionsCommand({ ...options, search: query }, command);
|
|
5910
|
-
}
|
|
5911
5886
|
async function getSessionCommand(sessionId, options, command) {
|
|
5912
5887
|
const runtime = getRuntimeOptions(command, options);
|
|
5913
5888
|
const config = requireConfig(runtime);
|
|
@@ -6262,7 +6237,7 @@ function addCreateOptions(cmd) {
|
|
|
6262
6237
|
"--poll-interval <ms>",
|
|
6263
6238
|
"Polling interval in milliseconds while waiting",
|
|
6264
6239
|
String(DEFAULT_WATCH_POLL_INTERVAL_MS)
|
|
6265
|
-
).option("--idempotency-key <uuid>", "Request idempotency key for safe manual retries").option("--
|
|
6240
|
+
).option("--idempotency-key <uuid>", "Request idempotency key for safe manual retries").option("--onboarding", "Create an onboarding session that authors the repo's Arcanist configuration").addHelpText(
|
|
6266
6241
|
"after",
|
|
6267
6242
|
`
|
|
6268
6243
|
Examples:
|
|
@@ -6489,18 +6464,6 @@ JSON:
|
|
|
6489
6464
|
JSON mode returns {sessions, nextCursor}. --all follows cursors until nextCursor is null.
|
|
6490
6465
|
`
|
|
6491
6466
|
).action((options, command) => listSessionsCommand(options, command));
|
|
6492
|
-
sessions.command("search").description("Search sessions by title and repo metadata").argument("<query>", "Search query").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").option("--all", "Fetch all pages").addHelpText(
|
|
6493
|
-
"after",
|
|
6494
|
-
`
|
|
6495
|
-
Examples:
|
|
6496
|
-
arcanist sessions search "architect agent"
|
|
6497
|
-
arcanist sessions search "mcp debugging" --repo owner/repo --json
|
|
6498
|
-
arcanist sessions search "repo access" --all --json
|
|
6499
|
-
|
|
6500
|
-
JSON:
|
|
6501
|
-
JSON mode returns {sessions, nextCursor}. --all follows cursors until nextCursor is null.
|
|
6502
|
-
`
|
|
6503
|
-
).action((query, options, command) => searchSessionsCommand(query, options, command));
|
|
6504
6467
|
sessions.command("events").description("Read or follow session replay events").argument("<session-id>", "Session ID").option("--after-sequence <n>", "Return events after this sequence").option("--after <n>", "Alias for --after-sequence").option("--before-sequence <n>", "Return events before this sequence").option("--before <n>", "Alias for --before-sequence").option("--prompt-id <id>", "Filter events by prompt ID").option("--limit <n>", "Maximum events to return").option("--follow", "Follow events until the session is idle").option("--poll-interval <ms>", "Polling interval in milliseconds", String(DEFAULT_WATCH_POLL_INTERVAL_MS)).addHelpText(
|
|
6505
6468
|
"after",
|
|
6506
6469
|
`
|
|
@@ -6525,14 +6488,6 @@ Examples:
|
|
|
6525
6488
|
arcanist sessions transcript <session-id> --last 20
|
|
6526
6489
|
`
|
|
6527
6490
|
).action((sessionId, options, command) => transcriptCommand(sessionId, options, command));
|
|
6528
|
-
sessions.command("watch").description("Watch session activity until it becomes idle").argument("<session-id>", "Session ID").option("--poll-interval <ms>", "Polling interval in milliseconds", String(DEFAULT_WATCH_POLL_INTERVAL_MS)).addHelpText(
|
|
6529
|
-
"after",
|
|
6530
|
-
`
|
|
6531
|
-
Examples:
|
|
6532
|
-
arcanist sessions watch <session-id>
|
|
6533
|
-
arcanist sessions watch <session-id> --json
|
|
6534
|
-
`
|
|
6535
|
-
).action((sessionId, options, command) => watchCommand(sessionId, options, command));
|
|
6536
6491
|
sessions.command("usage").description("Get token usage for a session").argument("<session-id>", "Session ID").addHelpText(
|
|
6537
6492
|
"after",
|
|
6538
6493
|
`
|