@tryarcanist/cli 0.1.220 → 0.1.222
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 +179 -190
- 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;
|
|
@@ -975,7 +984,13 @@ async function reviewCommand(prUrl, options = {}, command) {
|
|
|
975
984
|
const { config } = resolveBusinessContext(command, options);
|
|
976
985
|
const trigger = await apiFetch(config, "/api/pr-reviews", {
|
|
977
986
|
method: "POST",
|
|
978
|
-
body: JSON.stringify({
|
|
987
|
+
body: JSON.stringify({
|
|
988
|
+
prUrl,
|
|
989
|
+
...options.focus ? { focus: options.focus } : {},
|
|
990
|
+
// Send an explicitly passed model even when empty (e.g. --model "$VAR" with an
|
|
991
|
+
// unset VAR) so the server rejects it as invalid instead of silently routing.
|
|
992
|
+
...options.model !== void 0 ? { model: options.model } : {}
|
|
993
|
+
})
|
|
979
994
|
});
|
|
980
995
|
if (!options.wait || trigger.cached) {
|
|
981
996
|
emit(command, options, trigger, (payload) => {
|
|
@@ -1836,6 +1851,26 @@ var SESSION_START_MODEL_PROVIDER_GROUPS_BY_SUBSCRIPTIONS = {
|
|
|
1836
1851
|
"true:true": buildSubscriptionAwareGroups({ codexSubscription: true, grokSubscription: true })
|
|
1837
1852
|
};
|
|
1838
1853
|
|
|
1854
|
+
// src/utils/pagination.ts
|
|
1855
|
+
var MAX_ALL_PAGES = 1e3;
|
|
1856
|
+
async function fetchPages(label, all, initialCursor, fetchPage) {
|
|
1857
|
+
const items = [];
|
|
1858
|
+
let cursor2 = initialCursor;
|
|
1859
|
+
let nextCursor = null;
|
|
1860
|
+
let pageCount = 0;
|
|
1861
|
+
do {
|
|
1862
|
+
pageCount += 1;
|
|
1863
|
+
if (all && pageCount > MAX_ALL_PAGES) {
|
|
1864
|
+
throw new CliError("user", `${label} exceeded ${MAX_ALL_PAGES} pages without reaching the end.`);
|
|
1865
|
+
}
|
|
1866
|
+
const page = await fetchPage(cursor2);
|
|
1867
|
+
items.push(...page.items);
|
|
1868
|
+
nextCursor = page.nextCursor;
|
|
1869
|
+
cursor2 = nextCursor ?? void 0;
|
|
1870
|
+
} while (all && nextCursor);
|
|
1871
|
+
return { items, nextCursor: all ? null : nextCursor };
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1839
1874
|
// ../../shared/github/repo-url.ts
|
|
1840
1875
|
function parseGithubRepoFullName(value) {
|
|
1841
1876
|
const shorthand = value.match(/^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/);
|
|
@@ -1847,6 +1882,32 @@ function parseGithubRepoFullName(value) {
|
|
|
1847
1882
|
return null;
|
|
1848
1883
|
}
|
|
1849
1884
|
|
|
1885
|
+
// src/git.ts
|
|
1886
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
1887
|
+
function git(args) {
|
|
1888
|
+
try {
|
|
1889
|
+
return execFileSync2("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
1890
|
+
} catch (err) {
|
|
1891
|
+
throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
function currentRepo() {
|
|
1895
|
+
const parsed = parseGithubRepoFullName(git(["remote", "get-url", "origin"]));
|
|
1896
|
+
if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
|
|
1897
|
+
return { owner: parsed.owner, repo: parsed.repo };
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
// src/utils/repo-arg.ts
|
|
1901
|
+
function parseRepoArg(value, argName = "repo") {
|
|
1902
|
+
const parsed = parseGithubRepoFullName(value);
|
|
1903
|
+
if (!parsed) throw new CliError("user", `${argName} must be owner/name or a GitHub URL`);
|
|
1904
|
+
return { owner: parsed.owner, repo: parsed.repo.replace(/\.git$/, "") };
|
|
1905
|
+
}
|
|
1906
|
+
function parseRepoArgOrCurrent(value, argName = "repo") {
|
|
1907
|
+
if (value === void 0) return currentRepo();
|
|
1908
|
+
return parseRepoArg(value, argName);
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1850
1911
|
// src/commands/automations.ts
|
|
1851
1912
|
var AUTOMATION_ERROR_HINTS = {
|
|
1852
1913
|
invalid_cron: "Use a standard five-field cron expression, for example: */15 * * * *",
|
|
@@ -1863,9 +1924,8 @@ var AUTOMATION_ERROR_HINTS = {
|
|
|
1863
1924
|
repo_skills_unavailable: "Arcanist could not verify the repo's skills right now. Retry once GitHub skill discovery is healthy.",
|
|
1864
1925
|
unknown_skill: "That leading slash skill does not exist in the selected repository."
|
|
1865
1926
|
};
|
|
1866
|
-
var MAX_ALL_PAGES = 1e3;
|
|
1867
1927
|
async function createAutomationCommand(repoUrl, promptArg, options, command) {
|
|
1868
|
-
const repo =
|
|
1928
|
+
const repo = parseRepoArg(repoUrl, "repo-url");
|
|
1869
1929
|
const prompt = await resolvePromptInput(promptArg, options);
|
|
1870
1930
|
const slackTeamId = options.slackTeamId?.trim();
|
|
1871
1931
|
const slackChannelId = options.slackChannelId?.trim();
|
|
@@ -1912,27 +1972,22 @@ async function createAutomationCommand(repoUrl, promptArg, options, command) {
|
|
|
1912
1972
|
async function listAutomationsCommand(options, command) {
|
|
1913
1973
|
const runtime = getRuntimeOptions(command, options);
|
|
1914
1974
|
const config = requireConfig(runtime);
|
|
1915
|
-
const items =
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1975
|
+
const { items, nextCursor } = await fetchPages(
|
|
1976
|
+
"automations list --all",
|
|
1977
|
+
options.all === true,
|
|
1978
|
+
options.cursor,
|
|
1979
|
+
async (cursor2) => {
|
|
1980
|
+
const query = new URLSearchParams();
|
|
1981
|
+
if (options.limit) query.set("limit", options.limit);
|
|
1982
|
+
if (cursor2) query.set("cursor", cursor2);
|
|
1983
|
+
const payload = await automationApiFetch(
|
|
1984
|
+
config,
|
|
1985
|
+
`/api/automation/schedules${query.size ? `?${query.toString()}` : ""}`
|
|
1986
|
+
);
|
|
1987
|
+
return { items: payload.data.items, nextCursor: payload.data.nextCursor };
|
|
1923
1988
|
}
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
if (cursor2) query.set("cursor", cursor2);
|
|
1927
|
-
const payload = await automationApiFetch(
|
|
1928
|
-
config,
|
|
1929
|
-
`/api/automation/schedules${query.size ? `?${query.toString()}` : ""}`
|
|
1930
|
-
);
|
|
1931
|
-
items.push(...payload.data.items);
|
|
1932
|
-
nextCursor = payload.data.nextCursor;
|
|
1933
|
-
cursor2 = nextCursor ?? void 0;
|
|
1934
|
-
} while (options.all === true && nextCursor);
|
|
1935
|
-
const output = { items, nextCursor: options.all === true ? null : nextCursor };
|
|
1989
|
+
);
|
|
1990
|
+
const output = { items, nextCursor };
|
|
1936
1991
|
if (isJson(command, options)) {
|
|
1937
1992
|
writeJson(output);
|
|
1938
1993
|
return;
|
|
@@ -1964,14 +2019,6 @@ async function deleteAutomationCommand(id, options, command) {
|
|
|
1964
2019
|
}
|
|
1965
2020
|
console.log(`Deleted automation ${id}.`);
|
|
1966
2021
|
}
|
|
1967
|
-
function parseAutomationRepo(value) {
|
|
1968
|
-
if (/^git@/i.test(value) && !/^git@github\.com:/i.test(value)) {
|
|
1969
|
-
throw new CliError("user", "repo-url must be owner/name or a GitHub URL.");
|
|
1970
|
-
}
|
|
1971
|
-
const parsed = parseGithubRepoFullName(value);
|
|
1972
|
-
if (!parsed) throw new CliError("user", "repo-url must be owner/name or a GitHub URL.");
|
|
1973
|
-
return { owner: parsed.owner, repo: parsed.repo };
|
|
1974
|
-
}
|
|
1975
2022
|
function resolveAutomationModelBackend(rawModel, normalizedModel) {
|
|
1976
2023
|
if (normalizedModel) {
|
|
1977
2024
|
const backend = getAgentRuntimeBackendForModel(normalizedModel);
|
|
@@ -2380,6 +2427,21 @@ function normalizeUploadedFileOptions(files) {
|
|
|
2380
2427
|
function clampPollInterval(ms, minimum) {
|
|
2381
2428
|
return Math.max(ms, minimum);
|
|
2382
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
|
+
}
|
|
2383
2445
|
|
|
2384
2446
|
// ../../shared/session/no-change-outcome.ts
|
|
2385
2447
|
function isNoChangesPromptResult(result) {
|
|
@@ -3883,20 +3945,6 @@ function applyDisconnectMask(view, mask, now) {
|
|
|
3883
3945
|
}
|
|
3884
3946
|
|
|
3885
3947
|
// src/commands/watch.ts
|
|
3886
|
-
function parsePollInterval(raw) {
|
|
3887
|
-
if (!raw) return DEFAULT_WATCH_POLL_INTERVAL_MS;
|
|
3888
|
-
if (!/^\d+$/.test(raw)) {
|
|
3889
|
-
throw new CliError("user", "Polling interval must be a non-negative integer.");
|
|
3890
|
-
}
|
|
3891
|
-
const value = Number(raw);
|
|
3892
|
-
if (value < MIN_WATCH_POLL_INTERVAL_MS || value > MAX_WATCH_POLL_INTERVAL_MS) {
|
|
3893
|
-
throw new CliError(
|
|
3894
|
-
"user",
|
|
3895
|
-
`Polling interval must be between ${MIN_WATCH_POLL_INTERVAL_MS} and ${MAX_WATCH_POLL_INTERVAL_MS} milliseconds.`
|
|
3896
|
-
);
|
|
3897
|
-
}
|
|
3898
|
-
return value;
|
|
3899
|
-
}
|
|
3900
3948
|
function parseNonNegativeInteger(raw, name, defaultValue) {
|
|
3901
3949
|
if (!raw) return defaultValue;
|
|
3902
3950
|
if (!/^\d+$/.test(raw)) {
|
|
@@ -4321,16 +4369,6 @@ function parseEgressAllowlistSourceFile(content, key = EGRESS_ALLOWLIST_SOURCE_P
|
|
|
4321
4369
|
}
|
|
4322
4370
|
|
|
4323
4371
|
// src/commands/egress.ts
|
|
4324
|
-
function parseRepoArg(value) {
|
|
4325
|
-
const parsed = parseGithubRepoFullName(value);
|
|
4326
|
-
if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
4327
|
-
return {
|
|
4328
|
-
...parsed,
|
|
4329
|
-
// Preserve the legacy shorthand contract while URLs and SSH remotes are
|
|
4330
|
-
// normalized by the shared parser itself.
|
|
4331
|
-
repo: /^[^/:]+\/[^/]+\.git$/.test(value.trim()) ? parsed.repo.slice(0, -4) : parsed.repo
|
|
4332
|
-
};
|
|
4333
|
-
}
|
|
4334
4372
|
async function egressSourceSetCommand(sourceRepoArg, options = {}, command) {
|
|
4335
4373
|
const { config } = resolveBusinessContext(command, options);
|
|
4336
4374
|
const businessId = await resolveBusinessId(config, options);
|
|
@@ -4563,13 +4601,13 @@ function parseCreateConflict(error) {
|
|
|
4563
4601
|
let message = error.message;
|
|
4564
4602
|
let sessionId;
|
|
4565
4603
|
let sessionUrl;
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
const
|
|
4604
|
+
const parsed = parseConflictBody(error);
|
|
4605
|
+
if (parsed) {
|
|
4606
|
+
const rawError = parsed.error;
|
|
4607
|
+
const parsedMessage = typeof rawError === "string" ? rawError : rawError && typeof rawError === "object" && typeof rawError.message === "string" ? rawError.message : void 0;
|
|
4569
4608
|
if (parsedMessage) message = parsedMessage;
|
|
4570
4609
|
if (typeof parsed.sessionId === "string") sessionId = parsed.sessionId;
|
|
4571
4610
|
if (typeof parsed.sessionUrl === "string") sessionUrl = parsed.sessionUrl;
|
|
4572
|
-
} catch {
|
|
4573
4611
|
}
|
|
4574
4612
|
const location = sessionUrl ? ` (${sessionUrl})` : "";
|
|
4575
4613
|
const existing = sessionId ? ` Existing verifier session: ${sessionId}${location}.` : "";
|
|
@@ -4661,28 +4699,7 @@ async function qaCommand(prUrl, options, command) {
|
|
|
4661
4699
|
console.log(`Follow with: arcanist sessions events ${sessionId} --follow --json`);
|
|
4662
4700
|
}
|
|
4663
4701
|
|
|
4664
|
-
// src/git.ts
|
|
4665
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
4666
|
-
function git(args) {
|
|
4667
|
-
try {
|
|
4668
|
-
return execFileSync2("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
4669
|
-
} catch (err) {
|
|
4670
|
-
throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
|
|
4671
|
-
}
|
|
4672
|
-
}
|
|
4673
|
-
function currentRepo() {
|
|
4674
|
-
const parsed = parseGithubRepoFullName(git(["remote", "get-url", "origin"]));
|
|
4675
|
-
if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
|
|
4676
|
-
return { owner: parsed.owner, repo: parsed.repo };
|
|
4677
|
-
}
|
|
4678
|
-
|
|
4679
4702
|
// src/commands/repos.ts
|
|
4680
|
-
function parseRepoArg2(value) {
|
|
4681
|
-
if (value === void 0) return currentRepo();
|
|
4682
|
-
const parsed = parseGithubRepoFullName(value);
|
|
4683
|
-
if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
4684
|
-
return { owner: parsed.owner, repo: parsed.repo };
|
|
4685
|
-
}
|
|
4686
4703
|
async function reposListCommand(options = {}, command) {
|
|
4687
4704
|
const { config } = resolveBusinessContext(command, options);
|
|
4688
4705
|
const payload = await apiFetch(
|
|
@@ -4698,8 +4715,7 @@ async function reposListCommand(options = {}, command) {
|
|
|
4698
4715
|
}
|
|
4699
4716
|
async function repoBranchesCommand(repoArg, options = {}, command) {
|
|
4700
4717
|
const { config } = resolveBusinessContext(command, options);
|
|
4701
|
-
const repo =
|
|
4702
|
-
await apiFetch(config, "/api/repos");
|
|
4718
|
+
const repo = parseRepoArgOrCurrent(repoArg);
|
|
4703
4719
|
const payload = await apiFetch(
|
|
4704
4720
|
config,
|
|
4705
4721
|
`/api/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/branches`
|
|
@@ -4710,7 +4726,7 @@ async function repoBranchesCommand(repoArg, options = {}, command) {
|
|
|
4710
4726
|
}
|
|
4711
4727
|
async function repoSkillsCommand(repoArg, options = {}, command) {
|
|
4712
4728
|
const { config } = resolveBusinessContext(command, options);
|
|
4713
|
-
const repo =
|
|
4729
|
+
const repo = parseRepoArgOrCurrent(repoArg);
|
|
4714
4730
|
const payload = await apiFetch(
|
|
4715
4731
|
config,
|
|
4716
4732
|
`/api/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/skills`
|
|
@@ -4758,17 +4774,13 @@ async function respondCommand(sessionId, answerArg, options = {}, command) {
|
|
|
4758
4774
|
}
|
|
4759
4775
|
}
|
|
4760
4776
|
function parseRespondConflict(err) {
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
};
|
|
4769
|
-
} catch {
|
|
4770
|
-
return null;
|
|
4771
|
-
}
|
|
4777
|
+
const body = parseConflictBody(err);
|
|
4778
|
+
if (!body || body.error !== "session_not_respondable") return null;
|
|
4779
|
+
const reason = typeof body.reason === "string" ? body.reason : void 0;
|
|
4780
|
+
return {
|
|
4781
|
+
message: reason ? `Session cannot be answered from phase=${reason}.` : "Session does not have a pending question.",
|
|
4782
|
+
reason: reason ?? "session_not_respondable"
|
|
4783
|
+
};
|
|
4772
4784
|
}
|
|
4773
4785
|
|
|
4774
4786
|
// src/commands/sandbox.ts
|
|
@@ -5321,23 +5333,9 @@ function assertCleanAndPushed() {
|
|
|
5321
5333
|
function assertManifestExists(path) {
|
|
5322
5334
|
if (!existsSync3(path)) throw new CliError("user", `Missing sandbox manifest: ${path}`);
|
|
5323
5335
|
}
|
|
5324
|
-
function parseRepoArg3(value, fallback) {
|
|
5325
|
-
if (!value) return fallback();
|
|
5326
|
-
const parsed = parseGithubRepoFullName(value);
|
|
5327
|
-
if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
5328
|
-
return { owner: parsed.owner, repo: parsed.repo };
|
|
5329
|
-
}
|
|
5330
5336
|
function repoPath(repo) {
|
|
5331
5337
|
return `${repo.owner}/${repo.repo}`;
|
|
5332
5338
|
}
|
|
5333
|
-
function parsePollInterval2(value) {
|
|
5334
|
-
if (!value) return DEFAULT_POLL_INTERVAL_MS;
|
|
5335
|
-
const parsed = Number(value);
|
|
5336
|
-
if (!Number.isInteger(parsed) || parsed < MIN_POLL_INTERVAL_MS) {
|
|
5337
|
-
throw new CliError("user", `--poll-interval must be an integer >= ${MIN_POLL_INTERVAL_MS}.`);
|
|
5338
|
-
}
|
|
5339
|
-
return parsed;
|
|
5340
|
-
}
|
|
5341
5339
|
function printBuildRequest(buildRequest) {
|
|
5342
5340
|
console.log(
|
|
5343
5341
|
[
|
|
@@ -5609,8 +5607,8 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
5609
5607
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
5610
5608
|
assertManifestExists(manifestPath);
|
|
5611
5609
|
if (!options.ref) assertCleanAndPushed();
|
|
5612
|
-
const sourceRepo =
|
|
5613
|
-
const targetRepo = options.targetRepo ?
|
|
5610
|
+
const sourceRepo = parseRepoArgOrCurrent(sourceRepoArg);
|
|
5611
|
+
const targetRepo = options.targetRepo ? parseRepoArgOrCurrent(options.targetRepo) : null;
|
|
5614
5612
|
const businessId = await resolveBusinessId(config, options);
|
|
5615
5613
|
const ref = options.ref ?? currentRef();
|
|
5616
5614
|
let payload;
|
|
@@ -5648,7 +5646,11 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
5648
5646
|
if (!options.wait && !options.follow) return;
|
|
5649
5647
|
const finalBuild = await waitForBuild(config, businessId, payload.buildRequest.id, {
|
|
5650
5648
|
follow: options.follow,
|
|
5651
|
-
pollIntervalMs:
|
|
5649
|
+
pollIntervalMs: parsePollInterval(options.pollInterval, {
|
|
5650
|
+
defaultMs: DEFAULT_POLL_INTERVAL_MS,
|
|
5651
|
+
minMs: MIN_POLL_INTERVAL_MS,
|
|
5652
|
+
maxMs: null
|
|
5653
|
+
}),
|
|
5652
5654
|
json
|
|
5653
5655
|
});
|
|
5654
5656
|
if (json)
|
|
@@ -5661,7 +5663,7 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
5661
5663
|
async function sandboxStatusCommand(repoArg, options = {}, command) {
|
|
5662
5664
|
const { config } = resolveBusinessContext(command, options);
|
|
5663
5665
|
const businessId = await resolveBusinessId(config, options);
|
|
5664
|
-
const repo =
|
|
5666
|
+
const repo = parseRepoArgOrCurrent(repoArg);
|
|
5665
5667
|
const payload = await apiFetch(
|
|
5666
5668
|
config,
|
|
5667
5669
|
`/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(
|
|
@@ -5673,9 +5675,9 @@ async function sandboxStatusCommand(repoArg, options = {}, command) {
|
|
|
5673
5675
|
async function sandboxHistoryCommand(sourceRepoArg, options = {}, command) {
|
|
5674
5676
|
const { config } = resolveBusinessContext(command, options);
|
|
5675
5677
|
const businessId = await resolveBusinessId(config, options);
|
|
5676
|
-
const sourceRepo =
|
|
5678
|
+
const sourceRepo = parseRepoArgOrCurrent(sourceRepoArg);
|
|
5677
5679
|
const params = new URLSearchParams({ sourceRepo: repoPath(sourceRepo) });
|
|
5678
|
-
if (options.targetRepo) params.set("targetRepo", repoPath(
|
|
5680
|
+
if (options.targetRepo) params.set("targetRepo", repoPath(parseRepoArgOrCurrent(options.targetRepo)));
|
|
5679
5681
|
if (options.status) params.set("status", options.status);
|
|
5680
5682
|
if (options.limit) {
|
|
5681
5683
|
const limit = Number(options.limit);
|
|
@@ -5770,13 +5772,19 @@ async function sandboxLogsCommand(buildId, options = {}, command) {
|
|
|
5770
5772
|
}
|
|
5771
5773
|
if (!options.follow) return;
|
|
5772
5774
|
if (payload.logs.length > 0) afterSequence = payload.logs[payload.logs.length - 1].sequence;
|
|
5773
|
-
await sleep(
|
|
5775
|
+
await sleep(
|
|
5776
|
+
parsePollInterval(options.pollInterval, {
|
|
5777
|
+
defaultMs: DEFAULT_POLL_INTERVAL_MS,
|
|
5778
|
+
minMs: MIN_POLL_INTERVAL_MS,
|
|
5779
|
+
maxMs: null
|
|
5780
|
+
})
|
|
5781
|
+
);
|
|
5774
5782
|
}
|
|
5775
5783
|
}
|
|
5776
5784
|
async function sandboxAssignDefaultCommand(sourceRepoArg, options = {}, command) {
|
|
5777
5785
|
const { config } = resolveBusinessContext(command, options);
|
|
5778
5786
|
const businessId = await resolveBusinessId(config, options);
|
|
5779
|
-
const sourceRepo =
|
|
5787
|
+
const sourceRepo = parseRepoArgOrCurrent(sourceRepoArg);
|
|
5780
5788
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
5781
5789
|
const payload = await apiFetch(
|
|
5782
5790
|
config,
|
|
@@ -5793,8 +5801,8 @@ async function sandboxAssignDefaultCommand(sourceRepoArg, options = {}, command)
|
|
|
5793
5801
|
async function sandboxAssignRepoCommand(targetRepoArg, sourceRepoArg, options = {}, command) {
|
|
5794
5802
|
const { config } = resolveBusinessContext(command, options);
|
|
5795
5803
|
const businessId = await resolveBusinessId(config, options);
|
|
5796
|
-
const targetRepo =
|
|
5797
|
-
const sourceRepo =
|
|
5804
|
+
const targetRepo = parseRepoArgOrCurrent(targetRepoArg);
|
|
5805
|
+
const sourceRepo = parseRepoArgOrCurrent(sourceRepoArg);
|
|
5798
5806
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
5799
5807
|
const payload = await apiFetch(
|
|
5800
5808
|
config,
|
|
@@ -5832,7 +5840,7 @@ async function sandboxUnassignRepoCommand(targetRepoArg, options = {}, command)
|
|
|
5832
5840
|
}
|
|
5833
5841
|
const { config } = resolveBusinessContext(command, options);
|
|
5834
5842
|
const businessId = await resolveBusinessId(config, options);
|
|
5835
|
-
const targetRepo =
|
|
5843
|
+
const targetRepo = parseRepoArgOrCurrent(targetRepoArg);
|
|
5836
5844
|
if (!options.yes) await confirmOrThrow(`Clear sandbox layer assignment for ${repoPath(targetRepo)}?`);
|
|
5837
5845
|
const payload = await apiFetch(
|
|
5838
5846
|
config,
|
|
@@ -5845,35 +5853,29 @@ async function sandboxUnassignRepoCommand(targetRepoArg, options = {}, command)
|
|
|
5845
5853
|
}
|
|
5846
5854
|
|
|
5847
5855
|
// src/commands/sessions.ts
|
|
5848
|
-
var MAX_ALL_PAGES2 = 1e3;
|
|
5849
5856
|
async function listSessionsCommand(options, command) {
|
|
5850
5857
|
const runtime = getRuntimeOptions(command, options);
|
|
5851
5858
|
const config = requireConfig(runtime);
|
|
5852
|
-
const sessions2 =
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
sessions2.push(...payload2.sessions);
|
|
5873
|
-
nextCursor = payload2.nextCursor;
|
|
5874
|
-
cursor2 = nextCursor ?? void 0;
|
|
5875
|
-
} while (options.all === true && nextCursor);
|
|
5876
|
-
const payload = { sessions: sessions2, nextCursor: options.all === true ? null : nextCursor };
|
|
5859
|
+
const { items: sessions2, nextCursor } = await fetchPages(
|
|
5860
|
+
"sessions list --all",
|
|
5861
|
+
options.all === true,
|
|
5862
|
+
options.cursor,
|
|
5863
|
+
async (cursor2) => {
|
|
5864
|
+
const query = new URLSearchParams();
|
|
5865
|
+
if (options.status) query.set("status", options.status);
|
|
5866
|
+
if (options.scope) query.set("scope", options.scope);
|
|
5867
|
+
if (options.search) query.set("q", options.search);
|
|
5868
|
+
if (options.repo) query.set("repo", options.repo);
|
|
5869
|
+
if (options.limit) query.set("limit", options.limit);
|
|
5870
|
+
if (cursor2) query.set("cursor", cursor2);
|
|
5871
|
+
const payload2 = await apiFetch(
|
|
5872
|
+
config,
|
|
5873
|
+
`/api/sessions${query.size ? `?${query.toString()}` : ""}`
|
|
5874
|
+
);
|
|
5875
|
+
return { items: payload2.sessions, nextCursor: payload2.nextCursor };
|
|
5876
|
+
}
|
|
5877
|
+
);
|
|
5878
|
+
const payload = { sessions: sessions2, nextCursor };
|
|
5877
5879
|
if (isJson(command, options)) {
|
|
5878
5880
|
writeJson(payload);
|
|
5879
5881
|
return;
|
|
@@ -6015,25 +6017,15 @@ async function stopCommand(sessionId, options = {}, command) {
|
|
|
6015
6017
|
});
|
|
6016
6018
|
}
|
|
6017
6019
|
function parseStopBlocked(err) {
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
if (body.error !== "session_not_stoppable") return null;
|
|
6022
|
-
return { reason: body.reason ?? "not_stoppable" };
|
|
6023
|
-
} catch {
|
|
6024
|
-
return null;
|
|
6025
|
-
}
|
|
6020
|
+
const body = parseConflictBody(err);
|
|
6021
|
+
if (!body || body.error !== "session_not_stoppable") return null;
|
|
6022
|
+
return { reason: typeof body.reason === "string" ? body.reason : "not_stoppable" };
|
|
6026
6023
|
}
|
|
6027
6024
|
|
|
6028
6025
|
// src/commands/test-creds.ts
|
|
6029
6026
|
import { readFileSync as readFileSync4 } from "fs";
|
|
6030
|
-
function parseRepoArg4(value) {
|
|
6031
|
-
const parsed = parseGithubRepoFullName(value);
|
|
6032
|
-
if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
6033
|
-
return { owner: parsed.owner, name: parsed.repo };
|
|
6034
|
-
}
|
|
6035
6027
|
function basePath(businessId, repo) {
|
|
6036
|
-
return `/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.
|
|
6028
|
+
return `/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/test-credentials`;
|
|
6037
6029
|
}
|
|
6038
6030
|
function readValue(options) {
|
|
6039
6031
|
const sources = [options.value, options.valueFile, options.valueStdin].filter((v) => v !== void 0 && v !== false);
|
|
@@ -6046,11 +6038,11 @@ function readValue(options) {
|
|
|
6046
6038
|
}
|
|
6047
6039
|
async function listTestCredentialsCommand(repo, options, command) {
|
|
6048
6040
|
const { config } = resolveBusinessContext(command, options);
|
|
6049
|
-
const repoArg =
|
|
6041
|
+
const repoArg = parseRepoArg(repo);
|
|
6050
6042
|
const payload = await apiFetch(config, basePath(options.business, repoArg));
|
|
6051
6043
|
emit(command, options, payload, (listPayload) => {
|
|
6052
6044
|
if (listPayload.credentials.length === 0) {
|
|
6053
|
-
console.log(`No test credentials configured for ${repoArg.owner}/${repoArg.
|
|
6045
|
+
console.log(`No test credentials configured for ${repoArg.owner}/${repoArg.repo}.`);
|
|
6054
6046
|
return;
|
|
6055
6047
|
}
|
|
6056
6048
|
for (const cred of listPayload.credentials) {
|
|
@@ -6061,7 +6053,7 @@ async function listTestCredentialsCommand(repo, options, command) {
|
|
|
6061
6053
|
}
|
|
6062
6054
|
async function setTestCredentialCommand(repo, name, options, command) {
|
|
6063
6055
|
const { config } = resolveBusinessContext(command, options);
|
|
6064
|
-
const repoArg =
|
|
6056
|
+
const repoArg = parseRepoArg(repo);
|
|
6065
6057
|
const value = readValue(options);
|
|
6066
6058
|
const payload = await apiFetch(
|
|
6067
6059
|
config,
|
|
@@ -6072,7 +6064,7 @@ async function setTestCredentialCommand(repo, name, options, command) {
|
|
|
6072
6064
|
command,
|
|
6073
6065
|
options,
|
|
6074
6066
|
payload,
|
|
6075
|
-
() => console.log(`Set test credential '${name}' for ${repoArg.owner}/${repoArg.
|
|
6067
|
+
() => console.log(`Set test credential '${name}' for ${repoArg.owner}/${repoArg.repo}.`)
|
|
6076
6068
|
);
|
|
6077
6069
|
}
|
|
6078
6070
|
async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
@@ -6080,11 +6072,11 @@ async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
|
6080
6072
|
throw new CliError("user", "`test-creds delete --json` requires --yes.");
|
|
6081
6073
|
}
|
|
6082
6074
|
if (options.yes !== true) {
|
|
6083
|
-
const repoArg2 =
|
|
6084
|
-
await confirmOrThrow(`Delete test credential '${name}' for ${repoArg2.owner}/${repoArg2.
|
|
6075
|
+
const repoArg2 = parseRepoArg(repo);
|
|
6076
|
+
await confirmOrThrow(`Delete test credential '${name}' for ${repoArg2.owner}/${repoArg2.repo}?`);
|
|
6085
6077
|
}
|
|
6086
6078
|
const { config } = resolveBusinessContext(command, options);
|
|
6087
|
-
const repoArg =
|
|
6079
|
+
const repoArg = parseRepoArg(repo);
|
|
6088
6080
|
const payload = await apiFetch(
|
|
6089
6081
|
config,
|
|
6090
6082
|
`${basePath(options.business, repoArg)}/${encodeURIComponent(name)}`,
|
|
@@ -6094,35 +6086,29 @@ async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
|
6094
6086
|
command,
|
|
6095
6087
|
options,
|
|
6096
6088
|
payload,
|
|
6097
|
-
() => console.log(`Deleted test credential '${name}' for ${repoArg.owner}/${repoArg.
|
|
6089
|
+
() => console.log(`Deleted test credential '${name}' for ${repoArg.owner}/${repoArg.repo}.`)
|
|
6098
6090
|
);
|
|
6099
6091
|
}
|
|
6100
6092
|
|
|
6101
6093
|
// src/commands/tokens.ts
|
|
6102
|
-
var MAX_ALL_PAGES3 = 1e3;
|
|
6103
6094
|
async function listTokensCommand(options, command) {
|
|
6104
6095
|
const { config } = resolveBusinessContext(command, options);
|
|
6105
|
-
const data =
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6096
|
+
const { items: data, nextCursor } = await fetchPages(
|
|
6097
|
+
"tokens list --all",
|
|
6098
|
+
options.all === true,
|
|
6099
|
+
options.cursor,
|
|
6100
|
+
async (cursor2) => {
|
|
6101
|
+
const query = new URLSearchParams();
|
|
6102
|
+
if (options.limit) query.set("limit", options.limit);
|
|
6103
|
+
if (cursor2) query.set("cursor", cursor2);
|
|
6104
|
+
const payload2 = await apiFetch(
|
|
6105
|
+
config,
|
|
6106
|
+
`/api/cli-tokens${query.size ? `?${query.toString()}` : ""}`
|
|
6107
|
+
);
|
|
6108
|
+
return { items: payload2.data, nextCursor: payload2.nextCursor };
|
|
6113
6109
|
}
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
if (cursor2) query.set("cursor", cursor2);
|
|
6117
|
-
const payload2 = await apiFetch(
|
|
6118
|
-
config,
|
|
6119
|
-
`/api/cli-tokens${query.size ? `?${query.toString()}` : ""}`
|
|
6120
|
-
);
|
|
6121
|
-
data.push(...payload2.data);
|
|
6122
|
-
nextCursor = payload2.nextCursor;
|
|
6123
|
-
cursor2 = nextCursor ?? void 0;
|
|
6124
|
-
} while (options.all === true && nextCursor);
|
|
6125
|
-
const payload = { data, nextCursor: options.all === true ? null : nextCursor };
|
|
6110
|
+
);
|
|
6111
|
+
const payload = { data, nextCursor };
|
|
6126
6112
|
emit(command, options, payload, (listPayload) => {
|
|
6127
6113
|
if (listPayload.data.length === 0) {
|
|
6128
6114
|
console.log("No CLI tokens found.");
|
|
@@ -6405,14 +6391,17 @@ cursor.command("use <state>").description("Turn using your saved Cursor subscrip
|
|
|
6405
6391
|
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));
|
|
6406
6392
|
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));
|
|
6407
6393
|
var sessions = program.command("sessions").description("Session commands");
|
|
6408
|
-
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("--wait", "Wait for the review and print its verdict and findings").addHelpText(
|
|
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(
|
|
6409
6395
|
"after",
|
|
6410
6396
|
`
|
|
6411
6397
|
Examples:
|
|
6412
6398
|
arcanist review https://github.com/org/repo/pull/123 --wait --json
|
|
6413
6399
|
arcanist review https://github.com/org/repo/pull/123 --focus "Check auth" --wait
|
|
6400
|
+
arcanist review https://github.com/org/repo/pull/123 --model gpt-5.5 --wait
|
|
6414
6401
|
|
|
6415
|
-
|
|
6402
|
+
When the PR came from an Arcanist session, Zeus defaults to the runtime backend opposite
|
|
6403
|
+
the author's as a cross-check; otherwise it uses the default Codex reviewer. --model pins
|
|
6404
|
+
a specific reviewer model instead (members only).
|
|
6416
6405
|
Zeus review is currently available to Arcanist members only.
|
|
6417
6406
|
`
|
|
6418
6407
|
).action((prUrl, options, command) => reviewCommand(prUrl, options, command));
|