@tryarcanist/cli 0.1.222 → 0.1.223
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 -140
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -976,58 +976,97 @@ async function agentSubscriptionLogoutCommand(config, options, command) {
|
|
|
976
976
|
);
|
|
977
977
|
}
|
|
978
978
|
|
|
979
|
-
//
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
// unset VAR) so the server rejects it as invalid instead of silently routing.
|
|
992
|
-
...options.model !== void 0 ? { model: options.model } : {}
|
|
993
|
-
})
|
|
994
|
-
});
|
|
995
|
-
if (!options.wait || trigger.cached) {
|
|
996
|
-
emit(command, options, trigger, (payload) => {
|
|
997
|
-
if (payload.cached) console.log(`Zeus review already completed: ${payload.verdict ?? "unknown"}.`);
|
|
998
|
-
else console.log(`Started Zeus review${payload.sessionId ? ` (${payload.sessionId})` : ""}.`);
|
|
999
|
-
});
|
|
1000
|
-
return;
|
|
979
|
+
// ../../shared/utils/timing.ts
|
|
980
|
+
function sleep(ms) {
|
|
981
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// src/status-poll.ts
|
|
985
|
+
async function pollUntilSettled(opts) {
|
|
986
|
+
const deadline = Date.now() + opts.timeoutMs;
|
|
987
|
+
while (Date.now() < deadline) {
|
|
988
|
+
const settled = await opts.fetchStatus();
|
|
989
|
+
if (settled !== null) return settled;
|
|
990
|
+
await sleep(opts.pollIntervalMs);
|
|
1001
991
|
}
|
|
1002
|
-
|
|
1003
|
-
emit(command, options, result, (payload) => {
|
|
1004
|
-
console.log(`Zeus review: ${sanitizeTerminalText(payload.verdict)}. ${payload.findings.length} finding(s).`);
|
|
1005
|
-
for (const finding of payload.findings) {
|
|
1006
|
-
console.log(
|
|
1007
|
-
`${sanitizeTerminalText(finding.severity)} ${sanitizeTerminalText(finding.file)}:${finding.line} - ${sanitizeTerminalText(finding.title)}`
|
|
1008
|
-
);
|
|
1009
|
-
}
|
|
1010
|
-
});
|
|
992
|
+
throw new CliError("server", opts.timeoutMessage);
|
|
1011
993
|
}
|
|
1012
|
-
function
|
|
1013
|
-
|
|
994
|
+
function assertSameAttempt(triggeredSessionId, settledSessionId, label) {
|
|
995
|
+
if (triggeredSessionId && settledSessionId !== triggeredSessionId) {
|
|
996
|
+
throw new CliError("conflict", `${label} completed for a different attempt; retry the command.`);
|
|
997
|
+
}
|
|
1014
998
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
999
|
+
|
|
1000
|
+
// ../../shared/session/phase.ts
|
|
1001
|
+
var PHASES = [
|
|
1002
|
+
"idle",
|
|
1003
|
+
"running",
|
|
1004
|
+
"waiting_for_input",
|
|
1005
|
+
"finalizing",
|
|
1006
|
+
"review_listening",
|
|
1007
|
+
"completed",
|
|
1008
|
+
"superseded",
|
|
1009
|
+
"needs_you",
|
|
1010
|
+
"blocked",
|
|
1011
|
+
"failed",
|
|
1012
|
+
"stopped",
|
|
1013
|
+
"archived"
|
|
1014
|
+
];
|
|
1015
|
+
var TERMINAL_PHASES_ARRAY = [
|
|
1016
|
+
"completed",
|
|
1017
|
+
"superseded",
|
|
1018
|
+
"needs_you",
|
|
1019
|
+
"blocked",
|
|
1020
|
+
"failed",
|
|
1021
|
+
"stopped",
|
|
1022
|
+
"archived"
|
|
1023
|
+
];
|
|
1024
|
+
var TERMINAL_PHASES = new Set(TERMINAL_PHASES_ARRAY);
|
|
1025
|
+
var TERMINAL_FOR_FALLBACK_POLLING_PHASES = new Set(
|
|
1026
|
+
TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "completed")
|
|
1027
|
+
);
|
|
1028
|
+
var CHILD_SLOT_RELEASE_PHASES = new Set(
|
|
1029
|
+
TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "stopped")
|
|
1030
|
+
);
|
|
1031
|
+
var ARCHIVABLE_STALE_TERMINAL_PHASES_ARRAY = TERMINAL_PHASES_ARRAY.filter(
|
|
1032
|
+
(phase) => phase !== "archived" && phase !== "needs_you" && phase !== "blocked" && phase !== "stopped"
|
|
1033
|
+
);
|
|
1034
|
+
function isTerminalPhase(phase, _sessionKind) {
|
|
1035
|
+
return TERMINAL_PHASES.has(phase);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// src/constants/watch.ts
|
|
1039
|
+
var MIN_WATCH_POLL_INTERVAL_MS = 250;
|
|
1040
|
+
var DEFAULT_WATCH_POLL_INTERVAL_MS = 1e3;
|
|
1041
|
+
var MAX_WATCH_POLL_INTERVAL_MS = 6e4;
|
|
1042
|
+
var WATCH_REPLAY_PAGE_SIZE = 200;
|
|
1043
|
+
function isWatchTerminal(phase) {
|
|
1044
|
+
return isTerminalPhase(phase);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// src/utils/poll-interval.ts
|
|
1048
|
+
function clampPollInterval(ms, minimum) {
|
|
1049
|
+
return Math.max(ms, minimum);
|
|
1050
|
+
}
|
|
1051
|
+
function parsePollInterval(raw, opts = {}) {
|
|
1052
|
+
const defaultMs = opts.defaultMs ?? DEFAULT_WATCH_POLL_INTERVAL_MS;
|
|
1053
|
+
const minMs = opts.minMs ?? MIN_WATCH_POLL_INTERVAL_MS;
|
|
1054
|
+
const maxMs = opts.maxMs === void 0 ? MAX_WATCH_POLL_INTERVAL_MS : opts.maxMs;
|
|
1055
|
+
if (!raw) return defaultMs;
|
|
1056
|
+
if (!/^\d+$/.test(raw)) {
|
|
1057
|
+
throw new CliError("user", "Polling interval must be a non-negative integer.");
|
|
1029
1058
|
}
|
|
1030
|
-
|
|
1059
|
+
const value = Number(raw);
|
|
1060
|
+
if (value < minMs || maxMs !== null && value > maxMs) {
|
|
1061
|
+
const range = maxMs === null ? `at least ${minMs}` : `between ${minMs} and ${maxMs}`;
|
|
1062
|
+
throw new CliError("user", `Polling interval must be ${range} milliseconds.`);
|
|
1063
|
+
}
|
|
1064
|
+
return value;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// src/utils/terminal-text.ts
|
|
1068
|
+
function sanitizeTerminalText(value) {
|
|
1069
|
+
return value.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, "").replace(/\u001b\[[0-?]*[ -\/]*[@-~]/g, "").replace(/[\u0000-\u001f\u007f]/g, "");
|
|
1031
1070
|
}
|
|
1032
1071
|
|
|
1033
1072
|
// src/commands/anubis.ts
|
|
@@ -1035,6 +1074,7 @@ var ANUBIS_POLL_INTERVAL_MS = 15e3;
|
|
|
1035
1074
|
var ANUBIS_WAIT_TIMEOUT_MS = 45 * 60 * 1e3;
|
|
1036
1075
|
async function anubisCommand(prUrl, options = {}, command) {
|
|
1037
1076
|
assertArcanistSessionMutationAllowed("anubis");
|
|
1077
|
+
const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: ANUBIS_POLL_INTERVAL_MS, maxMs: null }) : null;
|
|
1038
1078
|
const { config } = resolveBusinessContext(command, options);
|
|
1039
1079
|
const trigger = await apiFetch(config, "/api/anubis-runs", {
|
|
1040
1080
|
method: "POST",
|
|
@@ -1046,29 +1086,25 @@ async function anubisCommand(prUrl, options = {}, command) {
|
|
|
1046
1086
|
});
|
|
1047
1087
|
return;
|
|
1048
1088
|
}
|
|
1049
|
-
const result = await
|
|
1089
|
+
const result = await pollUntilSettled({
|
|
1090
|
+
pollIntervalMs: pollIntervalMs ?? ANUBIS_POLL_INTERVAL_MS,
|
|
1091
|
+
timeoutMs: ANUBIS_WAIT_TIMEOUT_MS,
|
|
1092
|
+
timeoutMessage: "Timed out waiting for Anubis results.",
|
|
1093
|
+
fetchStatus: async () => {
|
|
1094
|
+
const status = await apiFetch(
|
|
1095
|
+
config,
|
|
1096
|
+
`/api/anubis-runs/status?prUrl=${encodeURIComponent(prUrl)}`
|
|
1097
|
+
);
|
|
1098
|
+
if (status.sessionStatus === "active") return null;
|
|
1099
|
+
assertSameAttempt(trigger.sessionId, status.sessionId, "Anubis");
|
|
1100
|
+
return status;
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1050
1103
|
emit(command, options, result, (payload) => {
|
|
1051
1104
|
console.log(`Anubis: ${sanitizeTerminalText(payload.verdict ?? "could-not-verify")}.`);
|
|
1052
1105
|
if (payload.summary) console.log(sanitizeTerminalText(payload.summary));
|
|
1053
1106
|
});
|
|
1054
1107
|
}
|
|
1055
|
-
async function waitForAnubis(config, prUrl, sessionId) {
|
|
1056
|
-
const deadline = Date.now() + ANUBIS_WAIT_TIMEOUT_MS;
|
|
1057
|
-
while (Date.now() < deadline) {
|
|
1058
|
-
const status = await apiFetch(
|
|
1059
|
-
config,
|
|
1060
|
-
`/api/anubis-runs/status?prUrl=${encodeURIComponent(prUrl)}`
|
|
1061
|
-
);
|
|
1062
|
-
if (status.sessionStatus !== "active") {
|
|
1063
|
-
if (status.sessionId !== sessionId) {
|
|
1064
|
-
throw new CliError("conflict", "Anubis completed for a different attempt; retry the command.");
|
|
1065
|
-
}
|
|
1066
|
-
return status;
|
|
1067
|
-
}
|
|
1068
|
-
await new Promise((resolve2) => setTimeout(resolve2, ANUBIS_POLL_INTERVAL_MS));
|
|
1069
|
-
}
|
|
1070
|
-
throw new CliError("server", "Timed out waiting for Anubis results.");
|
|
1071
|
-
}
|
|
1072
1108
|
|
|
1073
1109
|
// src/commands/auth.ts
|
|
1074
1110
|
async function whoamiCommand(options, command) {
|
|
@@ -2226,58 +2262,6 @@ async function codexLogoutCommand(options, command) {
|
|
|
2226
2262
|
emit(command, options, payload, () => console.log("Codex subscription auth deactivated and cleared."));
|
|
2227
2263
|
}
|
|
2228
2264
|
|
|
2229
|
-
// ../../shared/utils/timing.ts
|
|
2230
|
-
function sleep(ms) {
|
|
2231
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2232
|
-
}
|
|
2233
|
-
|
|
2234
|
-
// ../../shared/session/phase.ts
|
|
2235
|
-
var PHASES = [
|
|
2236
|
-
"idle",
|
|
2237
|
-
"running",
|
|
2238
|
-
"waiting_for_input",
|
|
2239
|
-
"finalizing",
|
|
2240
|
-
"review_listening",
|
|
2241
|
-
"completed",
|
|
2242
|
-
"superseded",
|
|
2243
|
-
"needs_you",
|
|
2244
|
-
"blocked",
|
|
2245
|
-
"failed",
|
|
2246
|
-
"stopped",
|
|
2247
|
-
"archived"
|
|
2248
|
-
];
|
|
2249
|
-
var TERMINAL_PHASES_ARRAY = [
|
|
2250
|
-
"completed",
|
|
2251
|
-
"superseded",
|
|
2252
|
-
"needs_you",
|
|
2253
|
-
"blocked",
|
|
2254
|
-
"failed",
|
|
2255
|
-
"stopped",
|
|
2256
|
-
"archived"
|
|
2257
|
-
];
|
|
2258
|
-
var TERMINAL_PHASES = new Set(TERMINAL_PHASES_ARRAY);
|
|
2259
|
-
var TERMINAL_FOR_FALLBACK_POLLING_PHASES = new Set(
|
|
2260
|
-
TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "completed")
|
|
2261
|
-
);
|
|
2262
|
-
var CHILD_SLOT_RELEASE_PHASES = new Set(
|
|
2263
|
-
TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "stopped")
|
|
2264
|
-
);
|
|
2265
|
-
var ARCHIVABLE_STALE_TERMINAL_PHASES_ARRAY = TERMINAL_PHASES_ARRAY.filter(
|
|
2266
|
-
(phase) => phase !== "archived" && phase !== "needs_you" && phase !== "blocked" && phase !== "stopped"
|
|
2267
|
-
);
|
|
2268
|
-
function isTerminalPhase(phase, _sessionKind) {
|
|
2269
|
-
return TERMINAL_PHASES.has(phase);
|
|
2270
|
-
}
|
|
2271
|
-
|
|
2272
|
-
// src/constants/watch.ts
|
|
2273
|
-
var MIN_WATCH_POLL_INTERVAL_MS = 250;
|
|
2274
|
-
var DEFAULT_WATCH_POLL_INTERVAL_MS = 1e3;
|
|
2275
|
-
var MAX_WATCH_POLL_INTERVAL_MS = 6e4;
|
|
2276
|
-
var WATCH_REPLAY_PAGE_SIZE = 200;
|
|
2277
|
-
function isWatchTerminal(phase) {
|
|
2278
|
-
return isTerminalPhase(phase);
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
2265
|
// src/uploads.ts
|
|
2282
2266
|
import { readFile as readFile3 } from "fs/promises";
|
|
2283
2267
|
import { basename, extname } from "path";
|
|
@@ -2423,26 +2407,6 @@ function normalizeUploadedFileOptions(files) {
|
|
|
2423
2407
|
return Array.isArray(files) ? files : [files];
|
|
2424
2408
|
}
|
|
2425
2409
|
|
|
2426
|
-
// src/utils/poll-interval.ts
|
|
2427
|
-
function clampPollInterval(ms, minimum) {
|
|
2428
|
-
return Math.max(ms, minimum);
|
|
2429
|
-
}
|
|
2430
|
-
function parsePollInterval(raw, opts = {}) {
|
|
2431
|
-
const defaultMs = opts.defaultMs ?? DEFAULT_WATCH_POLL_INTERVAL_MS;
|
|
2432
|
-
const minMs = opts.minMs ?? MIN_WATCH_POLL_INTERVAL_MS;
|
|
2433
|
-
const maxMs = opts.maxMs === void 0 ? MAX_WATCH_POLL_INTERVAL_MS : opts.maxMs;
|
|
2434
|
-
if (!raw) return defaultMs;
|
|
2435
|
-
if (!/^\d+$/.test(raw)) {
|
|
2436
|
-
throw new CliError("user", "Polling interval must be a non-negative integer.");
|
|
2437
|
-
}
|
|
2438
|
-
const value = Number(raw);
|
|
2439
|
-
if (value < minMs || maxMs !== null && value > maxMs) {
|
|
2440
|
-
const range = maxMs === null ? `at least ${minMs}` : `between ${minMs} and ${maxMs}`;
|
|
2441
|
-
throw new CliError("user", `Polling interval must be ${range} milliseconds.`);
|
|
2442
|
-
}
|
|
2443
|
-
return value;
|
|
2444
|
-
}
|
|
2445
|
-
|
|
2446
2410
|
// ../../shared/session/no-change-outcome.ts
|
|
2447
2411
|
function isNoChangesPromptResult(result) {
|
|
2448
2412
|
return typeof result === "object" && result !== null && "noChanges" in result && result.noChanges === true;
|
|
@@ -4783,6 +4747,55 @@ function parseRespondConflict(err) {
|
|
|
4783
4747
|
};
|
|
4784
4748
|
}
|
|
4785
4749
|
|
|
4750
|
+
// src/commands/review.ts
|
|
4751
|
+
var PR_REVIEW_POLL_INTERVAL_MS = 1e4;
|
|
4752
|
+
var PR_REVIEW_WAIT_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
4753
|
+
async function reviewCommand(prUrl, options = {}, command) {
|
|
4754
|
+
assertArcanistSessionMutationAllowed("review");
|
|
4755
|
+
const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: PR_REVIEW_POLL_INTERVAL_MS, maxMs: null }) : null;
|
|
4756
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4757
|
+
const trigger = await apiFetch(config, "/api/pr-reviews", {
|
|
4758
|
+
method: "POST",
|
|
4759
|
+
body: JSON.stringify({
|
|
4760
|
+
prUrl,
|
|
4761
|
+
...options.focus ? { focus: options.focus } : {},
|
|
4762
|
+
// Send an explicitly passed model even when empty (e.g. --model "$VAR" with an
|
|
4763
|
+
// unset VAR) so the server rejects it as invalid instead of silently routing.
|
|
4764
|
+
...options.model !== void 0 ? { model: options.model } : {}
|
|
4765
|
+
})
|
|
4766
|
+
});
|
|
4767
|
+
if (!options.wait || trigger.cached) {
|
|
4768
|
+
emit(command, options, trigger, (payload) => {
|
|
4769
|
+
if (payload.cached) console.log(`Zeus review already completed: ${payload.verdict ?? "unknown"}.`);
|
|
4770
|
+
else console.log(`Started Zeus review${payload.sessionId ? ` (${payload.sessionId})` : ""}.`);
|
|
4771
|
+
});
|
|
4772
|
+
return;
|
|
4773
|
+
}
|
|
4774
|
+
const triggeredSessionId = trigger.sessionId ?? null;
|
|
4775
|
+
const result = await pollUntilSettled({
|
|
4776
|
+
pollIntervalMs: pollIntervalMs ?? PR_REVIEW_POLL_INTERVAL_MS,
|
|
4777
|
+
timeoutMs: PR_REVIEW_WAIT_TIMEOUT_MS,
|
|
4778
|
+
timeoutMessage: "Timed out waiting for Zeus review results.",
|
|
4779
|
+
fetchStatus: async () => {
|
|
4780
|
+
const status = await apiFetch(
|
|
4781
|
+
config,
|
|
4782
|
+
`/api/pr-reviews/status?prUrl=${encodeURIComponent(prUrl)}`
|
|
4783
|
+
);
|
|
4784
|
+
if (status.status !== "completed") return null;
|
|
4785
|
+
assertSameAttempt(triggeredSessionId, status.sessionId, "PR review");
|
|
4786
|
+
return { ...status, sessionId: status.sessionId ?? triggeredSessionId };
|
|
4787
|
+
}
|
|
4788
|
+
});
|
|
4789
|
+
emit(command, options, result, (payload) => {
|
|
4790
|
+
console.log(`Zeus review: ${sanitizeTerminalText(payload.verdict)}. ${payload.findings.length} finding(s).`);
|
|
4791
|
+
for (const finding of payload.findings) {
|
|
4792
|
+
console.log(
|
|
4793
|
+
`${sanitizeTerminalText(finding.severity)} ${sanitizeTerminalText(finding.file)}:${finding.line} - ${sanitizeTerminalText(finding.title)}`
|
|
4794
|
+
);
|
|
4795
|
+
}
|
|
4796
|
+
});
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4786
4799
|
// src/commands/sandbox.ts
|
|
4787
4800
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
4788
4801
|
import { dirname as dirname2 } from "path";
|
|
@@ -6391,7 +6404,7 @@ cursor.command("use <state>").description("Turn using your saved Cursor subscrip
|
|
|
6391
6404
|
cursor.command("status").description("Show whether your workspace is eligible and whether a Cursor subscription auth is saved").action((options, command) => agentSubscriptionStatusCommand(CURSOR_SUBSCRIPTION_CLI_CONFIG, options, command));
|
|
6392
6405
|
cursor.command("logout").description("Deactivate the selector and remove the stored Cursor subscription auth for your user").action((options, command) => agentSubscriptionLogoutCommand(CURSOR_SUBSCRIPTION_CLI_CONFIG, options, command));
|
|
6393
6406
|
var sessions = program.command("sessions").description("Session commands");
|
|
6394
|
-
program.command("review <pr-url>").description("Run a Zeus review for a GitHub pull request").option("--focus <text>", "Focus the review on a specific concern").option("--model <model>", "Pin the reviewer model (Arcanist members only); defaults to automatic routing").option("--wait", "Wait for the review and print its verdict and findings").addHelpText(
|
|
6407
|
+
program.command("review <pr-url>").description("Run a Zeus review for a GitHub pull request").option("--focus <text>", "Focus the review on a specific concern").option("--model <model>", "Pin the reviewer model (Arcanist members only); defaults to automatic routing").option("--wait", "Wait for the review and print its verdict and findings").option("--poll-interval <ms>", "Polling interval in milliseconds while waiting (default 10000)").addHelpText(
|
|
6395
6408
|
"after",
|
|
6396
6409
|
`
|
|
6397
6410
|
Examples:
|
|
@@ -6405,7 +6418,7 @@ a specific reviewer model instead (members only).
|
|
|
6405
6418
|
Zeus review is currently available to Arcanist members only.
|
|
6406
6419
|
`
|
|
6407
6420
|
).action((prUrl, options, command) => reviewCommand(prUrl, options, command));
|
|
6408
|
-
program.command("anubis <pr-url>").description("Run an Anubis QA verification for a GitHub pull request").option("--wait", "Wait for the verdict").addHelpText(
|
|
6421
|
+
program.command("anubis <pr-url>").description("Run an Anubis QA verification for a GitHub pull request").option("--wait", "Wait for the verdict").option("--poll-interval <ms>", "Polling interval in milliseconds while waiting (default 15000)").addHelpText(
|
|
6409
6422
|
"after",
|
|
6410
6423
|
`
|
|
6411
6424
|
Examples:
|