@tryarcanist/cli 0.1.298 → 0.1.300
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 +5 -17
- package/dist/index.js +103 -153
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Arcanist CLI
|
|
2
2
|
|
|
3
|
-
Command-line interface for [Arcanist](https://www.tryarcanist.com):
|
|
3
|
+
Command-line interface for [Arcanist](https://www.tryarcanist.com): follow session output and manage access tokens from your terminal or automation.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -14,12 +14,12 @@ Requires Node.js 22 or newer.
|
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
arcanist auth login # paste a token from Settings > CLI
|
|
17
|
-
arcanist
|
|
17
|
+
arcanist sessions list --json
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
export ARCANIST_TOKEN=arc_... # write-scoped for
|
|
22
|
-
SESSION_ID=$(arcanist
|
|
21
|
+
export ARCANIST_TOKEN=arc_... # write-scoped for session mutations
|
|
22
|
+
SESSION_ID=$(arcanist sessions list --json | jq -r '.sessions[0].id')
|
|
23
23
|
arcanist sessions events "$SESSION_ID" --follow --json
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -351,24 +351,12 @@ arcanist tokens revoke 42 --yes --json
|
|
|
351
351
|
|
|
352
352
|
Under `--json`, `--yes` is required.
|
|
353
353
|
|
|
354
|
-
### `arcanist anubis <pr-url>`
|
|
355
|
-
|
|
356
|
-
Runs an Anubis QA verification on a pull request: Anubis boots your app and a browser in a sandbox, tests the PR's changed behavior through the running product, and posts the verdict as a PR comment marked `arcanist-anubis:v1`. Needs a write-scoped token and write access to the repository.
|
|
357
|
-
|
|
358
|
-
```bash
|
|
359
|
-
arcanist anubis https://github.com/your-org/your-repo/pull/123 --wait --json
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
Without `--wait`, JSON mode returns `{sessionId, claimed? | duplicate?}`; with `--wait`, `{sessionId, sessionStatus, verdict, summary?}`. Known verdicts are `works`, `broken`, and `could-not-verify`.
|
|
363
|
-
`--poll-interval <ms>` overrides the 15s status polling cadence while waiting.
|
|
364
|
-
Active runs are deduplicated server-side, so the idempotency-key option does not apply.
|
|
365
|
-
|
|
366
354
|
## Automation recipes
|
|
367
355
|
|
|
368
356
|
Chain commands with `--json` and `jq`:
|
|
369
357
|
|
|
370
358
|
```bash
|
|
371
|
-
SESSION_ID=$(arcanist
|
|
359
|
+
SESSION_ID=$(arcanist sessions list --json | jq -r '.sessions[0].id')
|
|
372
360
|
arcanist sessions events "$SESSION_ID" --follow --json | jq -r 'select(.type == "assistant_message")'
|
|
373
361
|
```
|
|
374
362
|
|
package/dist/index.js
CHANGED
|
@@ -7364,6 +7364,10 @@ var require_dist = __commonJS({
|
|
|
7364
7364
|
import { createRequire as createRequire2 } from "module";
|
|
7365
7365
|
import { Command } from "commander";
|
|
7366
7366
|
|
|
7367
|
+
// src/commands/artifacts.ts
|
|
7368
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
7369
|
+
import { basename, join as join2, resolve as resolve2 } from "path";
|
|
7370
|
+
|
|
7367
7371
|
// src/api.ts
|
|
7368
7372
|
import { createRequire } from "module";
|
|
7369
7373
|
|
|
@@ -7629,10 +7633,6 @@ async function resolveBusinessId(config, options) {
|
|
|
7629
7633
|
throw new CliError("user", "--business is required when the authenticated token has no business context.");
|
|
7630
7634
|
}
|
|
7631
7635
|
|
|
7632
|
-
// src/runtime.ts
|
|
7633
|
-
import { randomUUID } from "crypto";
|
|
7634
|
-
import { createInterface } from "readline/promises";
|
|
7635
|
-
|
|
7636
7636
|
// src/config.ts
|
|
7637
7637
|
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
7638
7638
|
import { homedir } from "os";
|
|
@@ -7824,6 +7824,8 @@ function isLoopbackHost(parsed) {
|
|
|
7824
7824
|
}
|
|
7825
7825
|
|
|
7826
7826
|
// src/runtime.ts
|
|
7827
|
+
import { randomUUID } from "crypto";
|
|
7828
|
+
import { createInterface } from "readline/promises";
|
|
7827
7829
|
var ASCII_FIRST_PRINTABLE = 32;
|
|
7828
7830
|
var ASCII_DELETE = 127;
|
|
7829
7831
|
var ANSI_CONTROL_SEQUENCE = /\u001b\[[0-9:;<=>?]*[ -/]*[@-~]/g;
|
|
@@ -7999,137 +8001,12 @@ function isControlCharacter(char) {
|
|
|
7999
8001
|
return code < ASCII_FIRST_PRINTABLE || code === ASCII_DELETE;
|
|
8000
8002
|
}
|
|
8001
8003
|
|
|
8002
|
-
// ../../shared/utils/timing.ts
|
|
8003
|
-
function sleep(ms) {
|
|
8004
|
-
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
8005
|
-
}
|
|
8006
|
-
|
|
8007
|
-
// src/status-poll.ts
|
|
8008
|
-
async function pollUntilSettled(opts) {
|
|
8009
|
-
const deadline = Date.now() + opts.timeoutMs;
|
|
8010
|
-
while (Date.now() < deadline) {
|
|
8011
|
-
const settled = await opts.fetchStatus();
|
|
8012
|
-
if (settled !== null) return settled;
|
|
8013
|
-
await sleep(opts.pollIntervalMs);
|
|
8014
|
-
}
|
|
8015
|
-
throw new CliError("server", opts.timeoutMessage);
|
|
8016
|
-
}
|
|
8017
|
-
function assertSameAttempt(triggeredSessionId, settledSessionId, label) {
|
|
8018
|
-
if (triggeredSessionId && settledSessionId !== triggeredSessionId) {
|
|
8019
|
-
throw new CliError("conflict", `${label} completed for a different attempt; retry the command.`);
|
|
8020
|
-
}
|
|
8021
|
-
}
|
|
8022
|
-
|
|
8023
|
-
// ../../shared/session/phase.ts
|
|
8024
|
-
var PHASES = [
|
|
8025
|
-
"idle",
|
|
8026
|
-
"running",
|
|
8027
|
-
"review_starting",
|
|
8028
|
-
"waiting_for_input",
|
|
8029
|
-
"finalizing",
|
|
8030
|
-
"review_listening",
|
|
8031
|
-
"completed",
|
|
8032
|
-
"superseded",
|
|
8033
|
-
"needs_you",
|
|
8034
|
-
"blocked",
|
|
8035
|
-
"failed",
|
|
8036
|
-
"stopped",
|
|
8037
|
-
"archived"
|
|
8038
|
-
];
|
|
8039
|
-
var TERMINAL_PHASES_ARRAY = [
|
|
8040
|
-
"completed",
|
|
8041
|
-
"superseded",
|
|
8042
|
-
"needs_you",
|
|
8043
|
-
"blocked",
|
|
8044
|
-
"failed",
|
|
8045
|
-
"stopped",
|
|
8046
|
-
"archived"
|
|
8047
|
-
];
|
|
8048
|
-
var TERMINAL_PHASES = new Set(TERMINAL_PHASES_ARRAY);
|
|
8049
|
-
var TERMINAL_FOR_FALLBACK_POLLING_PHASES = new Set(
|
|
8050
|
-
TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "completed")
|
|
8051
|
-
);
|
|
8052
|
-
var CHILD_SLOT_RELEASE_PHASES = new Set(
|
|
8053
|
-
TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "stopped")
|
|
8054
|
-
);
|
|
8055
|
-
var ARCHIVABLE_STALE_TERMINAL_PHASES_ARRAY = TERMINAL_PHASES_ARRAY.filter(
|
|
8056
|
-
(phase) => phase !== "archived" && phase !== "needs_you" && phase !== "blocked" && phase !== "stopped"
|
|
8057
|
-
);
|
|
8058
|
-
function isTerminalPhase(phase, _sessionKind) {
|
|
8059
|
-
return TERMINAL_PHASES.has(phase);
|
|
8060
|
-
}
|
|
8061
|
-
|
|
8062
|
-
// src/constants/watch.ts
|
|
8063
|
-
var MIN_WATCH_POLL_INTERVAL_MS = 250;
|
|
8064
|
-
var DEFAULT_WATCH_POLL_INTERVAL_MS = 1e3;
|
|
8065
|
-
var MAX_WATCH_POLL_INTERVAL_MS = 6e4;
|
|
8066
|
-
var WATCH_REPLAY_PAGE_SIZE = 200;
|
|
8067
|
-
function isWatchTerminal(phase) {
|
|
8068
|
-
return isTerminalPhase(phase);
|
|
8069
|
-
}
|
|
8070
|
-
|
|
8071
|
-
// src/utils/poll-interval.ts
|
|
8072
|
-
function parsePollInterval(raw, opts = {}) {
|
|
8073
|
-
const defaultMs = opts.defaultMs ?? DEFAULT_WATCH_POLL_INTERVAL_MS;
|
|
8074
|
-
const minMs = opts.minMs ?? MIN_WATCH_POLL_INTERVAL_MS;
|
|
8075
|
-
const maxMs = opts.maxMs === void 0 ? MAX_WATCH_POLL_INTERVAL_MS : opts.maxMs;
|
|
8076
|
-
if (!raw) return defaultMs;
|
|
8077
|
-
if (!/^\d+$/.test(raw)) {
|
|
8078
|
-
throw new CliError("user", "Polling interval must be a non-negative integer.");
|
|
8079
|
-
}
|
|
8080
|
-
const value = Number(raw);
|
|
8081
|
-
if (value < minMs || maxMs !== null && value > maxMs) {
|
|
8082
|
-
const range = maxMs === null ? `at least ${minMs}` : `between ${minMs} and ${maxMs}`;
|
|
8083
|
-
throw new CliError("user", `Polling interval must be ${range} milliseconds.`);
|
|
8084
|
-
}
|
|
8085
|
-
return value;
|
|
8086
|
-
}
|
|
8087
|
-
|
|
8088
8004
|
// src/utils/terminal-text.ts
|
|
8089
8005
|
function sanitizeTerminalText(value) {
|
|
8090
8006
|
return value.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, "").replace(/\u001b\[[0-?]*[ -\/]*[@-~]/g, "").replace(/[\u0000-\u001f\u007f]/g, "");
|
|
8091
8007
|
}
|
|
8092
8008
|
|
|
8093
|
-
// src/commands/anubis.ts
|
|
8094
|
-
var ANUBIS_POLL_INTERVAL_MS = 15e3;
|
|
8095
|
-
var ANUBIS_WAIT_TIMEOUT_MS = 45 * 60 * 1e3;
|
|
8096
|
-
async function anubisCommand(prUrl, options = {}, command) {
|
|
8097
|
-
assertArcanistSessionMutationAllowed("anubis");
|
|
8098
|
-
const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: ANUBIS_POLL_INTERVAL_MS, maxMs: null }) : null;
|
|
8099
|
-
const { config } = resolveBusinessContext(command, options);
|
|
8100
|
-
const trigger = await apiFetch(config, "/api/anubis-runs", {
|
|
8101
|
-
method: "POST",
|
|
8102
|
-
body: JSON.stringify({ prUrl })
|
|
8103
|
-
});
|
|
8104
|
-
if (!options.wait) {
|
|
8105
|
-
emit(command, options, trigger, (payload) => {
|
|
8106
|
-
console.log(`${payload.duplicate ? "Attached to existing" : "Started"} Anubis run (${payload.sessionId}).`);
|
|
8107
|
-
});
|
|
8108
|
-
return;
|
|
8109
|
-
}
|
|
8110
|
-
const result = await pollUntilSettled({
|
|
8111
|
-
pollIntervalMs: pollIntervalMs ?? ANUBIS_POLL_INTERVAL_MS,
|
|
8112
|
-
timeoutMs: ANUBIS_WAIT_TIMEOUT_MS,
|
|
8113
|
-
timeoutMessage: "Timed out waiting for Anubis results.",
|
|
8114
|
-
fetchStatus: async () => {
|
|
8115
|
-
const status = await apiFetch(
|
|
8116
|
-
config,
|
|
8117
|
-
`/api/anubis-runs/status?prUrl=${encodeURIComponent(prUrl)}`
|
|
8118
|
-
);
|
|
8119
|
-
if (status.sessionStatus === "in_flight") return null;
|
|
8120
|
-
assertSameAttempt(trigger.sessionId, status.sessionId, "Anubis");
|
|
8121
|
-
return status;
|
|
8122
|
-
}
|
|
8123
|
-
});
|
|
8124
|
-
emit(command, options, result, (payload) => {
|
|
8125
|
-
console.log(`Anubis: ${sanitizeTerminalText(payload.verdict ?? "no verdict")}.`);
|
|
8126
|
-
if (payload.summary) console.log(sanitizeTerminalText(payload.summary));
|
|
8127
|
-
});
|
|
8128
|
-
}
|
|
8129
|
-
|
|
8130
8009
|
// src/commands/artifacts.ts
|
|
8131
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
8132
|
-
import { basename, join as join2, resolve as resolve2 } from "path";
|
|
8133
8010
|
async function fetchArtifacts(config, sessionId) {
|
|
8134
8011
|
const payload = await apiFetch(
|
|
8135
8012
|
config,
|
|
@@ -8569,6 +8446,92 @@ async function egressValidateCommand(path = EGRESS_ALLOWLIST_SOURCE_PATH, option
|
|
|
8569
8446
|
});
|
|
8570
8447
|
}
|
|
8571
8448
|
|
|
8449
|
+
// ../../shared/utils/timing.ts
|
|
8450
|
+
function sleep(ms) {
|
|
8451
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
8452
|
+
}
|
|
8453
|
+
|
|
8454
|
+
// src/status-poll.ts
|
|
8455
|
+
async function pollUntilSettled(opts) {
|
|
8456
|
+
const deadline = Date.now() + opts.timeoutMs;
|
|
8457
|
+
while (Date.now() < deadline) {
|
|
8458
|
+
const settled = await opts.fetchStatus();
|
|
8459
|
+
if (settled !== null) return settled;
|
|
8460
|
+
await sleep(opts.pollIntervalMs);
|
|
8461
|
+
}
|
|
8462
|
+
throw new CliError("server", opts.timeoutMessage);
|
|
8463
|
+
}
|
|
8464
|
+
function assertSameAttempt(triggeredSessionId, settledSessionId, label) {
|
|
8465
|
+
if (triggeredSessionId && settledSessionId !== triggeredSessionId) {
|
|
8466
|
+
throw new CliError("conflict", `${label} completed for a different attempt; retry the command.`);
|
|
8467
|
+
}
|
|
8468
|
+
}
|
|
8469
|
+
|
|
8470
|
+
// ../../shared/session/phase.ts
|
|
8471
|
+
var PHASES = [
|
|
8472
|
+
"idle",
|
|
8473
|
+
"running",
|
|
8474
|
+
"review_starting",
|
|
8475
|
+
"waiting_for_input",
|
|
8476
|
+
"finalizing",
|
|
8477
|
+
"review_listening",
|
|
8478
|
+
"completed",
|
|
8479
|
+
"superseded",
|
|
8480
|
+
"needs_you",
|
|
8481
|
+
"blocked",
|
|
8482
|
+
"failed",
|
|
8483
|
+
"stopped",
|
|
8484
|
+
"archived"
|
|
8485
|
+
];
|
|
8486
|
+
var TERMINAL_PHASES_ARRAY = [
|
|
8487
|
+
"completed",
|
|
8488
|
+
"superseded",
|
|
8489
|
+
"needs_you",
|
|
8490
|
+
"blocked",
|
|
8491
|
+
"failed",
|
|
8492
|
+
"stopped",
|
|
8493
|
+
"archived"
|
|
8494
|
+
];
|
|
8495
|
+
var TERMINAL_PHASES = new Set(TERMINAL_PHASES_ARRAY);
|
|
8496
|
+
var TERMINAL_FOR_FALLBACK_POLLING_PHASES = new Set(
|
|
8497
|
+
TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "completed")
|
|
8498
|
+
);
|
|
8499
|
+
var CHILD_SLOT_RELEASE_PHASES = new Set(
|
|
8500
|
+
TERMINAL_PHASES_ARRAY.filter((phase) => phase !== "stopped")
|
|
8501
|
+
);
|
|
8502
|
+
var ARCHIVABLE_STALE_TERMINAL_PHASES_ARRAY = TERMINAL_PHASES_ARRAY.filter(
|
|
8503
|
+
(phase) => phase !== "archived" && phase !== "needs_you" && phase !== "blocked" && phase !== "stopped"
|
|
8504
|
+
);
|
|
8505
|
+
function isTerminalPhase(phase, _sessionKind) {
|
|
8506
|
+
return TERMINAL_PHASES.has(phase);
|
|
8507
|
+
}
|
|
8508
|
+
|
|
8509
|
+
// src/constants/watch.ts
|
|
8510
|
+
var MIN_WATCH_POLL_INTERVAL_MS = 250;
|
|
8511
|
+
var DEFAULT_WATCH_POLL_INTERVAL_MS = 1e3;
|
|
8512
|
+
var MAX_WATCH_POLL_INTERVAL_MS = 6e4;
|
|
8513
|
+
var WATCH_REPLAY_PAGE_SIZE = 200;
|
|
8514
|
+
function isWatchTerminal(phase) {
|
|
8515
|
+
return isTerminalPhase(phase);
|
|
8516
|
+
}
|
|
8517
|
+
|
|
8518
|
+
// src/utils/poll-interval.ts
|
|
8519
|
+
function parsePollInterval(raw, opts = {}) {
|
|
8520
|
+
const defaultMs = opts.defaultMs ?? DEFAULT_WATCH_POLL_INTERVAL_MS;
|
|
8521
|
+
const minMs = opts.minMs ?? MIN_WATCH_POLL_INTERVAL_MS;
|
|
8522
|
+
const maxMs = opts.maxMs === void 0 ? MAX_WATCH_POLL_INTERVAL_MS : opts.maxMs;
|
|
8523
|
+
if (!raw) return defaultMs;
|
|
8524
|
+
if (!/^\d+$/.test(raw)) {
|
|
8525
|
+
throw new CliError("user", "Polling interval must be a non-negative integer.");
|
|
8526
|
+
}
|
|
8527
|
+
const value = Number(raw);
|
|
8528
|
+
if (value < minMs || maxMs !== null && value > maxMs) {
|
|
8529
|
+
const range = maxMs === null ? `at least ${minMs}` : `between ${minMs} and ${maxMs}`;
|
|
8530
|
+
throw new CliError("user", `Polling interval must be ${range} milliseconds.`);
|
|
8531
|
+
}
|
|
8532
|
+
return value;
|
|
8533
|
+
}
|
|
8534
|
+
|
|
8572
8535
|
// src/commands/environments.ts
|
|
8573
8536
|
var SETUP_POLL_INTERVAL_MS = 5e3;
|
|
8574
8537
|
var SETUP_WAIT_TIMEOUT_MS = 2 * 60 * 60 * 1e3;
|
|
@@ -9332,6 +9295,11 @@ import { dirname as dirname2 } from "path";
|
|
|
9332
9295
|
// ../../shared/sandbox-layer/parser.ts
|
|
9333
9296
|
var import_yaml = __toESM(require_dist(), 1);
|
|
9334
9297
|
|
|
9298
|
+
// ../../shared/utils/bytes.ts
|
|
9299
|
+
function utf8ByteLength(value) {
|
|
9300
|
+
return new TextEncoder().encode(value).byteLength;
|
|
9301
|
+
}
|
|
9302
|
+
|
|
9335
9303
|
// ../../shared/utils/hex.ts
|
|
9336
9304
|
function bytesToHex(buf) {
|
|
9337
9305
|
const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
|
|
@@ -9395,9 +9363,6 @@ function issue(path, message, options = {}) {
|
|
|
9395
9363
|
message
|
|
9396
9364
|
};
|
|
9397
9365
|
}
|
|
9398
|
-
function byteLength(value) {
|
|
9399
|
-
return new TextEncoder().encode(value).byteLength;
|
|
9400
|
-
}
|
|
9401
9366
|
function rejectSecrets(path, text) {
|
|
9402
9367
|
const match = text.match(SECRET_PATTERN);
|
|
9403
9368
|
if (match) {
|
|
@@ -9411,7 +9376,7 @@ function assertSize(path, text, maxBytes) {
|
|
|
9411
9376
|
if (text.includes("\0")) {
|
|
9412
9377
|
throw new SandboxLayerValidationError(path, "file must not contain NUL bytes");
|
|
9413
9378
|
}
|
|
9414
|
-
if (
|
|
9379
|
+
if (utf8ByteLength(text) > maxBytes) {
|
|
9415
9380
|
throw new SandboxLayerValidationError(path, `file exceeds ${maxBytes} byte limit`);
|
|
9416
9381
|
}
|
|
9417
9382
|
}
|
|
@@ -9606,7 +9571,7 @@ function parseSandboxLayerManifest(path, text) {
|
|
|
9606
9571
|
const parsedCommand = [];
|
|
9607
9572
|
command.forEach((arg, argIndex) => {
|
|
9608
9573
|
const argField = `${field}.${argIndex}`;
|
|
9609
|
-
if (typeof arg !== "string" || arg.length === 0 || arg.includes("\0") || /[\r\n]/.test(arg) ||
|
|
9574
|
+
if (typeof arg !== "string" || arg.length === 0 || arg.includes("\0") || /[\r\n]/.test(arg) || utf8ByteLength(arg) > MAX_SMOKE_ARG_BYTES) {
|
|
9610
9575
|
issues.push(
|
|
9611
9576
|
issue(
|
|
9612
9577
|
path,
|
|
@@ -9658,7 +9623,7 @@ function splitLogicalInstructions(path, text) {
|
|
|
9658
9623
|
if (!current) startLine = lineNumber;
|
|
9659
9624
|
const continued = /\\\s*$/.test(rawLine);
|
|
9660
9625
|
current += (current ? " " : "") + rawLine.replace(/\\\s*$/, "").trim();
|
|
9661
|
-
if (
|
|
9626
|
+
if (utf8ByteLength(current) > MAX_LAYER_BYTES) {
|
|
9662
9627
|
throw new SandboxLayerValidationError(path, "continued instruction exceeds layer size limit", startLine);
|
|
9663
9628
|
}
|
|
9664
9629
|
if (!continued) {
|
|
@@ -9722,7 +9687,7 @@ function parseEnvValues(path, body, startLine) {
|
|
|
9722
9687
|
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
9723
9688
|
value = value.slice(1, -1);
|
|
9724
9689
|
}
|
|
9725
|
-
if (!value || value.includes("\0") || /[\r\n]/.test(value) ||
|
|
9690
|
+
if (!value || value.includes("\0") || /[\r\n]/.test(value) || utf8ByteLength(value) > MAX_ENV_VALUE_BYTES) {
|
|
9726
9691
|
throw new SandboxLayerValidationError(
|
|
9727
9692
|
path,
|
|
9728
9693
|
`ENV value for '${key}' must be 1-${MAX_ENV_VALUE_BYTES} bytes with no NUL or newline`,
|
|
@@ -9744,7 +9709,7 @@ function parseEnvValues(path, body, startLine) {
|
|
|
9744
9709
|
return values;
|
|
9745
9710
|
}
|
|
9746
9711
|
function assertRunSafety(path, command, startLine) {
|
|
9747
|
-
if (
|
|
9712
|
+
if (utf8ByteLength(command) > MAX_RUN_COMMAND_BYTES) {
|
|
9748
9713
|
throw new SandboxLayerValidationError(path, `RUN command exceeds ${MAX_RUN_COMMAND_BYTES} byte limit`, startLine);
|
|
9749
9714
|
}
|
|
9750
9715
|
if (/(^|\s)--mount=/.test(command)) {
|
|
@@ -12439,7 +12404,7 @@ var program = new Command().name("arcanist").description("Arcanist CLI").version
|
|
|
12439
12404
|
`
|
|
12440
12405
|
Examples:
|
|
12441
12406
|
arcanist auth login --token-stdin
|
|
12442
|
-
arcanist
|
|
12407
|
+
arcanist sessions list --json
|
|
12443
12408
|
arcanist sessions events <session-id> --follow --json
|
|
12444
12409
|
|
|
12445
12410
|
Exit codes:
|
|
@@ -12500,21 +12465,6 @@ instead (members only).
|
|
|
12500
12465
|
Zeus review is currently available to Arcanist members only.
|
|
12501
12466
|
`
|
|
12502
12467
|
).action((prUrl, options, command) => reviewCommand(prUrl, options, command));
|
|
12503
|
-
program.command("anubis <pr-url>").description("Run an Anubis QA verification for a GitHub pull request").option("--wait", "Wait for the verdict").option("--poll-interval <ms>", "Polling interval in milliseconds while waiting (default 15000)").addHelpText(
|
|
12504
|
-
"after",
|
|
12505
|
-
`
|
|
12506
|
-
Examples:
|
|
12507
|
-
arcanist anubis https://github.com/org/repo/pull/123 --wait --json
|
|
12508
|
-
arcanist anubis https://github.com/org/repo/pull/123
|
|
12509
|
-
|
|
12510
|
-
--json without --wait returns { sessionId, claimed? | duplicate? }.
|
|
12511
|
-
--wait --json returns { sessionId, sessionStatus, verdict, summary? }.
|
|
12512
|
-
Known verdicts are works, broken, and could-not-verify.
|
|
12513
|
-
Anubis deduplicates active runs server-side; it does not use --idempotency-key.
|
|
12514
|
-
The result is posted as a PR comment marked arcanist-anubis:v1.
|
|
12515
|
-
Anubis requires a write-scoped CLI token and repository write access.
|
|
12516
|
-
`
|
|
12517
|
-
).action((prUrl, options, command) => anubisCommand(prUrl, options, command));
|
|
12518
12468
|
program.command("hades <repo>").description("Start an internal Hades run measuring Zeus's review capability on a Try Arcanist repository").addHelpText(
|
|
12519
12469
|
"after",
|
|
12520
12470
|
`
|