@tryarcanist/cli 0.1.380 → 0.1.381
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -114
- package/dist/index.js +651 -1778
- package/package.json +4 -5
package/dist/index.js
CHANGED
|
@@ -3773,7 +3773,7 @@ ${pointer}
|
|
|
3773
3773
|
var require_resolve_props = __commonJS({
|
|
3774
3774
|
"../../node_modules/yaml/dist/compose/resolve-props.js"(exports) {
|
|
3775
3775
|
"use strict";
|
|
3776
|
-
function resolveProps(
|
|
3776
|
+
function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) {
|
|
3777
3777
|
let spaceBefore = false;
|
|
3778
3778
|
let atNewline = startOnNewline;
|
|
3779
3779
|
let hasSpace = startOnNewline;
|
|
@@ -3788,7 +3788,7 @@ var require_resolve_props = __commonJS({
|
|
|
3788
3788
|
let comma = null;
|
|
3789
3789
|
let found = null;
|
|
3790
3790
|
let start = null;
|
|
3791
|
-
for (const token of
|
|
3791
|
+
for (const token of tokens) {
|
|
3792
3792
|
if (reqSpace) {
|
|
3793
3793
|
if (token.type !== "space" && token.type !== "newline" && token.type !== "comma")
|
|
3794
3794
|
onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space");
|
|
@@ -3879,7 +3879,7 @@ var require_resolve_props = __commonJS({
|
|
|
3879
3879
|
hasSpace = false;
|
|
3880
3880
|
}
|
|
3881
3881
|
}
|
|
3882
|
-
const last =
|
|
3882
|
+
const last = tokens[tokens.length - 1];
|
|
3883
3883
|
const end = last ? last.offset + last.source.length : offset;
|
|
3884
3884
|
if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) {
|
|
3885
3885
|
onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space");
|
|
@@ -5218,8 +5218,8 @@ ${cb}` : comment;
|
|
|
5218
5218
|
* @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document.
|
|
5219
5219
|
* @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly.
|
|
5220
5220
|
*/
|
|
5221
|
-
*compose(
|
|
5222
|
-
for (const token of
|
|
5221
|
+
*compose(tokens, forceDoc = false, endOffset = -1) {
|
|
5222
|
+
for (const token of tokens)
|
|
5223
5223
|
yield* this.next(token);
|
|
5224
5224
|
yield* this.end(forceDoc, endOffset);
|
|
5225
5225
|
}
|
|
@@ -7409,8 +7409,8 @@ function isTerminalPhase(phase, _sessionKind) {
|
|
|
7409
7409
|
// package.json
|
|
7410
7410
|
var package_default = {
|
|
7411
7411
|
name: "@tryarcanist/cli",
|
|
7412
|
-
version: "0.1.
|
|
7413
|
-
description: "Command-line interface for Arcanist: inspect sessions, prepare
|
|
7412
|
+
version: "0.1.381",
|
|
7413
|
+
description: "Command-line interface for Arcanist: inspect sessions, prepare Arcanist environments, and trigger reviews",
|
|
7414
7414
|
type: "module",
|
|
7415
7415
|
bin: {
|
|
7416
7416
|
arcanist: "dist/index.js"
|
|
@@ -7423,9 +7423,8 @@ var package_default = {
|
|
|
7423
7423
|
],
|
|
7424
7424
|
scripts: {
|
|
7425
7425
|
build: "tsup",
|
|
7426
|
-
typecheck: "tsc --noEmit",
|
|
7427
|
-
prepublishOnly: "npm run build"
|
|
7428
|
-
"bundle:sandbox": "tsup --config tsup.sandbox.config.ts"
|
|
7426
|
+
typecheck: "node ../../node_modules/typescript-7/bin/tsc --noEmit",
|
|
7427
|
+
prepublishOnly: "npm run build"
|
|
7429
7428
|
},
|
|
7430
7429
|
publishConfig: {
|
|
7431
7430
|
access: "public"
|
|
@@ -7513,20 +7512,6 @@ function parseApiErrorBody(body) {
|
|
|
7513
7512
|
return null;
|
|
7514
7513
|
}
|
|
7515
7514
|
}
|
|
7516
|
-
function parseConflictBody(err) {
|
|
7517
|
-
if (!(err instanceof ApiError) || err.status !== 409) return null;
|
|
7518
|
-
try {
|
|
7519
|
-
const parsed = JSON.parse(err.body);
|
|
7520
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
7521
|
-
const body = parsed;
|
|
7522
|
-
return {
|
|
7523
|
-
...typeof body.error === "string" ? { error: body.error } : {},
|
|
7524
|
-
...typeof body.reason === "string" ? { reason: body.reason } : {}
|
|
7525
|
-
};
|
|
7526
|
-
} catch {
|
|
7527
|
-
return null;
|
|
7528
|
-
}
|
|
7529
|
-
}
|
|
7530
7515
|
|
|
7531
7516
|
// src/errors.ts
|
|
7532
7517
|
var EXIT_CODE_INTERRUPTED = 130;
|
|
@@ -7600,7 +7585,6 @@ function messageForApiError(status, body, parsed = parseApiErrorBody(body)) {
|
|
|
7600
7585
|
function hintForHttpStatus(status, resourceNoun, parsed) {
|
|
7601
7586
|
if (status === 401 || status === 403) return "Run `arcanist auth login` or set `ARCANIST_TOKEN`.";
|
|
7602
7587
|
if (status === 404) {
|
|
7603
|
-
if (resourceNoun === "token") return "List tokens with `arcanist tokens list`.";
|
|
7604
7588
|
if (resourceNoun === "repo") return "Check accessible repositories with `arcanist repos list`.";
|
|
7605
7589
|
return "List sessions with `arcanist sessions list`.";
|
|
7606
7590
|
}
|
|
@@ -7701,7 +7685,6 @@ async function apiRequest(connection, path, init, read) {
|
|
|
7701
7685
|
}
|
|
7702
7686
|
}
|
|
7703
7687
|
function resourceNounForPath(path) {
|
|
7704
|
-
if (path.startsWith("/api/cli-tokens")) return "token";
|
|
7705
7688
|
if (path.startsWith("/api/repos")) return "repo";
|
|
7706
7689
|
if (path.startsWith("/api/sessions")) return "session";
|
|
7707
7690
|
return void 0;
|
|
@@ -7709,9 +7692,6 @@ function resourceNounForPath(path) {
|
|
|
7709
7692
|
async function apiFetch(config2, path, init) {
|
|
7710
7693
|
return apiRequest(config2, path, init, (res) => res.json());
|
|
7711
7694
|
}
|
|
7712
|
-
async function publicApiFetch(apiUrl, path, init) {
|
|
7713
|
-
return apiRequest({ apiUrl }, path, init, (res) => res.json());
|
|
7714
|
-
}
|
|
7715
7695
|
async function apiFetchText(config2, path, init) {
|
|
7716
7696
|
return apiRequest(config2, path, init, (res) => res.text());
|
|
7717
7697
|
}
|
|
@@ -7733,8 +7713,6 @@ var CONFIG_DIR = join(homedir(), ".arcanist");
|
|
|
7733
7713
|
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
7734
7714
|
var PROJECT_CONFIG_FILE = ".arcanist-cli.json";
|
|
7735
7715
|
var DEFAULT_API_URL = "https://app.tryarcanist.com";
|
|
7736
|
-
var SANDBOX_CLI_AUTH_WAIT_TIMEOUT_MS = 1e4;
|
|
7737
|
-
var SANDBOX_CLI_AUTH_WAIT_INTERVAL_MS = 100;
|
|
7738
7716
|
function loadFileConfig() {
|
|
7739
7717
|
try {
|
|
7740
7718
|
return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
@@ -7747,7 +7725,7 @@ function loadConfig(overrides = {}) {
|
|
|
7747
7725
|
if (completeEnvOrFlagConfig) return validateAndNormalizeConfig(completeEnvOrFlagConfig);
|
|
7748
7726
|
const projectConfig = loadProjectConfig();
|
|
7749
7727
|
const envOrFlagConfig = resolveEnvOrFlagConfig(overrides, projectConfig !== null);
|
|
7750
|
-
const fileConfig =
|
|
7728
|
+
const fileConfig = loadFileConfig();
|
|
7751
7729
|
const apiUrl = envOrFlagConfig?.apiUrl ?? projectConfig?.apiUrl ?? fileConfig?.apiUrl;
|
|
7752
7730
|
const token = envOrFlagConfig?.token ?? projectConfig?.token ?? fileConfig?.token;
|
|
7753
7731
|
if (!apiUrl || !token) return null;
|
|
@@ -7834,24 +7812,6 @@ function validateAndNormalizeConfig(config2) {
|
|
|
7834
7812
|
if (urlError) throw new CliError("user", urlError);
|
|
7835
7813
|
return { apiUrl: normalizeBaseUrl(config2.apiUrl), token: config2.token };
|
|
7836
7814
|
}
|
|
7837
|
-
function shouldWaitForSandboxCliAuth() {
|
|
7838
|
-
const pendingPath = process.env.ARCANIST_CLI_AUTH_PENDING_PATH;
|
|
7839
|
-
return Boolean(pendingPath && existsSync(pendingPath));
|
|
7840
|
-
}
|
|
7841
|
-
function waitForSandboxCliAuthConfig() {
|
|
7842
|
-
const readyPath = process.env.ARCANIST_CLI_AUTH_READY_PATH;
|
|
7843
|
-
const failedPath = process.env.ARCANIST_CLI_AUTH_FAILED_PATH;
|
|
7844
|
-
const deadline = Date.now() + SANDBOX_CLI_AUTH_WAIT_TIMEOUT_MS;
|
|
7845
|
-
while (Date.now() <= deadline) {
|
|
7846
|
-
if (readyPath && existsSync(readyPath)) return loadFileConfig();
|
|
7847
|
-
if (failedPath && existsSync(failedPath)) return null;
|
|
7848
|
-
sleepSync(SANDBOX_CLI_AUTH_WAIT_INTERVAL_MS);
|
|
7849
|
-
}
|
|
7850
|
-
return readyPath && existsSync(readyPath) ? loadFileConfig() : null;
|
|
7851
|
-
}
|
|
7852
|
-
function sleepSync(ms) {
|
|
7853
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
7854
|
-
}
|
|
7855
7815
|
function findGitRoot(cwd) {
|
|
7856
7816
|
let current = cwd;
|
|
7857
7817
|
while (true) {
|
|
@@ -7917,7 +7877,6 @@ function isLoopbackHost(parsed) {
|
|
|
7917
7877
|
|
|
7918
7878
|
// src/runtime.ts
|
|
7919
7879
|
import { randomUUID } from "crypto";
|
|
7920
|
-
import { createInterface } from "readline/promises";
|
|
7921
7880
|
function getRuntimeOptions(command, options = {}) {
|
|
7922
7881
|
const globals = command?.optsWithGlobals?.();
|
|
7923
7882
|
const merged = { ...globals, ...options };
|
|
@@ -7951,20 +7910,6 @@ async function readStdin() {
|
|
|
7951
7910
|
async function readStdinTrimmed() {
|
|
7952
7911
|
return (await readStdin()).trim();
|
|
7953
7912
|
}
|
|
7954
|
-
async function confirmOrThrow(message) {
|
|
7955
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
7956
|
-
throw new CliError("user", "Confirmation required. Re-run with --yes in non-interactive environments.");
|
|
7957
|
-
}
|
|
7958
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
7959
|
-
try {
|
|
7960
|
-
const answer = (await rl.question(`${message} [y/N] `)).trim().toLowerCase();
|
|
7961
|
-
if (answer !== "y" && answer !== "yes") {
|
|
7962
|
-
throw new CliError("user", "Aborted.");
|
|
7963
|
-
}
|
|
7964
|
-
} finally {
|
|
7965
|
-
rl.close();
|
|
7966
|
-
}
|
|
7967
|
-
}
|
|
7968
7913
|
function applyColorEnvironment(options) {
|
|
7969
7914
|
if (options.noColor === true || !process.stdout.isTTY) {
|
|
7970
7915
|
process.env.NO_COLOR = "1";
|
|
@@ -7973,17 +7918,6 @@ function applyColorEnvironment(options) {
|
|
|
7973
7918
|
function randomIdempotencyKey() {
|
|
7974
7919
|
return randomUUID();
|
|
7975
7920
|
}
|
|
7976
|
-
function assertArcanistSessionMutationAllowed(subcommand) {
|
|
7977
|
-
const role = process.env.ARCANIST_AGENT_ROLE?.trim();
|
|
7978
|
-
if (role !== "verification" && role !== "review") return;
|
|
7979
|
-
throw new CliError(
|
|
7980
|
-
"user",
|
|
7981
|
-
`\`arcanist ${subcommand}\` is disabled inside ${role} sessions because nested Arcanist work is not observable to read-only agents.`,
|
|
7982
|
-
{
|
|
7983
|
-
hint: "Use direct local/sandbox evidence instead, or report a verification gap rather than spawning another Arcanist session."
|
|
7984
|
-
}
|
|
7985
|
-
);
|
|
7986
|
-
}
|
|
7987
7921
|
|
|
7988
7922
|
// src/utils/terminal-text.ts
|
|
7989
7923
|
function sanitizeTerminalText(value) {
|
|
@@ -27026,7 +26960,6 @@ var INTEGRATION_REGISTRY = {
|
|
|
27026
26960
|
};
|
|
27027
26961
|
|
|
27028
26962
|
// ../../shared/constants/onboarding.ts
|
|
27029
|
-
var MAX_ENVIRONMENT_SETUP_REPOSITORIES = 20;
|
|
27030
26963
|
var CREDENTIAL_VALIDATION_STATUS = {
|
|
27031
26964
|
VALIDATED: "validated",
|
|
27032
26965
|
SAVED_UNVERIFIED: "saved_unverified",
|
|
@@ -27275,191 +27208,11 @@ async function codexStatusCommand(options, command) {
|
|
|
27275
27208
|
if (status) console.log(`Status: ${status}`);
|
|
27276
27209
|
});
|
|
27277
27210
|
}
|
|
27278
|
-
async function codexLogoutCommand(options, command) {
|
|
27279
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
27280
|
-
await setCodexSubscriptionEnabled(config2, false).catch(() => {
|
|
27281
|
-
});
|
|
27282
|
-
const payload = parseCodexResponse(
|
|
27283
|
-
codexSubscriptionCredentialStateSchema,
|
|
27284
|
-
await apiFetch(config2, CODEX_SUBSCRIPTION_AUTH_JSON_PATH, { method: "DELETE" })
|
|
27285
|
-
);
|
|
27286
|
-
emit(command, options, payload, () => console.log("Codex subscription auth deactivated and cleared."));
|
|
27287
|
-
}
|
|
27288
|
-
|
|
27289
|
-
// src/commands/egress.ts
|
|
27290
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
27291
|
-
|
|
27292
|
-
// ../../shared/types/business-egress-policy.ts
|
|
27293
|
-
var MAX_BUSINESS_EGRESS_DOMAINS = 100;
|
|
27294
|
-
var MAX_DOMAIN_LENGTH = 253;
|
|
27295
|
-
function normalizeEgressDomains(entries, key = "egressAllowlist") {
|
|
27296
|
-
const unique = [...new Set(entries.map((entry) => entry.trim().toLowerCase()).filter(Boolean))].sort();
|
|
27297
|
-
if (unique.length > MAX_BUSINESS_EGRESS_DOMAINS) {
|
|
27298
|
-
throw new Error(`${key} must contain at most ${MAX_BUSINESS_EGRESS_DOMAINS} domain names`);
|
|
27299
|
-
}
|
|
27300
|
-
for (const entry of unique) {
|
|
27301
|
-
assertValidEgressDomain(entry, key);
|
|
27302
|
-
}
|
|
27303
|
-
return unique;
|
|
27304
|
-
}
|
|
27305
|
-
function assertValidEgressDomain(value, key = "egressAllowlist") {
|
|
27306
|
-
if (value.includes("*") || value.startsWith(".") || value.endsWith(".") || value.includes("/") || value.includes(":")) {
|
|
27307
|
-
throw new Error(`${key} entries must be exact domain names`);
|
|
27308
|
-
}
|
|
27309
|
-
if (value.length > MAX_DOMAIN_LENGTH) {
|
|
27310
|
-
throw new Error(`${key} entries must be exact domain names`);
|
|
27311
|
-
}
|
|
27312
|
-
const labels = value.split(".");
|
|
27313
|
-
if (labels.length === 4 && labels.every((label) => /^\d+$/.test(label) && Number(label) >= 0 && Number(label) <= 255)) {
|
|
27314
|
-
throw new Error(`${key} entries must be exact domain names`);
|
|
27315
|
-
}
|
|
27316
|
-
if (labels.length < 2 || labels.some(
|
|
27317
|
-
(label) => label.length < 1 || label.length > 63 || !/^[a-z0-9-]+$/.test(label) || label.startsWith("-") || label.endsWith("-")
|
|
27318
|
-
)) {
|
|
27319
|
-
throw new Error(`${key} entries must be exact domain names`);
|
|
27320
|
-
}
|
|
27321
|
-
}
|
|
27322
|
-
|
|
27323
|
-
// ../../shared/egress-allowlist/parser.ts
|
|
27324
|
-
var EGRESS_ALLOWLIST_SOURCE_PATH = ".arcanist/egress-allowlist.txt";
|
|
27325
|
-
function parseEgressAllowlistSourceFile(content, key = EGRESS_ALLOWLIST_SOURCE_PATH) {
|
|
27326
|
-
const domains = content.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
27327
|
-
return normalizeEgressDomains(domains, key);
|
|
27328
|
-
}
|
|
27329
|
-
|
|
27330
|
-
// ../../shared/github/repo-url.ts
|
|
27331
|
-
var REPO_SEGMENT_PATTERN = /^[A-Za-z0-9_.][A-Za-z0-9_.-]*$/;
|
|
27332
|
-
var MAX_REPO_SEGMENT_LENGTH = 100;
|
|
27333
|
-
function isValidGithubRepoSegment(value) {
|
|
27334
|
-
if (typeof value !== "string") return false;
|
|
27335
|
-
if (!value || value.length > MAX_REPO_SEGMENT_LENGTH) return false;
|
|
27336
|
-
if (value === "." || value === "..") return false;
|
|
27337
|
-
return REPO_SEGMENT_PATTERN.test(value);
|
|
27338
|
-
}
|
|
27339
|
-
function parseGithubRepoCoordinate(owner, repo) {
|
|
27340
|
-
return isValidGithubRepoSegment(owner) && isValidGithubRepoSegment(repo) ? { owner, repo } : null;
|
|
27341
|
-
}
|
|
27342
|
-
function parseGithubRepoFullName(value) {
|
|
27343
|
-
const match = value.match(/^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/) ?? value.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/) ?? value.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
|
|
27344
|
-
return match ? parseGithubRepoCoordinate(match[1], match[2]) : null;
|
|
27345
|
-
}
|
|
27346
|
-
|
|
27347
|
-
// src/git.ts
|
|
27348
|
-
import { execFileSync } from "child_process";
|
|
27349
|
-
function git(args) {
|
|
27350
|
-
try {
|
|
27351
|
-
return execFileSync("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
27352
|
-
} catch (err) {
|
|
27353
|
-
throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
|
|
27354
|
-
}
|
|
27355
|
-
}
|
|
27356
|
-
function currentRepo() {
|
|
27357
|
-
const parsed = parseGithubRepoFullName(git(["remote", "get-url", "origin"]));
|
|
27358
|
-
if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
|
|
27359
|
-
return { owner: parsed.owner, repo: parsed.repo };
|
|
27360
|
-
}
|
|
27361
|
-
|
|
27362
|
-
// src/utils/repo-arg.ts
|
|
27363
|
-
function parseRepoArg(value, argName = "repo") {
|
|
27364
|
-
const parsed = parseGithubRepoFullName(value);
|
|
27365
|
-
if (!parsed) throw new CliError("user", `${argName} must be owner/name or a GitHub URL`);
|
|
27366
|
-
return { owner: parsed.owner, repo: parsed.repo.replace(/\.git$/, "") };
|
|
27367
|
-
}
|
|
27368
|
-
function parseRepoArgOrCurrent(value, argName = "repo") {
|
|
27369
|
-
if (value === void 0) return currentRepo();
|
|
27370
|
-
return parseRepoArg(value, argName);
|
|
27371
|
-
}
|
|
27372
|
-
|
|
27373
|
-
// src/commands/egress.ts
|
|
27374
|
-
async function egressSourceSetCommand(sourceRepoArg, options = {}, command) {
|
|
27375
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
27376
|
-
const businessId = await resolveBusinessId(config2, options);
|
|
27377
|
-
const source = parseRepoArg(sourceRepoArg);
|
|
27378
|
-
const payload = await apiFetch(config2, `/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/source`, {
|
|
27379
|
-
method: "PUT",
|
|
27380
|
-
body: JSON.stringify({ sourceRepoOwner: source.owner, sourceRepoName: source.repo })
|
|
27381
|
-
});
|
|
27382
|
-
emit(
|
|
27383
|
-
command,
|
|
27384
|
-
options,
|
|
27385
|
-
payload,
|
|
27386
|
-
() => console.log(`Set egress allowlist source to ${source.owner}/${source.repo}:${EGRESS_ALLOWLIST_SOURCE_PATH}.`)
|
|
27387
|
-
);
|
|
27388
|
-
}
|
|
27389
|
-
async function egressSourceGetCommand(options = {}, command) {
|
|
27390
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
27391
|
-
const businessId = await resolveBusinessId(config2, options);
|
|
27392
|
-
const payload = await apiFetch(
|
|
27393
|
-
config2,
|
|
27394
|
-
`/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/source`
|
|
27395
|
-
);
|
|
27396
|
-
emit(command, options, payload, (sourcePayload) => {
|
|
27397
|
-
if (!sourcePayload.source) {
|
|
27398
|
-
console.log("No egress allowlist source configured.");
|
|
27399
|
-
return;
|
|
27400
|
-
}
|
|
27401
|
-
console.log(
|
|
27402
|
-
`${sourcePayload.source.sourceRepoOwner}/${sourcePayload.source.sourceRepoName}:${sourcePayload.path} (${sourcePayload.defaultBranch ?? "default branch"})`
|
|
27403
|
-
);
|
|
27404
|
-
console.log(`${sourcePayload.domains.length} ${sourcePayload.domains.length === 1 ? "domain" : "domains"}.`);
|
|
27405
|
-
});
|
|
27406
|
-
}
|
|
27407
|
-
async function egressAddCommand(domains, options = {}, command) {
|
|
27408
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
27409
|
-
const businessId = await resolveBusinessId(config2, options);
|
|
27410
|
-
const normalized = normalizeEgressDomains(domains, "domains");
|
|
27411
|
-
const payload = await apiFetch(
|
|
27412
|
-
config2,
|
|
27413
|
-
`/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/pull-request`,
|
|
27414
|
-
{
|
|
27415
|
-
method: "POST",
|
|
27416
|
-
body: JSON.stringify({ domains: normalized, ...options.reason ? { reason: options.reason } : {} })
|
|
27417
|
-
}
|
|
27418
|
-
);
|
|
27419
|
-
emit(command, options, payload, (addPayload) => {
|
|
27420
|
-
if (addPayload.status === "unchanged") {
|
|
27421
|
-
console.log("All requested domains are already present in the source file.");
|
|
27422
|
-
return;
|
|
27423
|
-
}
|
|
27424
|
-
console.log(`Egress allowlist PR ${addPayload.status}: ${addPayload.prUrl}`);
|
|
27425
|
-
console.log(`Added: ${addPayload.addedDomains.join(", ")}`);
|
|
27426
|
-
});
|
|
27427
|
-
}
|
|
27428
|
-
async function egressSyncCommand(options = {}, command) {
|
|
27429
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
27430
|
-
const businessId = await resolveBusinessId(config2, options);
|
|
27431
|
-
const payload = await apiFetch(
|
|
27432
|
-
config2,
|
|
27433
|
-
`/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/sync`,
|
|
27434
|
-
{ method: "POST" }
|
|
27435
|
-
);
|
|
27436
|
-
emit(
|
|
27437
|
-
command,
|
|
27438
|
-
options,
|
|
27439
|
-
payload,
|
|
27440
|
-
(syncPayload) => console.log(
|
|
27441
|
-
`Applied ${syncPayload.egressAllowlist.length} egress allowlist domains from ${syncPayload.source.sourceRepoOwner}/${syncPayload.source.sourceRepoName}:${syncPayload.path}.`
|
|
27442
|
-
)
|
|
27443
|
-
);
|
|
27444
|
-
}
|
|
27445
|
-
async function egressValidateCommand(path = EGRESS_ALLOWLIST_SOURCE_PATH, options = {}, command) {
|
|
27446
|
-
const content = readFileSync2(path, "utf8");
|
|
27447
|
-
const domains = parseEgressAllowlistSourceFile(content, path);
|
|
27448
|
-
emit(command, options, { ok: true, path, domains }, (payload) => {
|
|
27449
|
-
console.log(`Valid egress allowlist: ${payload.path}`);
|
|
27450
|
-
console.log(`${payload.domains.length} ${payload.domains.length === 1 ? "domain" : "domains"}.`);
|
|
27451
|
-
});
|
|
27452
|
-
}
|
|
27453
27211
|
|
|
27454
27212
|
// ../../shared/utils/type-guards.ts
|
|
27455
27213
|
function isRecord(value) {
|
|
27456
27214
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
27457
27215
|
}
|
|
27458
|
-
function asNonEmptyString(value) {
|
|
27459
|
-
if (typeof value !== "string") return void 0;
|
|
27460
|
-
const trimmed = value.trim();
|
|
27461
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
27462
|
-
}
|
|
27463
27216
|
|
|
27464
27217
|
// ../../shared/browser-state/storage-state.ts
|
|
27465
27218
|
var MAX_BROWSER_STATE_BYTES = 1024 * 1024;
|
|
@@ -27709,7 +27462,7 @@ async function captureBrowserLogin(opts) {
|
|
|
27709
27462
|
}
|
|
27710
27463
|
|
|
27711
27464
|
// src/utils/prompt-line.ts
|
|
27712
|
-
import { createInterface
|
|
27465
|
+
import { createInterface } from "readline/promises";
|
|
27713
27466
|
async function promptLine(question, options = {}) {
|
|
27714
27467
|
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
27715
27468
|
throw new CliError(
|
|
@@ -27719,7 +27472,7 @@ async function promptLine(question, options = {}) {
|
|
|
27719
27472
|
}
|
|
27720
27473
|
const { signal } = options;
|
|
27721
27474
|
if (signal?.aborted) throw new CliError("user", "Cancelled.");
|
|
27722
|
-
const rl =
|
|
27475
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
27723
27476
|
const onAbort = () => rl.close();
|
|
27724
27477
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
27725
27478
|
try {
|
|
@@ -27734,6 +27487,49 @@ async function promptLine(question, options = {}) {
|
|
|
27734
27487
|
}
|
|
27735
27488
|
}
|
|
27736
27489
|
|
|
27490
|
+
// ../../shared/github/repo-url.ts
|
|
27491
|
+
var REPO_SEGMENT_PATTERN = /^[A-Za-z0-9_.][A-Za-z0-9_.-]*$/;
|
|
27492
|
+
var MAX_REPO_SEGMENT_LENGTH = 100;
|
|
27493
|
+
function isValidGithubRepoSegment(value) {
|
|
27494
|
+
if (typeof value !== "string") return false;
|
|
27495
|
+
if (!value || value.length > MAX_REPO_SEGMENT_LENGTH) return false;
|
|
27496
|
+
if (value === "." || value === "..") return false;
|
|
27497
|
+
return REPO_SEGMENT_PATTERN.test(value);
|
|
27498
|
+
}
|
|
27499
|
+
function parseGithubRepoCoordinate(owner, repo) {
|
|
27500
|
+
return isValidGithubRepoSegment(owner) && isValidGithubRepoSegment(repo) ? { owner, repo } : null;
|
|
27501
|
+
}
|
|
27502
|
+
function parseGithubRepoFullName(value) {
|
|
27503
|
+
const match = value.match(/^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/) ?? value.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/) ?? value.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
|
|
27504
|
+
return match ? parseGithubRepoCoordinate(match[1], match[2]) : null;
|
|
27505
|
+
}
|
|
27506
|
+
|
|
27507
|
+
// src/git.ts
|
|
27508
|
+
import { execFileSync } from "child_process";
|
|
27509
|
+
function git(args) {
|
|
27510
|
+
try {
|
|
27511
|
+
return execFileSync("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
27512
|
+
} catch (err) {
|
|
27513
|
+
throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
|
|
27514
|
+
}
|
|
27515
|
+
}
|
|
27516
|
+
function currentRepo() {
|
|
27517
|
+
const parsed = parseGithubRepoFullName(git(["remote", "get-url", "origin"]));
|
|
27518
|
+
if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
|
|
27519
|
+
return { owner: parsed.owner, repo: parsed.repo };
|
|
27520
|
+
}
|
|
27521
|
+
|
|
27522
|
+
// src/utils/repo-arg.ts
|
|
27523
|
+
function parseRepoArg(value, argName = "repo") {
|
|
27524
|
+
const parsed = parseGithubRepoFullName(value);
|
|
27525
|
+
if (!parsed) throw new CliError("user", `${argName} must be owner/name or a GitHub URL`);
|
|
27526
|
+
return { owner: parsed.owner, repo: parsed.repo.replace(/\.git$/, "") };
|
|
27527
|
+
}
|
|
27528
|
+
function parseRepoArgOrCurrent(value, argName = "repo") {
|
|
27529
|
+
if (value === void 0) return currentRepo();
|
|
27530
|
+
return parseRepoArg(value, argName);
|
|
27531
|
+
}
|
|
27532
|
+
|
|
27737
27533
|
// src/commands/environment-login.ts
|
|
27738
27534
|
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
27739
27535
|
async function resolveEnvironment(config2, target) {
|
|
@@ -27954,11 +27750,6 @@ async function pollUntilSettled(opts) {
|
|
|
27954
27750
|
}
|
|
27955
27751
|
throw new CliError("server", opts.timeoutMessage);
|
|
27956
27752
|
}
|
|
27957
|
-
function assertSameAttempt(triggeredSessionId, settledSessionId, label) {
|
|
27958
|
-
if (triggeredSessionId && settledSessionId !== triggeredSessionId) {
|
|
27959
|
-
throw new CliError("conflict", `${label} completed for a different attempt; retry the command.`);
|
|
27960
|
-
}
|
|
27961
|
-
}
|
|
27962
27753
|
|
|
27963
27754
|
// src/constants/watch.ts
|
|
27964
27755
|
var MIN_WATCH_POLL_INTERVAL_MS = 250;
|
|
@@ -28156,8 +27947,6 @@ async function environmentRetryCommand(repoArg, options = {}, command) {
|
|
|
28156
27947
|
}
|
|
28157
27948
|
|
|
28158
27949
|
// src/commands/login.ts
|
|
28159
|
-
var DEVICE_AUTH_START_PATH = "/api/auth/cli/device";
|
|
28160
|
-
var DEVICE_AUTH_TOKEN_PATH = "/api/auth/cli/device/token";
|
|
28161
27950
|
async function loginCommand(options, command) {
|
|
28162
27951
|
const runtime = getRuntimeOptions(command, options);
|
|
28163
27952
|
const apiUrl = resolveLoginApiUrl(runtime.apiUrl);
|
|
@@ -28167,11 +27956,11 @@ async function loginCommand(options, command) {
|
|
|
28167
27956
|
token = await readStdinTrimmed();
|
|
28168
27957
|
} else if (envOrFlag?.token) {
|
|
28169
27958
|
token = envOrFlag.token;
|
|
28170
|
-
} else {
|
|
28171
|
-
token = await loginWithDeviceAuthorization(apiUrl, runtime);
|
|
28172
27959
|
}
|
|
28173
27960
|
if (!token) {
|
|
28174
|
-
throw new CliError("user", "No token provided."
|
|
27961
|
+
throw new CliError("user", "No token provided.", {
|
|
27962
|
+
hint: "Pass --token-stdin or set ARCANIST_TOKEN. Mint a token in Settings > CLI."
|
|
27963
|
+
});
|
|
28175
27964
|
}
|
|
28176
27965
|
if (!token.startsWith("arc_")) {
|
|
28177
27966
|
throw new CliError("user", "Invalid token format. Token must start with 'arc_'.");
|
|
@@ -28194,640 +27983,90 @@ async function loginCommand(options, command) {
|
|
|
28194
27983
|
throw err;
|
|
28195
27984
|
}
|
|
28196
27985
|
}
|
|
28197
|
-
|
|
28198
|
-
|
|
28199
|
-
|
|
28200
|
-
|
|
27986
|
+
|
|
27987
|
+
// src/commands/repos.ts
|
|
27988
|
+
async function reposListCommand(options = {}, command) {
|
|
27989
|
+
const { config: config2 } = resolveBusinessContext(command, options);
|
|
27990
|
+
const payload = await apiFetch(config2, "/api/repos");
|
|
27991
|
+
emit(command, options, { repos: payload.repos, ssoOrgs: payload.ssoOrgs }, (data) => {
|
|
27992
|
+
for (const repo of data.repos) {
|
|
27993
|
+
console.log(`${String(repo.fullName ?? repo.name ?? repo.repo ?? "")} ${String(repo.defaultBranch ?? "")}`);
|
|
27994
|
+
}
|
|
27995
|
+
if (data.ssoOrgs.length > 0) console.log(`SSO withheld orgs: ${data.ssoOrgs.length}`);
|
|
28201
27996
|
});
|
|
28202
|
-
|
|
28203
|
-
|
|
28204
|
-
|
|
28205
|
-
|
|
28206
|
-
|
|
28207
|
-
|
|
28208
|
-
|
|
28209
|
-
|
|
28210
|
-
|
|
28211
|
-
|
|
28212
|
-
|
|
27997
|
+
}
|
|
27998
|
+
|
|
27999
|
+
// src/commands/review.ts
|
|
28000
|
+
var PR_REVIEW_POLL_INTERVAL_MS = 1e4;
|
|
28001
|
+
var PR_REVIEW_WAIT_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
28002
|
+
var SANDBOX_RUNTIME_BACKENDS = {
|
|
28003
|
+
e2b: "e2b_cloud",
|
|
28004
|
+
modal: "modal_cloud"
|
|
28005
|
+
};
|
|
28006
|
+
function isSandboxRuntimeOption(runtime) {
|
|
28007
|
+
return Object.hasOwn(SANDBOX_RUNTIME_BACKENDS, runtime);
|
|
28008
|
+
}
|
|
28009
|
+
function resolveSandboxRuntimeBackend(runtime) {
|
|
28010
|
+
if (runtime === void 0) return null;
|
|
28011
|
+
if (!isSandboxRuntimeOption(runtime)) {
|
|
28012
|
+
throw new CliError("user", "Sandbox runtime must be modal or e2b.");
|
|
28213
28013
|
}
|
|
28214
|
-
|
|
28014
|
+
return SANDBOX_RUNTIME_BACKENDS[runtime];
|
|
28215
28015
|
}
|
|
28216
|
-
function
|
|
28217
|
-
|
|
28218
|
-
|
|
28219
|
-
|
|
28220
|
-
|
|
28221
|
-
|
|
28016
|
+
async function reviewCommand(prUrl, options = {}, command) {
|
|
28017
|
+
const sandboxRuntimeBackend = resolveSandboxRuntimeBackend(options.sandboxRuntime);
|
|
28018
|
+
const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: PR_REVIEW_POLL_INTERVAL_MS, maxMs: null }) : null;
|
|
28019
|
+
const { config: config2 } = resolveBusinessContext(command, options);
|
|
28020
|
+
const trigger = await apiFetch(config2, "/api/pr-reviews", {
|
|
28021
|
+
method: "POST",
|
|
28022
|
+
body: JSON.stringify({
|
|
28023
|
+
prUrl,
|
|
28024
|
+
...sandboxRuntimeBackend !== null ? { sandboxRuntimeBackend } : {}
|
|
28025
|
+
})
|
|
28026
|
+
});
|
|
28027
|
+
if (!options.wait || trigger.cached) {
|
|
28028
|
+
emit(command, options, trigger, (payload) => {
|
|
28029
|
+
if (payload.cached) console.log(`Arcanist review already completed: ${payload.verdict ?? "unknown"}.`);
|
|
28030
|
+
else console.log(`Started Arcanist review${payload.requestId ? ` (${payload.requestId})` : ""}.`);
|
|
28031
|
+
});
|
|
28222
28032
|
return;
|
|
28223
28033
|
}
|
|
28224
|
-
|
|
28225
|
-
|
|
28226
|
-
|
|
28227
|
-
`);
|
|
28228
|
-
}
|
|
28229
|
-
|
|
28230
|
-
// ../../shared/agent/agent-runtime-backend.ts
|
|
28231
|
-
var CODEX_AGENT_RUNTIME_BACKEND = "codex";
|
|
28232
|
-
var AGENT_RUNTIME_BACKENDS = [CODEX_AGENT_RUNTIME_BACKEND];
|
|
28233
|
-
|
|
28234
|
-
// ../../shared/constants/models.ts
|
|
28235
|
-
var OpenAIModel = {
|
|
28236
|
-
GPT6Astra: "gpt-6-astra",
|
|
28237
|
-
GPT56: "gpt-5.6",
|
|
28238
|
-
GPT56Sol: "gpt-5.6-sol",
|
|
28239
|
-
GPT56Terra: "gpt-5.6-terra",
|
|
28240
|
-
GPT56Luna: "gpt-5.6-luna",
|
|
28241
|
-
GPT55: "gpt-5.5",
|
|
28242
|
-
GPT54: "gpt-5.4",
|
|
28243
|
-
GPT54Pro: "gpt-5.4-pro",
|
|
28244
|
-
GPT54Mini: "gpt-5.4-mini",
|
|
28245
|
-
GPT54Nano: "gpt-5.4-nano",
|
|
28246
|
-
GPT53CodexSpark: "gpt-5.3-codex-spark",
|
|
28247
|
-
GPT53Codex: "gpt-5.3-codex",
|
|
28248
|
-
GPT52: "gpt-5.2",
|
|
28249
|
-
GPT52ChatLatest: "gpt-5.2-chat-latest",
|
|
28250
|
-
GPT52Codex: "gpt-5.2-codex"
|
|
28251
|
-
};
|
|
28252
|
-
var MODEL_PROVIDERS_SET = /* @__PURE__ */ new Set(["openai", "anthropic"]);
|
|
28253
|
-
var BACKEND_DESKTOP_IMAGE_FEEDBACK_CONFIGS = {
|
|
28254
|
-
[CODEX_AGENT_RUNTIME_BACKEND]: {
|
|
28255
|
-
backend: CODEX_AGENT_RUNTIME_BACKEND,
|
|
28256
|
-
fixture: "known_image_fixture",
|
|
28257
|
-
// Codex 0.146.1 code mode flattens tool-result images to text, so the bridge steers the image
|
|
28258
|
-
// into the active turn as a user message; DEFAULT_CODEX_IMAGE_FEEDBACK_CAPABILITY mirrors this.
|
|
28259
|
-
deliveryPath: "steer_image_message",
|
|
28260
|
-
verifiedAt: "2026-09-14"
|
|
28034
|
+
const requestId = trigger.requestId?.trim() ?? "";
|
|
28035
|
+
if (requestId.length === 0) {
|
|
28036
|
+
throw new CliError("server", "Arcanist review start did not return a requestId.");
|
|
28261
28037
|
}
|
|
28262
|
-
|
|
28263
|
-
|
|
28264
|
-
|
|
28265
|
-
|
|
28266
|
-
|
|
28267
|
-
|
|
28268
|
-
|
|
28269
|
-
|
|
28270
|
-
|
|
28271
|
-
|
|
28272
|
-
|
|
28273
|
-
|
|
28274
|
-
|
|
28275
|
-
|
|
28276
|
-
// column for gpt-5.4 and gpt-5.5, so the absence is deliberate, not an
|
|
28277
|
-
// oversight (checked 2026-07-30). The 5.6 family does publish that rate.
|
|
28278
|
-
pricing: {
|
|
28279
|
-
inputPerMillion: 2.5,
|
|
28280
|
-
outputPerMillion: 15,
|
|
28281
|
-
cacheReadPerMillion: 0.25,
|
|
28282
|
-
flex: { inputPerMillion: 1.25, outputPerMillion: 7.5, cacheReadPerMillion: 0.13 },
|
|
28283
|
-
longContext: {
|
|
28284
|
-
thresholdTokens: 272e3,
|
|
28285
|
-
flex: { inputPerMillion: 2.5, outputPerMillion: 11.25, cacheReadPerMillion: 0.25 },
|
|
28286
|
-
inputPerMillion: 5,
|
|
28287
|
-
outputPerMillion: 22.5,
|
|
28288
|
-
cacheReadPerMillion: 0.5
|
|
28289
|
-
}
|
|
28290
|
-
},
|
|
28291
|
-
sessionStart: { eligible: true, isDefault: true }
|
|
28292
|
-
},
|
|
28293
|
-
{
|
|
28294
|
-
id: OpenAIModel.GPT6Astra,
|
|
28295
|
-
name: "GPT-6 Astra",
|
|
28296
|
-
provider: "openai",
|
|
28297
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28298
|
-
codexProviderRoute: "openai",
|
|
28299
|
-
capabilities: { codexToolSearch: true },
|
|
28300
|
-
contextWindow: 105e4,
|
|
28301
|
-
reasoning: { efforts: ["low", "medium", "high", "xhigh", "max"], default: "medium" },
|
|
28302
|
-
// Standard and Flex rates: https://developers.openai.com/api/docs/models/gpt-6-astra (2026-09-15).
|
|
28303
|
-
pricing: {
|
|
28304
|
-
inputPerMillion: 10,
|
|
28305
|
-
outputPerMillion: 50,
|
|
28306
|
-
cacheReadPerMillion: 1,
|
|
28307
|
-
cacheWritePerMillion: 12.5,
|
|
28308
|
-
flex: { inputPerMillion: 5, outputPerMillion: 25, cacheReadPerMillion: 0.5, cacheWritePerMillion: 6.25 },
|
|
28309
|
-
longContext: {
|
|
28310
|
-
thresholdTokens: 272e3,
|
|
28311
|
-
inputPerMillion: 20,
|
|
28312
|
-
outputPerMillion: 75,
|
|
28313
|
-
cacheReadPerMillion: 2,
|
|
28314
|
-
cacheWritePerMillion: 25,
|
|
28315
|
-
flex: { inputPerMillion: 10, outputPerMillion: 37.5, cacheReadPerMillion: 1, cacheWritePerMillion: 12.5 }
|
|
28316
|
-
}
|
|
28317
|
-
},
|
|
28318
|
-
sessionStart: { eligible: true }
|
|
28319
|
-
},
|
|
28320
|
-
{
|
|
28321
|
-
id: OpenAIModel.GPT56,
|
|
28322
|
-
name: "GPT-5.6",
|
|
28323
|
-
provider: "openai",
|
|
28324
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28325
|
-
codexProviderRoute: "openai",
|
|
28326
|
-
capabilities: { codexToolSearch: true },
|
|
28327
|
-
contextWindow: 105e4,
|
|
28328
|
-
// Verified 2026-07-09 against OpenAI's GPT-5.6 migration guide and model
|
|
28329
|
-
// catalog: the alias routes to gpt-5.6-sol and supports max effort. Pricing
|
|
28330
|
-
// mirrors gpt-5.6-sol since the alias resolves to it. Cache-write rates
|
|
28331
|
-
// verified 2026-07-30 against the Standard tier at
|
|
28332
|
-
// developers.openai.com/api/docs/pricing.
|
|
28333
|
-
reasoning: { efforts: ["none", "low", "medium", "high", "xhigh", "max"], default: "medium" },
|
|
28334
|
-
pricing: {
|
|
28335
|
-
inputPerMillion: 5,
|
|
28336
|
-
outputPerMillion: 30,
|
|
28337
|
-
cacheReadPerMillion: 0.5,
|
|
28338
|
-
cacheWritePerMillion: 6.25,
|
|
28339
|
-
flex: { inputPerMillion: 2, outputPerMillion: 10, cacheReadPerMillion: 0.2, cacheWritePerMillion: 2.5 },
|
|
28340
|
-
longContext: {
|
|
28341
|
-
thresholdTokens: 272e3,
|
|
28342
|
-
flex: { inputPerMillion: 4, outputPerMillion: 15, cacheReadPerMillion: 0.4, cacheWritePerMillion: 5 },
|
|
28343
|
-
inputPerMillion: 10,
|
|
28344
|
-
outputPerMillion: 45,
|
|
28345
|
-
cacheReadPerMillion: 1,
|
|
28346
|
-
cacheWritePerMillion: 12.5
|
|
28038
|
+
const result = await pollUntilSettled({
|
|
28039
|
+
pollIntervalMs: pollIntervalMs ?? PR_REVIEW_POLL_INTERVAL_MS,
|
|
28040
|
+
timeoutMs: PR_REVIEW_WAIT_TIMEOUT_MS,
|
|
28041
|
+
timeoutMessage: "Timed out waiting for Arcanist review results.",
|
|
28042
|
+
fetchStatus: async () => {
|
|
28043
|
+
const status = await apiFetch(
|
|
28044
|
+
config2,
|
|
28045
|
+
`/api/pr-reviews/requests/${encodeURIComponent(requestId)}`
|
|
28046
|
+
);
|
|
28047
|
+
if (status.status === "cancelled") {
|
|
28048
|
+
throw new CliError(
|
|
28049
|
+
"conflict",
|
|
28050
|
+
status.terminalReason ? `Arcanist review was cancelled: ${status.terminalReason}` : "Arcanist review was cancelled."
|
|
28051
|
+
);
|
|
28347
28052
|
}
|
|
28348
|
-
|
|
28349
|
-
|
|
28350
|
-
|
|
28351
|
-
|
|
28352
|
-
|
|
28353
|
-
name: "GPT-5.6 Sol",
|
|
28354
|
-
provider: "openai",
|
|
28355
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28356
|
-
codexProviderRoute: "openai",
|
|
28357
|
-
capabilities: { codexToolSearch: true },
|
|
28358
|
-
contextWindow: 105e4,
|
|
28359
|
-
reasoning: { efforts: ["none", "low", "medium", "high", "xhigh", "max"], default: "medium" },
|
|
28360
|
-
// Cache-write rates verified 2026-07-30 against the Standard tier at
|
|
28361
|
-
// developers.openai.com/api/docs/pricing.
|
|
28362
|
-
pricing: {
|
|
28363
|
-
inputPerMillion: 5,
|
|
28364
|
-
outputPerMillion: 30,
|
|
28365
|
-
cacheReadPerMillion: 0.5,
|
|
28366
|
-
cacheWritePerMillion: 6.25,
|
|
28367
|
-
flex: { inputPerMillion: 2, outputPerMillion: 10, cacheReadPerMillion: 0.2, cacheWritePerMillion: 2.5 },
|
|
28368
|
-
longContext: {
|
|
28369
|
-
thresholdTokens: 272e3,
|
|
28370
|
-
flex: { inputPerMillion: 4, outputPerMillion: 15, cacheReadPerMillion: 0.4, cacheWritePerMillion: 5 },
|
|
28371
|
-
inputPerMillion: 10,
|
|
28372
|
-
outputPerMillion: 45,
|
|
28373
|
-
cacheReadPerMillion: 1,
|
|
28374
|
-
cacheWritePerMillion: 12.5
|
|
28375
|
-
}
|
|
28376
|
-
},
|
|
28377
|
-
sessionStart: { eligible: true }
|
|
28378
|
-
},
|
|
28379
|
-
{
|
|
28380
|
-
id: OpenAIModel.GPT56Terra,
|
|
28381
|
-
name: "GPT-5.6 Terra",
|
|
28382
|
-
provider: "openai",
|
|
28383
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28384
|
-
codexProviderRoute: "openai",
|
|
28385
|
-
capabilities: { codexToolSearch: true },
|
|
28386
|
-
contextWindow: 105e4,
|
|
28387
|
-
reasoning: { efforts: ["none", "low", "medium", "high", "xhigh", "max"], default: "medium" },
|
|
28388
|
-
// Verified 2026-07-30 against the Standard tier at
|
|
28389
|
-
// developers.openai.com/api/docs/pricing: short and long context input,
|
|
28390
|
-
// cached input, cache writes, and output.
|
|
28391
|
-
pricing: {
|
|
28392
|
-
inputPerMillion: 2,
|
|
28393
|
-
outputPerMillion: 12,
|
|
28394
|
-
cacheReadPerMillion: 0.2,
|
|
28395
|
-
cacheWritePerMillion: 2.5,
|
|
28396
|
-
flex: { inputPerMillion: 1, outputPerMillion: 6, cacheReadPerMillion: 0.1, cacheWritePerMillion: 1.25 },
|
|
28397
|
-
longContext: {
|
|
28398
|
-
thresholdTokens: 272e3,
|
|
28399
|
-
flex: { inputPerMillion: 2, outputPerMillion: 9, cacheReadPerMillion: 0.2, cacheWritePerMillion: 2.5 },
|
|
28400
|
-
inputPerMillion: 4,
|
|
28401
|
-
outputPerMillion: 18,
|
|
28402
|
-
cacheReadPerMillion: 0.4,
|
|
28403
|
-
cacheWritePerMillion: 5
|
|
28404
|
-
}
|
|
28405
|
-
},
|
|
28406
|
-
sessionStart: { eligible: true }
|
|
28407
|
-
},
|
|
28408
|
-
{
|
|
28409
|
-
id: OpenAIModel.GPT56Luna,
|
|
28410
|
-
name: "GPT-5.6 Luna",
|
|
28411
|
-
provider: "openai",
|
|
28412
|
-
overloadFallback: OpenAIModel.GPT56Terra,
|
|
28413
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28414
|
-
codexProviderRoute: "openai",
|
|
28415
|
-
capabilities: { codexToolSearch: true },
|
|
28416
|
-
contextWindow: 105e4,
|
|
28417
|
-
reasoning: { efforts: ["none", "low", "medium", "high", "xhigh", "max"], default: "medium" },
|
|
28418
|
-
// Verified 2026-07-30 against the Standard tier at
|
|
28419
|
-
// developers.openai.com/api/docs/pricing: short and long context input,
|
|
28420
|
-
// cached input, cache writes, and output.
|
|
28421
|
-
pricing: {
|
|
28422
|
-
inputPerMillion: 0.2,
|
|
28423
|
-
outputPerMillion: 1.2,
|
|
28424
|
-
cacheReadPerMillion: 0.02,
|
|
28425
|
-
cacheWritePerMillion: 0.25,
|
|
28426
|
-
flex: { inputPerMillion: 0.1, outputPerMillion: 0.6, cacheReadPerMillion: 0.01, cacheWritePerMillion: 0.125 },
|
|
28427
|
-
longContext: {
|
|
28428
|
-
thresholdTokens: 272e3,
|
|
28429
|
-
flex: { inputPerMillion: 0.2, outputPerMillion: 0.9, cacheReadPerMillion: 0.02, cacheWritePerMillion: 0.25 },
|
|
28430
|
-
inputPerMillion: 0.4,
|
|
28431
|
-
outputPerMillion: 1.8,
|
|
28432
|
-
cacheReadPerMillion: 0.04,
|
|
28433
|
-
cacheWritePerMillion: 0.5
|
|
28434
|
-
}
|
|
28435
|
-
},
|
|
28436
|
-
sessionStart: { eligible: true }
|
|
28437
|
-
},
|
|
28438
|
-
{
|
|
28439
|
-
id: OpenAIModel.GPT55,
|
|
28440
|
-
name: "GPT-5.5",
|
|
28441
|
-
provider: "openai",
|
|
28442
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28443
|
-
codexProviderRoute: "openai",
|
|
28444
|
-
capabilities: { codexToolSearch: true },
|
|
28445
|
-
contextWindow: 1e6,
|
|
28446
|
-
reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: "medium" },
|
|
28447
|
-
pricing: {
|
|
28448
|
-
inputPerMillion: 5,
|
|
28449
|
-
outputPerMillion: 30,
|
|
28450
|
-
cacheReadPerMillion: 0.5,
|
|
28451
|
-
flex: { inputPerMillion: 2.5, outputPerMillion: 15, cacheReadPerMillion: 0.25 },
|
|
28452
|
-
longContext: {
|
|
28453
|
-
thresholdTokens: 272e3,
|
|
28454
|
-
flex: { inputPerMillion: 5, outputPerMillion: 22.5, cacheReadPerMillion: 0.5 },
|
|
28455
|
-
inputPerMillion: 10,
|
|
28456
|
-
outputPerMillion: 45,
|
|
28457
|
-
cacheReadPerMillion: 1
|
|
28458
|
-
}
|
|
28459
|
-
},
|
|
28460
|
-
sessionStart: { eligible: true }
|
|
28461
|
-
},
|
|
28462
|
-
{
|
|
28463
|
-
id: OpenAIModel.GPT54Mini,
|
|
28464
|
-
name: "GPT-5.4 Mini",
|
|
28465
|
-
provider: "openai",
|
|
28466
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28467
|
-
codexProviderRoute: "openai",
|
|
28468
|
-
capabilities: { codexToolSearch: true },
|
|
28469
|
-
contextWindow: 4e5,
|
|
28470
|
-
reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: void 0 },
|
|
28471
|
-
pricing: {
|
|
28472
|
-
inputPerMillion: 0.75,
|
|
28473
|
-
outputPerMillion: 4.5,
|
|
28474
|
-
cacheReadPerMillion: 0.075,
|
|
28475
|
-
// Flex bills at Batch rates: a flat 50% off standard across input,
|
|
28476
|
-
// cached input, and output (verified against the OpenAI pricing page,
|
|
28477
|
-
// June 2026). Bridge cost estimates deliberately strip this gateway-only
|
|
28478
|
-
// axis when deriving MODEL_PRICING.
|
|
28479
|
-
flex: { inputPerMillion: 0.375, outputPerMillion: 2.25, cacheReadPerMillion: 0.0375 }
|
|
28480
|
-
},
|
|
28481
|
-
sessionStart: { eligible: true }
|
|
28482
|
-
},
|
|
28483
|
-
{
|
|
28484
|
-
id: OpenAIModel.GPT54Nano,
|
|
28485
|
-
name: "GPT-5.4 Nano",
|
|
28486
|
-
provider: "openai",
|
|
28487
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28488
|
-
codexProviderRoute: "openai",
|
|
28489
|
-
capabilities: { codexToolSearch: false },
|
|
28490
|
-
contextWindow: 4e5,
|
|
28491
|
-
reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: void 0 },
|
|
28492
|
-
pricing: { inputPerMillion: 0.2, outputPerMillion: 1.25, cacheReadPerMillion: 0.02 },
|
|
28493
|
-
sessionStart: { eligible: true }
|
|
28494
|
-
},
|
|
28495
|
-
{
|
|
28496
|
-
id: OpenAIModel.GPT53CodexSpark,
|
|
28497
|
-
name: "GPT-5.3 Codex Spark",
|
|
28498
|
-
provider: "openai",
|
|
28499
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28500
|
-
codexProviderRoute: "openai",
|
|
28501
|
-
capabilities: { codexToolSearch: true },
|
|
28502
|
-
contextWindow: 128e3,
|
|
28503
|
-
reasoning: { efforts: ["low", "medium", "high", "xhigh"], default: "high" },
|
|
28504
|
-
costTracked: false,
|
|
28505
|
-
requiresCodexSubscriptionAuth: true,
|
|
28506
|
-
sessionStart: { eligible: true },
|
|
28507
|
-
visibility: "internal_probe"
|
|
28508
|
-
},
|
|
28509
|
-
{
|
|
28510
|
-
id: OpenAIModel.GPT53Codex,
|
|
28511
|
-
name: "GPT-5.3 Codex",
|
|
28512
|
-
provider: "openai",
|
|
28513
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28514
|
-
codexProviderRoute: "openai",
|
|
28515
|
-
capabilities: { codexToolSearch: true },
|
|
28516
|
-
contextWindow: 4e5,
|
|
28517
|
-
reasoning: { efforts: ["low", "medium", "high", "xhigh"], default: "high" },
|
|
28518
|
-
pricing: { inputPerMillion: 1.75, outputPerMillion: 14, cacheReadPerMillion: 0.175 }
|
|
28519
|
-
},
|
|
28520
|
-
{
|
|
28521
|
-
id: OpenAIModel.GPT52,
|
|
28522
|
-
name: "GPT-5.2",
|
|
28523
|
-
provider: "openai",
|
|
28524
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28525
|
-
codexProviderRoute: "openai",
|
|
28526
|
-
capabilities: { codexToolSearch: true },
|
|
28527
|
-
contextWindow: 4e5,
|
|
28528
|
-
reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: void 0 },
|
|
28529
|
-
pricing: {
|
|
28530
|
-
inputPerMillion: 1.75,
|
|
28531
|
-
outputPerMillion: 14,
|
|
28532
|
-
cacheReadPerMillion: 0.175,
|
|
28533
|
-
flex: { inputPerMillion: 0.875, outputPerMillion: 7, cacheReadPerMillion: 0.0875 }
|
|
28534
|
-
}
|
|
28535
|
-
},
|
|
28536
|
-
{
|
|
28537
|
-
id: OpenAIModel.GPT52ChatLatest,
|
|
28538
|
-
name: "GPT-5.2 Chat",
|
|
28539
|
-
provider: "openai",
|
|
28540
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28541
|
-
codexProviderRoute: "openai",
|
|
28542
|
-
capabilities: { codexToolSearch: true },
|
|
28543
|
-
contextWindow: 128e3,
|
|
28544
|
-
reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: void 0 },
|
|
28545
|
-
pricing: { inputPerMillion: 1.75, outputPerMillion: 14, cacheReadPerMillion: 0.175 }
|
|
28546
|
-
},
|
|
28547
|
-
{
|
|
28548
|
-
id: OpenAIModel.GPT52Codex,
|
|
28549
|
-
name: "GPT-5.2 Codex",
|
|
28550
|
-
provider: "openai",
|
|
28551
|
-
backends: [CODEX_AGENT_RUNTIME_BACKEND],
|
|
28552
|
-
codexProviderRoute: "openai",
|
|
28553
|
-
capabilities: { codexToolSearch: true },
|
|
28554
|
-
contextWindow: 4e5,
|
|
28555
|
-
reasoning: { efforts: ["low", "medium", "high", "xhigh"], default: "high" },
|
|
28556
|
-
pricing: { inputPerMillion: 1.75, outputPerMillion: 14, cacheReadPerMillion: 0.175 }
|
|
28557
|
-
}
|
|
28558
|
-
];
|
|
28559
|
-
var MODEL_PROVIDER_NAMES = {
|
|
28560
|
-
openai: "OpenAI",
|
|
28561
|
-
anthropic: "Anthropic"
|
|
28562
|
-
};
|
|
28563
|
-
function buildSessionStartModelIdsByBackend() {
|
|
28564
|
-
const byBackend = Object.fromEntries(AGENT_RUNTIME_BACKENDS.map((backend) => [backend, []]));
|
|
28565
|
-
for (const model of MODEL_REGISTRY) {
|
|
28566
|
-
if (!model.sessionStart?.eligible) continue;
|
|
28567
|
-
for (const backend of model.backends) {
|
|
28568
|
-
if (!modelSupportsRequiredSessionStartCapabilities(model, backend)) continue;
|
|
28569
|
-
byBackend[backend].push(model.id);
|
|
28570
|
-
}
|
|
28571
|
-
}
|
|
28572
|
-
return byBackend;
|
|
28573
|
-
}
|
|
28574
|
-
function modelSupportsRequiredSessionStartCapabilities(model, backend) {
|
|
28575
|
-
if (backend !== CODEX_AGENT_RUNTIME_BACKEND) return true;
|
|
28576
|
-
return model.capabilities?.codexToolSearch === true;
|
|
28577
|
-
}
|
|
28578
|
-
function buildDefaultSessionStartModelIdByBackend() {
|
|
28579
|
-
const defaults = {};
|
|
28580
|
-
for (const backend of AGENT_RUNTIME_BACKENDS) {
|
|
28581
|
-
const backendDefaults = MODEL_REGISTRY.filter(
|
|
28582
|
-
(model) => model.sessionStart?.eligible && model.sessionStart.isDefault === true && model.backends.includes(backend)
|
|
28583
|
-
);
|
|
28584
|
-
if (backendDefaults.length !== 1) {
|
|
28585
|
-
throw new Error(`Expected exactly one default session-start model for ${backend}, got ${backendDefaults.length}`);
|
|
28586
|
-
}
|
|
28587
|
-
defaults[backend] = backendDefaults[0].id;
|
|
28588
|
-
}
|
|
28589
|
-
return defaults;
|
|
28590
|
-
}
|
|
28591
|
-
var SESSION_START_MODEL_IDS_BY_BACKEND = buildSessionStartModelIdsByBackend();
|
|
28592
|
-
var DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND = buildDefaultSessionStartModelIdByBackend();
|
|
28593
|
-
var DEFAULT_SESSION_START_MODEL_ID = DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND[CODEX_AGENT_RUNTIME_BACKEND];
|
|
28594
|
-
var VALID_MODEL_IDS = new Set(MODEL_REGISTRY.map((model) => model.id));
|
|
28595
|
-
var VALID_SESSION_START_MODEL_IDS_BY_BACKEND = {
|
|
28596
|
-
[CODEX_AGENT_RUNTIME_BACKEND]: new Set(SESSION_START_MODEL_IDS_BY_BACKEND[CODEX_AGENT_RUNTIME_BACKEND])
|
|
28597
|
-
};
|
|
28598
|
-
var MODEL_CONTEXT_WINDOWS = {
|
|
28599
|
-
...Object.fromEntries(
|
|
28600
|
-
MODEL_REGISTRY.flatMap((model) => model.contextWindow === void 0 ? [] : [[model.id, model.contextWindow]])
|
|
28601
|
-
)
|
|
28602
|
-
};
|
|
28603
|
-
var MODEL_PROVIDERS = {
|
|
28604
|
-
...Object.fromEntries(MODEL_REGISTRY.map((model) => [model.id, model.provider]))
|
|
28605
|
-
};
|
|
28606
|
-
var MODEL_REASONING_CONFIG = Object.fromEntries(
|
|
28607
|
-
MODEL_REGISTRY.flatMap((model) => model.reasoning ? [[model.id, model.reasoning]] : [])
|
|
28608
|
-
);
|
|
28609
|
-
var MODEL_DEFINITIONS_BY_ID = Object.fromEntries(MODEL_REGISTRY.map((model) => [model.id, model]));
|
|
28610
|
-
function splitModelIdentifier(value) {
|
|
28611
|
-
let hasSeparator = false;
|
|
28612
|
-
for (const separator of [":", "/"]) {
|
|
28613
|
-
const index = value.indexOf(separator);
|
|
28614
|
-
if (index <= 0 || index >= value.length - 1) continue;
|
|
28615
|
-
hasSeparator = true;
|
|
28616
|
-
const providerID = value.slice(0, index);
|
|
28617
|
-
const modelID = value.slice(index + 1);
|
|
28618
|
-
if (MODEL_PROVIDERS_SET.has(providerID) && modelID.length > 0) {
|
|
28619
|
-
return { providerID, modelID };
|
|
28620
|
-
}
|
|
28621
|
-
}
|
|
28622
|
-
if (hasSeparator) return void 0;
|
|
28623
|
-
return value.length > 0 ? { modelID: value } : void 0;
|
|
28624
|
-
}
|
|
28625
|
-
function getRawModelValue(raw) {
|
|
28626
|
-
if (typeof raw === "string") return asNonEmptyString(raw);
|
|
28627
|
-
if (!isRecord(raw)) return void 0;
|
|
28628
|
-
return asNonEmptyString(raw.modelID) ?? asNonEmptyString(raw.modelId) ?? asNonEmptyString(raw.id);
|
|
28629
|
-
}
|
|
28630
|
-
function getExplicitProvider(raw) {
|
|
28631
|
-
if (isRecord(raw)) {
|
|
28632
|
-
const providerID = asNonEmptyString(raw.providerID) ?? asNonEmptyString(raw.providerId);
|
|
28633
|
-
if (providerID && MODEL_PROVIDERS_SET.has(providerID)) {
|
|
28634
|
-
return providerID;
|
|
28635
|
-
}
|
|
28636
|
-
}
|
|
28637
|
-
const modelValue = getRawModelValue(raw);
|
|
28638
|
-
if (!modelValue) return void 0;
|
|
28639
|
-
return splitModelIdentifier(modelValue)?.providerID;
|
|
28640
|
-
}
|
|
28641
|
-
function extractModelId(raw) {
|
|
28642
|
-
const value = getRawModelValue(raw);
|
|
28643
|
-
if (!value) return void 0;
|
|
28644
|
-
return splitModelIdentifier(value)?.modelID;
|
|
28645
|
-
}
|
|
28646
|
-
function getProviderForModel(modelId) {
|
|
28647
|
-
return MODEL_PROVIDERS[modelId] ?? "openai";
|
|
28648
|
-
}
|
|
28649
|
-
function toModelSelection(raw) {
|
|
28650
|
-
const modelID = extractModelId(raw);
|
|
28651
|
-
if (!modelID) return void 0;
|
|
28652
|
-
return {
|
|
28653
|
-
providerID: getExplicitProvider(raw) ?? getProviderForModel(modelID),
|
|
28654
|
-
modelID
|
|
28655
|
-
};
|
|
28656
|
-
}
|
|
28657
|
-
function getModelDefinition(raw) {
|
|
28658
|
-
const modelID = extractModelId(raw);
|
|
28659
|
-
if (!modelID) return void 0;
|
|
28660
|
-
return MODEL_DEFINITIONS_BY_ID[modelID];
|
|
28661
|
-
}
|
|
28662
|
-
function formatModelLabel(raw, options = {}) {
|
|
28663
|
-
const includeProvider = options.includeProvider ?? false;
|
|
28664
|
-
const definition = getModelDefinition(raw);
|
|
28665
|
-
if (definition) {
|
|
28666
|
-
return includeProvider ? `${MODEL_PROVIDER_NAMES[definition.provider]} / ${definition.name}` : definition.name;
|
|
28667
|
-
}
|
|
28668
|
-
const selection = toModelSelection(raw);
|
|
28669
|
-
if (!selection) return void 0;
|
|
28670
|
-
const providerName = MODEL_PROVIDER_NAMES[selection.providerID];
|
|
28671
|
-
return includeProvider ? `${providerName} / ${selection.modelID}` : selection.modelID;
|
|
28672
|
-
}
|
|
28673
|
-
function buildModelProviderGroups(models2) {
|
|
28674
|
-
const groups = /* @__PURE__ */ new Map();
|
|
28675
|
-
for (const model of models2) {
|
|
28676
|
-
let group = groups.get(model.provider);
|
|
28677
|
-
if (!group) {
|
|
28678
|
-
group = { id: model.provider, name: MODEL_PROVIDER_NAMES[model.provider], models: [] };
|
|
28679
|
-
groups.set(model.provider, group);
|
|
28680
|
-
}
|
|
28681
|
-
group.models.push({
|
|
28682
|
-
id: model.id,
|
|
28683
|
-
name: model.name,
|
|
28684
|
-
label: formatModelLabel(model, { includeProvider: true }) ?? model.name,
|
|
28685
|
-
backends: model.backends,
|
|
28686
|
-
reasoning: model.reasoning
|
|
28687
|
-
});
|
|
28688
|
-
}
|
|
28689
|
-
const defaultModelIds = new Set(
|
|
28690
|
-
models2.filter((model) => model.sessionStart?.isDefault === true).map((model) => model.id)
|
|
28691
|
-
);
|
|
28692
|
-
for (const group of groups.values()) {
|
|
28693
|
-
group.models.sort((a, b) => Number(defaultModelIds.has(b.id)) - Number(defaultModelIds.has(a.id)));
|
|
28694
|
-
}
|
|
28695
|
-
return [...groups.values()];
|
|
28696
|
-
}
|
|
28697
|
-
var SESSION_START_MODEL_ID_SET_ANY_BACKEND = new Set(
|
|
28698
|
-
AGENT_RUNTIME_BACKENDS.flatMap((backend) => [...SESSION_START_MODEL_IDS_BY_BACKEND[backend]])
|
|
28699
|
-
);
|
|
28700
|
-
var PUBLIC_SESSION_START_MODEL_PROVIDER_GROUPS = buildModelProviderGroups(
|
|
28701
|
-
MODEL_REGISTRY.filter(
|
|
28702
|
-
(model) => SESSION_START_MODEL_ID_SET_ANY_BACKEND.has(model.id) && model.visibility !== "internal_probe"
|
|
28703
|
-
)
|
|
28704
|
-
);
|
|
28705
|
-
var CODEX_SUBSCRIPTION_SESSION_START_MODEL_PROVIDER_GROUPS = buildModelProviderGroups(
|
|
28706
|
-
MODEL_REGISTRY.filter(
|
|
28707
|
-
(model) => SESSION_START_MODEL_ID_SET_ANY_BACKEND.has(model.id) && (model.visibility !== "internal_probe" || model.requiresCodexSubscriptionAuth === true)
|
|
28708
|
-
)
|
|
28709
|
-
);
|
|
28710
|
-
|
|
28711
|
-
// src/commands/models.ts
|
|
28712
|
-
async function modelsListCommand(options = {}, command) {
|
|
28713
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
28714
|
-
const groups = await apiFetch(config2, "/api/models");
|
|
28715
|
-
const models2 = groups.flatMap(
|
|
28716
|
-
(group) => group.models.map((model) => {
|
|
28717
|
-
const backends = model.backends ?? [group.id];
|
|
28718
|
-
return {
|
|
28719
|
-
...model,
|
|
28720
|
-
backend: group.id,
|
|
28721
|
-
provider: group.id,
|
|
28722
|
-
backends,
|
|
28723
|
-
default: backends.some(
|
|
28724
|
-
(backend) => DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND[backend] === model.id
|
|
28725
|
-
)
|
|
28726
|
-
};
|
|
28727
|
-
})
|
|
28728
|
-
);
|
|
28729
|
-
emit(command, options, { models: models2 }, (payload) => {
|
|
28730
|
-
for (const model of payload.models) {
|
|
28731
|
-
console.log(`${String(model.id)} ${String(model.backend)}${model.default ? " default" : ""}`);
|
|
28732
|
-
}
|
|
28733
|
-
});
|
|
28734
|
-
}
|
|
28735
|
-
|
|
28736
|
-
// src/commands/repos.ts
|
|
28737
|
-
async function reposListCommand(options = {}, command) {
|
|
28738
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
28739
|
-
const payload = await apiFetch(config2, "/api/repos");
|
|
28740
|
-
emit(command, options, { repos: payload.repos, ssoOrgs: payload.ssoOrgs }, (data) => {
|
|
28741
|
-
for (const repo of data.repos) {
|
|
28742
|
-
console.log(`${String(repo.fullName ?? repo.name ?? repo.repo ?? "")} ${String(repo.defaultBranch ?? "")}`);
|
|
28743
|
-
}
|
|
28744
|
-
if (data.ssoOrgs.length > 0) console.log(`SSO withheld orgs: ${data.ssoOrgs.length}`);
|
|
28745
|
-
});
|
|
28746
|
-
}
|
|
28747
|
-
async function repoBranchesCommand(repoArg, options = {}, command) {
|
|
28748
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
28749
|
-
const repo = parseRepoArgOrCurrent(repoArg);
|
|
28750
|
-
const payload = await apiFetch(
|
|
28751
|
-
config2,
|
|
28752
|
-
`/api/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/branches`
|
|
28753
|
-
);
|
|
28754
|
-
emit(command, options, payload, (data) => {
|
|
28755
|
-
for (const branch of data.branches) console.log(String(branch.name ?? branch.branch ?? ""));
|
|
28756
|
-
});
|
|
28757
|
-
}
|
|
28758
|
-
|
|
28759
|
-
// src/commands/review.ts
|
|
28760
|
-
var PR_REVIEW_POLL_INTERVAL_MS = 1e4;
|
|
28761
|
-
var PR_REVIEW_WAIT_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
28762
|
-
var SANDBOX_RUNTIME_BACKENDS = {
|
|
28763
|
-
e2b: "e2b_cloud",
|
|
28764
|
-
modal: "modal_cloud",
|
|
28765
|
-
sailbox: "sail_cloud"
|
|
28766
|
-
};
|
|
28767
|
-
function isSandboxRuntimeOption(runtime) {
|
|
28768
|
-
return Object.hasOwn(SANDBOX_RUNTIME_BACKENDS, runtime);
|
|
28769
|
-
}
|
|
28770
|
-
function resolveSandboxRuntimeBackend(runtime) {
|
|
28771
|
-
if (runtime === void 0) return null;
|
|
28772
|
-
if (!isSandboxRuntimeOption(runtime)) {
|
|
28773
|
-
throw new CliError("user", "Sandbox runtime must be modal, e2b, or sailbox.");
|
|
28774
|
-
}
|
|
28775
|
-
return SANDBOX_RUNTIME_BACKENDS[runtime];
|
|
28776
|
-
}
|
|
28777
|
-
async function reviewCommand(prUrl, options = {}, command) {
|
|
28778
|
-
assertArcanistSessionMutationAllowed("review");
|
|
28779
|
-
const sandboxRuntimeBackend = resolveSandboxRuntimeBackend(options.sandboxRuntime);
|
|
28780
|
-
const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: PR_REVIEW_POLL_INTERVAL_MS, maxMs: null }) : null;
|
|
28781
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
28782
|
-
const trigger = await apiFetch(config2, "/api/pr-reviews", {
|
|
28783
|
-
method: "POST",
|
|
28784
|
-
body: JSON.stringify({
|
|
28785
|
-
prUrl,
|
|
28786
|
-
...sandboxRuntimeBackend !== null ? { sandboxRuntimeBackend } : {}
|
|
28787
|
-
})
|
|
28788
|
-
});
|
|
28789
|
-
if (!options.wait || trigger.cached) {
|
|
28790
|
-
emit(command, options, trigger, (payload) => {
|
|
28791
|
-
if (payload.cached) console.log(`Zeus review already completed: ${payload.verdict ?? "unknown"}.`);
|
|
28792
|
-
else console.log(`Started Zeus review${payload.sessionId ? ` (${payload.sessionId})` : ""}.`);
|
|
28793
|
-
});
|
|
28794
|
-
return;
|
|
28795
|
-
}
|
|
28796
|
-
const triggeredSessionId = trigger.sessionId ?? null;
|
|
28797
|
-
const result = await pollUntilSettled({
|
|
28798
|
-
pollIntervalMs: pollIntervalMs ?? PR_REVIEW_POLL_INTERVAL_MS,
|
|
28799
|
-
timeoutMs: PR_REVIEW_WAIT_TIMEOUT_MS,
|
|
28800
|
-
timeoutMessage: "Timed out waiting for Zeus review results.",
|
|
28801
|
-
fetchStatus: async () => {
|
|
28802
|
-
const status = await apiFetch(
|
|
28803
|
-
config2,
|
|
28804
|
-
`/api/pr-reviews/status?prUrl=${encodeURIComponent(prUrl)}`
|
|
28805
|
-
);
|
|
28806
|
-
if (status.status === "cancelled" || status.status === "admission_rejected") {
|
|
28807
|
-
assertSameAttempt(triggeredSessionId, status.sessionId, "PR review");
|
|
28808
|
-
if (status.status === "cancelled") {
|
|
28809
|
-
throw new CliError(
|
|
28810
|
-
"conflict",
|
|
28811
|
-
status.terminalReason ? `Zeus review was cancelled: ${status.terminalReason}` : "Zeus review was cancelled."
|
|
28812
|
-
);
|
|
28813
|
-
}
|
|
28814
|
-
throw new CliError(
|
|
28815
|
-
"user",
|
|
28816
|
-
status.terminalReason ? `Zeus review was rejected at admission: ${status.terminalReason}` : "Zeus review was rejected at admission."
|
|
28817
|
-
);
|
|
28053
|
+
if (status.status === "admission_rejected") {
|
|
28054
|
+
throw new CliError(
|
|
28055
|
+
"user",
|
|
28056
|
+
status.terminalReason ? `Arcanist review was rejected at admission: ${status.terminalReason}` : "Arcanist review was rejected at admission."
|
|
28057
|
+
);
|
|
28818
28058
|
}
|
|
28819
28059
|
if (status.status !== "completed") return null;
|
|
28820
|
-
assertSameAttempt(triggeredSessionId, status.sessionId, "PR review");
|
|
28821
28060
|
return {
|
|
28822
28061
|
...status,
|
|
28823
28062
|
status: "completed",
|
|
28824
|
-
sessionId: status.sessionId ??
|
|
28063
|
+
sessionId: status.sessionId ?? trigger.sessionId ?? null,
|
|
28825
28064
|
verdict: status.verdict ?? "unknown"
|
|
28826
28065
|
};
|
|
28827
28066
|
}
|
|
28828
28067
|
});
|
|
28829
28068
|
emit(command, options, result, (payload) => {
|
|
28830
|
-
console.log(`
|
|
28069
|
+
console.log(`Arcanist review: ${sanitizeTerminalText(payload.verdict)}. ${payload.findings.length} finding(s).`);
|
|
28831
28070
|
for (const finding of payload.findings) {
|
|
28832
28071
|
console.log(
|
|
28833
28072
|
`${sanitizeTerminalText(finding.severity)} ${sanitizeTerminalText(finding.file)}:${finding.line} - ${sanitizeTerminalText(finding.title)}`
|
|
@@ -28837,7 +28076,7 @@ async function reviewCommand(prUrl, options = {}, command) {
|
|
|
28837
28076
|
}
|
|
28838
28077
|
|
|
28839
28078
|
// src/commands/sandbox.ts
|
|
28840
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as
|
|
28079
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
28841
28080
|
import { dirname as dirname2 } from "path";
|
|
28842
28081
|
|
|
28843
28082
|
// ../../shared/sandbox-layer/parser.ts
|
|
@@ -29179,7 +28418,7 @@ function splitLogicalInstructions(path, text) {
|
|
|
29179
28418
|
return result;
|
|
29180
28419
|
}
|
|
29181
28420
|
function splitEnvTokens(path, body, startLine) {
|
|
29182
|
-
const
|
|
28421
|
+
const tokens = [];
|
|
29183
28422
|
let current = "";
|
|
29184
28423
|
let quote = null;
|
|
29185
28424
|
for (const char of body) {
|
|
@@ -29195,7 +28434,7 @@ function splitEnvTokens(path, body, startLine) {
|
|
|
29195
28434
|
}
|
|
29196
28435
|
if (/\s/.test(char)) {
|
|
29197
28436
|
if (current) {
|
|
29198
|
-
|
|
28437
|
+
tokens.push(current);
|
|
29199
28438
|
current = "";
|
|
29200
28439
|
}
|
|
29201
28440
|
continue;
|
|
@@ -29203,16 +28442,16 @@ function splitEnvTokens(path, body, startLine) {
|
|
|
29203
28442
|
current += char;
|
|
29204
28443
|
}
|
|
29205
28444
|
if (quote) throw new SandboxLayerValidationError(path, "ENV contains an unterminated quote", startLine);
|
|
29206
|
-
if (current)
|
|
29207
|
-
return
|
|
28445
|
+
if (current) tokens.push(current);
|
|
28446
|
+
return tokens;
|
|
29208
28447
|
}
|
|
29209
28448
|
function parseEnvValues(path, body, startLine) {
|
|
29210
28449
|
const values = {};
|
|
29211
|
-
const
|
|
29212
|
-
if (
|
|
28450
|
+
const tokens = splitEnvTokens(path, body, startLine);
|
|
28451
|
+
if (tokens.length > MAX_ENV_VARS) {
|
|
29213
28452
|
throw new SandboxLayerValidationError(path, `ENV may set at most ${MAX_ENV_VARS} variables`, startLine);
|
|
29214
28453
|
}
|
|
29215
|
-
for (const token of
|
|
28454
|
+
for (const token of tokens) {
|
|
29216
28455
|
const separator = token.indexOf("=");
|
|
29217
28456
|
if (separator <= 0) {
|
|
29218
28457
|
throw new SandboxLayerValidationError(path, "ENV entries must use KEY=value form", startLine);
|
|
@@ -29441,19 +28680,11 @@ var sandboxLayerBuildLogsResponseSchema = external_exports.object({
|
|
|
29441
28680
|
logs: external_exports.array(sandboxLayerBuildLogSchema)
|
|
29442
28681
|
});
|
|
29443
28682
|
var sandboxLayerCliBuildRequestSchema = sandboxLayerBuildRequestSchema.partial().required({ id: true, status: true }).extend({ createdBy: sandboxLayerBuildActorSchema.nullable().optional() });
|
|
29444
|
-
var sandboxLayerCliBuildHistoryItemSchema = sandboxLayerBuildHistoryItemSchema.extend({
|
|
29445
|
-
startedAt: external_exports.number().nullable().optional(),
|
|
29446
|
-
failureSummary: sandboxLayerFailureSummarySchema.nullable().optional()
|
|
29447
|
-
});
|
|
29448
28683
|
var sandboxLayerCliBuildLogSchema = sandboxLayerBuildLogSchema.partial().required({ sequence: true, message: true }).extend({ created_at: external_exports.union([external_exports.string(), external_exports.number()]).optional() });
|
|
29449
28684
|
var sandboxLayerCliBuildRequestResponseSchema = external_exports.object({
|
|
29450
28685
|
ok: external_exports.boolean(),
|
|
29451
28686
|
buildRequest: sandboxLayerCliBuildRequestSchema
|
|
29452
28687
|
});
|
|
29453
|
-
var sandboxLayerCliBuildHistoryResponseSchema = external_exports.object({
|
|
29454
|
-
ok: external_exports.boolean(),
|
|
29455
|
-
builds: external_exports.array(sandboxLayerCliBuildHistoryItemSchema)
|
|
29456
|
-
});
|
|
29457
28688
|
var sandboxLayerCliBuildLogsResponseSchema = external_exports.object({
|
|
29458
28689
|
ok: external_exports.boolean(),
|
|
29459
28690
|
logs: external_exports.array(sandboxLayerCliBuildLogSchema)
|
|
@@ -29547,52 +28778,6 @@ function printTerminalBuildSummary(buildRequest) {
|
|
|
29547
28778
|
if (failure?.stderrPreview) console.log(`Stderr: ${failure.stderrPreview}`);
|
|
29548
28779
|
printActiveTemplateStatus(buildRequest);
|
|
29549
28780
|
}
|
|
29550
|
-
function printBuildHistory(builds) {
|
|
29551
|
-
if (builds.length === 0) {
|
|
29552
|
-
console.log("No sandbox builds found.");
|
|
29553
|
-
return;
|
|
29554
|
-
}
|
|
29555
|
-
for (const build of builds) {
|
|
29556
|
-
console.log(
|
|
29557
|
-
[
|
|
29558
|
-
`Build ${build.id} is ${build.status}.`,
|
|
29559
|
-
`source=${build.sourceRepo}`,
|
|
29560
|
-
`commit=${build.commitSha}`,
|
|
29561
|
-
`profile=${build.resourceProfileKey}`,
|
|
29562
|
-
build.templateId ? `template=${build.templateId}` : null,
|
|
29563
|
-
`base=${build.baseTemplateRef}@${build.baseVersion}`,
|
|
29564
|
-
`builtBy=${buildActorLabel(build.createdBy)}`
|
|
29565
|
-
].filter(Boolean).join(" ")
|
|
29566
|
-
);
|
|
29567
|
-
}
|
|
29568
|
-
}
|
|
29569
|
-
function selectionTierLabel(tier) {
|
|
29570
|
-
if (tier === "repo_local") return "repo-local";
|
|
29571
|
-
if (tier === "repo_assignment") return "assigned";
|
|
29572
|
-
return "workspace default";
|
|
29573
|
-
}
|
|
29574
|
-
function printSandboxStatus(payload) {
|
|
29575
|
-
if (payload.selection) {
|
|
29576
|
-
console.log(`Using ${selectionTierLabel(payload.selection.tier)} sandbox.`);
|
|
29577
|
-
console.log(`Template: ${payload.selection.templateId}`);
|
|
29578
|
-
console.log(`Source: ${payload.selection.sourceRepo}`);
|
|
29579
|
-
console.log(`Commit: ${payload.selection.commitSha}`);
|
|
29580
|
-
console.log(`Profile: ${payload.selection.resourceProfileKey}`);
|
|
29581
|
-
console.log(`Built from: ${payload.selection.baseTemplateRef} @ ${payload.selection.baseVersion}`);
|
|
29582
|
-
console.log(`Base status: ${payload.selection.baseStatus}`);
|
|
29583
|
-
console.log(`Built by: ${buildActorLabel(payload.selection.createdBy)}`);
|
|
29584
|
-
return;
|
|
29585
|
-
}
|
|
29586
|
-
console.log("Using Arcanist default sandbox.");
|
|
29587
|
-
if (payload.fallback?.templateId) console.log(`Template: ${payload.fallback.templateId}`);
|
|
29588
|
-
if (payload.latestRepoBuild) {
|
|
29589
|
-
console.log(`Latest custom build: ${payload.latestRepoBuild.status}`);
|
|
29590
|
-
console.log(`Commit: ${payload.latestRepoBuild.commitSha}`);
|
|
29591
|
-
console.log(`Profile: ${payload.latestRepoBuild.resourceProfileKey}`);
|
|
29592
|
-
console.log(`Built from: ${payload.latestRepoBuild.baseTemplateRef} @ ${payload.latestRepoBuild.baseVersion}`);
|
|
29593
|
-
console.log(`Built by: ${buildActorLabel(payload.latestRepoBuild.createdBy)}`);
|
|
29594
|
-
}
|
|
29595
|
-
}
|
|
29596
28781
|
function printLogs(logs) {
|
|
29597
28782
|
for (const log of logs) {
|
|
29598
28783
|
console.log(log.message);
|
|
@@ -29692,13 +28877,6 @@ function handleValidationError(err, options, command) {
|
|
|
29692
28877
|
}
|
|
29693
28878
|
throw err;
|
|
29694
28879
|
}
|
|
29695
|
-
function sourceBody(sourceRepo, manifestPath) {
|
|
29696
|
-
return {
|
|
29697
|
-
sourceRepoOwner: sourceRepo.owner,
|
|
29698
|
-
sourceRepoName: sourceRepo.repo,
|
|
29699
|
-
manifestPath
|
|
29700
|
-
};
|
|
29701
|
-
}
|
|
29702
28880
|
async function sandboxInitCommand(options = {}, command) {
|
|
29703
28881
|
const runtime = getRuntimeOptions(command, options);
|
|
29704
28882
|
if (existsSync3(DEFAULT_MANIFEST_PATH)) throw new CliError("conflict", `${DEFAULT_MANIFEST_PATH} already exists`);
|
|
@@ -29734,12 +28912,12 @@ async function sandboxInitCommand(options = {}, command) {
|
|
|
29734
28912
|
async function sandboxValidateCommand(options = {}, command) {
|
|
29735
28913
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
29736
28914
|
assertManifestExists(manifestPath);
|
|
29737
|
-
const manifestText =
|
|
28915
|
+
const manifestText = readFileSync2(manifestPath, "utf8");
|
|
29738
28916
|
let layerPath;
|
|
29739
28917
|
try {
|
|
29740
28918
|
layerPath = parseSandboxLayerManifest(manifestPath, manifestText).layer.dockerfile;
|
|
29741
28919
|
assertManifestExists(layerPath);
|
|
29742
|
-
const layerText =
|
|
28920
|
+
const layerText = readFileSync2(layerPath, "utf8");
|
|
29743
28921
|
const parsed = await parseSandboxLayerSource({ manifestPath, manifestText, layerPath, layerText });
|
|
29744
28922
|
const payload = formatValidationPayload({ manifestPath, layerPath, parsed });
|
|
29745
28923
|
emit(command, options, payload, (validationPayload) => {
|
|
@@ -29815,206 +28993,518 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
29815
28993
|
process.exitCode = 1;
|
|
29816
28994
|
}
|
|
29817
28995
|
}
|
|
29818
|
-
|
|
29819
|
-
|
|
29820
|
-
|
|
29821
|
-
|
|
29822
|
-
|
|
29823
|
-
|
|
29824
|
-
|
|
29825
|
-
|
|
29826
|
-
|
|
29827
|
-
|
|
29828
|
-
|
|
29829
|
-
|
|
29830
|
-
|
|
29831
|
-
|
|
29832
|
-
|
|
29833
|
-
|
|
29834
|
-
|
|
29835
|
-
|
|
29836
|
-
|
|
29837
|
-
|
|
29838
|
-
|
|
29839
|
-
|
|
29840
|
-
|
|
29841
|
-
|
|
29842
|
-
|
|
29843
|
-
|
|
29844
|
-
|
|
29845
|
-
|
|
29846
|
-
|
|
29847
|
-
|
|
29848
|
-
)
|
|
29849
|
-
|
|
29850
|
-
|
|
29851
|
-
|
|
29852
|
-
|
|
29853
|
-
|
|
29854
|
-
|
|
29855
|
-
throw new CliError("user", "Use --dry-run to preview or --yes to start a rebuild campaign.");
|
|
29856
|
-
}
|
|
29857
|
-
if (options.all && options.business) {
|
|
29858
|
-
throw new CliError("user", "Use either --all or --business, not both.");
|
|
28996
|
+
|
|
28997
|
+
// ../../shared/constants/observability.ts
|
|
28998
|
+
var ZEUS_SCOUT_HEALTH_PUBLISH_OUTCOMES = ["published", "publish_failed", "gate_rejected"];
|
|
28999
|
+
var ZEUS_SCOUT_HEALTHS = ["not_applicable", "never_ran", "all_empty", "completed_empty", "yielded"];
|
|
29000
|
+
|
|
29001
|
+
// ../../shared/constants/agent-runtime-evidence.ts
|
|
29002
|
+
var PR_REVIEW_CAPTURE_ID_PATTERN = /^cap-[0-9a-f]{8}$/;
|
|
29003
|
+
var PR_REVIEW_CAPTURE_LABEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9 ._-]{0,79}$/;
|
|
29004
|
+
var PR_REVIEW_CAPTURE_ALT_MAX_CHARS = 200;
|
|
29005
|
+
var PR_REVIEW_APP_REQUEST_ID_PATTERN = /^req-[0-9a-f]{8}$/;
|
|
29006
|
+
var PR_REVIEW_VIDEO_MAX_DURATION_MS = 9e4;
|
|
29007
|
+
|
|
29008
|
+
// ../../shared/types/doc-lookup.ts
|
|
29009
|
+
var DOC_LOOKUP_QUERY_MAX_CHARS = 300;
|
|
29010
|
+
var DOC_LOOKUP_URL_MAX_CHARS = 500;
|
|
29011
|
+
var DOC_LOOKUP_SEARCH_RESULTS = 3;
|
|
29012
|
+
var DOC_LOOKUP_TEXT_MAX_CHARS = 6e3;
|
|
29013
|
+
var DOC_LOOKUP_ID_PATTERN = /^dl-[0-9a-f]{8}$/;
|
|
29014
|
+
var DOC_LOOKUP_BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
29015
|
+
"localhost",
|
|
29016
|
+
"localhost.localdomain",
|
|
29017
|
+
"metadata",
|
|
29018
|
+
"metadata.google.internal",
|
|
29019
|
+
"instance-data",
|
|
29020
|
+
"instance-data.ec2.internal"
|
|
29021
|
+
]);
|
|
29022
|
+
function isBlockedIpLiteral(host) {
|
|
29023
|
+
const ipv43 = host.split(".");
|
|
29024
|
+
if (ipv43.length === 4 && ipv43.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255)) {
|
|
29025
|
+
const [a, b] = ipv43.map(Number);
|
|
29026
|
+
if (a === 0 || a === 10 || a === 127) return true;
|
|
29027
|
+
if (a === 169 && b === 254) return true;
|
|
29028
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
29029
|
+
if (a === 192 && b === 168) return true;
|
|
29030
|
+
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
29031
|
+
if (a === 198 && (b === 18 || b === 19)) return true;
|
|
29032
|
+
return false;
|
|
29859
29033
|
}
|
|
29860
|
-
|
|
29861
|
-
|
|
29862
|
-
|
|
29863
|
-
|
|
29864
|
-
|
|
29865
|
-
|
|
29866
|
-
|
|
29867
|
-
|
|
29868
|
-
|
|
29869
|
-
|
|
29870
|
-
|
|
29871
|
-
|
|
29034
|
+
if (host.includes(":")) {
|
|
29035
|
+
const lower = host.toLowerCase();
|
|
29036
|
+
if (lower === "::" || lower === "::1") return true;
|
|
29037
|
+
if (/^fe[89ab]/.test(lower)) return true;
|
|
29038
|
+
if (/^f[cd]/.test(lower)) return true;
|
|
29039
|
+
if (lower.startsWith("::ffff:")) {
|
|
29040
|
+
const tail = lower.slice("::ffff:".length);
|
|
29041
|
+
const hexWords = tail.match(/^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
|
|
29042
|
+
if (hexWords) {
|
|
29043
|
+
const hi = Number.parseInt(hexWords[1], 16);
|
|
29044
|
+
const lo = Number.parseInt(hexWords[2], 16);
|
|
29045
|
+
return isBlockedIpLiteral(`${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`);
|
|
29046
|
+
}
|
|
29047
|
+
return isBlockedIpLiteral(tail);
|
|
29872
29048
|
}
|
|
29873
|
-
|
|
29874
|
-
});
|
|
29875
|
-
if (options.follow && !options.dryRun) {
|
|
29876
|
-
payload = await followRebuildCampaign(config2, payload.campaign.id);
|
|
29049
|
+
return false;
|
|
29877
29050
|
}
|
|
29878
|
-
|
|
29051
|
+
return false;
|
|
29879
29052
|
}
|
|
29880
|
-
|
|
29881
|
-
|
|
29882
|
-
const
|
|
29883
|
-
|
|
29884
|
-
|
|
29885
|
-
|
|
29886
|
-
|
|
29887
|
-
await sleep(DEFAULT_POLL_INTERVAL_MS);
|
|
29888
|
-
}
|
|
29889
|
-
}
|
|
29890
|
-
function isCampaignTerminal(status) {
|
|
29891
|
-
return status === "completed" || status === "completed_with_failures" || status === "failed";
|
|
29892
|
-
}
|
|
29893
|
-
function printRebuildCampaign(payload) {
|
|
29894
|
-
console.log(`Campaign: ${payload.campaign.id}`);
|
|
29895
|
-
console.log(`Status: ${payload.campaign.status}`);
|
|
29896
|
-
console.log(`Active artifacts scanned: ${payload.campaign.summary.activeArtifactsScanned}`);
|
|
29897
|
-
console.log(`Stale artifacts found: ${payload.campaign.summary.staleArtifactsFound}`);
|
|
29898
|
-
console.log(`Current artifacts skipped: ${payload.campaign.summary.currentArtifactsSkipped}`);
|
|
29899
|
-
console.log(`Missing installation skips: ${payload.campaign.summary.missingInstallationSkips}`);
|
|
29900
|
-
console.log(`Source unavailable skips: ${payload.campaign.summary.sourceUnavailableSkips}`);
|
|
29901
|
-
console.log(`Unversioned base skips: ${payload.campaign.summary.unversionedBaseSkips}`);
|
|
29902
|
-
console.log(`Active changed skips: ${payload.campaign.summary.activeChangedSkips}`);
|
|
29903
|
-
console.log(`Failed items: ${payload.campaign.summary.failedItems}`);
|
|
29904
|
-
console.log(`Promoted items: ${payload.campaign.summary.promotedItems}`);
|
|
29905
|
-
console.log(`Builds queued: ${payload.campaign.summary.buildsQueued}`);
|
|
29906
|
-
if (payload.campaign.status === "completed_with_failures" || payload.campaign.status === "failed") {
|
|
29907
|
-
console.log("Previous active templates remain active for failed or skipped rebuild items.");
|
|
29908
|
-
}
|
|
29909
|
-
}
|
|
29910
|
-
async function sandboxLogsCommand(buildId, options = {}, command) {
|
|
29911
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
29912
|
-
const businessId = await resolveBusinessId(config2, options);
|
|
29913
|
-
let afterSequence = options.afterSequence == null ? void 0 : Number(options.afterSequence);
|
|
29914
|
-
if (afterSequence != null && (!Number.isInteger(afterSequence) || afterSequence < 0)) {
|
|
29915
|
-
throw new CliError("user", "--after-sequence must be a non-negative integer.");
|
|
29916
|
-
}
|
|
29917
|
-
const limit = options.limit == null ? void 0 : Number(options.limit);
|
|
29918
|
-
if (limit != null && (!Number.isInteger(limit) || limit < 1)) {
|
|
29919
|
-
throw new CliError("user", "--limit must be a positive integer.");
|
|
29920
|
-
}
|
|
29921
|
-
const json2 = isJson(command, options);
|
|
29922
|
-
if (options.follow) {
|
|
29923
|
-
const finalBuild = await waitForBuild(config2, businessId, buildId, {
|
|
29924
|
-
follow: true,
|
|
29925
|
-
pollIntervalMs: parsePollInterval(options.pollInterval, {
|
|
29926
|
-
defaultMs: DEFAULT_POLL_INTERVAL_MS,
|
|
29927
|
-
minMs: MIN_POLL_INTERVAL_MS,
|
|
29928
|
-
maxMs: null
|
|
29929
|
-
}),
|
|
29930
|
-
json: json2,
|
|
29931
|
-
afterSequence,
|
|
29932
|
-
limit
|
|
29933
|
-
});
|
|
29934
|
-
if (json2) writeJson({ type: "build", buildRequest: finalBuild });
|
|
29935
|
-
else printTerminalBuildSummary(finalBuild);
|
|
29936
|
-
if (finalBuild.status !== "completed") process.exitCode = 1;
|
|
29937
|
-
return;
|
|
29053
|
+
function isPlainHttpUrl(value) {
|
|
29054
|
+
try {
|
|
29055
|
+
const parsed = new URL(value);
|
|
29056
|
+
const host = parsed.hostname.toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
29057
|
+
return (parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username === "" && parsed.password === "" && !DOC_LOOKUP_BLOCKED_HOSTNAMES.has(host) && !isBlockedIpLiteral(host);
|
|
29058
|
+
} catch {
|
|
29059
|
+
return false;
|
|
29938
29060
|
}
|
|
29939
|
-
const payload = sandboxLayerCliBuildLogsResponseSchema.parse(
|
|
29940
|
-
await apiFetch(config2, buildLogsPath(businessId, buildId, { afterSequence, limit }))
|
|
29941
|
-
);
|
|
29942
|
-
if (json2) writeJson(payload);
|
|
29943
|
-
else printLogs(payload.logs);
|
|
29944
29061
|
}
|
|
29945
|
-
|
|
29946
|
-
|
|
29947
|
-
|
|
29948
|
-
|
|
29949
|
-
|
|
29950
|
-
|
|
29951
|
-
|
|
29952
|
-
|
|
29953
|
-
|
|
29954
|
-
|
|
29955
|
-
|
|
29956
|
-
|
|
29957
|
-
|
|
29958
|
-
|
|
29959
|
-
|
|
29960
|
-
|
|
29961
|
-
|
|
29962
|
-
|
|
29963
|
-
|
|
29964
|
-
|
|
29965
|
-
|
|
29966
|
-
|
|
29967
|
-
|
|
29968
|
-
|
|
29969
|
-
|
|
29970
|
-
|
|
29971
|
-
|
|
29972
|
-
|
|
29973
|
-
|
|
29974
|
-
|
|
29975
|
-
|
|
29976
|
-
|
|
29977
|
-
|
|
29978
|
-
|
|
29979
|
-
|
|
29980
|
-
|
|
29981
|
-
|
|
29062
|
+
var DocLookupRequestSchema = external_exports.discriminatedUnion("kind", [
|
|
29063
|
+
external_exports.object({
|
|
29064
|
+
kind: external_exports.literal("search"),
|
|
29065
|
+
query: external_exports.string().trim().min(3).max(DOC_LOOKUP_QUERY_MAX_CHARS)
|
|
29066
|
+
}).strict(),
|
|
29067
|
+
external_exports.object({
|
|
29068
|
+
kind: external_exports.literal("fetch"),
|
|
29069
|
+
url: external_exports.string().url().max(DOC_LOOKUP_URL_MAX_CHARS).refine(isPlainHttpUrl, { message: "url must be http(s) without credentials" })
|
|
29070
|
+
}).strict()
|
|
29071
|
+
]);
|
|
29072
|
+
var DocLookupResultSchema = external_exports.object({
|
|
29073
|
+
url: external_exports.string().max(DOC_LOOKUP_URL_MAX_CHARS),
|
|
29074
|
+
title: external_exports.string().max(300).nullable(),
|
|
29075
|
+
publishedDate: external_exports.string().max(40).nullable(),
|
|
29076
|
+
text: external_exports.string().max(DOC_LOOKUP_TEXT_MAX_CHARS)
|
|
29077
|
+
}).strict();
|
|
29078
|
+
var DocLookupResponseSchema = external_exports.object({
|
|
29079
|
+
results: external_exports.array(DocLookupResultSchema).max(DOC_LOOKUP_SEARCH_RESULTS)
|
|
29080
|
+
}).strict();
|
|
29081
|
+
|
|
29082
|
+
// ../../shared/constants/durations.ts
|
|
29083
|
+
var SECOND = 1e3;
|
|
29084
|
+
var DATADOG_POST_TIMEOUT_MS = 2 * SECOND;
|
|
29085
|
+
var MINUTE = 60 * SECOND;
|
|
29086
|
+
var PROVIDER_RETRY_ATTEMPT_TIMEOUT_MS = 10 * MINUTE;
|
|
29087
|
+
var HOUR = 60 * MINUTE;
|
|
29088
|
+
var SANDBOX_TELEMETRY_CAPABILITY_TTL_MS = 2 * HOUR;
|
|
29089
|
+
var SANDBOX_TELEMETRY_RATE_LIMIT_WINDOW_MS = 1 * MINUTE;
|
|
29090
|
+
var WORKER_VERSION_HANDOFF_CLOSE_WINDOW_MS = 2 * MINUTE;
|
|
29091
|
+
var ZEUS_TELEMETRY_MAX_DURATION_MS = 24 * HOUR;
|
|
29092
|
+
var D1_READ_ATTEMPT_TIMEOUT_MS = 5 * SECOND;
|
|
29093
|
+
var DAY = 24 * HOUR;
|
|
29094
|
+
var ENVIRONMENT_IMAGE_AGE_THRESHOLD_MS = 7 * DAY;
|
|
29095
|
+
var INTERNAL_ZEUS_COHORT_MAX_WINDOW_MS = 31 * DAY;
|
|
29096
|
+
var ZEUS_REVIEW_TELEMETRY_DETAIL_RETENTION_MS = 30 * DAY;
|
|
29097
|
+
var PR_CHAT_TIMEOUT_MS = 45 * SECOND;
|
|
29098
|
+
var SLACK_API_TIMEOUT_MS = 20 * SECOND;
|
|
29099
|
+
var EXA_REQUEST_TIMEOUT_MS = 15 * SECOND;
|
|
29100
|
+
var ONBOARDING_SETUP_SCRIPT_DURATION_TARGET_MS = 5 * MINUTE;
|
|
29101
|
+
var AUTHORED_RUNTIME_ABI_READ_TIMEOUT_MS = 10 * SECOND;
|
|
29102
|
+
var GIGAFLOW_SEND_TIMEOUT_MS = 5 * SECOND;
|
|
29103
|
+
var GITHUB_REQUEST_SLEEP_BUDGET_MS = 20 * SECOND;
|
|
29104
|
+
var GITHUB_REQUEST_ATTEMPT_TIMEOUT_MS = 5 * SECOND;
|
|
29105
|
+
var GITHUB_REQUEST_WAIT_UNTIL_SLEEP_BUDGET_MS = 8 * SECOND;
|
|
29106
|
+
var PR_REVIEW_DELIVERY_LEASE_MS = 50 * SECOND;
|
|
29107
|
+
var PR_REVIEW_DETACHED_TRIGGER_LEASE_MS = 50 * SECOND;
|
|
29108
|
+
var PR_REVIEW_REQUEST_LEASE_MS = 5 * MINUTE;
|
|
29109
|
+
var ZEUS_FINDING_FIX_JUDGE_TIMEOUT_MS = 20 * SECOND;
|
|
29110
|
+
var ZEUS_FINDING_FIX_JEV_TIMEOUT_MS = 10 * SECOND;
|
|
29111
|
+
var ZEUS_FINDING_REPLY_JEV_TIMEOUT_MS = 5 * SECOND;
|
|
29112
|
+
var ZEUS_FINDING_EVIDENCE_JEV_TIMEOUT_MS = 5 * SECOND;
|
|
29113
|
+
var ZEUS_PRIOR_FINDING_MATCH_JEV_TIMEOUT_MS = 5 * SECOND;
|
|
29114
|
+
var PR_MENTION_INTENT_TIMEOUT_MS = 5 * SECOND;
|
|
29115
|
+
var PR_BROWSER_QA_INTENT_TIMEOUT_MS = 10 * SECOND;
|
|
29116
|
+
var ANUBIS_LANDMARK_FALLBACK_TIMEOUT_MS = 8 * SECOND;
|
|
29117
|
+
var ANUBIS_FIELD_TEXT_TIMEOUT_MS = 20 * SECOND;
|
|
29118
|
+
var ZEUS_FINDING_FIX_JUDGE_PR_BUDGET_MS = 25 * SECOND;
|
|
29119
|
+
var ZEUS_FINDING_FIX_JUDGE_TURTLE_BUDGET_MS = 5 * MINUTE;
|
|
29120
|
+
var ZEUS_FINDING_FIX_JUDGE_SWEEP_LOOKBACK_MS = 14 * DAY;
|
|
29121
|
+
var PR_REVIEW_STORED_SHAPE_SUPPORT_MS = 14 * DAY;
|
|
29122
|
+
var PR_REVIEW_DELIVERY_AMBIGUITY_MS = 60 * SECOND;
|
|
29123
|
+
var PR_REVIEW_SUMMARY_REFRESH_MS = 5 * MINUTE;
|
|
29124
|
+
var PR_REVIEW_SUMMARY_WRITER_LEASE_MS = 2 * MINUTE;
|
|
29125
|
+
var PR_REVIEW_GENERATION_RETRY_MS = 5 * MINUTE;
|
|
29126
|
+
var PR_REVIEW_ATTACHED_GENERATION_PROBE_MS = 20 * MINUTE;
|
|
29127
|
+
var PR_REVIEW_GENERATION_FAST_RETRY_MS = 10 * SECOND;
|
|
29128
|
+
var PR_REVIEW_GENERATION_FAST_FAIL_MAX_MS = 60 * SECOND;
|
|
29129
|
+
var PR_REVIEW_DELIVERY_RETRY_MS = 5 * MINUTE;
|
|
29130
|
+
var SESSION_INTERVENTION_TTL_MS = 7 * 24 * HOUR;
|
|
29131
|
+
var BROWSER_STATE_RELOGIN_INTERVENTION_TTL_MS = 30 * DAY;
|
|
29132
|
+
var REVIEW_SCREENSHOT_PUBLIC_URL_TTL_MS = 30 * DAY;
|
|
29133
|
+
var ENVIRONMENT_FINDING_COLLECTION_WINDOW_MS = 4 * HOUR;
|
|
29134
|
+
var ENVIRONMENT_LIMITATION_TRIAGE_JEV_TIMEOUT_MS = 5 * SECOND;
|
|
29135
|
+
var ENVIRONMENT_RECENT_REVIEW_WINDOW_MS = 7 * 24 * HOUR;
|
|
29136
|
+
var WARM_DEMAND_WEEK_MS = 7 * DAY;
|
|
29137
|
+
var WARM_POOL_CLAIM_LEASE_MS = 5 * MINUTE;
|
|
29138
|
+
var WARM_POOL_MAX_CLOCK_SKEW_MS = 60 * SECOND;
|
|
29139
|
+
var WARM_POOL_PREPARE_LEASE_MS = 20 * MINUTE;
|
|
29140
|
+
var WARM_POOL_REPLACE_AFTER_MS = 90 * MINUTE;
|
|
29141
|
+
var WARM_POOL_MAX_AGE_MS = 2 * HOUR;
|
|
29142
|
+
var WARM_POOL_PREPARE_BUDGET_MS = 8 * MINUTE;
|
|
29143
|
+
var WARM_POOL_MARKER_WRITE_TIMEOUT_MS = 10 * SECOND;
|
|
29144
|
+
var WARM_POOL_PREFLIGHT_TIMEOUT_MS = 90 * SECOND;
|
|
29145
|
+
var WARM_POOL_CLAIM_BUDGET_MS = 30 * SECOND;
|
|
29146
|
+
var WARM_POOL_BRIDGE_CONNECT_TIMEOUT_MS = 45 * SECOND;
|
|
29147
|
+
var ENVIRONMENT_JOB_LOCK_WAIT_MS = 10 * MINUTE;
|
|
29148
|
+
var ENVIRONMENT_JOB_LOCK_RENEWAL_MS = 30 * SECOND;
|
|
29149
|
+
var ENVIRONMENT_JOB_LOCK_TTL_MS = 90 * SECOND;
|
|
29150
|
+
var TURTLE_SANDBOX_TIMEOUT_MS = 20 * MINUTE;
|
|
29151
|
+
var TURTLE_COMMAND_TIMEOUT_MS = 30 * SECOND;
|
|
29152
|
+
var TURTLE_INSTALL_TIMEOUT_MS = 2 * MINUTE;
|
|
29153
|
+
var TURTLE_MODEL_TIMEOUT_MS = 8 * MINUTE;
|
|
29154
|
+
var TURTLE_PR_METADATA_TIMEOUT_MS = 90 * SECOND;
|
|
29155
|
+
var TURTLE_TRANSCRIPT_FLUSH_MS = 2 * SECOND;
|
|
29156
|
+
var TURTLE_CI_REPAIR_LEASE_MS = 3 * MINUTE;
|
|
29157
|
+
var TURTLE_CI_REPAIR_LOST_MESSAGE_BUDGET_MS = 30 * MINUTE;
|
|
29158
|
+
var TURTLE_CI_REPAIR_PUSH_BUDGET_MS = 24 * HOUR;
|
|
29159
|
+
var TURTLE_CI_REPAIR_AWAITING_PR_BUDGET_MS = 30 * MINUTE;
|
|
29160
|
+
var TURTLE_ZEUS_REPAIR_LEASE_MS = 3 * MINUTE;
|
|
29161
|
+
var TURTLE_ZEUS_REPAIR_LOST_MESSAGE_BUDGET_MS = 30 * MINUTE;
|
|
29162
|
+
var TURTLE_ZEUS_REPAIR_PUSH_BUDGET_MS = 24 * HOUR;
|
|
29163
|
+
var TURTLE_ZEUS_REPAIR_WAIT_CI_BUDGET_MS = TURTLE_CI_REPAIR_PUSH_BUDGET_MS + HOUR;
|
|
29164
|
+
var ANUBIS_READINESS_TIMEOUT_MAX_MS = 10 * MINUTE;
|
|
29165
|
+
|
|
29166
|
+
// ../../shared/constants/prompt-budgets.ts
|
|
29167
|
+
var PROMPT_BUDGET_NODES = {
|
|
29168
|
+
// The lifecycle deadline is a root because callers may override it per session.
|
|
29169
|
+
PROMPT_STARTUP: { ms: 24e4, parent: null },
|
|
29170
|
+
// Dispatch begins after startup and has a separate lifecycle override surface.
|
|
29171
|
+
PROMPT_DISPATCH: { ms: 18e4, parent: null },
|
|
29172
|
+
// Transport liveness is phase-independent and does not sit inside prompt startup.
|
|
29173
|
+
SANDBOX_LIVENESS: { ms: 6e4, parent: null },
|
|
29174
|
+
CODEX_STARTUP: { ms: 3e4, parent: "PROMPT_STARTUP" },
|
|
29175
|
+
PR_HEAD_CHECKOUT: { ms: 24e4, parent: "PROMPT_STARTUP" },
|
|
29176
|
+
WORKSPACE_SETUP_WAIT: { ms: 6e5, parent: "PROMPT_STARTUP" },
|
|
29177
|
+
APP_RUNTIME_READY: { ms: 93e4, parent: "PROMPT_STARTUP" },
|
|
29178
|
+
// Review preflight is background work; no wall budget is declared today.
|
|
29179
|
+
REVIEW_PREFLIGHT: { ms: null, parent: "PROMPT_STARTUP" }
|
|
29180
|
+
};
|
|
29181
|
+
var ZEUS_SCOUT_TIMEOUT_MS = 10 * 6e4;
|
|
29182
|
+
var ZEUS_REVIEWER_LANE_TIMEOUT_MS = 10 * 6e4;
|
|
29183
|
+
var ZEUS_PROMPT_STARTUP_TIMEOUT_MS = PROMPT_BUDGET_NODES.PROMPT_STARTUP.ms + ZEUS_SCOUT_TIMEOUT_MS;
|
|
29184
|
+
var ANUBIS_QA_PLANNING_DEADLINE_MS = 3e5;
|
|
29185
|
+
var ANUBIS_QA_INVESTIGATION_DEADLINE_MS = 15 * 6e4;
|
|
29186
|
+
var ANUBIS_QA_SETTLEMENT_WALL_MS = 5 * 6e4;
|
|
29187
|
+
var ANUBIS_QA_PROMPT_WALL_MS = ANUBIS_READINESS_TIMEOUT_MAX_MS + ANUBIS_QA_PLANNING_DEADLINE_MS + ANUBIS_QA_INVESTIGATION_DEADLINE_MS + ANUBIS_QA_SETTLEMENT_WALL_MS;
|
|
29188
|
+
var ZEUS_SCOUT_MAX_CANDIDATES_PER_SCOUT = 8;
|
|
29189
|
+
var ZEUS_SCOUT_EXCERPT_MAX_FILE_BYTES = 256 * 1024;
|
|
29190
|
+
var ZEUS_REVIEW_INVESTIGATION_DEADLINE_MS = 15.5 * 6e4;
|
|
29191
|
+
var ZEUS_SCOUT_TARGETED_MAX_QUESTIONS = 7;
|
|
29192
|
+
var ZEUS_SCOUT_DISCOVERY_MAX_SHARDS = 4;
|
|
29193
|
+
var ZEUS_SCOUT_TARGETED_MAX_FLEET = ZEUS_SCOUT_TARGETED_MAX_QUESTIONS + ZEUS_SCOUT_DISCOVERY_MAX_SHARDS + 1;
|
|
29194
|
+
var ZEUS_SCOUT_TARGETED_INPUT_MAX_BYTES = 64 * 1024;
|
|
29195
|
+
var ZEUS_SCOUT_TARGETED_OUTPUT_MAX_BYTES = 64 * 1024;
|
|
29196
|
+
|
|
29197
|
+
// ../../shared/types/zeus-review-investigation.ts
|
|
29198
|
+
var ZEUS_SCOUT_MAX_CANDIDATES = ZEUS_SCOUT_MAX_CANDIDATES_PER_SCOUT * ZEUS_SCOUT_TARGETED_MAX_FLEET;
|
|
29199
|
+
var prose = external_exports.string().trim().min(1).max(4e3);
|
|
29200
|
+
var ZeusReviewWorkItemBaseSchema = external_exports.object({
|
|
29201
|
+
id: external_exports.string().regex(/^[a-zA-Z0-9._:-]+$/).max(80).describe("Stable id you choose for this work item; reuse it to update the item."),
|
|
29202
|
+
changedPath: prose.describe("Changed file path from the admitted diff."),
|
|
29203
|
+
producer: prose.describe("Code that produces the changed value or behavior."),
|
|
29204
|
+
consumer: prose.describe("Actual code that consumes it."),
|
|
29205
|
+
inputState: prose.describe("Concrete input or state that reaches the consumer."),
|
|
29206
|
+
status: external_exports.enum(["pending", "settled", "refuted", "below_bar", "external", "environment", "budget_exhausted"]).describe("pending while open; any other status closes the item and needs reason."),
|
|
29207
|
+
evidence: external_exports.array(prose).max(ZEUS_SCOUT_MAX_CANDIDATES).describe("Source or probe references. Required for settled, refuted, below_bar."),
|
|
29208
|
+
reason: prose.describe("Why the item has this status. Optional only while pending."),
|
|
29209
|
+
candidateIds: external_exports.array(external_exports.string().min(1).max(80)).max(ZEUS_SCOUT_MAX_CANDIDATES).describe("Delivered scout candidate ids this item covers."),
|
|
29210
|
+
findingIds: external_exports.array(external_exports.string().min(1).max(80)).max(ZEUS_SCOUT_MAX_CANDIDATES).describe("Ids of findings this item supports.")
|
|
29211
|
+
}).strict();
|
|
29212
|
+
var ZeusReviewWorkItemSchema = ZeusReviewWorkItemBaseSchema.refine(
|
|
29213
|
+
(item) => !["settled", "refuted", "below_bar"].includes(item.status) || item.evidence.length > 0,
|
|
29214
|
+
{
|
|
29215
|
+
message: "A settled decision needs source or probe evidence."
|
|
29216
|
+
}
|
|
29217
|
+
);
|
|
29218
|
+
var ZEUS_REVIEW_PENDING_REASON = "Pending: not yet investigated.";
|
|
29219
|
+
var CANDIDATE_REFERENCE_PREFIX = "candidate:";
|
|
29220
|
+
var ZeusReviewWorkItemInputSchema = ZeusReviewWorkItemBaseSchema.extend({
|
|
29221
|
+
status: ZeusReviewWorkItemBaseSchema.shape.status.default("pending"),
|
|
29222
|
+
evidence: ZeusReviewWorkItemBaseSchema.shape.evidence.default([]),
|
|
29223
|
+
// Accepts the `candidate:<id>` evidence reference form; the prefix is stripped before the id length cap applies.
|
|
29224
|
+
candidateIds: external_exports.array(
|
|
29225
|
+
external_exports.string().min(1).max(CANDIDATE_REFERENCE_PREFIX.length + 80).transform(
|
|
29226
|
+
(id) => id.startsWith(CANDIDATE_REFERENCE_PREFIX) ? id.slice(CANDIDATE_REFERENCE_PREFIX.length) : id
|
|
29227
|
+
)
|
|
29228
|
+
).max(ZEUS_SCOUT_MAX_CANDIDATES).describe(ZeusReviewWorkItemBaseSchema.shape.candidateIds.description).optional(),
|
|
29229
|
+
findingIds: ZeusReviewWorkItemBaseSchema.shape.findingIds.optional(),
|
|
29230
|
+
reason: ZeusReviewWorkItemBaseSchema.shape.reason.optional()
|
|
29231
|
+
}).transform((item, ctx) => {
|
|
29232
|
+
if (item.reason === void 0 && item.status !== "pending") {
|
|
29233
|
+
ctx.addIssue({ code: "custom", path: ["reason"], message: `reason is required when status is ${item.status}` });
|
|
29234
|
+
return external_exports.NEVER;
|
|
29235
|
+
}
|
|
29236
|
+
const parsed = ZeusReviewWorkItemSchema.safeParse({
|
|
29237
|
+
...item,
|
|
29238
|
+
candidateIds: item.candidateIds ?? [],
|
|
29239
|
+
findingIds: item.findingIds ?? [],
|
|
29240
|
+
reason: item.reason ?? ZEUS_REVIEW_PENDING_REASON
|
|
29982
29241
|
});
|
|
29983
|
-
|
|
29984
|
-
|
|
29985
|
-
|
|
29986
|
-
throw new CliError("user", "`sandbox unassign default --json` requires --yes.");
|
|
29242
|
+
if (!parsed.success) {
|
|
29243
|
+
for (const issue3 of parsed.error.issues) ctx.addIssue({ code: "custom", path: issue3.path, message: issue3.message });
|
|
29244
|
+
return external_exports.NEVER;
|
|
29987
29245
|
}
|
|
29988
|
-
const {
|
|
29989
|
-
|
|
29990
|
-
|
|
29991
|
-
|
|
29992
|
-
|
|
29993
|
-
|
|
29994
|
-
|
|
29995
|
-
|
|
29996
|
-
|
|
29997
|
-
|
|
29998
|
-
|
|
29999
|
-
|
|
30000
|
-
|
|
29246
|
+
const { candidateIds, findingIds, ...normalized } = parsed.data;
|
|
29247
|
+
return {
|
|
29248
|
+
...normalized,
|
|
29249
|
+
...item.candidateIds === void 0 ? {} : { candidateIds },
|
|
29250
|
+
...item.findingIds === void 0 ? {} : { findingIds }
|
|
29251
|
+
};
|
|
29252
|
+
});
|
|
29253
|
+
var ZeusScoutInvestigationIdSchema = external_exports.string().regex(/^q-[1-9][0-9]*$/).max(16);
|
|
29254
|
+
var ZeusReviewInvestigationSchema = external_exports.object({
|
|
29255
|
+
baseSha: external_exports.string().nullable(),
|
|
29256
|
+
headSha: external_exports.string().nullable(),
|
|
29257
|
+
items: external_exports.array(ZeusReviewWorkItemSchema),
|
|
29258
|
+
unreadRanges: external_exports.array(external_exports.object({ path: external_exports.string(), start: external_exports.number().int(), end: external_exports.number().int() })),
|
|
29259
|
+
reconciliationUsed: external_exports.boolean(),
|
|
29260
|
+
budgetExhausted: external_exports.boolean(),
|
|
29261
|
+
pendingPaths: external_exports.array(external_exports.string()),
|
|
29262
|
+
omittedRangeCount: external_exports.number().int().nonnegative(),
|
|
29263
|
+
omittedPendingPathCount: external_exports.number().int().nonnegative()
|
|
29264
|
+
}).refine(
|
|
29265
|
+
(value) => new TextEncoder().encode(JSON.stringify(value)).byteLength <= ZEUS_SCOUT_TARGETED_INPUT_MAX_BYTES,
|
|
29266
|
+
{
|
|
29267
|
+
message: "Investigation ledger exceeds the transport budget."
|
|
30001
29268
|
}
|
|
30002
|
-
|
|
30003
|
-
|
|
30004
|
-
|
|
30005
|
-
|
|
30006
|
-
|
|
30007
|
-
|
|
30008
|
-
|
|
30009
|
-
|
|
30010
|
-
|
|
30011
|
-
|
|
30012
|
-
)
|
|
30013
|
-
|
|
30014
|
-
|
|
29269
|
+
);
|
|
29270
|
+
|
|
29271
|
+
// ../../shared/types/agent-runtime-evidence.ts
|
|
29272
|
+
var captureIdSchema = external_exports.string().regex(PR_REVIEW_CAPTURE_ID_PATTERN);
|
|
29273
|
+
var PrReviewCaptureKindSchema = external_exports.enum(["screenshot", "video"]);
|
|
29274
|
+
var PrReviewCaptureSchema = external_exports.object({
|
|
29275
|
+
captureId: captureIdSchema,
|
|
29276
|
+
label: external_exports.string().regex(PR_REVIEW_CAPTURE_LABEL_PATTERN),
|
|
29277
|
+
artifactId: external_exports.string().min(1).max(80),
|
|
29278
|
+
url: external_exports.string().url().max(2e3),
|
|
29279
|
+
kind: PrReviewCaptureKindSchema.default("screenshot"),
|
|
29280
|
+
/** Video length in milliseconds; absent on a screenshot. */
|
|
29281
|
+
durationMs: external_exports.number().int().min(0).max(PR_REVIEW_VIDEO_MAX_DURATION_MS).optional(),
|
|
29282
|
+
/** The uncropped frame behind a cropped screenshot; the published image links to it. */
|
|
29283
|
+
fullUrl: external_exports.string().url().max(2e3).optional(),
|
|
29284
|
+
/** A still the published comment shows in place of a video, linking to the clip. */
|
|
29285
|
+
posterUrl: external_exports.string().url().max(2e3).optional(),
|
|
29286
|
+
/** One sentence saying what the image shows; it is all that remains once the signed URL expires. */
|
|
29287
|
+
alt: external_exports.string().min(1).max(PR_REVIEW_CAPTURE_ALT_MAX_CHARS).optional()
|
|
29288
|
+
}).strict();
|
|
29289
|
+
var PrReviewSessionCaptureSchema = PrReviewCaptureSchema.extend({
|
|
29290
|
+
finalPath: external_exports.string().max(500).optional()
|
|
29291
|
+
}).strict();
|
|
29292
|
+
var ZeusSessionFlywheelAppRequestSchema = external_exports.object({
|
|
29293
|
+
requestId: external_exports.string().regex(PR_REVIEW_APP_REQUEST_ID_PATTERN),
|
|
29294
|
+
method: external_exports.enum(["GET", "HEAD"]),
|
|
29295
|
+
/** Path plus query as the model asked for it, on the app origin. */
|
|
29296
|
+
path: external_exports.string().min(1).max(2e3),
|
|
29297
|
+
status: external_exports.number().int().min(100).max(599).nullable(),
|
|
29298
|
+
authenticated: external_exports.boolean(),
|
|
29299
|
+
redirected: external_exports.object({ location: external_exports.string().max(2e3) }).strict().nullable(),
|
|
29300
|
+
contentType: external_exports.string().max(200).nullable(),
|
|
29301
|
+
bodySha256: external_exports.string().regex(/^[0-9a-f]{64}$/i).nullable(),
|
|
29302
|
+
bodyExcerpt: external_exports.string().max(2e3).nullable(),
|
|
29303
|
+
promptId: external_exports.string().min(1),
|
|
29304
|
+
bodyOmitted: external_exports.enum(["binary", "too_large", "unreadable"]).optional()
|
|
29305
|
+
}).strict();
|
|
29306
|
+
var DOC_LOOKUP_STATUSES = [
|
|
29307
|
+
"ok",
|
|
29308
|
+
"empty",
|
|
29309
|
+
"rate_limited",
|
|
29310
|
+
"upstream_error",
|
|
29311
|
+
"invalid_input",
|
|
29312
|
+
"budget_exhausted",
|
|
29313
|
+
"page_unavailable",
|
|
29314
|
+
"unavailable"
|
|
29315
|
+
];
|
|
29316
|
+
var ZeusSessionFlywheelDocLookupSchema = external_exports.object({
|
|
29317
|
+
lookupId: external_exports.string().regex(DOC_LOOKUP_ID_PATTERN),
|
|
29318
|
+
kind: external_exports.enum(["search", "fetch"]),
|
|
29319
|
+
/** Model-authored query or URL, verbatim after redact(). This is the audit record; keep it. */
|
|
29320
|
+
input: external_exports.string().min(1).max(DOC_LOOKUP_URL_MAX_CHARS),
|
|
29321
|
+
status: external_exports.enum(DOC_LOOKUP_STATUSES),
|
|
29322
|
+
resultUrls: external_exports.array(external_exports.string().max(DOC_LOOKUP_URL_MAX_CHARS)).max(DOC_LOOKUP_SEARCH_RESULTS),
|
|
29323
|
+
durationMs: external_exports.number().int().nonnegative(),
|
|
29324
|
+
promptId: external_exports.string().min(1),
|
|
29325
|
+
thread: external_exports.enum(["primary", "scout"]),
|
|
29326
|
+
investigationId: ZeusScoutInvestigationIdSchema.optional()
|
|
29327
|
+
}).strict();
|
|
29328
|
+
|
|
29329
|
+
// ../../shared/zeus/candidate-ledger-conservation.ts
|
|
29330
|
+
var ZEUS_CANDIDATE_LEDGER_STAGES = ["bundle", "ledger", "disposition", "lineage"];
|
|
30015
29331
|
|
|
30016
29332
|
// ../../shared/events/bridge.ts
|
|
30017
29333
|
var ZEUS_TELEMETRY_EVENT_TYPE = "zeus_telemetry";
|
|
29334
|
+
var ZEUS_TELEMETRY_PROJECTED_FIELDS = {
|
|
29335
|
+
"zeus.verification_observed": {
|
|
29336
|
+
observedCount: "number",
|
|
29337
|
+
hasObservedCommand: "boolean",
|
|
29338
|
+
matchedNoTestsCount: "number",
|
|
29339
|
+
runnerMismatchCount: "number",
|
|
29340
|
+
runnerResolved: "boolean",
|
|
29341
|
+
resolvedPackageManager: { kind: "string_enum", values: ["npm", "pnpm", "yarn", "bun"] }
|
|
29342
|
+
},
|
|
29343
|
+
"zeus.publish_rejected": {
|
|
29344
|
+
reason: "string",
|
|
29345
|
+
checkCount: "number",
|
|
29346
|
+
findingCount: "number",
|
|
29347
|
+
verdict: "string",
|
|
29348
|
+
firstAttempt: "boolean",
|
|
29349
|
+
attemptOrdinal: "number",
|
|
29350
|
+
contractVersion: "string",
|
|
29351
|
+
schemaPath: "string",
|
|
29352
|
+
schemaIssue: "string",
|
|
29353
|
+
schemaValue: "string"
|
|
29354
|
+
},
|
|
29355
|
+
// Not a scout counter, but the same transit contract: no metric anywhere derives a redaction
|
|
29356
|
+
// count from the publication, so the event stream is the only place it can be observed at all.
|
|
29357
|
+
// Over-masking is a reader-facing defect, so a per-publish count is the signal that catches it.
|
|
29358
|
+
"zeus.publish_secret_value_redacted": { count: "number" },
|
|
29359
|
+
"zeus.publish_note_pipeline_state": {
|
|
29360
|
+
attemptOrdinal: "number",
|
|
29361
|
+
droppedCount: "number",
|
|
29362
|
+
contractVersion: "string"
|
|
29363
|
+
},
|
|
29364
|
+
"zeus.publish_notes_unverified": {
|
|
29365
|
+
attemptOrdinal: "number",
|
|
29366
|
+
findingCount: "number",
|
|
29367
|
+
contractVersion: "string"
|
|
29368
|
+
},
|
|
29369
|
+
"zeus.publish_empty_review_unattested": {
|
|
29370
|
+
attemptOrdinal: "number",
|
|
29371
|
+
noteCount: "number",
|
|
29372
|
+
contractVersion: "string"
|
|
29373
|
+
},
|
|
29374
|
+
// Record-only base-control audit, one per review with blockers. `detail` is the post-deploy
|
|
29375
|
+
// signal: a high `no_base_run` share means the base-checkout teaching is not landing.
|
|
29376
|
+
"zeus.publish_blocking_downgraded": {
|
|
29377
|
+
detail: { kind: "string_enum", values: ["no_base_run", "no_matched_pair"] },
|
|
29378
|
+
blockingCount: "number",
|
|
29379
|
+
attemptOrdinal: "number"
|
|
29380
|
+
},
|
|
29381
|
+
// One per publish that found stray files. The only channel that shows whether
|
|
29382
|
+
// stale-main branches and sandbox-written paths keep reaching reviewed diffs.
|
|
29383
|
+
"zeus.diff_hygiene": {
|
|
29384
|
+
alreadyOnBaseCount: "number",
|
|
29385
|
+
sandboxGeneratedCount: "number",
|
|
29386
|
+
truncated: "boolean"
|
|
29387
|
+
},
|
|
29388
|
+
"zeus.scout.health": {
|
|
29389
|
+
health: { kind: "string_enum", values: ZEUS_SCOUT_HEALTHS },
|
|
29390
|
+
dispatch_mode: { kind: "string_enum", values: ["targeted", "broad"] },
|
|
29391
|
+
unresolvedInvestigationCount: "number",
|
|
29392
|
+
// Declared as an enum, not a free string: the blackout monitor filters on this value, and an
|
|
29393
|
+
// undeclared or misspelled outcome would silently drop every session out of the alert rather
|
|
29394
|
+
// than into it.
|
|
29395
|
+
publishOutcome: { kind: "string_enum", values: ZEUS_SCOUT_HEALTH_PUBLISH_OUTCOMES },
|
|
29396
|
+
candidateTotal: "number",
|
|
29397
|
+
expectedScoutCount: "number",
|
|
29398
|
+
observedScoutCount: "number",
|
|
29399
|
+
statuses: "string_array"
|
|
29400
|
+
},
|
|
29401
|
+
"zeus.scout.agreement": {
|
|
29402
|
+
candidateCount: "number",
|
|
29403
|
+
publishedCandidateCount: "number",
|
|
29404
|
+
examinedRejectedCount: "number",
|
|
29405
|
+
notExaminedCount: "number",
|
|
29406
|
+
notExaminedEnvironmentCount: "number",
|
|
29407
|
+
notExaminedExternalCount: "number",
|
|
29408
|
+
notExaminedNoneCount: "number",
|
|
29409
|
+
unrunPublishedCount: "number",
|
|
29410
|
+
unrunRemovedCount: "number",
|
|
29411
|
+
notDeliveredCount: "number",
|
|
29412
|
+
notSelectedCount: "number"
|
|
29413
|
+
},
|
|
29414
|
+
// One per publish. `conserved:false` is a bridge defect in `violationStage`; the enum keeps a
|
|
29415
|
+
// misspelled stage out of the alarm instead of silently accepting it.
|
|
29416
|
+
"zeus.candidate_ledger": {
|
|
29417
|
+
conserved: "boolean",
|
|
29418
|
+
candidateCount: "number",
|
|
29419
|
+
droppedCount: "number",
|
|
29420
|
+
publishedCandidateCount: "number",
|
|
29421
|
+
rejectedCount: "number",
|
|
29422
|
+
notDeliveredCount: "number",
|
|
29423
|
+
notSelectedCount: "number",
|
|
29424
|
+
violationStage: { kind: "string_enum", values: ["none", ...ZEUS_CANDIDATE_LEDGER_STAGES] }
|
|
29425
|
+
},
|
|
29426
|
+
"zeus.judge.withheld_defects": { count: "number" },
|
|
29427
|
+
"zeus.judge.intent_claims": {
|
|
29428
|
+
count: "number",
|
|
29429
|
+
verified: "number",
|
|
29430
|
+
contradicted: "number",
|
|
29431
|
+
notChecked: "number",
|
|
29432
|
+
uncitedContradicted: "number",
|
|
29433
|
+
danglingContradicted: "number",
|
|
29434
|
+
unmatched: "number",
|
|
29435
|
+
overflow: "number"
|
|
29436
|
+
},
|
|
29437
|
+
"zeus.scout.completion": {
|
|
29438
|
+
role: "string",
|
|
29439
|
+
status: "string",
|
|
29440
|
+
durationMs: "number",
|
|
29441
|
+
inputTokens: "number",
|
|
29442
|
+
outputTokens: "number",
|
|
29443
|
+
candidateCount: "number"
|
|
29444
|
+
},
|
|
29445
|
+
// One per reader lane per review. The only production evidence of each reader's outcome,
|
|
29446
|
+
// whether its lead delegated to zeus-specialist subagents, and — via `runtime`/`model` —
|
|
29447
|
+
// which lane produced it; the --by-reader ledger split grades on those fields.
|
|
29448
|
+
"zeus.reviewer_lane.completed": {
|
|
29449
|
+
status: "string",
|
|
29450
|
+
error: "string",
|
|
29451
|
+
durationMs: "number",
|
|
29452
|
+
finishedAfterScouts: "boolean",
|
|
29453
|
+
readerFinishMinusScoutMs: "number",
|
|
29454
|
+
scoutJoinWaitMs: "number",
|
|
29455
|
+
candidateCount: "number",
|
|
29456
|
+
droppedCount: "number",
|
|
29457
|
+
subagentDispatched: "boolean",
|
|
29458
|
+
modelRequestCount: "number",
|
|
29459
|
+
readToolCalls: "number",
|
|
29460
|
+
grepToolCalls: "number",
|
|
29461
|
+
globToolCalls: "number",
|
|
29462
|
+
otherToolCalls: "number",
|
|
29463
|
+
readerModel: "string",
|
|
29464
|
+
runtime: "string",
|
|
29465
|
+
model: "string"
|
|
29466
|
+
},
|
|
29467
|
+
"zeus.scout.bundle_omitted": { omittedCandidateCount: "number", droppedExcerptCount: "number" },
|
|
29468
|
+
"zeus.scout.patch_truncated": {
|
|
29469
|
+
role: "string",
|
|
29470
|
+
sampleIndex: "number",
|
|
29471
|
+
investigationId: "string",
|
|
29472
|
+
ownedFileCount: "number",
|
|
29473
|
+
truncatedFileCount: "number",
|
|
29474
|
+
omittedFileCount: "number",
|
|
29475
|
+
withheldChars: "number"
|
|
29476
|
+
},
|
|
29477
|
+
"zeus.scout.patch_unavailable": {
|
|
29478
|
+
role: "string",
|
|
29479
|
+
sampleIndex: "number",
|
|
29480
|
+
reason: "string",
|
|
29481
|
+
ownedFileCount: "number",
|
|
29482
|
+
patchedFileCount: "number"
|
|
29483
|
+
},
|
|
29484
|
+
// One per brokered doc lookup; the only channel tying a lookup's outcome and
|
|
29485
|
+
// broker result count back to its scout investigation.
|
|
29486
|
+
"zeus.doc_lookup": {
|
|
29487
|
+
kind: { kind: "string_enum", values: ["search", "fetch"] },
|
|
29488
|
+
status: { kind: "string_enum", values: DOC_LOOKUP_STATUSES },
|
|
29489
|
+
thread: { kind: "string_enum", values: ["primary", "scout"] },
|
|
29490
|
+
investigationId: "string",
|
|
29491
|
+
durationMs: "number",
|
|
29492
|
+
resultCount: "number"
|
|
29493
|
+
},
|
|
29494
|
+
"zeus.review.prompt_budget_truncated": {
|
|
29495
|
+
excessChars: "number",
|
|
29496
|
+
reviewContextPacketTruncated: "boolean",
|
|
29497
|
+
droppedSectionNames: "string_array",
|
|
29498
|
+
truncatedSectionNames: "string_array",
|
|
29499
|
+
beforeChars: "number",
|
|
29500
|
+
afterChars: "number"
|
|
29501
|
+
},
|
|
29502
|
+
"zeus.review.prompt_budget_externalized": {
|
|
29503
|
+
beforeChars: "number",
|
|
29504
|
+
afterChars: "number",
|
|
29505
|
+
externalizedSectionNames: "string_array"
|
|
29506
|
+
}
|
|
29507
|
+
};
|
|
30018
29508
|
|
|
30019
29509
|
// ../../shared/onboarding/events.ts
|
|
30020
29510
|
var ONBOARDING_STAGES = [
|
|
@@ -31130,6 +30620,9 @@ var ERROR_CODES = [
|
|
|
31130
30620
|
// Review preflight could not read a changed file at the PR head. Deterministic for a
|
|
31131
30621
|
// given head, so the control plane must not spend its retry budget on it.
|
|
31132
30622
|
"review_preflight_input_rejected",
|
|
30623
|
+
// The PR checkout (token, fetch, import, or reset) failed before review dispatch. Retryable:
|
|
30624
|
+
// the durable review request owns the replacement generation.
|
|
30625
|
+
"review_checkout_failed",
|
|
31133
30626
|
"unknown"
|
|
31134
30627
|
];
|
|
31135
30628
|
var ERROR_CODE_SET = new Set(ERROR_CODES);
|
|
@@ -31178,6 +30671,7 @@ var ERROR_CODE_LABELS = {
|
|
|
31178
30671
|
question_delivery_failed: "Answer could not be delivered to the agent",
|
|
31179
30672
|
malformed_search_command: "A search command with invalid syntax was blocked",
|
|
31180
30673
|
review_preflight_input_rejected: "The review could not read a changed file at the PR head",
|
|
30674
|
+
review_checkout_failed: "The PR checkout could not be prepared",
|
|
31181
30675
|
unknown: "Unknown failure"
|
|
31182
30676
|
};
|
|
31183
30677
|
var LEGACY_ERROR_CODE_LABELS = {
|
|
@@ -31842,11 +31336,11 @@ async function getSessionCommand(sessionId, options, command) {
|
|
|
31842
31336
|
if (resultLine) console.log(resultLine);
|
|
31843
31337
|
}
|
|
31844
31338
|
async function sessionEventsCommand(sessionId, options, command) {
|
|
31845
|
-
const afterSequence =
|
|
31846
|
-
const beforeSequence =
|
|
31339
|
+
const afterSequence = options.afterSequence;
|
|
31340
|
+
const beforeSequence = options.beforeSequence;
|
|
31847
31341
|
if (options.follow) {
|
|
31848
31342
|
if (beforeSequence) {
|
|
31849
|
-
throw new CliError("user", "--before-sequence
|
|
31343
|
+
throw new CliError("user", "--before-sequence cannot be used with --follow.");
|
|
31850
31344
|
}
|
|
31851
31345
|
if (options.promptId) {
|
|
31852
31346
|
throw new CliError(
|
|
@@ -31901,519 +31395,6 @@ async function sessionEventsCommand(sessionId, options, command) {
|
|
|
31901
31395
|
}
|
|
31902
31396
|
if (textOpen) process.stdout.write("\n");
|
|
31903
31397
|
}
|
|
31904
|
-
function resolveSequenceOption(canonicalValue, aliasValue, canonicalFlag, aliasFlag) {
|
|
31905
|
-
if (canonicalValue !== void 0 && aliasValue !== void 0) {
|
|
31906
|
-
throw new CliError("user", `${aliasFlag} cannot be combined with ${canonicalFlag}.`);
|
|
31907
|
-
}
|
|
31908
|
-
return canonicalValue ?? aliasValue;
|
|
31909
|
-
}
|
|
31910
|
-
async function usageCommand(sessionId, options, command) {
|
|
31911
|
-
const runtime = getRuntimeOptions(command, options);
|
|
31912
|
-
const config2 = requireConfig(runtime);
|
|
31913
|
-
const payload = await apiFetch(config2, `/api/sessions/${sessionId}/usage`);
|
|
31914
|
-
if (isJson(command, options)) {
|
|
31915
|
-
writeJson(payload);
|
|
31916
|
-
return;
|
|
31917
|
-
}
|
|
31918
|
-
const usage = payload.usage && typeof payload.usage === "object" && !Array.isArray(payload.usage) ? payload.usage : payload;
|
|
31919
|
-
console.log(`Input tokens: ${String(usage.inputTokens ?? 0)}`);
|
|
31920
|
-
console.log(`Output tokens: ${String(usage.outputTokens ?? 0)}`);
|
|
31921
|
-
console.log(`Total tokens: ${String(usage.totalTokens ?? 0)}`);
|
|
31922
|
-
if (usage.totalCostUsd !== void 0) console.log(`Cost: ${String(usage.totalCostUsd)}`);
|
|
31923
|
-
}
|
|
31924
|
-
|
|
31925
|
-
// ../../shared/utils/shell.ts
|
|
31926
|
-
var SHELL_ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
31927
|
-
|
|
31928
|
-
// ../../shared/types/sandbox.ts
|
|
31929
|
-
var APP_RUNTIME_ENTRY_TYPE = {
|
|
31930
|
-
Compose: "compose",
|
|
31931
|
-
/**
|
|
31932
|
-
* Contract-only: the control plane synthesizes a command entry from a profile's `bootScript`.
|
|
31933
|
-
* Profiles no longer store command entries (see the App Runtime schema in repo-runtime.ts).
|
|
31934
|
-
*/
|
|
31935
|
-
Command: "command"
|
|
31936
|
-
};
|
|
31937
|
-
|
|
31938
|
-
// ../../shared/types/repo-runtime.ts
|
|
31939
|
-
var ENVIRONMENT_REPOS_MAX = 8;
|
|
31940
|
-
var RENDERED_PATH_PREFIXES_MAX = 64;
|
|
31941
|
-
var SANDBOX_PATH_PATTERN = /^\/workspace\/(?!\.+$)(?!.*\.\.)[A-Za-z0-9._-]+$/;
|
|
31942
|
-
var EnvironmentRepoSchema = external_exports.object({
|
|
31943
|
-
repoOwner: external_exports.string().trim().min(1, "repoOwner is required"),
|
|
31944
|
-
repoName: external_exports.string().trim().min(1, "repoName is required"),
|
|
31945
|
-
sandboxPath: external_exports.string().regex(SANDBOX_PATH_PATTERN, "sandboxPath must be a single path segment under /workspace").optional()
|
|
31946
|
-
}).strict();
|
|
31947
|
-
function environmentRepoSandboxPath(repo) {
|
|
31948
|
-
return repo.sandboxPath ?? `/workspace/${repo.repoName}`;
|
|
31949
|
-
}
|
|
31950
|
-
var EnvironmentReposSchema = external_exports.array(EnvironmentRepoSchema).min(1, "at least one repo is required").max(ENVIRONMENT_REPOS_MAX, `at most ${ENVIRONMENT_REPOS_MAX} repos are allowed`).superRefine((repos2, ctx) => {
|
|
31951
|
-
if (repos2.length === 0) return;
|
|
31952
|
-
const primaryOwner = repos2[0].repoOwner.toLowerCase();
|
|
31953
|
-
if (repos2[0].sandboxPath !== void 0) {
|
|
31954
|
-
ctx.addIssue({
|
|
31955
|
-
code: "custom",
|
|
31956
|
-
path: [0, "sandboxPath"],
|
|
31957
|
-
message: `the primary repo is always cloned to /workspace/${repos2[0].repoName}`
|
|
31958
|
-
});
|
|
31959
|
-
}
|
|
31960
|
-
const seenRepos = /* @__PURE__ */ new Set();
|
|
31961
|
-
const seenPaths = /* @__PURE__ */ new Set();
|
|
31962
|
-
repos2.forEach((repo, index) => {
|
|
31963
|
-
if (repo.repoOwner.toLowerCase() !== primaryOwner) {
|
|
31964
|
-
ctx.addIssue({
|
|
31965
|
-
code: "custom",
|
|
31966
|
-
path: [index, "repoOwner"],
|
|
31967
|
-
message: "all repos must share the primary repo's owner"
|
|
31968
|
-
});
|
|
31969
|
-
}
|
|
31970
|
-
const repoKey = `${repo.repoOwner}/${repo.repoName}`.toLowerCase();
|
|
31971
|
-
if (seenRepos.has(repoKey)) {
|
|
31972
|
-
ctx.addIssue({ code: "custom", path: [index], message: `duplicate repo ${repo.repoOwner}/${repo.repoName}` });
|
|
31973
|
-
}
|
|
31974
|
-
seenRepos.add(repoKey);
|
|
31975
|
-
const path = environmentRepoSandboxPath(repo);
|
|
31976
|
-
if (!SANDBOX_PATH_PATTERN.test(path)) {
|
|
31977
|
-
ctx.addIssue({
|
|
31978
|
-
code: "custom",
|
|
31979
|
-
path: [index],
|
|
31980
|
-
message: `repo name ${repo.repoName} needs an explicit sandboxPath`
|
|
31981
|
-
});
|
|
31982
|
-
return;
|
|
31983
|
-
}
|
|
31984
|
-
if (seenPaths.has(path)) {
|
|
31985
|
-
ctx.addIssue({ code: "custom", path: [index], message: `sandbox path ${path} is already taken` });
|
|
31986
|
-
}
|
|
31987
|
-
seenPaths.add(path);
|
|
31988
|
-
});
|
|
31989
|
-
});
|
|
31990
|
-
var CredentialDeclarationSchema = external_exports.object({
|
|
31991
|
-
name: external_exports.string(),
|
|
31992
|
-
envVar: external_exports.string(),
|
|
31993
|
-
source: external_exports.enum(["business_openai_key", "business_anthropic_key"]).optional()
|
|
31994
|
-
});
|
|
31995
|
-
var StringRecordSchema = external_exports.record(
|
|
31996
|
-
external_exports.string().regex(SHELL_ENV_KEY_PATTERN, "must be a POSIX-style environment variable name"),
|
|
31997
|
-
external_exports.string()
|
|
31998
|
-
);
|
|
31999
|
-
var ComposeAppRuntimeEntrySchema = external_exports.object({
|
|
32000
|
-
type: external_exports.enum([APP_RUNTIME_ENTRY_TYPE.Compose]),
|
|
32001
|
-
files: external_exports.array(external_exports.string()).optional(),
|
|
32002
|
-
service: external_exports.string().optional(),
|
|
32003
|
-
profiles: external_exports.array(external_exports.string()).optional()
|
|
32004
|
-
}).strict();
|
|
32005
|
-
var AppRuntimeProfileConfigSchema = external_exports.object({
|
|
32006
|
-
cwd: external_exports.string().optional(),
|
|
32007
|
-
bootScript: external_exports.string().max(65536).describe(
|
|
32008
|
-
"Optional bash bringup script. When set it overrides the entry: arcanist-app start runs it on the sandbox host from cwd with runtimeVariables plus HOST and PORT, and waits on ready.path. The app must listen on url.hostPort. Leave unset to boot from the entry."
|
|
32009
|
-
).optional(),
|
|
32010
|
-
kind: external_exports.string().optional(),
|
|
32011
|
-
runner: external_exports.string().optional(),
|
|
32012
|
-
entry: ComposeAppRuntimeEntrySchema.optional(),
|
|
32013
|
-
url: external_exports.object({ hostPort: external_exports.number().int().optional(), path: external_exports.string().optional() }).strict().optional(),
|
|
32014
|
-
portMapping: external_exports.object({ containerPort: external_exports.number().int().optional() }).strict().optional(),
|
|
32015
|
-
additionalPorts: external_exports.array(
|
|
32016
|
-
external_exports.object({
|
|
32017
|
-
service: external_exports.string().optional(),
|
|
32018
|
-
hostPort: external_exports.number().int().optional(),
|
|
32019
|
-
containerPort: external_exports.number().int().optional()
|
|
32020
|
-
}).strict()
|
|
32021
|
-
).optional(),
|
|
32022
|
-
runtimeVariables: StringRecordSchema.optional(),
|
|
32023
|
-
composeEnv: StringRecordSchema.optional(),
|
|
32024
|
-
generatedComposeEnv: external_exports.record(
|
|
32025
|
-
external_exports.string(),
|
|
32026
|
-
external_exports.union([
|
|
32027
|
-
external_exports.object({ type: external_exports.literal("alphanumeric"), length: external_exports.number().int() }).strict(),
|
|
32028
|
-
external_exports.object({ type: external_exports.literal("hex"), bytes: external_exports.number().int() }).strict()
|
|
32029
|
-
])
|
|
32030
|
-
).optional(),
|
|
32031
|
-
env: StringRecordSchema.optional(),
|
|
32032
|
-
ready: external_exports.object({
|
|
32033
|
-
path: external_exports.string().optional(),
|
|
32034
|
-
nonEmptyPath: external_exports.string().optional(),
|
|
32035
|
-
timeoutSeconds: external_exports.number().int().optional()
|
|
32036
|
-
}).strict().optional(),
|
|
32037
|
-
open: external_exports.object({ path: external_exports.string().optional() }).strict().optional(),
|
|
32038
|
-
renderedPathPrefixes: external_exports.array(external_exports.string().max(512)).max(RENDERED_PATH_PREFIXES_MAX).describe(
|
|
32039
|
-
"Repo-relative directories the booted app is built from. When set, a review boots the app only when the diff touches a non-test file under one of them; when unset, the file-extension heuristic decides."
|
|
32040
|
-
).optional(),
|
|
32041
|
-
auth: external_exports.object({
|
|
32042
|
-
command: external_exports.string().optional(),
|
|
32043
|
-
validatePath: external_exports.string().optional(),
|
|
32044
|
-
browserStateEnvVar: external_exports.string().optional(),
|
|
32045
|
-
credentials: external_exports.array(CredentialDeclarationSchema).optional()
|
|
32046
|
-
}).strict().optional(),
|
|
32047
|
-
e2e: external_exports.object({
|
|
32048
|
-
testCommand: external_exports.string(),
|
|
32049
|
-
seedCommand: external_exports.string().optional(),
|
|
32050
|
-
resetCommand: external_exports.string().optional(),
|
|
32051
|
-
credentials: external_exports.array(CredentialDeclarationSchema).optional()
|
|
32052
|
-
}).strict().optional()
|
|
32053
|
-
}).strict();
|
|
32054
|
-
var LEGACY_DOCKERFILE_ENTRY_TYPE = "dockerfile";
|
|
32055
|
-
var LegacyAppRuntimeEntrySchema = external_exports.union([
|
|
32056
|
-
external_exports.object({ type: external_exports.literal(APP_RUNTIME_ENTRY_TYPE.Command), command: external_exports.string().optional() }).passthrough(),
|
|
32057
|
-
external_exports.object({
|
|
32058
|
-
type: external_exports.literal(LEGACY_DOCKERFILE_ENTRY_TYPE),
|
|
32059
|
-
context: external_exports.string().optional(),
|
|
32060
|
-
dockerfile: external_exports.string().optional(),
|
|
32061
|
-
service: external_exports.string().optional()
|
|
32062
|
-
}).passthrough()
|
|
32063
|
-
]);
|
|
32064
|
-
var AppRuntimeProfileConfigReadSchema = AppRuntimeProfileConfigSchema.extend({
|
|
32065
|
-
entry: external_exports.union([ComposeAppRuntimeEntrySchema, LegacyAppRuntimeEntrySchema]).optional()
|
|
32066
|
-
});
|
|
32067
|
-
var EnvironmentProvisioningConfigSchema = external_exports.object({
|
|
32068
|
-
sandbox: external_exports.object({ appRuntime: AppRuntimeProfileConfigSchema.optional() }).strict().optional()
|
|
32069
|
-
}).strict();
|
|
32070
|
-
var EnvironmentProvisioningConfigReadSchema = external_exports.object({
|
|
32071
|
-
sandbox: external_exports.object({ appRuntime: AppRuntimeProfileConfigReadSchema.optional() }).strict().optional()
|
|
32072
|
-
}).strict();
|
|
32073
|
-
|
|
32074
|
-
// src/commands/setup.ts
|
|
32075
|
-
var INSTALL_POLL_INTERVAL_MS = 5e3;
|
|
32076
|
-
var INSTALL_WAIT_TIMEOUT_MS = 20 * 60 * 1e3;
|
|
32077
|
-
var ONBOARDING_REPOS_PATH = "/api/onboarding/repos";
|
|
32078
|
-
async function setupCommand(repoArgs = [], options = {}, command) {
|
|
32079
|
-
if (repoArgs.length > MAX_ENVIRONMENT_SETUP_REPOSITORIES) {
|
|
32080
|
-
throw new CliError("user", `At most ${MAX_ENVIRONMENT_SETUP_REPOSITORIES} repositories can be prepared at once.`);
|
|
32081
|
-
}
|
|
32082
|
-
if (options.workspace && repoArgs.length === 1) {
|
|
32083
|
-
throw new CliError("user", "A multi-repository workspace requires at least two repositories.");
|
|
32084
|
-
}
|
|
32085
|
-
if (options.workspace && repoArgs.length > ENVIRONMENT_REPOS_MAX) {
|
|
32086
|
-
throw new CliError("user", `A workspace supports at most ${ENVIRONMENT_REPOS_MAX} repositories.`);
|
|
32087
|
-
}
|
|
32088
|
-
const runtime = getRuntimeOptions(command, options);
|
|
32089
|
-
let config2 = loadConfig(runtime);
|
|
32090
|
-
if (!config2) {
|
|
32091
|
-
const envOrFlag = resolveEnvOrFlagConfig(runtime, false);
|
|
32092
|
-
if ((process.stdin.isTTY !== true || process.stdout.isTTY !== true) && !envOrFlag?.token) {
|
|
32093
|
-
throw new CliError("auth", "Not logged in.", { hint: "Run `arcanist auth login` or set `ARCANIST_TOKEN`." });
|
|
32094
|
-
}
|
|
32095
|
-
config2 = await loginCommand({ ...runtime, emitResult: false }, command);
|
|
32096
|
-
}
|
|
32097
|
-
const requested = repoArgs.map((value) => {
|
|
32098
|
-
const repo = parseRepoArg(value);
|
|
32099
|
-
return `${repo.owner}/${repo.repo}`;
|
|
32100
|
-
});
|
|
32101
|
-
const inventory = await ensureGitHubRepositories({ config: config2, requested, runtime });
|
|
32102
|
-
if (!inventory) return;
|
|
32103
|
-
const eligible = eligibleRepositoryNames(inventory.repos);
|
|
32104
|
-
const selected = selectRepositories({ eligible, requested, runtime });
|
|
32105
|
-
if (!selected) return;
|
|
32106
|
-
const repositories = await prepareEnvironments(config2, {
|
|
32107
|
-
repositories: selected.map((value) => parseRepoArg(value)),
|
|
32108
|
-
workspace: options.workspace
|
|
32109
|
-
});
|
|
32110
|
-
const rejected = repositories.filter((result) => result.status === "forbidden" || result.status === "failed");
|
|
32111
|
-
const accepted = repositories.filter((result) => result.status !== "forbidden" && result.status !== "failed");
|
|
32112
|
-
if (accepted.length === 0) {
|
|
32113
|
-
throw new CliError(
|
|
32114
|
-
rejected.some((result) => result.status === "forbidden") ? "auth" : "server",
|
|
32115
|
-
`Environment setup was rejected for: ${rejected.map((result) => result.fullName).join(", ")}.`
|
|
32116
|
-
);
|
|
32117
|
-
}
|
|
32118
|
-
const queued = accepted.filter((result) => result.status !== "already_ready");
|
|
32119
|
-
const tracking = trackingCommands(queued.map((result) => result.fullName));
|
|
32120
|
-
const payload = {
|
|
32121
|
-
status: rejected.length > 0 ? "partial" : queued.length > 0 ? "queued" : "ready",
|
|
32122
|
-
repositories,
|
|
32123
|
-
workspace: options.workspace === true,
|
|
32124
|
-
tracking
|
|
32125
|
-
};
|
|
32126
|
-
emit(void 0, runtime, payload, () => {
|
|
32127
|
-
console.log(
|
|
32128
|
-
queued.length > 0 ? `Environment preparation started for ${queued.length} ${queued.length === 1 ? "repository" : "repositories"}.` : "The selected environments are ready."
|
|
32129
|
-
);
|
|
32130
|
-
if (rejected.length > 0) {
|
|
32131
|
-
console.log(`Could not start: ${rejected.map((result) => sanitizeTerminalText(result.fullName)).join(", ")}.`);
|
|
32132
|
-
}
|
|
32133
|
-
for (const line of tracking) console.log(line);
|
|
32134
|
-
});
|
|
32135
|
-
}
|
|
32136
|
-
function eligibleRepositoryNames(repos2) {
|
|
32137
|
-
return repos2.filter((repo) => {
|
|
32138
|
-
return typeof repo.fullName === "string" && repo.canReview === true;
|
|
32139
|
-
}).map((repo) => repo.fullName).sort((a, b) => a.localeCompare(b));
|
|
32140
|
-
}
|
|
32141
|
-
function selectRepositories(options) {
|
|
32142
|
-
const { eligible, requested, runtime } = options;
|
|
32143
|
-
if (requested.length === 0) {
|
|
32144
|
-
emit(
|
|
32145
|
-
void 0,
|
|
32146
|
-
runtime,
|
|
32147
|
-
{
|
|
32148
|
-
status: "selection_required",
|
|
32149
|
-
repositories: eligible,
|
|
32150
|
-
next: "Rerun with one or more owner/repo arguments."
|
|
32151
|
-
},
|
|
32152
|
-
() => {
|
|
32153
|
-
console.log("Repositories available to prepare:");
|
|
32154
|
-
for (const repository of eligible) console.log(` ${sanitizeTerminalText(repository)}`);
|
|
32155
|
-
console.log("Rerun with one or more owner/repo arguments.");
|
|
32156
|
-
}
|
|
32157
|
-
);
|
|
32158
|
-
return null;
|
|
32159
|
-
}
|
|
32160
|
-
const eligibleNames = new Map(eligible.map((repository) => [repository.toLowerCase(), repository]));
|
|
32161
|
-
const selected = [
|
|
32162
|
-
...new Map(
|
|
32163
|
-
requested.map((repository) => [
|
|
32164
|
-
repository.toLowerCase(),
|
|
32165
|
-
eligibleNames.get(repository.toLowerCase()) ?? repository
|
|
32166
|
-
])
|
|
32167
|
-
).values()
|
|
32168
|
-
];
|
|
32169
|
-
const unavailable = selected.filter((repository) => !eligibleNames.has(repository.toLowerCase()));
|
|
32170
|
-
if (unavailable.length > 0) {
|
|
32171
|
-
throw new CliError("auth", `Repositories are unavailable or lack write access: ${unavailable.join(", ")}.`);
|
|
32172
|
-
}
|
|
32173
|
-
return selected;
|
|
32174
|
-
}
|
|
32175
|
-
async function ensureGitHubRepositories(options) {
|
|
32176
|
-
const { config: config2, requested, runtime } = options;
|
|
32177
|
-
let inventory = await apiFetch(config2, `${ONBOARDING_REPOS_PATH}?refresh=1`);
|
|
32178
|
-
const readOnly = new Set(
|
|
32179
|
-
inventory.repos.filter((repo) => repo.canReview !== true && typeof repo.fullName === "string").map((repo) => String(repo.fullName).toLowerCase())
|
|
32180
|
-
);
|
|
32181
|
-
const inaccessible = requested.filter((repository) => readOnly.has(repository.toLowerCase()));
|
|
32182
|
-
if (inaccessible.length > 0) {
|
|
32183
|
-
throw new CliError("auth", `Repositories lack write access: ${inaccessible.join(", ")}.`);
|
|
32184
|
-
}
|
|
32185
|
-
if (inventorySatisfiesRequest(inventory, requested)) return inventory;
|
|
32186
|
-
if (inventory.ssoOrgs.length > 0) return emitSsoRequirement({ apiUrl: config2.apiUrl, inventory, runtime });
|
|
32187
|
-
const install = await apiFetch(config2, "/api/github/install-url");
|
|
32188
|
-
emitSetupAction(runtime, { kind: "install_github", url: install.url });
|
|
32189
|
-
if (!runtime.json && !runtime.quiet) console.error("Waiting for GitHub repository access...");
|
|
32190
|
-
const deadline = Date.now() + INSTALL_WAIT_TIMEOUT_MS;
|
|
32191
|
-
while (Date.now() < deadline) {
|
|
32192
|
-
await sleep(INSTALL_POLL_INTERVAL_MS);
|
|
32193
|
-
inventory = await apiFetch(config2, ONBOARDING_REPOS_PATH);
|
|
32194
|
-
if (inventorySatisfiesRequest(inventory, requested)) return inventory;
|
|
32195
|
-
if (inventory.ssoOrgs.length > 0) return emitSsoRequirement({ apiUrl: config2.apiUrl, inventory, runtime });
|
|
32196
|
-
}
|
|
32197
|
-
throw new CliError("server", "Timed out waiting for GitHub repository access.", {
|
|
32198
|
-
hint: `Finish repository selection at ${install.url}, then rerun \`arcanist setup\`.`
|
|
32199
|
-
});
|
|
32200
|
-
}
|
|
32201
|
-
function inventorySatisfiesRequest(inventory, requested) {
|
|
32202
|
-
const eligible = new Set(eligibleRepositoryNames(inventory.repos).map((repository) => repository.toLowerCase()));
|
|
32203
|
-
if (requested.length > 0) return requested.every((repository) => eligible.has(repository.toLowerCase()));
|
|
32204
|
-
return inventory.complete === true && eligible.size > 0;
|
|
32205
|
-
}
|
|
32206
|
-
function emitSsoRequirement(options) {
|
|
32207
|
-
const { apiUrl, inventory, runtime } = options;
|
|
32208
|
-
for (const org of inventory.ssoOrgs) {
|
|
32209
|
-
if (typeof org.authorizeUrl !== "string") continue;
|
|
32210
|
-
const url2 = org.authorizeUrl.startsWith("/") ? `${apiUrl}${org.authorizeUrl}` : org.authorizeUrl;
|
|
32211
|
-
emitSetupAction(runtime, {
|
|
32212
|
-
kind: "authorize_github_sso",
|
|
32213
|
-
url: url2,
|
|
32214
|
-
organization: org.login ?? org.orgId
|
|
32215
|
-
});
|
|
32216
|
-
}
|
|
32217
|
-
emit(
|
|
32218
|
-
void 0,
|
|
32219
|
-
runtime,
|
|
32220
|
-
{
|
|
32221
|
-
status: "action_required",
|
|
32222
|
-
action: "authorize_github_sso",
|
|
32223
|
-
repositories: eligibleRepositoryNames(inventory.repos),
|
|
32224
|
-
ssoOrgs: inventory.ssoOrgs,
|
|
32225
|
-
next: "Authorize GitHub SSO, then rerun arcanist setup."
|
|
32226
|
-
},
|
|
32227
|
-
() => console.log("Authorize GitHub SSO, then rerun arcanist setup.")
|
|
32228
|
-
);
|
|
32229
|
-
return null;
|
|
32230
|
-
}
|
|
32231
|
-
function emitSetupAction(runtime, action) {
|
|
32232
|
-
if (runtime.json) process.stderr.write(`${JSON.stringify({ type: "action_required", action })}
|
|
32233
|
-
`);
|
|
32234
|
-
else process.stderr.write(`Action required: ${action.url}
|
|
32235
|
-
`);
|
|
32236
|
-
}
|
|
32237
|
-
function trackingCommands(repositories) {
|
|
32238
|
-
return repositories.map((repository) => `Track later: ${commandForNextAction("wait", repository)}`);
|
|
32239
|
-
}
|
|
32240
|
-
|
|
32241
|
-
// src/commands/stop.ts
|
|
32242
|
-
async function stopCommand(sessionId, options = {}, command) {
|
|
32243
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
32244
|
-
let status;
|
|
32245
|
-
try {
|
|
32246
|
-
const response = await apiFetch(config2, `/api/sessions/${sessionId}/stop`, {
|
|
32247
|
-
method: "POST"
|
|
32248
|
-
});
|
|
32249
|
-
status = response.status ?? "stopping";
|
|
32250
|
-
} catch (err) {
|
|
32251
|
-
const parsed = parseStopBlocked(err);
|
|
32252
|
-
if (!parsed) throw err;
|
|
32253
|
-
status = parsed.reason;
|
|
32254
|
-
}
|
|
32255
|
-
emit(command, options, { sessionId, status }, (payload) => {
|
|
32256
|
-
if (payload.status === "already_stopped") {
|
|
32257
|
-
console.log(`Session ${payload.sessionId} is already stopped.`);
|
|
32258
|
-
} else if (payload.status === "stopping" || payload.status === "stopped") {
|
|
32259
|
-
console.log(`Stop requested for session ${payload.sessionId}.`);
|
|
32260
|
-
} else {
|
|
32261
|
-
console.log(`Session ${payload.sessionId} cannot be stopped from phase=${payload.status}.`);
|
|
32262
|
-
}
|
|
32263
|
-
});
|
|
32264
|
-
}
|
|
32265
|
-
function parseStopBlocked(err) {
|
|
32266
|
-
const body = parseConflictBody(err);
|
|
32267
|
-
if (!body || body.error !== "session_not_stoppable") return null;
|
|
32268
|
-
return { reason: typeof body.reason === "string" ? body.reason : "not_stoppable" };
|
|
32269
|
-
}
|
|
32270
|
-
|
|
32271
|
-
// src/commands/test-creds.ts
|
|
32272
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
32273
|
-
function basePath(businessId, repo) {
|
|
32274
|
-
return `/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/test-credentials`;
|
|
32275
|
-
}
|
|
32276
|
-
function readValue(options) {
|
|
32277
|
-
const sources = [options.value, options.valueFile, options.valueStdin].filter((v) => v !== void 0 && v !== false);
|
|
32278
|
-
if (sources.length !== 1) {
|
|
32279
|
-
throw new CliError("user", "Provide exactly one of --value, --value-file, or --value-stdin.");
|
|
32280
|
-
}
|
|
32281
|
-
if (options.value !== void 0) return options.value;
|
|
32282
|
-
const raw = options.valueFile ? readFileSync4(options.valueFile, "utf8") : readFileSync4(0, "utf8");
|
|
32283
|
-
return raw.replace(/\r?\n$/, "");
|
|
32284
|
-
}
|
|
32285
|
-
async function listTestCredentialsCommand(repo, options, command) {
|
|
32286
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
32287
|
-
const repoArg = parseRepoArg(repo);
|
|
32288
|
-
const payload = await apiFetch(config2, basePath(options.business, repoArg));
|
|
32289
|
-
emit(command, options, payload, (listPayload) => {
|
|
32290
|
-
if (listPayload.credentials.length === 0) {
|
|
32291
|
-
console.log(`No test credentials configured for ${repoArg.owner}/${repoArg.repo}.`);
|
|
32292
|
-
return;
|
|
32293
|
-
}
|
|
32294
|
-
for (const cred of listPayload.credentials) {
|
|
32295
|
-
const ts = new Date(cred.updatedAt).toISOString();
|
|
32296
|
-
console.log(`${cred.name} ${ts} ${cred.rotatedByUserId ?? "-"}`);
|
|
32297
|
-
}
|
|
32298
|
-
});
|
|
32299
|
-
}
|
|
32300
|
-
async function setTestCredentialCommand(repo, name, options, command) {
|
|
32301
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
32302
|
-
const repoArg = parseRepoArg(repo);
|
|
32303
|
-
const value = readValue(options);
|
|
32304
|
-
const payload = await apiFetch(
|
|
32305
|
-
config2,
|
|
32306
|
-
`${basePath(options.business, repoArg)}/${encodeURIComponent(name)}`,
|
|
32307
|
-
{ method: "PUT", body: JSON.stringify({ value }) }
|
|
32308
|
-
);
|
|
32309
|
-
emit(
|
|
32310
|
-
command,
|
|
32311
|
-
options,
|
|
32312
|
-
payload,
|
|
32313
|
-
() => console.log(`Set test credential '${name}' for ${repoArg.owner}/${repoArg.repo}.`)
|
|
32314
|
-
);
|
|
32315
|
-
}
|
|
32316
|
-
async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
32317
|
-
if (isJson(command, options) && options.yes !== true) {
|
|
32318
|
-
throw new CliError("user", "`test-creds delete --json` requires --yes.");
|
|
32319
|
-
}
|
|
32320
|
-
if (options.yes !== true) {
|
|
32321
|
-
const repoArg2 = parseRepoArg(repo);
|
|
32322
|
-
await confirmOrThrow(`Delete test credential '${name}' for ${repoArg2.owner}/${repoArg2.repo}?`);
|
|
32323
|
-
}
|
|
32324
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
32325
|
-
const repoArg = parseRepoArg(repo);
|
|
32326
|
-
const payload = await apiFetch(
|
|
32327
|
-
config2,
|
|
32328
|
-
`${basePath(options.business, repoArg)}/${encodeURIComponent(name)}`,
|
|
32329
|
-
{ method: "DELETE" }
|
|
32330
|
-
);
|
|
32331
|
-
emit(
|
|
32332
|
-
command,
|
|
32333
|
-
options,
|
|
32334
|
-
payload,
|
|
32335
|
-
() => console.log(`Deleted test credential '${name}' for ${repoArg.owner}/${repoArg.repo}.`)
|
|
32336
|
-
);
|
|
32337
|
-
}
|
|
32338
|
-
|
|
32339
|
-
// src/commands/tokens.ts
|
|
32340
|
-
async function listTokensCommand(options, command) {
|
|
32341
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
32342
|
-
const { items: data, nextCursor } = await fetchPages(
|
|
32343
|
-
"tokens list --all",
|
|
32344
|
-
options.all === true,
|
|
32345
|
-
options.cursor,
|
|
32346
|
-
async (cursor) => {
|
|
32347
|
-
const query = new URLSearchParams();
|
|
32348
|
-
if (options.limit) query.set("limit", options.limit);
|
|
32349
|
-
if (cursor) query.set("cursor", cursor);
|
|
32350
|
-
const payload2 = await apiFetch(
|
|
32351
|
-
config2,
|
|
32352
|
-
`/api/cli-tokens${query.size ? `?${query.toString()}` : ""}`
|
|
32353
|
-
);
|
|
32354
|
-
return { items: payload2.data, nextCursor: payload2.nextCursor };
|
|
32355
|
-
}
|
|
32356
|
-
);
|
|
32357
|
-
const payload = { data, nextCursor };
|
|
32358
|
-
emit(command, options, payload, (listPayload) => {
|
|
32359
|
-
if (listPayload.data.length === 0) {
|
|
32360
|
-
console.log("No CLI tokens found.");
|
|
32361
|
-
return;
|
|
32362
|
-
}
|
|
32363
|
-
for (const token of listPayload.data) {
|
|
32364
|
-
console.log(
|
|
32365
|
-
`${String(token.id)} ${String(token.scope)} ${String(token.tokenPrefix)} ${String(token.revokedAt ? "revoked" : "active")}`
|
|
32366
|
-
);
|
|
32367
|
-
}
|
|
32368
|
-
if (listPayload.nextCursor) console.log(`Next cursor: ${listPayload.nextCursor}`);
|
|
32369
|
-
});
|
|
32370
|
-
}
|
|
32371
|
-
async function createTokenCommand(options, command) {
|
|
32372
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
32373
|
-
const expiresInDays = options.expiresInDays === void 0 ? void 0 : Number(options.expiresInDays);
|
|
32374
|
-
if (expiresInDays !== void 0 && (!Number.isInteger(expiresInDays) || expiresInDays <= 0)) {
|
|
32375
|
-
throw new CliError("user", "expiresInDays must be a positive integer.");
|
|
32376
|
-
}
|
|
32377
|
-
let payload;
|
|
32378
|
-
try {
|
|
32379
|
-
payload = await apiFetch(config2, "/api/cli-tokens", {
|
|
32380
|
-
method: "POST",
|
|
32381
|
-
headers: { "Idempotency-Key": options.idempotencyKey ?? randomIdempotencyKey() },
|
|
32382
|
-
body: JSON.stringify({
|
|
32383
|
-
scope: options.scope ?? "read",
|
|
32384
|
-
...expiresInDays !== void 0 ? { expiresInDays } : {}
|
|
32385
|
-
})
|
|
32386
|
-
});
|
|
32387
|
-
} catch (err) {
|
|
32388
|
-
if (err instanceof ApiError && err.status === 409 && err.data?.serverCode === "duplicate_request") {
|
|
32389
|
-
throw new CliError(err.code, err.message, {
|
|
32390
|
-
exitCode: err.exitCode,
|
|
32391
|
-
requestId: err.requestId,
|
|
32392
|
-
data: err.data,
|
|
32393
|
-
hint: "Use a new --idempotency-key for a new token, or retry the original command only if the first result was lost."
|
|
32394
|
-
});
|
|
32395
|
-
}
|
|
32396
|
-
throw err;
|
|
32397
|
-
}
|
|
32398
|
-
emit(command, options, payload, (tokenPayload) => {
|
|
32399
|
-
console.log(`Token: ${String(tokenPayload.token)}`);
|
|
32400
|
-
console.log(`ID: ${String(tokenPayload.id)}`);
|
|
32401
|
-
console.log(`Scope: ${String(tokenPayload.scope)}`);
|
|
32402
|
-
});
|
|
32403
|
-
}
|
|
32404
|
-
async function revokeTokenCommand(tokenId, options, command) {
|
|
32405
|
-
if (isJson(command, options) && options.yes !== true) {
|
|
32406
|
-
throw new CliError("user", "`tokens revoke --json` requires --yes.");
|
|
32407
|
-
}
|
|
32408
|
-
if (options.yes !== true) {
|
|
32409
|
-
await confirmOrThrow(`Revoke CLI token ${tokenId}?`);
|
|
32410
|
-
}
|
|
32411
|
-
const { config: config2 } = resolveBusinessContext(command, options);
|
|
32412
|
-
const payload = await apiFetch(config2, `/api/cli-tokens/${tokenId}/revoke`, {
|
|
32413
|
-
method: "POST"
|
|
32414
|
-
});
|
|
32415
|
-
emit(command, options, payload, () => console.log(`Revoked token ${tokenId}.`));
|
|
32416
|
-
}
|
|
32417
31398
|
|
|
32418
31399
|
// src/commands/transcript.ts
|
|
32419
31400
|
async function transcriptCommand(sessionId, options, command) {
|
|
@@ -32488,11 +31469,10 @@ program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
|
32488
31469
|
applyColorEnvironment(getRuntimeOptions(actionCommand));
|
|
32489
31470
|
});
|
|
32490
31471
|
var auth = program.command("auth").description("Authentication commands");
|
|
32491
|
-
auth.command("login").description("
|
|
31472
|
+
auth.command("login").description("Store a CLI token minted in Settings > CLI").option("--token-stdin", "Read token from stdin").addHelpText(
|
|
32492
31473
|
"after",
|
|
32493
31474
|
`
|
|
32494
31475
|
Examples:
|
|
32495
|
-
arcanist auth login
|
|
32496
31476
|
printf "arc_..." | arcanist auth login --token-stdin
|
|
32497
31477
|
ARCANIST_TOKEN=arc_... arcanist auth whoami --json
|
|
32498
31478
|
`
|
|
@@ -32507,21 +31487,6 @@ Examples:
|
|
|
32507
31487
|
ARCANIST_TOKEN=arc_... arcanist auth whoami --json
|
|
32508
31488
|
`
|
|
32509
31489
|
).action((options, command) => whoamiCommand(options, command));
|
|
32510
|
-
program.command("setup").description("Connect GitHub repositories and prepare their Zeus environments").argument("[repos...]", "Repositories in owner/name form").option("--workspace", "Prepare the repositories as one multi-repository workspace").addHelpText(
|
|
32511
|
-
"after",
|
|
32512
|
-
`
|
|
32513
|
-
Examples:
|
|
32514
|
-
arcanist setup
|
|
32515
|
-
arcanist setup acme/web
|
|
32516
|
-
arcanist setup acme/web acme/api --json
|
|
32517
|
-
arcanist setup acme/web acme/api --workspace
|
|
32518
|
-
|
|
32519
|
-
With no repositories, the command returns the eligible repositories. Rerun with the
|
|
32520
|
-
repositories to prepare. By default each repository gets its own environment; use
|
|
32521
|
-
--workspace to prepare them together. Builds continue after the command exits; use
|
|
32522
|
-
the emitted environment status commands to track them.
|
|
32523
|
-
`
|
|
32524
|
-
).action((repos2, options, command) => setupCommand(repos2, options, command));
|
|
32525
31490
|
var codex = program.command("codex").description("Codex subscription (bring-your-own-subscription) commands");
|
|
32526
31491
|
codex.command("login").description("Authenticate a Codex/ChatGPT subscription and store it for your Codex sessions").option("--codex-path <path>", "Path to the codex executable (default: codex on PATH, or ARCANIST_CODEX_BIN)").addHelpText(
|
|
32527
31492
|
"after",
|
|
@@ -32537,32 +31502,19 @@ Examples:
|
|
|
32537
31502
|
).action((options, command) => codexLoginCommand(options, command));
|
|
32538
31503
|
codex.command("use <state>").description("Turn using your saved Codex subscription auth for OpenAI sessions on or off (state: on|off)").action((state, options, command) => codexUseCommand(state, options, command));
|
|
32539
31504
|
codex.command("status").description("Show Codex subscription availability and whether auth is saved").action((options, command) => codexStatusCommand(options, command));
|
|
32540
|
-
codex.command("logout").description("Deactivate the selector and remove the stored Codex subscription auth for your user").action((options, command) => codexLogoutCommand(options, command));
|
|
32541
31505
|
var sessions = program.command("sessions").description("Session commands");
|
|
32542
|
-
program.command("review <pr-url>").description("Run
|
|
31506
|
+
program.command("review <pr-url>").description("Run an Arcanist review for a GitHub pull request").option("--sandbox-runtime <runtime>", "Force the sandbox runtime (Arcanist members only): modal or e2b").option("--wait", "Wait until the review is published, cancelled, or admission-rejected").option("--poll-interval <ms>", "Polling interval in milliseconds while waiting (default 10000)").addHelpText(
|
|
32543
31507
|
"after",
|
|
32544
31508
|
`
|
|
32545
31509
|
Examples:
|
|
32546
31510
|
arcanist review https://github.com/org/repo/pull/123 --wait --json
|
|
32547
|
-
arcanist review https://github.com/org/repo/pull/123 --sandbox-runtime
|
|
31511
|
+
arcanist review https://github.com/org/repo/pull/123 --sandbox-runtime modal --wait
|
|
32548
31512
|
|
|
32549
31513
|
--wait returns when the review is published, cancelled, or admission-rejected. The 30-minute timeout applies only while status is in_flight.
|
|
32550
|
-
|
|
32551
|
-
|
|
31514
|
+
Arcanist always uses the pinned Codex reviewer.
|
|
31515
|
+
Arcanist review is currently available to Arcanist members only.
|
|
32552
31516
|
`
|
|
32553
31517
|
).action((prUrl, options, command) => reviewCommand(prUrl, options, command));
|
|
32554
|
-
sessions.command("stop").description("Stop the active run for a session").argument("<session-id>", "Session ID").addHelpText(
|
|
32555
|
-
"after",
|
|
32556
|
-
`
|
|
32557
|
-
Examples:
|
|
32558
|
-
arcanist sessions stop <session-id>
|
|
32559
|
-
arcanist sessions stop <session-id> --json
|
|
32560
|
-
|
|
32561
|
-
JSON:
|
|
32562
|
-
JSON mode returns {sessionId, status}
|
|
32563
|
-
status is the server stop status or 409 stop-block reason. Known values include stopping, stopped, already_stopped, not_stoppable, and lifecycle phase names.
|
|
32564
|
-
`
|
|
32565
|
-
).action((sessionId, options, command) => stopCommand(sessionId, options, command));
|
|
32566
31518
|
sessions.command("get").description("Get session details").argument("<session-id>", "Session ID").addHelpText(
|
|
32567
31519
|
"after",
|
|
32568
31520
|
`
|
|
@@ -32587,13 +31539,12 @@ JSON:
|
|
|
32587
31539
|
JSON mode returns {sessions, nextCursor}. --all follows cursors until nextCursor is null.
|
|
32588
31540
|
`
|
|
32589
31541
|
).action((options, command) => listSessionsCommand(options, command));
|
|
32590
|
-
sessions.command("events").description("Read or follow session replay events").argument("<session-id>", "Session ID").option("--after-sequence <n>", "Return events after this sequence").option("--
|
|
31542
|
+
sessions.command("events").description("Read or follow session replay events").argument("<session-id>", "Session ID").option("--after-sequence <n>", "Return events after this sequence").option("--before-sequence <n>", "Return events before this sequence").option("--prompt-id <id>", "Filter events by prompt ID").option("--limit <n>", "Maximum events to return").option("--follow", "Follow events until a terminal phase").option("--poll-interval <ms>", "Polling interval in milliseconds", String(DEFAULT_WATCH_POLL_INTERVAL_MS)).option("--timeout <ms>", "Stop following after this many milliseconds; 0 or omitted means unbounded").addHelpText(
|
|
32591
31543
|
"after",
|
|
32592
31544
|
`
|
|
32593
31545
|
Examples:
|
|
32594
31546
|
arcanist sessions events <session-id> --json
|
|
32595
31547
|
arcanist sessions events <session-id> --after-sequence 540 --limit 250 --json
|
|
32596
|
-
arcanist sessions events <session-id> --after 540 --limit 250 --json
|
|
32597
31548
|
arcanist sessions events <session-id> --follow --json
|
|
32598
31549
|
arcanist sessions events <session-id> --follow --timeout 60000 --json
|
|
32599
31550
|
|
|
@@ -32613,14 +31564,6 @@ Examples:
|
|
|
32613
31564
|
arcanist sessions transcript <session-id> --last 20
|
|
32614
31565
|
`
|
|
32615
31566
|
).action((sessionId, options, command) => transcriptCommand(sessionId, options, command));
|
|
32616
|
-
sessions.command("usage").description("Get token usage for a session").argument("<session-id>", "Session ID").addHelpText(
|
|
32617
|
-
"after",
|
|
32618
|
-
`
|
|
32619
|
-
Examples:
|
|
32620
|
-
arcanist sessions usage <session-id>
|
|
32621
|
-
arcanist sessions usage <session-id> --json
|
|
32622
|
-
`
|
|
32623
|
-
).action((sessionId, options, command) => usageCommand(sessionId, options, command));
|
|
32624
31567
|
var artifacts = sessions.command("artifacts").description("List or download session artifacts (screenshots, videos)").argument("<session-id>", "Session ID").option("-o, --output <dir>", "Directory to write downloads into; defaults to the current directory").option("--all", "Download every artifact for the session instead of listing them").addHelpText(
|
|
32625
31568
|
"after",
|
|
32626
31569
|
`
|
|
@@ -32652,15 +31595,8 @@ JSON:
|
|
|
32652
31595
|
JSON mode returns {repos, ssoOrgs}
|
|
32653
31596
|
`
|
|
32654
31597
|
).action((options, command) => reposListCommand(options, command));
|
|
32655
|
-
|
|
32656
|
-
|
|
32657
|
-
`
|
|
32658
|
-
JSON:
|
|
32659
|
-
JSON mode returns {branches}
|
|
32660
|
-
`
|
|
32661
|
-
).action((repo, options, command) => repoBranchesCommand(repo, options, command));
|
|
32662
|
-
var environments = program.command("environments").alias("env").description("Zeus environment setup commands");
|
|
32663
|
-
environments.command("prepare").description("Prepare a repository for Zeus reviews").argument("[repo]", "Repository owner/name; defaults to current git remote").option("--wait", "Wait until setup is ready or needs attention").option("--poll-interval <ms>", "Polling interval while waiting (default 5000)").addHelpText(
|
|
31598
|
+
var environments = program.command("environments").alias("env").description("Arcanist environment setup commands");
|
|
31599
|
+
environments.command("prepare").description("Prepare a repository for Arcanist reviews").argument("[repo]", "Repository owner/name; defaults to current git remote").option("--wait", "Wait until setup is ready or needs attention").option("--poll-interval <ms>", "Polling interval while waiting (default 5000)").addHelpText(
|
|
32664
31600
|
"after",
|
|
32665
31601
|
`
|
|
32666
31602
|
Examples:
|
|
@@ -32680,7 +31616,7 @@ The stable status values are not_started, pending, running, escalated, succeeded
|
|
|
32680
31616
|
`
|
|
32681
31617
|
).action((repo, options, command) => environmentStatusCommand(repo, options, command));
|
|
32682
31618
|
environments.command("retry").description("Retry a failed repository environment setup").argument("[repo]", "Repository owner/name; defaults to current git remote").option("--wait", "Wait until setup is ready or needs attention").option("--poll-interval <ms>", "Polling interval while waiting (default 5000)").action((repo, options, command) => environmentRetryCommand(repo, options, command));
|
|
32683
|
-
environments.command("login").description("Log into your app once in a local browser and save the login for
|
|
31619
|
+
environments.command("login").description("Log into your app once in a local browser and save the login for Arcanist sessions").argument("[environment]", "Environment id or repository owner/name; defaults to current git remote").option("--url <url>", "Page to open first instead of the app runtime URL").option("--validate-path <path>", "Signed-in-only page Arcanist opens to verify the login, e.g. /dashboard").option("--yes", "Add captured provider hosts to trusted domains without asking").option("--no-network-policy", "Do not touch the environment's trusted domains").option("--clear", "Remove the saved browser login").addHelpText(
|
|
32684
31620
|
"after",
|
|
32685
31621
|
`
|
|
32686
31622
|
Examples:
|
|
@@ -32693,14 +31629,6 @@ JSON mode returns {environmentId, version, counts: {cookieDomainCount, storageOr
|
|
|
32693
31629
|
With --clear it returns {environmentId, deleted}. Instructions and prompts go to stderr; the capture path needs an interactive terminal (pass --yes --validate-path to skip the other prompts).
|
|
32694
31630
|
`
|
|
32695
31631
|
).action((environment, options, command) => environmentLoginCommand(environment, options, command));
|
|
32696
|
-
var models = program.command("models").description("Model discovery commands");
|
|
32697
|
-
models.command("list").description("List session-start models").addHelpText(
|
|
32698
|
-
"after",
|
|
32699
|
-
`
|
|
32700
|
-
JSON:
|
|
32701
|
-
JSON mode returns {models}
|
|
32702
|
-
`
|
|
32703
|
-
).action((options, command) => modelsListCommand(options, command));
|
|
32704
31632
|
var sandbox = program.command("sandbox").description("Sandbox layer commands");
|
|
32705
31633
|
sandbox.command("init").description("Create sandbox layer source files").action((options, command) => sandboxInitCommand(options, command));
|
|
32706
31634
|
sandbox.command("validate").description("Validate local sandbox layer source files").option("--manifest <path>", "Manifest path", ".arcanist/sandbox.yaml").action((options, command) => sandboxValidateCommand(options, command));
|
|
@@ -32715,61 +31643,6 @@ JSON:
|
|
|
32715
31643
|
{type:"build", buildRequest:{...}}
|
|
32716
31644
|
`
|
|
32717
31645
|
).action((sourceRepo, options, command) => sandboxBuildCommand(sourceRepo, options, command));
|
|
32718
|
-
sandbox.command("status").description("Show sandbox layer status").argument("[repo]", "Repository owner/name; defaults to current git remote").option("--business <id>", "Business ID; defaults to authenticated user's business").action((repo, options, command) => sandboxStatusCommand(repo, options, command));
|
|
32719
|
-
sandbox.command("history").description("Show sandbox layer build history").argument("[source-repo]", "Source repository owner/name; defaults to current git remote").option("--business <id>", "Business ID; defaults to authenticated user's business").option("--target-repo <repo>", "Target repository owner/name for resource-profile coverage").option("--status <status>", "Filter by build status").option("--limit <n>", "Maximum builds to return").action((sourceRepo, options, command) => sandboxHistoryCommand(sourceRepo, options, command));
|
|
32720
|
-
sandbox.command("logs").description("Print sandbox layer build logs").argument("<build-id>", "Build ID").option("--business <id>", "Business ID; defaults to authenticated user's business").option("--follow", "Follow logs").option("--after-sequence <n>", "Return logs after this sequence").option("--limit <n>", "Maximum log chunks to return").option("--poll-interval <ms>", "Polling interval in milliseconds").addHelpText(
|
|
32721
|
-
"after",
|
|
32722
|
-
`
|
|
32723
|
-
JSON:
|
|
32724
|
-
Without --follow, --json prints one object: {ok, logs}
|
|
32725
|
-
With --follow, --json emits {type:"logs"} lines, then a terminal {type:"build"} line, and exits.
|
|
32726
|
-
`
|
|
32727
|
-
).action((buildId, options, command) => sandboxLogsCommand(buildId, options, command));
|
|
32728
|
-
sandbox.command("rebuild-stale").description("Create stale sandbox template rebuild campaigns").option("--business <id>", "Business ID to scan; defaults to authenticated user's business").option("--all", "Scan all businesses").option("--dry-run", "Preview stale rebuilds without queueing provider builds").option("--yes", "Start a rebuild campaign").option("--follow", "Follow campaign progress").action((options, command) => sandboxRebuildStaleCommand(options, command));
|
|
32729
|
-
var sandboxAssign = sandbox.command("assign").description("Assign sandbox layer sources");
|
|
32730
|
-
sandboxAssign.command("default").description("Set the business default sandbox layer source").argument("<source-repo>", "Source repository owner/name").option("--manifest <path>", "Manifest path", ".arcanist/sandbox.yaml").option("--business <id>", "Business ID; defaults to authenticated user's business").action((sourceRepo, options, command) => sandboxAssignDefaultCommand(sourceRepo, options, command));
|
|
32731
|
-
sandboxAssign.command("repo").description("Assign a sandbox layer source to one target repo").argument("<target-repo>", "Target repository owner/name").argument("<source-repo>", "Source repository owner/name").option("--manifest <path>", "Manifest path", ".arcanist/sandbox.yaml").option("--business <id>", "Business ID; defaults to authenticated user's business").action(
|
|
32732
|
-
(targetRepo, sourceRepo, options, command) => sandboxAssignRepoCommand(targetRepo, sourceRepo, options, command)
|
|
32733
|
-
);
|
|
32734
|
-
var sandboxUnassign = sandbox.command("unassign").description("Remove sandbox layer source assignments");
|
|
32735
|
-
sandboxUnassign.command("default").description("Clear the business default sandbox layer source").option("--business <id>", "Business ID; defaults to authenticated user's business").option("--yes", "Skip confirmation").action((options, command) => sandboxUnassignDefaultCommand(options, command));
|
|
32736
|
-
sandboxUnassign.command("repo").description("Clear a target repo sandbox layer assignment").argument("<target-repo>", "Target repository owner/name").option("--business <id>", "Business ID; defaults to authenticated user's business").option("--yes", "Skip confirmation").action((targetRepo, options, command) => sandboxUnassignRepoCommand(targetRepo, options, command));
|
|
32737
|
-
var egress = program.command("egress").description("Workspace egress allowlist commands");
|
|
32738
|
-
var egressSource = egress.command("source").description("Manage the workspace egress allowlist source repo");
|
|
32739
|
-
egressSource.command("set").description("Set the source repo for .arcanist/egress-allowlist.txt").argument("<source-repo>", "Source repository owner/name").option("--business <id>", "Business ID; defaults to authenticated user's business").action((sourceRepo, options, command) => egressSourceSetCommand(sourceRepo, options, command));
|
|
32740
|
-
egressSource.command("get").description("Show the configured egress allowlist source").option("--business <id>", "Business ID; defaults to authenticated user's business").action((options, command) => egressSourceGetCommand(options, command));
|
|
32741
|
-
egress.command("add").description("Open or update a PR adding domains to the egress allowlist source file").argument("<domains...>", "Exact domain names to add").option("--business <id>", "Business ID; defaults to authenticated user's business").option("--reason <text>", "Reason to include in the PR body").action((domains, options, command) => egressAddCommand(domains, options, command));
|
|
32742
|
-
egress.command("sync").description("Apply the merged egress allowlist source file to the runtime business policy").option("--business <id>", "Business ID; defaults to authenticated user's business").action((options, command) => egressSyncCommand(options, command));
|
|
32743
|
-
egress.command("validate").description("Validate a local egress allowlist source file").argument("[path]", "Path to validate", ".arcanist/egress-allowlist.txt").action((path, options, command) => egressValidateCommand(path, options, command));
|
|
32744
|
-
var tokens = program.command("tokens").description("CLI token commands");
|
|
32745
|
-
tokens.command("list").description("List CLI tokens").option("--limit <n>", "Maximum tokens to return").option("--cursor <cursor>", "Pagination cursor").option("--all", "Fetch all pages").action((options, command) => listTokensCommand(options, command));
|
|
32746
|
-
tokens.command("create").description("Create a CLI token and print it once").option("--scope <scope>", "Token scope: read or write", "read").option("--expires-in-days <days>", "Token expiry in days").option("--idempotency-key <uuid>", "Request idempotency key for safe manual retries").addHelpText(
|
|
32747
|
-
"after",
|
|
32748
|
-
`
|
|
32749
|
-
Examples:
|
|
32750
|
-
arcanist tokens create --scope read
|
|
32751
|
-
arcanist tokens create --scope read --json
|
|
32752
|
-
`
|
|
32753
|
-
).action((options, command) => createTokenCommand(options, command));
|
|
32754
|
-
tokens.command("revoke").description("Revoke a CLI token").argument("<id>", "Token ID").option("--yes", "Confirm revocation without prompting").addHelpText(
|
|
32755
|
-
"after",
|
|
32756
|
-
`
|
|
32757
|
-
Examples:
|
|
32758
|
-
arcanist tokens revoke 42
|
|
32759
|
-
arcanist tokens revoke 42 --yes --json
|
|
32760
|
-
`
|
|
32761
|
-
).action((id, options, command) => revokeTokenCommand(id, options, command));
|
|
32762
|
-
var testCreds = program.command("test-creds").description("Manage repo-scoped test credentials referenced by the Environment App Runtime Profile");
|
|
32763
|
-
testCreds.command("list").description("List test credentials configured for a repo").argument("<repo>", "Repository in 'owner/name' form").requiredOption("--business <id>", "Business ID that owns the repo").action((repo, options, command) => listTestCredentialsCommand(repo, options, command));
|
|
32764
|
-
testCreds.command("set").description("Set or rotate a test credential value").argument("<repo>", "Repository in 'owner/name' form").argument("<name>", "Credential name (must match the Environment profile's credentials[].name)").requiredOption("--business <id>", "Business ID that owns the repo").option("--value <value>", "Credential value (insecure on shared shells; prefer --value-stdin)").option("--value-file <path>", "Read value from a file").option("--value-stdin", "Read value from stdin").addHelpText(
|
|
32765
|
-
"after",
|
|
32766
|
-
`
|
|
32767
|
-
Examples:
|
|
32768
|
-
echo "$E2E_PASS" | arcanist test-creds set tryarcanist/arcanist test_user_password --business biz-1 --value-stdin
|
|
32769
|
-
arcanist test-creds set tryarcanist/arcanist test_user_email --business biz-1 --value-file ./email.txt
|
|
32770
|
-
`
|
|
32771
|
-
).action((repo, name, options, command) => setTestCredentialCommand(repo, name, options, command));
|
|
32772
|
-
testCreds.command("delete").description("Delete a test credential").argument("<repo>", "Repository in 'owner/name' form").argument("<name>", "Credential name").requiredOption("--business <id>", "Business ID that owns the repo").option("--yes", "Confirm deletion without prompting").action((repo, name, options, command) => deleteTestCredentialCommand(repo, name, options, command));
|
|
32773
31646
|
program.command("help").description("display help for command").argument("[path...]", "Command path to explain").action((path) => {
|
|
32774
31647
|
commandForHelpPath(program, path).help({ error: false });
|
|
32775
31648
|
});
|