@tryarcanist/cli 0.1.221 → 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 +304 -311
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -69,6 +69,15 @@ function parseApiErrorBody(body) {
|
|
|
69
69
|
return null;
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
+
function parseConflictBody(err) {
|
|
73
|
+
if (!(err instanceof ApiError) || err.status !== 409) return null;
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(err.body);
|
|
76
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
72
81
|
|
|
73
82
|
// src/errors.ts
|
|
74
83
|
var EXIT_CODE_INTERRUPTED = 130;
|
|
@@ -967,58 +976,97 @@ async function agentSubscriptionLogoutCommand(config, options, command) {
|
|
|
967
976
|
);
|
|
968
977
|
}
|
|
969
978
|
|
|
970
|
-
//
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
// unset VAR) so the server rejects it as invalid instead of silently routing.
|
|
983
|
-
...options.model !== void 0 ? { model: options.model } : {}
|
|
984
|
-
})
|
|
985
|
-
});
|
|
986
|
-
if (!options.wait || trigger.cached) {
|
|
987
|
-
emit(command, options, trigger, (payload) => {
|
|
988
|
-
if (payload.cached) console.log(`Zeus review already completed: ${payload.verdict ?? "unknown"}.`);
|
|
989
|
-
else console.log(`Started Zeus review${payload.sessionId ? ` (${payload.sessionId})` : ""}.`);
|
|
990
|
-
});
|
|
991
|
-
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);
|
|
992
991
|
}
|
|
993
|
-
|
|
994
|
-
emit(command, options, result, (payload) => {
|
|
995
|
-
console.log(`Zeus review: ${sanitizeTerminalText(payload.verdict)}. ${payload.findings.length} finding(s).`);
|
|
996
|
-
for (const finding of payload.findings) {
|
|
997
|
-
console.log(
|
|
998
|
-
`${sanitizeTerminalText(finding.severity)} ${sanitizeTerminalText(finding.file)}:${finding.line} - ${sanitizeTerminalText(finding.title)}`
|
|
999
|
-
);
|
|
1000
|
-
}
|
|
1001
|
-
});
|
|
992
|
+
throw new CliError("server", opts.timeoutMessage);
|
|
1002
993
|
}
|
|
1003
|
-
function
|
|
1004
|
-
|
|
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
|
+
}
|
|
1005
998
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
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.");
|
|
1058
|
+
}
|
|
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.`);
|
|
1020
1063
|
}
|
|
1021
|
-
|
|
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, "");
|
|
1022
1070
|
}
|
|
1023
1071
|
|
|
1024
1072
|
// src/commands/anubis.ts
|
|
@@ -1026,6 +1074,7 @@ var ANUBIS_POLL_INTERVAL_MS = 15e3;
|
|
|
1026
1074
|
var ANUBIS_WAIT_TIMEOUT_MS = 45 * 60 * 1e3;
|
|
1027
1075
|
async function anubisCommand(prUrl, options = {}, command) {
|
|
1028
1076
|
assertArcanistSessionMutationAllowed("anubis");
|
|
1077
|
+
const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: ANUBIS_POLL_INTERVAL_MS, maxMs: null }) : null;
|
|
1029
1078
|
const { config } = resolveBusinessContext(command, options);
|
|
1030
1079
|
const trigger = await apiFetch(config, "/api/anubis-runs", {
|
|
1031
1080
|
method: "POST",
|
|
@@ -1037,29 +1086,25 @@ async function anubisCommand(prUrl, options = {}, command) {
|
|
|
1037
1086
|
});
|
|
1038
1087
|
return;
|
|
1039
1088
|
}
|
|
1040
|
-
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
|
+
});
|
|
1041
1103
|
emit(command, options, result, (payload) => {
|
|
1042
1104
|
console.log(`Anubis: ${sanitizeTerminalText(payload.verdict ?? "could-not-verify")}.`);
|
|
1043
1105
|
if (payload.summary) console.log(sanitizeTerminalText(payload.summary));
|
|
1044
1106
|
});
|
|
1045
1107
|
}
|
|
1046
|
-
async function waitForAnubis(config, prUrl, sessionId) {
|
|
1047
|
-
const deadline = Date.now() + ANUBIS_WAIT_TIMEOUT_MS;
|
|
1048
|
-
while (Date.now() < deadline) {
|
|
1049
|
-
const status = await apiFetch(
|
|
1050
|
-
config,
|
|
1051
|
-
`/api/anubis-runs/status?prUrl=${encodeURIComponent(prUrl)}`
|
|
1052
|
-
);
|
|
1053
|
-
if (status.sessionStatus !== "active") {
|
|
1054
|
-
if (status.sessionId !== sessionId) {
|
|
1055
|
-
throw new CliError("conflict", "Anubis completed for a different attempt; retry the command.");
|
|
1056
|
-
}
|
|
1057
|
-
return status;
|
|
1058
|
-
}
|
|
1059
|
-
await new Promise((resolve2) => setTimeout(resolve2, ANUBIS_POLL_INTERVAL_MS));
|
|
1060
|
-
}
|
|
1061
|
-
throw new CliError("server", "Timed out waiting for Anubis results.");
|
|
1062
|
-
}
|
|
1063
1108
|
|
|
1064
1109
|
// src/commands/auth.ts
|
|
1065
1110
|
async function whoamiCommand(options, command) {
|
|
@@ -1842,6 +1887,26 @@ var SESSION_START_MODEL_PROVIDER_GROUPS_BY_SUBSCRIPTIONS = {
|
|
|
1842
1887
|
"true:true": buildSubscriptionAwareGroups({ codexSubscription: true, grokSubscription: true })
|
|
1843
1888
|
};
|
|
1844
1889
|
|
|
1890
|
+
// src/utils/pagination.ts
|
|
1891
|
+
var MAX_ALL_PAGES = 1e3;
|
|
1892
|
+
async function fetchPages(label, all, initialCursor, fetchPage) {
|
|
1893
|
+
const items = [];
|
|
1894
|
+
let cursor2 = initialCursor;
|
|
1895
|
+
let nextCursor = null;
|
|
1896
|
+
let pageCount = 0;
|
|
1897
|
+
do {
|
|
1898
|
+
pageCount += 1;
|
|
1899
|
+
if (all && pageCount > MAX_ALL_PAGES) {
|
|
1900
|
+
throw new CliError("user", `${label} exceeded ${MAX_ALL_PAGES} pages without reaching the end.`);
|
|
1901
|
+
}
|
|
1902
|
+
const page = await fetchPage(cursor2);
|
|
1903
|
+
items.push(...page.items);
|
|
1904
|
+
nextCursor = page.nextCursor;
|
|
1905
|
+
cursor2 = nextCursor ?? void 0;
|
|
1906
|
+
} while (all && nextCursor);
|
|
1907
|
+
return { items, nextCursor: all ? null : nextCursor };
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1845
1910
|
// ../../shared/github/repo-url.ts
|
|
1846
1911
|
function parseGithubRepoFullName(value) {
|
|
1847
1912
|
const shorthand = value.match(/^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/);
|
|
@@ -1853,6 +1918,32 @@ function parseGithubRepoFullName(value) {
|
|
|
1853
1918
|
return null;
|
|
1854
1919
|
}
|
|
1855
1920
|
|
|
1921
|
+
// src/git.ts
|
|
1922
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
1923
|
+
function git(args) {
|
|
1924
|
+
try {
|
|
1925
|
+
return execFileSync2("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
1926
|
+
} catch (err) {
|
|
1927
|
+
throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
function currentRepo() {
|
|
1931
|
+
const parsed = parseGithubRepoFullName(git(["remote", "get-url", "origin"]));
|
|
1932
|
+
if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
|
|
1933
|
+
return { owner: parsed.owner, repo: parsed.repo };
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
// src/utils/repo-arg.ts
|
|
1937
|
+
function parseRepoArg(value, argName = "repo") {
|
|
1938
|
+
const parsed = parseGithubRepoFullName(value);
|
|
1939
|
+
if (!parsed) throw new CliError("user", `${argName} must be owner/name or a GitHub URL`);
|
|
1940
|
+
return { owner: parsed.owner, repo: parsed.repo.replace(/\.git$/, "") };
|
|
1941
|
+
}
|
|
1942
|
+
function parseRepoArgOrCurrent(value, argName = "repo") {
|
|
1943
|
+
if (value === void 0) return currentRepo();
|
|
1944
|
+
return parseRepoArg(value, argName);
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1856
1947
|
// src/commands/automations.ts
|
|
1857
1948
|
var AUTOMATION_ERROR_HINTS = {
|
|
1858
1949
|
invalid_cron: "Use a standard five-field cron expression, for example: */15 * * * *",
|
|
@@ -1869,9 +1960,8 @@ var AUTOMATION_ERROR_HINTS = {
|
|
|
1869
1960
|
repo_skills_unavailable: "Arcanist could not verify the repo's skills right now. Retry once GitHub skill discovery is healthy.",
|
|
1870
1961
|
unknown_skill: "That leading slash skill does not exist in the selected repository."
|
|
1871
1962
|
};
|
|
1872
|
-
var MAX_ALL_PAGES = 1e3;
|
|
1873
1963
|
async function createAutomationCommand(repoUrl, promptArg, options, command) {
|
|
1874
|
-
const repo =
|
|
1964
|
+
const repo = parseRepoArg(repoUrl, "repo-url");
|
|
1875
1965
|
const prompt = await resolvePromptInput(promptArg, options);
|
|
1876
1966
|
const slackTeamId = options.slackTeamId?.trim();
|
|
1877
1967
|
const slackChannelId = options.slackChannelId?.trim();
|
|
@@ -1918,27 +2008,22 @@ async function createAutomationCommand(repoUrl, promptArg, options, command) {
|
|
|
1918
2008
|
async function listAutomationsCommand(options, command) {
|
|
1919
2009
|
const runtime = getRuntimeOptions(command, options);
|
|
1920
2010
|
const config = requireConfig(runtime);
|
|
1921
|
-
const items =
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
2011
|
+
const { items, nextCursor } = await fetchPages(
|
|
2012
|
+
"automations list --all",
|
|
2013
|
+
options.all === true,
|
|
2014
|
+
options.cursor,
|
|
2015
|
+
async (cursor2) => {
|
|
2016
|
+
const query = new URLSearchParams();
|
|
2017
|
+
if (options.limit) query.set("limit", options.limit);
|
|
2018
|
+
if (cursor2) query.set("cursor", cursor2);
|
|
2019
|
+
const payload = await automationApiFetch(
|
|
2020
|
+
config,
|
|
2021
|
+
`/api/automation/schedules${query.size ? `?${query.toString()}` : ""}`
|
|
2022
|
+
);
|
|
2023
|
+
return { items: payload.data.items, nextCursor: payload.data.nextCursor };
|
|
1929
2024
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
if (cursor2) query.set("cursor", cursor2);
|
|
1933
|
-
const payload = await automationApiFetch(
|
|
1934
|
-
config,
|
|
1935
|
-
`/api/automation/schedules${query.size ? `?${query.toString()}` : ""}`
|
|
1936
|
-
);
|
|
1937
|
-
items.push(...payload.data.items);
|
|
1938
|
-
nextCursor = payload.data.nextCursor;
|
|
1939
|
-
cursor2 = nextCursor ?? void 0;
|
|
1940
|
-
} while (options.all === true && nextCursor);
|
|
1941
|
-
const output = { items, nextCursor: options.all === true ? null : nextCursor };
|
|
2025
|
+
);
|
|
2026
|
+
const output = { items, nextCursor };
|
|
1942
2027
|
if (isJson(command, options)) {
|
|
1943
2028
|
writeJson(output);
|
|
1944
2029
|
return;
|
|
@@ -1970,14 +2055,6 @@ async function deleteAutomationCommand(id, options, command) {
|
|
|
1970
2055
|
}
|
|
1971
2056
|
console.log(`Deleted automation ${id}.`);
|
|
1972
2057
|
}
|
|
1973
|
-
function parseAutomationRepo(value) {
|
|
1974
|
-
if (/^git@/i.test(value) && !/^git@github\.com:/i.test(value)) {
|
|
1975
|
-
throw new CliError("user", "repo-url must be owner/name or a GitHub URL.");
|
|
1976
|
-
}
|
|
1977
|
-
const parsed = parseGithubRepoFullName(value);
|
|
1978
|
-
if (!parsed) throw new CliError("user", "repo-url must be owner/name or a GitHub URL.");
|
|
1979
|
-
return { owner: parsed.owner, repo: parsed.repo };
|
|
1980
|
-
}
|
|
1981
2058
|
function resolveAutomationModelBackend(rawModel, normalizedModel) {
|
|
1982
2059
|
if (normalizedModel) {
|
|
1983
2060
|
const backend = getAgentRuntimeBackendForModel(normalizedModel);
|
|
@@ -2185,58 +2262,6 @@ async function codexLogoutCommand(options, command) {
|
|
|
2185
2262
|
emit(command, options, payload, () => console.log("Codex subscription auth deactivated and cleared."));
|
|
2186
2263
|
}
|
|
2187
2264
|
|
|
2188
|
-
// ../../shared/utils/timing.ts
|
|
2189
|
-
function sleep(ms) {
|
|
2190
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2193
|
-
// ../../shared/session/phase.ts
|
|
2194
|
-
var PHASES = [
|
|
2195
|
-
"idle",
|
|
2196
|
-
"running",
|
|
2197
|
-
"waiting_for_input",
|
|
2198
|
-
"finalizing",
|
|
2199
|
-
"review_listening",
|
|
2200
|
-
"completed",
|
|
2201
|
-
"superseded",
|
|
2202
|
-
"needs_you",
|
|
2203
|
-
"blocked",
|
|
2204
|
-
"failed",
|
|
2205
|
-
"stopped",
|
|
2206
|
-
"archived"
|
|
2207
|
-
];
|
|
2208
|
-
var TERMINAL_PHASES_ARRAY = [
|
|
2209
|
-
"completed",
|
|
2210
|
-
"superseded",
|
|
2211
|
-
"needs_you",
|
|
2212
|
-
"blocked",
|
|
2213
|
-
"failed",
|
|
2214
|
-
"stopped",
|
|
2215
|
-
"archived"
|
|
2216
|
-
];
|
|
2217
|
-
var TERMINAL_PHASES = new Set(TERMINAL_PHASES_ARRAY);
|
|
2218
|
-
var TERMINAL_FOR_FALLBACK_POLLING_PHASES = new Set(
|
|
2219
|
-
TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "completed")
|
|
2220
|
-
);
|
|
2221
|
-
var CHILD_SLOT_RELEASE_PHASES = new Set(
|
|
2222
|
-
TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "stopped")
|
|
2223
|
-
);
|
|
2224
|
-
var ARCHIVABLE_STALE_TERMINAL_PHASES_ARRAY = TERMINAL_PHASES_ARRAY.filter(
|
|
2225
|
-
(phase) => phase !== "archived" && phase !== "needs_you" && phase !== "blocked" && phase !== "stopped"
|
|
2226
|
-
);
|
|
2227
|
-
function isTerminalPhase(phase, _sessionKind) {
|
|
2228
|
-
return TERMINAL_PHASES.has(phase);
|
|
2229
|
-
}
|
|
2230
|
-
|
|
2231
|
-
// src/constants/watch.ts
|
|
2232
|
-
var MIN_WATCH_POLL_INTERVAL_MS = 250;
|
|
2233
|
-
var DEFAULT_WATCH_POLL_INTERVAL_MS = 1e3;
|
|
2234
|
-
var MAX_WATCH_POLL_INTERVAL_MS = 6e4;
|
|
2235
|
-
var WATCH_REPLAY_PAGE_SIZE = 200;
|
|
2236
|
-
function isWatchTerminal(phase) {
|
|
2237
|
-
return isTerminalPhase(phase);
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
2265
|
// src/uploads.ts
|
|
2241
2266
|
import { readFile as readFile3 } from "fs/promises";
|
|
2242
2267
|
import { basename, extname } from "path";
|
|
@@ -2382,11 +2407,6 @@ function normalizeUploadedFileOptions(files) {
|
|
|
2382
2407
|
return Array.isArray(files) ? files : [files];
|
|
2383
2408
|
}
|
|
2384
2409
|
|
|
2385
|
-
// src/utils/poll-interval.ts
|
|
2386
|
-
function clampPollInterval(ms, minimum) {
|
|
2387
|
-
return Math.max(ms, minimum);
|
|
2388
|
-
}
|
|
2389
|
-
|
|
2390
2410
|
// ../../shared/session/no-change-outcome.ts
|
|
2391
2411
|
function isNoChangesPromptResult(result) {
|
|
2392
2412
|
return typeof result === "object" && result !== null && "noChanges" in result && result.noChanges === true;
|
|
@@ -3889,20 +3909,6 @@ function applyDisconnectMask(view, mask, now) {
|
|
|
3889
3909
|
}
|
|
3890
3910
|
|
|
3891
3911
|
// src/commands/watch.ts
|
|
3892
|
-
function parsePollInterval(raw) {
|
|
3893
|
-
if (!raw) return DEFAULT_WATCH_POLL_INTERVAL_MS;
|
|
3894
|
-
if (!/^\d+$/.test(raw)) {
|
|
3895
|
-
throw new CliError("user", "Polling interval must be a non-negative integer.");
|
|
3896
|
-
}
|
|
3897
|
-
const value = Number(raw);
|
|
3898
|
-
if (value < MIN_WATCH_POLL_INTERVAL_MS || value > MAX_WATCH_POLL_INTERVAL_MS) {
|
|
3899
|
-
throw new CliError(
|
|
3900
|
-
"user",
|
|
3901
|
-
`Polling interval must be between ${MIN_WATCH_POLL_INTERVAL_MS} and ${MAX_WATCH_POLL_INTERVAL_MS} milliseconds.`
|
|
3902
|
-
);
|
|
3903
|
-
}
|
|
3904
|
-
return value;
|
|
3905
|
-
}
|
|
3906
3912
|
function parseNonNegativeInteger(raw, name, defaultValue) {
|
|
3907
3913
|
if (!raw) return defaultValue;
|
|
3908
3914
|
if (!/^\d+$/.test(raw)) {
|
|
@@ -4327,16 +4333,6 @@ function parseEgressAllowlistSourceFile(content, key = EGRESS_ALLOWLIST_SOURCE_P
|
|
|
4327
4333
|
}
|
|
4328
4334
|
|
|
4329
4335
|
// src/commands/egress.ts
|
|
4330
|
-
function parseRepoArg(value) {
|
|
4331
|
-
const parsed = parseGithubRepoFullName(value);
|
|
4332
|
-
if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
4333
|
-
return {
|
|
4334
|
-
...parsed,
|
|
4335
|
-
// Preserve the legacy shorthand contract while URLs and SSH remotes are
|
|
4336
|
-
// normalized by the shared parser itself.
|
|
4337
|
-
repo: /^[^/:]+\/[^/]+\.git$/.test(value.trim()) ? parsed.repo.slice(0, -4) : parsed.repo
|
|
4338
|
-
};
|
|
4339
|
-
}
|
|
4340
4336
|
async function egressSourceSetCommand(sourceRepoArg, options = {}, command) {
|
|
4341
4337
|
const { config } = resolveBusinessContext(command, options);
|
|
4342
4338
|
const businessId = await resolveBusinessId(config, options);
|
|
@@ -4569,13 +4565,13 @@ function parseCreateConflict(error) {
|
|
|
4569
4565
|
let message = error.message;
|
|
4570
4566
|
let sessionId;
|
|
4571
4567
|
let sessionUrl;
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
const
|
|
4568
|
+
const parsed = parseConflictBody(error);
|
|
4569
|
+
if (parsed) {
|
|
4570
|
+
const rawError = parsed.error;
|
|
4571
|
+
const parsedMessage = typeof rawError === "string" ? rawError : rawError && typeof rawError === "object" && typeof rawError.message === "string" ? rawError.message : void 0;
|
|
4575
4572
|
if (parsedMessage) message = parsedMessage;
|
|
4576
4573
|
if (typeof parsed.sessionId === "string") sessionId = parsed.sessionId;
|
|
4577
4574
|
if (typeof parsed.sessionUrl === "string") sessionUrl = parsed.sessionUrl;
|
|
4578
|
-
} catch {
|
|
4579
4575
|
}
|
|
4580
4576
|
const location = sessionUrl ? ` (${sessionUrl})` : "";
|
|
4581
4577
|
const existing = sessionId ? ` Existing verifier session: ${sessionId}${location}.` : "";
|
|
@@ -4667,28 +4663,7 @@ async function qaCommand(prUrl, options, command) {
|
|
|
4667
4663
|
console.log(`Follow with: arcanist sessions events ${sessionId} --follow --json`);
|
|
4668
4664
|
}
|
|
4669
4665
|
|
|
4670
|
-
// src/git.ts
|
|
4671
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
4672
|
-
function git(args) {
|
|
4673
|
-
try {
|
|
4674
|
-
return execFileSync2("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
4675
|
-
} catch (err) {
|
|
4676
|
-
throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
|
|
4677
|
-
}
|
|
4678
|
-
}
|
|
4679
|
-
function currentRepo() {
|
|
4680
|
-
const parsed = parseGithubRepoFullName(git(["remote", "get-url", "origin"]));
|
|
4681
|
-
if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
|
|
4682
|
-
return { owner: parsed.owner, repo: parsed.repo };
|
|
4683
|
-
}
|
|
4684
|
-
|
|
4685
4666
|
// src/commands/repos.ts
|
|
4686
|
-
function parseRepoArg2(value) {
|
|
4687
|
-
if (value === void 0) return currentRepo();
|
|
4688
|
-
const parsed = parseGithubRepoFullName(value);
|
|
4689
|
-
if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
4690
|
-
return { owner: parsed.owner, repo: parsed.repo };
|
|
4691
|
-
}
|
|
4692
4667
|
async function reposListCommand(options = {}, command) {
|
|
4693
4668
|
const { config } = resolveBusinessContext(command, options);
|
|
4694
4669
|
const payload = await apiFetch(
|
|
@@ -4704,8 +4679,7 @@ async function reposListCommand(options = {}, command) {
|
|
|
4704
4679
|
}
|
|
4705
4680
|
async function repoBranchesCommand(repoArg, options = {}, command) {
|
|
4706
4681
|
const { config } = resolveBusinessContext(command, options);
|
|
4707
|
-
const repo =
|
|
4708
|
-
await apiFetch(config, "/api/repos");
|
|
4682
|
+
const repo = parseRepoArgOrCurrent(repoArg);
|
|
4709
4683
|
const payload = await apiFetch(
|
|
4710
4684
|
config,
|
|
4711
4685
|
`/api/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/branches`
|
|
@@ -4716,7 +4690,7 @@ async function repoBranchesCommand(repoArg, options = {}, command) {
|
|
|
4716
4690
|
}
|
|
4717
4691
|
async function repoSkillsCommand(repoArg, options = {}, command) {
|
|
4718
4692
|
const { config } = resolveBusinessContext(command, options);
|
|
4719
|
-
const repo =
|
|
4693
|
+
const repo = parseRepoArgOrCurrent(repoArg);
|
|
4720
4694
|
const payload = await apiFetch(
|
|
4721
4695
|
config,
|
|
4722
4696
|
`/api/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/skills`
|
|
@@ -4764,17 +4738,62 @@ async function respondCommand(sessionId, answerArg, options = {}, command) {
|
|
|
4764
4738
|
}
|
|
4765
4739
|
}
|
|
4766
4740
|
function parseRespondConflict(err) {
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4741
|
+
const body = parseConflictBody(err);
|
|
4742
|
+
if (!body || body.error !== "session_not_respondable") return null;
|
|
4743
|
+
const reason = typeof body.reason === "string" ? body.reason : void 0;
|
|
4744
|
+
return {
|
|
4745
|
+
message: reason ? `Session cannot be answered from phase=${reason}.` : "Session does not have a pending question.",
|
|
4746
|
+
reason: reason ?? "session_not_respondable"
|
|
4747
|
+
};
|
|
4748
|
+
}
|
|
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;
|
|
4777
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
|
+
});
|
|
4778
4797
|
}
|
|
4779
4798
|
|
|
4780
4799
|
// src/commands/sandbox.ts
|
|
@@ -5327,23 +5346,9 @@ function assertCleanAndPushed() {
|
|
|
5327
5346
|
function assertManifestExists(path) {
|
|
5328
5347
|
if (!existsSync3(path)) throw new CliError("user", `Missing sandbox manifest: ${path}`);
|
|
5329
5348
|
}
|
|
5330
|
-
function parseRepoArg3(value, fallback) {
|
|
5331
|
-
if (!value) return fallback();
|
|
5332
|
-
const parsed = parseGithubRepoFullName(value);
|
|
5333
|
-
if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
5334
|
-
return { owner: parsed.owner, repo: parsed.repo };
|
|
5335
|
-
}
|
|
5336
5349
|
function repoPath(repo) {
|
|
5337
5350
|
return `${repo.owner}/${repo.repo}`;
|
|
5338
5351
|
}
|
|
5339
|
-
function parsePollInterval2(value) {
|
|
5340
|
-
if (!value) return DEFAULT_POLL_INTERVAL_MS;
|
|
5341
|
-
const parsed = Number(value);
|
|
5342
|
-
if (!Number.isInteger(parsed) || parsed < MIN_POLL_INTERVAL_MS) {
|
|
5343
|
-
throw new CliError("user", `--poll-interval must be an integer >= ${MIN_POLL_INTERVAL_MS}.`);
|
|
5344
|
-
}
|
|
5345
|
-
return parsed;
|
|
5346
|
-
}
|
|
5347
5352
|
function printBuildRequest(buildRequest) {
|
|
5348
5353
|
console.log(
|
|
5349
5354
|
[
|
|
@@ -5615,8 +5620,8 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
5615
5620
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
5616
5621
|
assertManifestExists(manifestPath);
|
|
5617
5622
|
if (!options.ref) assertCleanAndPushed();
|
|
5618
|
-
const sourceRepo =
|
|
5619
|
-
const targetRepo = options.targetRepo ?
|
|
5623
|
+
const sourceRepo = parseRepoArgOrCurrent(sourceRepoArg);
|
|
5624
|
+
const targetRepo = options.targetRepo ? parseRepoArgOrCurrent(options.targetRepo) : null;
|
|
5620
5625
|
const businessId = await resolveBusinessId(config, options);
|
|
5621
5626
|
const ref = options.ref ?? currentRef();
|
|
5622
5627
|
let payload;
|
|
@@ -5654,7 +5659,11 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
5654
5659
|
if (!options.wait && !options.follow) return;
|
|
5655
5660
|
const finalBuild = await waitForBuild(config, businessId, payload.buildRequest.id, {
|
|
5656
5661
|
follow: options.follow,
|
|
5657
|
-
pollIntervalMs:
|
|
5662
|
+
pollIntervalMs: parsePollInterval(options.pollInterval, {
|
|
5663
|
+
defaultMs: DEFAULT_POLL_INTERVAL_MS,
|
|
5664
|
+
minMs: MIN_POLL_INTERVAL_MS,
|
|
5665
|
+
maxMs: null
|
|
5666
|
+
}),
|
|
5658
5667
|
json
|
|
5659
5668
|
});
|
|
5660
5669
|
if (json)
|
|
@@ -5667,7 +5676,7 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
5667
5676
|
async function sandboxStatusCommand(repoArg, options = {}, command) {
|
|
5668
5677
|
const { config } = resolveBusinessContext(command, options);
|
|
5669
5678
|
const businessId = await resolveBusinessId(config, options);
|
|
5670
|
-
const repo =
|
|
5679
|
+
const repo = parseRepoArgOrCurrent(repoArg);
|
|
5671
5680
|
const payload = await apiFetch(
|
|
5672
5681
|
config,
|
|
5673
5682
|
`/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(
|
|
@@ -5679,9 +5688,9 @@ async function sandboxStatusCommand(repoArg, options = {}, command) {
|
|
|
5679
5688
|
async function sandboxHistoryCommand(sourceRepoArg, options = {}, command) {
|
|
5680
5689
|
const { config } = resolveBusinessContext(command, options);
|
|
5681
5690
|
const businessId = await resolveBusinessId(config, options);
|
|
5682
|
-
const sourceRepo =
|
|
5691
|
+
const sourceRepo = parseRepoArgOrCurrent(sourceRepoArg);
|
|
5683
5692
|
const params = new URLSearchParams({ sourceRepo: repoPath(sourceRepo) });
|
|
5684
|
-
if (options.targetRepo) params.set("targetRepo", repoPath(
|
|
5693
|
+
if (options.targetRepo) params.set("targetRepo", repoPath(parseRepoArgOrCurrent(options.targetRepo)));
|
|
5685
5694
|
if (options.status) params.set("status", options.status);
|
|
5686
5695
|
if (options.limit) {
|
|
5687
5696
|
const limit = Number(options.limit);
|
|
@@ -5776,13 +5785,19 @@ async function sandboxLogsCommand(buildId, options = {}, command) {
|
|
|
5776
5785
|
}
|
|
5777
5786
|
if (!options.follow) return;
|
|
5778
5787
|
if (payload.logs.length > 0) afterSequence = payload.logs[payload.logs.length - 1].sequence;
|
|
5779
|
-
await sleep(
|
|
5788
|
+
await sleep(
|
|
5789
|
+
parsePollInterval(options.pollInterval, {
|
|
5790
|
+
defaultMs: DEFAULT_POLL_INTERVAL_MS,
|
|
5791
|
+
minMs: MIN_POLL_INTERVAL_MS,
|
|
5792
|
+
maxMs: null
|
|
5793
|
+
})
|
|
5794
|
+
);
|
|
5780
5795
|
}
|
|
5781
5796
|
}
|
|
5782
5797
|
async function sandboxAssignDefaultCommand(sourceRepoArg, options = {}, command) {
|
|
5783
5798
|
const { config } = resolveBusinessContext(command, options);
|
|
5784
5799
|
const businessId = await resolveBusinessId(config, options);
|
|
5785
|
-
const sourceRepo =
|
|
5800
|
+
const sourceRepo = parseRepoArgOrCurrent(sourceRepoArg);
|
|
5786
5801
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
5787
5802
|
const payload = await apiFetch(
|
|
5788
5803
|
config,
|
|
@@ -5799,8 +5814,8 @@ async function sandboxAssignDefaultCommand(sourceRepoArg, options = {}, command)
|
|
|
5799
5814
|
async function sandboxAssignRepoCommand(targetRepoArg, sourceRepoArg, options = {}, command) {
|
|
5800
5815
|
const { config } = resolveBusinessContext(command, options);
|
|
5801
5816
|
const businessId = await resolveBusinessId(config, options);
|
|
5802
|
-
const targetRepo =
|
|
5803
|
-
const sourceRepo =
|
|
5817
|
+
const targetRepo = parseRepoArgOrCurrent(targetRepoArg);
|
|
5818
|
+
const sourceRepo = parseRepoArgOrCurrent(sourceRepoArg);
|
|
5804
5819
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
5805
5820
|
const payload = await apiFetch(
|
|
5806
5821
|
config,
|
|
@@ -5838,7 +5853,7 @@ async function sandboxUnassignRepoCommand(targetRepoArg, options = {}, command)
|
|
|
5838
5853
|
}
|
|
5839
5854
|
const { config } = resolveBusinessContext(command, options);
|
|
5840
5855
|
const businessId = await resolveBusinessId(config, options);
|
|
5841
|
-
const targetRepo =
|
|
5856
|
+
const targetRepo = parseRepoArgOrCurrent(targetRepoArg);
|
|
5842
5857
|
if (!options.yes) await confirmOrThrow(`Clear sandbox layer assignment for ${repoPath(targetRepo)}?`);
|
|
5843
5858
|
const payload = await apiFetch(
|
|
5844
5859
|
config,
|
|
@@ -5851,35 +5866,29 @@ async function sandboxUnassignRepoCommand(targetRepoArg, options = {}, command)
|
|
|
5851
5866
|
}
|
|
5852
5867
|
|
|
5853
5868
|
// src/commands/sessions.ts
|
|
5854
|
-
var MAX_ALL_PAGES2 = 1e3;
|
|
5855
5869
|
async function listSessionsCommand(options, command) {
|
|
5856
5870
|
const runtime = getRuntimeOptions(command, options);
|
|
5857
5871
|
const config = requireConfig(runtime);
|
|
5858
|
-
const sessions2 =
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
sessions2.push(...payload2.sessions);
|
|
5879
|
-
nextCursor = payload2.nextCursor;
|
|
5880
|
-
cursor2 = nextCursor ?? void 0;
|
|
5881
|
-
} while (options.all === true && nextCursor);
|
|
5882
|
-
const payload = { sessions: sessions2, nextCursor: options.all === true ? null : nextCursor };
|
|
5872
|
+
const { items: sessions2, nextCursor } = await fetchPages(
|
|
5873
|
+
"sessions list --all",
|
|
5874
|
+
options.all === true,
|
|
5875
|
+
options.cursor,
|
|
5876
|
+
async (cursor2) => {
|
|
5877
|
+
const query = new URLSearchParams();
|
|
5878
|
+
if (options.status) query.set("status", options.status);
|
|
5879
|
+
if (options.scope) query.set("scope", options.scope);
|
|
5880
|
+
if (options.search) query.set("q", options.search);
|
|
5881
|
+
if (options.repo) query.set("repo", options.repo);
|
|
5882
|
+
if (options.limit) query.set("limit", options.limit);
|
|
5883
|
+
if (cursor2) query.set("cursor", cursor2);
|
|
5884
|
+
const payload2 = await apiFetch(
|
|
5885
|
+
config,
|
|
5886
|
+
`/api/sessions${query.size ? `?${query.toString()}` : ""}`
|
|
5887
|
+
);
|
|
5888
|
+
return { items: payload2.sessions, nextCursor: payload2.nextCursor };
|
|
5889
|
+
}
|
|
5890
|
+
);
|
|
5891
|
+
const payload = { sessions: sessions2, nextCursor };
|
|
5883
5892
|
if (isJson(command, options)) {
|
|
5884
5893
|
writeJson(payload);
|
|
5885
5894
|
return;
|
|
@@ -6021,25 +6030,15 @@ async function stopCommand(sessionId, options = {}, command) {
|
|
|
6021
6030
|
});
|
|
6022
6031
|
}
|
|
6023
6032
|
function parseStopBlocked(err) {
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
if (body.error !== "session_not_stoppable") return null;
|
|
6028
|
-
return { reason: body.reason ?? "not_stoppable" };
|
|
6029
|
-
} catch {
|
|
6030
|
-
return null;
|
|
6031
|
-
}
|
|
6033
|
+
const body = parseConflictBody(err);
|
|
6034
|
+
if (!body || body.error !== "session_not_stoppable") return null;
|
|
6035
|
+
return { reason: typeof body.reason === "string" ? body.reason : "not_stoppable" };
|
|
6032
6036
|
}
|
|
6033
6037
|
|
|
6034
6038
|
// src/commands/test-creds.ts
|
|
6035
6039
|
import { readFileSync as readFileSync4 } from "fs";
|
|
6036
|
-
function parseRepoArg4(value) {
|
|
6037
|
-
const parsed = parseGithubRepoFullName(value);
|
|
6038
|
-
if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
6039
|
-
return { owner: parsed.owner, name: parsed.repo };
|
|
6040
|
-
}
|
|
6041
6040
|
function basePath(businessId, repo) {
|
|
6042
|
-
return `/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.
|
|
6041
|
+
return `/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/test-credentials`;
|
|
6043
6042
|
}
|
|
6044
6043
|
function readValue(options) {
|
|
6045
6044
|
const sources = [options.value, options.valueFile, options.valueStdin].filter((v) => v !== void 0 && v !== false);
|
|
@@ -6052,11 +6051,11 @@ function readValue(options) {
|
|
|
6052
6051
|
}
|
|
6053
6052
|
async function listTestCredentialsCommand(repo, options, command) {
|
|
6054
6053
|
const { config } = resolveBusinessContext(command, options);
|
|
6055
|
-
const repoArg =
|
|
6054
|
+
const repoArg = parseRepoArg(repo);
|
|
6056
6055
|
const payload = await apiFetch(config, basePath(options.business, repoArg));
|
|
6057
6056
|
emit(command, options, payload, (listPayload) => {
|
|
6058
6057
|
if (listPayload.credentials.length === 0) {
|
|
6059
|
-
console.log(`No test credentials configured for ${repoArg.owner}/${repoArg.
|
|
6058
|
+
console.log(`No test credentials configured for ${repoArg.owner}/${repoArg.repo}.`);
|
|
6060
6059
|
return;
|
|
6061
6060
|
}
|
|
6062
6061
|
for (const cred of listPayload.credentials) {
|
|
@@ -6067,7 +6066,7 @@ async function listTestCredentialsCommand(repo, options, command) {
|
|
|
6067
6066
|
}
|
|
6068
6067
|
async function setTestCredentialCommand(repo, name, options, command) {
|
|
6069
6068
|
const { config } = resolveBusinessContext(command, options);
|
|
6070
|
-
const repoArg =
|
|
6069
|
+
const repoArg = parseRepoArg(repo);
|
|
6071
6070
|
const value = readValue(options);
|
|
6072
6071
|
const payload = await apiFetch(
|
|
6073
6072
|
config,
|
|
@@ -6078,7 +6077,7 @@ async function setTestCredentialCommand(repo, name, options, command) {
|
|
|
6078
6077
|
command,
|
|
6079
6078
|
options,
|
|
6080
6079
|
payload,
|
|
6081
|
-
() => console.log(`Set test credential '${name}' for ${repoArg.owner}/${repoArg.
|
|
6080
|
+
() => console.log(`Set test credential '${name}' for ${repoArg.owner}/${repoArg.repo}.`)
|
|
6082
6081
|
);
|
|
6083
6082
|
}
|
|
6084
6083
|
async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
@@ -6086,11 +6085,11 @@ async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
|
6086
6085
|
throw new CliError("user", "`test-creds delete --json` requires --yes.");
|
|
6087
6086
|
}
|
|
6088
6087
|
if (options.yes !== true) {
|
|
6089
|
-
const repoArg2 =
|
|
6090
|
-
await confirmOrThrow(`Delete test credential '${name}' for ${repoArg2.owner}/${repoArg2.
|
|
6088
|
+
const repoArg2 = parseRepoArg(repo);
|
|
6089
|
+
await confirmOrThrow(`Delete test credential '${name}' for ${repoArg2.owner}/${repoArg2.repo}?`);
|
|
6091
6090
|
}
|
|
6092
6091
|
const { config } = resolveBusinessContext(command, options);
|
|
6093
|
-
const repoArg =
|
|
6092
|
+
const repoArg = parseRepoArg(repo);
|
|
6094
6093
|
const payload = await apiFetch(
|
|
6095
6094
|
config,
|
|
6096
6095
|
`${basePath(options.business, repoArg)}/${encodeURIComponent(name)}`,
|
|
@@ -6100,35 +6099,29 @@ async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
|
6100
6099
|
command,
|
|
6101
6100
|
options,
|
|
6102
6101
|
payload,
|
|
6103
|
-
() => console.log(`Deleted test credential '${name}' for ${repoArg.owner}/${repoArg.
|
|
6102
|
+
() => console.log(`Deleted test credential '${name}' for ${repoArg.owner}/${repoArg.repo}.`)
|
|
6104
6103
|
);
|
|
6105
6104
|
}
|
|
6106
6105
|
|
|
6107
6106
|
// src/commands/tokens.ts
|
|
6108
|
-
var MAX_ALL_PAGES3 = 1e3;
|
|
6109
6107
|
async function listTokensCommand(options, command) {
|
|
6110
6108
|
const { config } = resolveBusinessContext(command, options);
|
|
6111
|
-
const data =
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6109
|
+
const { items: data, nextCursor } = await fetchPages(
|
|
6110
|
+
"tokens list --all",
|
|
6111
|
+
options.all === true,
|
|
6112
|
+
options.cursor,
|
|
6113
|
+
async (cursor2) => {
|
|
6114
|
+
const query = new URLSearchParams();
|
|
6115
|
+
if (options.limit) query.set("limit", options.limit);
|
|
6116
|
+
if (cursor2) query.set("cursor", cursor2);
|
|
6117
|
+
const payload2 = await apiFetch(
|
|
6118
|
+
config,
|
|
6119
|
+
`/api/cli-tokens${query.size ? `?${query.toString()}` : ""}`
|
|
6120
|
+
);
|
|
6121
|
+
return { items: payload2.data, nextCursor: payload2.nextCursor };
|
|
6119
6122
|
}
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
if (cursor2) query.set("cursor", cursor2);
|
|
6123
|
-
const payload2 = await apiFetch(
|
|
6124
|
-
config,
|
|
6125
|
-
`/api/cli-tokens${query.size ? `?${query.toString()}` : ""}`
|
|
6126
|
-
);
|
|
6127
|
-
data.push(...payload2.data);
|
|
6128
|
-
nextCursor = payload2.nextCursor;
|
|
6129
|
-
cursor2 = nextCursor ?? void 0;
|
|
6130
|
-
} while (options.all === true && nextCursor);
|
|
6131
|
-
const payload = { data, nextCursor: options.all === true ? null : nextCursor };
|
|
6123
|
+
);
|
|
6124
|
+
const payload = { data, nextCursor };
|
|
6132
6125
|
emit(command, options, payload, (listPayload) => {
|
|
6133
6126
|
if (listPayload.data.length === 0) {
|
|
6134
6127
|
console.log("No CLI tokens found.");
|
|
@@ -6411,7 +6404,7 @@ cursor.command("use <state>").description("Turn using your saved Cursor subscrip
|
|
|
6411
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));
|
|
6412
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));
|
|
6413
6406
|
var sessions = program.command("sessions").description("Session commands");
|
|
6414
|
-
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(
|
|
6415
6408
|
"after",
|
|
6416
6409
|
`
|
|
6417
6410
|
Examples:
|
|
@@ -6425,7 +6418,7 @@ a specific reviewer model instead (members only).
|
|
|
6425
6418
|
Zeus review is currently available to Arcanist members only.
|
|
6426
6419
|
`
|
|
6427
6420
|
).action((prUrl, options, command) => reviewCommand(prUrl, options, command));
|
|
6428
|
-
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(
|
|
6429
6422
|
"after",
|
|
6430
6423
|
`
|
|
6431
6424
|
Examples:
|