@tryarcanist/cli 0.1.291 → 0.1.293
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 +12 -0
- package/dist/index.js +150 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -120,6 +120,18 @@ Mutation commands send an `Idempotency-Key` header. The CLI does not auto-retry
|
|
|
120
120
|
|
|
121
121
|
## Commands
|
|
122
122
|
|
|
123
|
+
### `arcanist environments prepare|status`
|
|
124
|
+
|
|
125
|
+
Prepare the current repository for Zeus, or inspect its machine-readable setup state. Pass `owner/repo` outside a checkout. A write-scoped token is required to prepare; read-scoped tokens can inspect status.
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
arcanist environments prepare --wait --json
|
|
129
|
+
arcanist environments status owner/repo --json
|
|
130
|
+
arcanist environments retry owner/repo --wait --json
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Agents should follow `repository.nextAction` and request a Zeus review only when it is `review`. The command waits through internal handoffs until setup is ready or there is a real action for the user or Arcanist.
|
|
134
|
+
|
|
123
135
|
### `arcanist auth login`
|
|
124
136
|
|
|
125
137
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -7368,8 +7368,9 @@ import { Command } from "commander";
|
|
|
7368
7368
|
import { createRequire } from "module";
|
|
7369
7369
|
|
|
7370
7370
|
// ../../shared/utils/errors.ts
|
|
7371
|
-
function stringifyError(error) {
|
|
7372
|
-
|
|
7371
|
+
function stringifyError(error, fallback = "") {
|
|
7372
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7373
|
+
return message || fallback;
|
|
7373
7374
|
}
|
|
7374
7375
|
|
|
7375
7376
|
// ../../shared/utils/url.ts
|
|
@@ -7404,18 +7405,25 @@ function parseApiErrorBody(body) {
|
|
|
7404
7405
|
try {
|
|
7405
7406
|
const parsed = JSON.parse(body);
|
|
7406
7407
|
if (!parsed || typeof parsed !== "object") return null;
|
|
7408
|
+
const details = parsed;
|
|
7409
|
+
const recovery = {
|
|
7410
|
+
...typeof details.repository === "string" ? { repository: details.repository } : {},
|
|
7411
|
+
...typeof details.environmentsUrl === "string" ? { environmentsUrl: details.environmentsUrl } : {}
|
|
7412
|
+
};
|
|
7407
7413
|
const envelope = parseApiErrorEnvelope(parsed);
|
|
7408
7414
|
if (!envelope) return null;
|
|
7409
7415
|
if (typeof envelope.raw === "string" && envelope.raw) {
|
|
7410
7416
|
return {
|
|
7411
7417
|
...envelope.code ? { serverCode: envelope.code } : {},
|
|
7412
7418
|
rawError: envelope.raw,
|
|
7413
|
-
...envelope.message ? { message: envelope.message } : {}
|
|
7419
|
+
...envelope.message ? { message: envelope.message } : {},
|
|
7420
|
+
...recovery
|
|
7414
7421
|
};
|
|
7415
7422
|
}
|
|
7416
7423
|
return envelope.message || envelope.code ? {
|
|
7417
7424
|
...envelope.message ? { message: envelope.message } : {},
|
|
7418
|
-
...envelope.code ? { serverCode: envelope.code, rawError: envelope.code } : {}
|
|
7425
|
+
...envelope.code ? { serverCode: envelope.code, rawError: envelope.code } : {},
|
|
7426
|
+
...recovery
|
|
7419
7427
|
} : null;
|
|
7420
7428
|
} catch {
|
|
7421
7429
|
return null;
|
|
@@ -7462,9 +7470,13 @@ var ApiError = class extends CliError {
|
|
|
7462
7470
|
const parsed = parseApiErrorBody(body);
|
|
7463
7471
|
super(code, messageForApiError(status, body, parsed), {
|
|
7464
7472
|
exitCode: exitCodeForHttpStatus(status),
|
|
7465
|
-
hint: hintForHttpStatus(status, resourceNoun),
|
|
7473
|
+
hint: hintForHttpStatus(status, resourceNoun, parsed),
|
|
7466
7474
|
requestId,
|
|
7467
|
-
data: parsed?.serverCode
|
|
7475
|
+
data: parsed?.serverCode || parsed?.repository || parsed?.environmentsUrl ? {
|
|
7476
|
+
...parsed.serverCode ? { serverCode: parsed.serverCode } : {},
|
|
7477
|
+
...parsed.repository ? { repository: parsed.repository } : {},
|
|
7478
|
+
...parsed.environmentsUrl ? { environmentsUrl: parsed.environmentsUrl } : {}
|
|
7479
|
+
} : void 0
|
|
7468
7480
|
});
|
|
7469
7481
|
this.name = "ApiError";
|
|
7470
7482
|
this.status = status;
|
|
@@ -7501,13 +7513,21 @@ function messageForApiError(status, body, parsed = parseApiErrorBody(body)) {
|
|
|
7501
7513
|
if (parsed?.serverCode) return parsed.serverCode;
|
|
7502
7514
|
return body ? `API error ${status}: ${body}` : `API error ${status}`;
|
|
7503
7515
|
}
|
|
7504
|
-
function hintForHttpStatus(status, resourceNoun) {
|
|
7516
|
+
function hintForHttpStatus(status, resourceNoun, parsed) {
|
|
7505
7517
|
if (status === 401 || status === 403) return "Run `arcanist auth login` or set `ARCANIST_TOKEN`.";
|
|
7506
7518
|
if (status === 404) {
|
|
7507
7519
|
if (resourceNoun === "token") return "List tokens with `arcanist tokens list`.";
|
|
7508
7520
|
if (resourceNoun === "repo") return "Check accessible repositories with `arcanist repos list`.";
|
|
7509
7521
|
return "List sessions with `arcanist sessions list`.";
|
|
7510
7522
|
}
|
|
7523
|
+
if (status === 409 && parsed?.repository) {
|
|
7524
|
+
if (parsed.serverCode === "environment_required") {
|
|
7525
|
+
return `Run \`arcanist environments prepare ${parsed.repository} --wait\`, then retry the review.`;
|
|
7526
|
+
}
|
|
7527
|
+
if (parsed.serverCode === "environment_preparing" || parsed.serverCode === "environment_needs_attention") {
|
|
7528
|
+
return `Run \`arcanist environments status ${parsed.repository} --wait\` for the latest setup state.`;
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7511
7531
|
if (status === 409) return "Check the current resource state and retry the command when it is ready.";
|
|
7512
7532
|
if (status >= 500) return "Retry later or check the control-plane logs.";
|
|
7513
7533
|
return void 0;
|
|
@@ -8549,6 +8569,105 @@ async function egressValidateCommand(path = EGRESS_ALLOWLIST_SOURCE_PATH, option
|
|
|
8549
8569
|
});
|
|
8550
8570
|
}
|
|
8551
8571
|
|
|
8572
|
+
// src/commands/environments.ts
|
|
8573
|
+
var SETUP_POLL_INTERVAL_MS = 5e3;
|
|
8574
|
+
var SETUP_WAIT_TIMEOUT_MS = 2 * 60 * 60 * 1e3;
|
|
8575
|
+
function requestedRepo(repoArg) {
|
|
8576
|
+
const repo = parseRepoArgOrCurrent(repoArg);
|
|
8577
|
+
return { ...repo, fullName: `${repo.owner}/${repo.repo}` };
|
|
8578
|
+
}
|
|
8579
|
+
async function fetchSetupState(config, fullName) {
|
|
8580
|
+
const response = await apiFetch(config, "/api/settings/repositories/onboarding");
|
|
8581
|
+
const state = response.repositories.find((item) => item.fullName.toLowerCase() === fullName.toLowerCase()) ?? {
|
|
8582
|
+
fullName,
|
|
8583
|
+
status: "not_started",
|
|
8584
|
+
stage: null
|
|
8585
|
+
};
|
|
8586
|
+
return { ...state, nextAction: nextAction(state) };
|
|
8587
|
+
}
|
|
8588
|
+
function isActive(state) {
|
|
8589
|
+
return state.nextAction === "wait";
|
|
8590
|
+
}
|
|
8591
|
+
function nextAction(state) {
|
|
8592
|
+
if (state.status === "not_started") return "prepare";
|
|
8593
|
+
if (state.status === "ready") return "review";
|
|
8594
|
+
if (state.secretRequest) return "add_credentials";
|
|
8595
|
+
if (state.question) return "answer_question";
|
|
8596
|
+
if (state.status === "failed") {
|
|
8597
|
+
return state.failureReason === "installation_missing" ? "authorize_github" : "retry";
|
|
8598
|
+
}
|
|
8599
|
+
if (state.status === "escalated" && state.stage !== "escalated_to_customer" && state.stage !== "question_for_customer") {
|
|
8600
|
+
return "wait_for_arcanist";
|
|
8601
|
+
}
|
|
8602
|
+
return "wait";
|
|
8603
|
+
}
|
|
8604
|
+
function printState(state) {
|
|
8605
|
+
const detail = state.failureReason ?? state.stage ?? state.buildStatus ?? state.nextAction;
|
|
8606
|
+
console.log(
|
|
8607
|
+
`${sanitizeTerminalText(state.fullName)}: ${sanitizeTerminalText(state.status)}${detail ? ` (${sanitizeTerminalText(detail)})` : ""}`
|
|
8608
|
+
);
|
|
8609
|
+
}
|
|
8610
|
+
async function waitForSetup(config, fullName, pollIntervalMs) {
|
|
8611
|
+
return pollUntilSettled({
|
|
8612
|
+
pollIntervalMs,
|
|
8613
|
+
timeoutMs: SETUP_WAIT_TIMEOUT_MS,
|
|
8614
|
+
timeoutMessage: `Timed out waiting for ${fullName} environment setup.`,
|
|
8615
|
+
fetchStatus: async () => {
|
|
8616
|
+
const state = await fetchSetupState(config, fullName);
|
|
8617
|
+
return isActive(state) ? null : state;
|
|
8618
|
+
}
|
|
8619
|
+
});
|
|
8620
|
+
}
|
|
8621
|
+
async function environmentStatusCommand(repoArg, options = {}, command) {
|
|
8622
|
+
const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: SETUP_POLL_INTERVAL_MS, maxMs: null }) : null;
|
|
8623
|
+
const repo = requestedRepo(repoArg);
|
|
8624
|
+
const { config } = resolveBusinessContext(command, options);
|
|
8625
|
+
await apiFetch(config, "/api/repos");
|
|
8626
|
+
const state = options.wait ? await waitForSetup(config, repo.fullName, pollIntervalMs ?? SETUP_POLL_INTERVAL_MS) : await fetchSetupState(config, repo.fullName);
|
|
8627
|
+
emit(command, options, { repository: state }, ({ repository }) => printState(repository));
|
|
8628
|
+
}
|
|
8629
|
+
async function environmentPrepareCommand(repoArg, options = {}, command) {
|
|
8630
|
+
const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: SETUP_POLL_INTERVAL_MS, maxMs: null }) : null;
|
|
8631
|
+
const repo = requestedRepo(repoArg);
|
|
8632
|
+
const { config } = resolveBusinessContext(command, options);
|
|
8633
|
+
const response = await apiFetch(config, "/api/settings/repositories/onboarding", {
|
|
8634
|
+
method: "POST",
|
|
8635
|
+
body: JSON.stringify({ repositories: [{ owner: repo.owner, name: repo.repo }] })
|
|
8636
|
+
});
|
|
8637
|
+
const result = response.repositories[0];
|
|
8638
|
+
if (!result) throw new CliError("server", "Environment setup returned no repository result.");
|
|
8639
|
+
if (result.status === "forbidden") {
|
|
8640
|
+
throw new CliError("auth", `You must have write access to ${repo.fullName} to prepare its environment.`);
|
|
8641
|
+
}
|
|
8642
|
+
if (result.status === "failed") {
|
|
8643
|
+
throw new CliError("server", `Environment setup could not be started for ${repo.fullName}.`);
|
|
8644
|
+
}
|
|
8645
|
+
if (options.wait && (result.status === "queued" || result.status === "already_queued")) {
|
|
8646
|
+
const state2 = await waitForSetup(config, repo.fullName, pollIntervalMs ?? SETUP_POLL_INTERVAL_MS);
|
|
8647
|
+
emit(command, options, { repository: state2 }, ({ repository }) => printState(repository));
|
|
8648
|
+
return;
|
|
8649
|
+
}
|
|
8650
|
+
const state = {
|
|
8651
|
+
fullName: result.fullName,
|
|
8652
|
+
status: result.status === "already_ready" ? "ready" : "pending",
|
|
8653
|
+
stage: null,
|
|
8654
|
+
nextAction: result.status === "already_ready" ? "review" : "wait"
|
|
8655
|
+
};
|
|
8656
|
+
emit(command, options, { repository: state }, ({ repository }) => printState(repository));
|
|
8657
|
+
}
|
|
8658
|
+
async function environmentRetryCommand(repoArg, options = {}, command) {
|
|
8659
|
+
const pollIntervalMs = options.wait ? parsePollInterval(options.pollInterval, { defaultMs: SETUP_POLL_INTERVAL_MS, maxMs: null }) : null;
|
|
8660
|
+
const repo = requestedRepo(repoArg);
|
|
8661
|
+
const { config } = resolveBusinessContext(command, options);
|
|
8662
|
+
await apiFetch(
|
|
8663
|
+
config,
|
|
8664
|
+
`/api/settings/repositories/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/onboarding/retry`,
|
|
8665
|
+
{ method: "POST" }
|
|
8666
|
+
);
|
|
8667
|
+
const state = options.wait ? await waitForSetup(config, repo.fullName, pollIntervalMs ?? SETUP_POLL_INTERVAL_MS) : await fetchSetupState(config, repo.fullName);
|
|
8668
|
+
emit(command, options, { repository: state }, ({ repository }) => printState(repository));
|
|
8669
|
+
}
|
|
8670
|
+
|
|
8552
8671
|
// src/commands/hades.ts
|
|
8553
8672
|
async function hadesCommand(repo, options = {}, command) {
|
|
8554
8673
|
assertArcanistSessionMutationAllowed("hades");
|
|
@@ -9199,7 +9318,12 @@ async function reviewCommand(prUrl, options = {}, command) {
|
|
|
9199
9318
|
);
|
|
9200
9319
|
if (status.status !== "completed") return null;
|
|
9201
9320
|
assertSameAttempt(triggeredSessionId, status.sessionId, "PR review");
|
|
9202
|
-
return {
|
|
9321
|
+
return {
|
|
9322
|
+
...status,
|
|
9323
|
+
status: "completed",
|
|
9324
|
+
sessionId: status.sessionId ?? triggeredSessionId,
|
|
9325
|
+
verdict: status.verdict ?? "unknown"
|
|
9326
|
+
};
|
|
9203
9327
|
}
|
|
9204
9328
|
});
|
|
9205
9329
|
emit(command, options, result, (payload) => {
|
|
@@ -12556,6 +12680,24 @@ JSON:
|
|
|
12556
12680
|
JSON mode returns {skills}
|
|
12557
12681
|
`
|
|
12558
12682
|
).action((repo, options, command) => repoSkillsCommand(repo, options, command));
|
|
12683
|
+
var environments = program.command("environments").description("Zeus environment setup commands");
|
|
12684
|
+
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(
|
|
12685
|
+
"after",
|
|
12686
|
+
`
|
|
12687
|
+
Examples:
|
|
12688
|
+
arcanist environments prepare
|
|
12689
|
+
arcanist environments prepare owner/repo --wait --json
|
|
12690
|
+
`
|
|
12691
|
+
).action((repo, options, command) => environmentPrepareCommand(repo, options, command));
|
|
12692
|
+
environments.command("status").description("Show repository environment setup status").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(
|
|
12693
|
+
"after",
|
|
12694
|
+
`
|
|
12695
|
+
JSON:
|
|
12696
|
+
Returns {repository: {fullName, status, stage, nextAction, buildStatus?, failureReason?, secretRequest?, question?}}.
|
|
12697
|
+
The stable status values are not_started, pending, running, escalated, succeeded, failed, and ready.
|
|
12698
|
+
`
|
|
12699
|
+
).action((repo, options, command) => environmentStatusCommand(repo, options, command));
|
|
12700
|
+
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));
|
|
12559
12701
|
var models = program.command("models").description("Model discovery commands");
|
|
12560
12702
|
models.command("list").description("List session-start models").addHelpText(
|
|
12561
12703
|
"after",
|