@tryarcanist/cli 0.1.172 → 0.1.174
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 +13 -0
- package/dist/index.js +71 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -226,6 +226,19 @@ Repeatable `--uploaded-file <path>` flags attach local UTF-8 text files to the f
|
|
|
226
226
|
JSON mode returns `{sessionId, promptId?}`.
|
|
227
227
|
With `--wait`, JSON mode also includes best-effort result fields when available: `prUrl?`, `publishedBranch?`, `lastBranch?`.
|
|
228
228
|
|
|
229
|
+
### `arcanist sessions respond <session-id> [answer]`
|
|
230
|
+
|
|
231
|
+
Answers a pending `question` event from `sessions events --follow`.
|
|
232
|
+
Pass the `questionId` from the question event so the answer is durable and safe across reconnects.
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
arcanist sessions respond abc123 "Use PostgreSQL" --question-id q_123
|
|
236
|
+
printf "Use PostgreSQL" | arcanist sessions respond abc123 --question-id q_123 --answer-stdin --json
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
JSON mode returns the raw respond payload plus `sessionId`.
|
|
240
|
+
If the session is no longer waiting for input, the command exits with conflict and points back to `sessions events --follow --json`.
|
|
241
|
+
|
|
229
242
|
### `arcanist sessions stop <session-id>`
|
|
230
243
|
|
|
231
244
|
Stops the active run for a session. Idempotent: if no sandbox is active, the server returns `already_stopped`.
|
package/dist/index.js
CHANGED
|
@@ -506,6 +506,15 @@ function randomIdempotencyKey() {
|
|
|
506
506
|
function assertArcanistSessionMutationAllowed(subcommand) {
|
|
507
507
|
const role = process.env.ARCANIST_AGENT_ROLE?.trim();
|
|
508
508
|
if (role !== "verification") return;
|
|
509
|
+
if (subcommand === "respond") {
|
|
510
|
+
throw new CliError(
|
|
511
|
+
"user",
|
|
512
|
+
"`arcanist sessions respond` is disabled inside verification sessions because verification agents cannot mutate the session under review.",
|
|
513
|
+
{
|
|
514
|
+
hint: "Report the unanswered question as a verification blocker instead."
|
|
515
|
+
}
|
|
516
|
+
);
|
|
517
|
+
}
|
|
509
518
|
throw new CliError(
|
|
510
519
|
"user",
|
|
511
520
|
`\`arcanist sessions ${subcommand}\` is disabled inside ${role} sessions because nested Arcanist sessions are not observable to the verifier.`,
|
|
@@ -3502,6 +3511,57 @@ async function qaCommand(prUrl, options, command) {
|
|
|
3502
3511
|
console.log(`Follow with: arcanist sessions events ${sessionId} --follow --json`);
|
|
3503
3512
|
}
|
|
3504
3513
|
|
|
3514
|
+
// src/commands/respond.ts
|
|
3515
|
+
async function resolveAnswerInput(answerArg, options) {
|
|
3516
|
+
const shouldReadStdin = options.answerStdin === true || answerArg === "-";
|
|
3517
|
+
const answer = shouldReadStdin ? await readStdin() : answerArg;
|
|
3518
|
+
if (!answer || answer.trim().length === 0) {
|
|
3519
|
+
throw new CliError(
|
|
3520
|
+
"user",
|
|
3521
|
+
"Missing answer. Pass an answer argument, use '-' to read stdin, or pass --answer-stdin."
|
|
3522
|
+
);
|
|
3523
|
+
}
|
|
3524
|
+
return answer;
|
|
3525
|
+
}
|
|
3526
|
+
async function respondCommand(sessionId, answerArg, options = {}, command) {
|
|
3527
|
+
assertArcanistSessionMutationAllowed("respond");
|
|
3528
|
+
const questionId = options.questionId?.trim();
|
|
3529
|
+
if (!questionId) {
|
|
3530
|
+
throw new CliError("user", "Missing --question-id for the pending question.", {
|
|
3531
|
+
hint: `Find the question id with: arcanist sessions events ${sessionId} --follow --json`
|
|
3532
|
+
});
|
|
3533
|
+
}
|
|
3534
|
+
const { config } = resolveBusinessContext(command, options);
|
|
3535
|
+
const answer = await resolveAnswerInput(answerArg, options);
|
|
3536
|
+
try {
|
|
3537
|
+
const payload = await apiFetch(config, `/api/sessions/${sessionId}/respond`, {
|
|
3538
|
+
method: "POST",
|
|
3539
|
+
body: JSON.stringify({ answer, questionId })
|
|
3540
|
+
});
|
|
3541
|
+
emit(command, options, { sessionId, ...payload }, () => console.log(`Answer sent to session ${sessionId}.`));
|
|
3542
|
+
} catch (err) {
|
|
3543
|
+
const conflict = parseRespondConflict(err);
|
|
3544
|
+
if (!conflict) throw err;
|
|
3545
|
+
throw new CliError("conflict", conflict.message, {
|
|
3546
|
+
data: { reason: conflict.reason },
|
|
3547
|
+
hint: `Inspect pending questions with: arcanist sessions events ${sessionId} --follow --json`
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
function parseRespondConflict(err) {
|
|
3552
|
+
if (!(err instanceof ApiError) || err.status !== 409) return null;
|
|
3553
|
+
try {
|
|
3554
|
+
const body = JSON.parse(err.body);
|
|
3555
|
+
if (body.error !== "session_not_respondable") return null;
|
|
3556
|
+
return {
|
|
3557
|
+
message: body.reason ? `Session cannot be answered from phase=${body.reason}.` : "Session does not have a pending question.",
|
|
3558
|
+
reason: body.reason ?? "session_not_respondable"
|
|
3559
|
+
};
|
|
3560
|
+
} catch {
|
|
3561
|
+
return null;
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
|
|
3505
3565
|
// src/commands/sandbox.ts
|
|
3506
3566
|
import { execFileSync } from "child_process";
|
|
3507
3567
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -5009,6 +5069,17 @@ addSendOptions(sessions.command("send").description("Send a message to an existi
|
|
|
5009
5069
|
addQaOptions(sessions.command("qa").description("Start a QA verification session for a GitHub pull request")).action(
|
|
5010
5070
|
(prUrl, options, command) => qaCommand(prUrl, options, command)
|
|
5011
5071
|
);
|
|
5072
|
+
sessions.command("respond").description("Answer a pending session question").argument("<session-id>", "Session ID").argument("[answer]", "Answer text, or '-' to read stdin").option("--answer-stdin", "Read answer from stdin").requiredOption("--question-id <id>", "Question ID from the question event").addHelpText(
|
|
5073
|
+
"after",
|
|
5074
|
+
`
|
|
5075
|
+
Examples:
|
|
5076
|
+
arcanist sessions respond <session-id> "Use PostgreSQL" --question-id q_123
|
|
5077
|
+
printf "Use PostgreSQL" | arcanist sessions respond <session-id> --question-id q_123 --answer-stdin --json
|
|
5078
|
+
|
|
5079
|
+
JSON:
|
|
5080
|
+
JSON mode returns the raw respond payload plus sessionId.
|
|
5081
|
+
`
|
|
5082
|
+
).action((sessionId, answer, options, command) => respondCommand(sessionId, answer, options, command));
|
|
5012
5083
|
sessions.command("stop").description("Stop the active run for a session").argument("<session-id>", "Session ID").addHelpText(
|
|
5013
5084
|
"after",
|
|
5014
5085
|
`
|