@tryarcanist/cli 0.1.80 → 0.1.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +139 -40
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -200,7 +200,7 @@ function validateApiUrl(url) {
|
|
|
200
200
|
// src/runtime.ts
|
|
201
201
|
import { randomUUID } from "crypto";
|
|
202
202
|
import { createInterface } from "readline/promises";
|
|
203
|
-
var ANSI_CONTROL_SEQUENCE = /\u001b\[[0-9
|
|
203
|
+
var ANSI_CONTROL_SEQUENCE = /\u001b\[[0-9:;<=>?]*[ -/]*[@-~]/g;
|
|
204
204
|
function getRuntimeOptions(command, options = {}) {
|
|
205
205
|
const globals = command?.optsWithGlobals?.();
|
|
206
206
|
const merged = { ...globals, ...options };
|
|
@@ -388,19 +388,31 @@ function sleep(ms) {
|
|
|
388
388
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
-
//
|
|
392
|
-
var
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
391
|
+
// ../../shared/session/phase.ts
|
|
392
|
+
var NON_REPO_SESSION_KINDS = /* @__PURE__ */ new Set(["terminal_bench", "swe_bench_pro", "swe_polybench"]);
|
|
393
|
+
function isRepoSession(sessionKind) {
|
|
394
|
+
return !sessionKind || !NON_REPO_SESSION_KINDS.has(sessionKind);
|
|
395
|
+
}
|
|
396
|
+
var TERMINAL_PHASES_ARRAY = [
|
|
397
397
|
"completed",
|
|
398
398
|
"blocked",
|
|
399
399
|
"failed",
|
|
400
400
|
"stopped",
|
|
401
|
-
"stopped_resumable",
|
|
402
401
|
"archived"
|
|
403
|
-
]
|
|
402
|
+
];
|
|
403
|
+
var TERMINAL_PHASES = new Set(TERMINAL_PHASES_ARRAY);
|
|
404
|
+
function isTerminalPhase(phase, sessionKind) {
|
|
405
|
+
if (TERMINAL_PHASES.has(phase)) return true;
|
|
406
|
+
return phase === "idle" && !isRepoSession(sessionKind);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// src/constants/watch.ts
|
|
410
|
+
var MIN_WATCH_POLL_INTERVAL_MS = 1;
|
|
411
|
+
var DEFAULT_WATCH_POLL_INTERVAL_MS = 1e3;
|
|
412
|
+
var WATCH_REPLAY_PAGE_SIZE = 200;
|
|
413
|
+
function isWatchTerminal(phase, sessionKind) {
|
|
414
|
+
return isTerminalPhase(phase, sessionKind);
|
|
415
|
+
}
|
|
404
416
|
|
|
405
417
|
// src/uploads.ts
|
|
406
418
|
import { readFile } from "fs/promises";
|
|
@@ -434,6 +446,27 @@ function validateUploadedName(name, kind) {
|
|
|
434
446
|
function trimNamedItems(items) {
|
|
435
447
|
return items.map((item) => ({ ...item, name: item.name.trim() }));
|
|
436
448
|
}
|
|
449
|
+
function validateUploadItems({
|
|
450
|
+
items,
|
|
451
|
+
existingNames = [],
|
|
452
|
+
kind,
|
|
453
|
+
max,
|
|
454
|
+
label,
|
|
455
|
+
normalizeItems,
|
|
456
|
+
validateItem
|
|
457
|
+
}) {
|
|
458
|
+
const normalized = normalizeItems ? normalizeItems(items) : [...items];
|
|
459
|
+
const deduped = deduplicateByName(normalized, existingNames);
|
|
460
|
+
const capacityError = checkUploadCapacity(existingNames.length, deduped.length, max, label);
|
|
461
|
+
if (capacityError) return fail(capacityError);
|
|
462
|
+
for (const item of deduped) {
|
|
463
|
+
const nameError = validateUploadedName(item.name, kind);
|
|
464
|
+
if (nameError) return fail(nameError);
|
|
465
|
+
const itemError = validateItem(item);
|
|
466
|
+
if (itemError) return fail(itemError);
|
|
467
|
+
}
|
|
468
|
+
return ok(deduped);
|
|
469
|
+
}
|
|
437
470
|
function hasNullBytes(content) {
|
|
438
471
|
return content.slice(0, 8192).includes("\0");
|
|
439
472
|
}
|
|
@@ -455,22 +488,24 @@ function deduplicateByName(items, existingNames = []) {
|
|
|
455
488
|
return deduped;
|
|
456
489
|
}
|
|
457
490
|
function validateUploadedFilePayload(files, existingNames = []) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
491
|
+
return validateUploadItems({
|
|
492
|
+
items: files,
|
|
493
|
+
existingNames,
|
|
494
|
+
kind: "file",
|
|
495
|
+
max: MAX_UPLOADED_FILES,
|
|
496
|
+
label: "uploaded files",
|
|
497
|
+
normalizeItems: trimNamedItems,
|
|
498
|
+
validateItem: (file) => {
|
|
499
|
+
const byteLength = new TextEncoder().encode(file.content).byteLength;
|
|
500
|
+
if (byteLength > MAX_UPLOADED_FILE_SIZE_BYTES) {
|
|
501
|
+
return `Uploaded file too large: ${file.name} (${byteLength} bytes, max ${MAX_UPLOADED_FILE_SIZE_BYTES})`;
|
|
502
|
+
}
|
|
503
|
+
if (hasNullBytes(file.content)) {
|
|
504
|
+
return `Binary files are not supported: ${file.name}`;
|
|
505
|
+
}
|
|
506
|
+
return null;
|
|
471
507
|
}
|
|
472
|
-
}
|
|
473
|
-
return ok(deduped);
|
|
508
|
+
});
|
|
474
509
|
}
|
|
475
510
|
|
|
476
511
|
// src/uploads.ts
|
|
@@ -938,6 +973,15 @@ function mergeMemoryRefs(ids, rawRefs) {
|
|
|
938
973
|
const ref = { id };
|
|
939
974
|
if (typeof record.path === "string" && record.path.trim()) ref.path = record.path.trim();
|
|
940
975
|
if (typeof record.title === "string" && record.title.trim()) ref.title = record.title.trim();
|
|
976
|
+
if (typeof record.selectionRank === "number") ref.selectionRank = record.selectionRank;
|
|
977
|
+
if (typeof record.selectionScore === "number") ref.selectionScore = record.selectionScore;
|
|
978
|
+
if (typeof record.reason === "string" && record.reason.trim()) ref.reason = record.reason.trim();
|
|
979
|
+
if (typeof record.expectedEffect === "string" && record.expectedEffect.trim()) {
|
|
980
|
+
ref.expectedEffect = record.expectedEffect.trim();
|
|
981
|
+
}
|
|
982
|
+
if (typeof record.observedEffect === "string" && record.observedEffect.trim()) {
|
|
983
|
+
ref.observedEffect = record.observedEffect.trim();
|
|
984
|
+
}
|
|
941
985
|
byId.set(id, ref);
|
|
942
986
|
}
|
|
943
987
|
}
|
|
@@ -1379,6 +1423,20 @@ function formatSessionErrorMessage(error, code) {
|
|
|
1379
1423
|
}
|
|
1380
1424
|
|
|
1381
1425
|
// src/utils/session-output.ts
|
|
1426
|
+
var VALID_PHASES = /* @__PURE__ */ new Set([
|
|
1427
|
+
"idle",
|
|
1428
|
+
"running",
|
|
1429
|
+
"waiting_for_input",
|
|
1430
|
+
"finalizing",
|
|
1431
|
+
"completed",
|
|
1432
|
+
"blocked",
|
|
1433
|
+
"failed",
|
|
1434
|
+
"stopped",
|
|
1435
|
+
"archived"
|
|
1436
|
+
]);
|
|
1437
|
+
var VALID_SANDBOX_SUBSTATES = /* @__PURE__ */ new Set(["creating", "reconnecting", "stopping", "none"]);
|
|
1438
|
+
var VALID_STOP_MODES = /* @__PURE__ */ new Set(["user", "resumable", "none"]);
|
|
1439
|
+
var VALID_FINALIZING_STEPS = /* @__PURE__ */ new Set(["post_execution", "publishing", "none"]);
|
|
1382
1440
|
function formatDate(value) {
|
|
1383
1441
|
const date = new Date(value);
|
|
1384
1442
|
if (Number.isNaN(date.getTime())) return value;
|
|
@@ -1578,9 +1636,21 @@ function parseSsePayload(payload) {
|
|
|
1578
1636
|
for (const message of messages) {
|
|
1579
1637
|
const data = message.data ? parseJsonObject(message.data) : {};
|
|
1580
1638
|
if (message.event === "status") {
|
|
1639
|
+
const phaseRaw = data.phase;
|
|
1640
|
+
const phase = typeof phaseRaw === "string" && VALID_PHASES.has(phaseRaw) ? phaseRaw : null;
|
|
1581
1641
|
const entry = {
|
|
1582
|
-
|
|
1642
|
+
phase
|
|
1583
1643
|
};
|
|
1644
|
+
if (typeof data.sandboxSubstate === "string" && VALID_SANDBOX_SUBSTATES.has(data.sandboxSubstate)) {
|
|
1645
|
+
entry.sandboxSubstate = data.sandboxSubstate;
|
|
1646
|
+
}
|
|
1647
|
+
if (typeof data.stopMode === "string" && VALID_STOP_MODES.has(data.stopMode)) {
|
|
1648
|
+
entry.stopMode = data.stopMode;
|
|
1649
|
+
}
|
|
1650
|
+
if (typeof data.finalizingStep === "string" && VALID_FINALIZING_STEPS.has(data.finalizingStep)) {
|
|
1651
|
+
entry.finalizingStep = data.finalizingStep;
|
|
1652
|
+
}
|
|
1653
|
+
if (typeof data.sessionKind === "string") entry.sessionKind = data.sessionKind;
|
|
1584
1654
|
if (typeof data.title === "string") entry.title = data.title;
|
|
1585
1655
|
if (typeof data.spawnDurationMs === "number" || data.spawnDurationMs === null) {
|
|
1586
1656
|
entry.spawnDurationMs = data.spawnDurationMs;
|
|
@@ -1711,10 +1781,16 @@ function parseNonNegativeInteger(raw, name, defaultValue) {
|
|
|
1711
1781
|
return Number(raw);
|
|
1712
1782
|
}
|
|
1713
1783
|
function formatStatusLine(status) {
|
|
1784
|
+
const label = status.phase ?? "unknown";
|
|
1785
|
+
const substate = [];
|
|
1786
|
+
if (status.sandboxSubstate && status.sandboxSubstate !== "none") substate.push(status.sandboxSubstate);
|
|
1787
|
+
if (status.stopMode && status.stopMode !== "none") substate.push(status.stopMode);
|
|
1788
|
+
if (status.finalizingStep && status.finalizingStep !== "none") substate.push(status.finalizingStep);
|
|
1789
|
+
const phaseLabel = substate.length > 0 ? `${label}/${substate.join("/")}` : label;
|
|
1714
1790
|
const details = [];
|
|
1715
1791
|
if (status.title) details.push(status.title);
|
|
1716
1792
|
if (status.spawnDurationMs != null) details.push(`spawn ${(status.spawnDurationMs / 1e3).toFixed(1)}s`);
|
|
1717
|
-
return details.length > 0 ? `[status] ${
|
|
1793
|
+
return details.length > 0 ? `[status] ${phaseLabel} | ${details.join(" | ")}` : `[status] ${phaseLabel}`;
|
|
1718
1794
|
}
|
|
1719
1795
|
async function fetchPromptLabels(config, sessionId) {
|
|
1720
1796
|
const data = await apiFetch(config, `/api/sessions/${sessionId}/prompts`);
|
|
@@ -1794,7 +1870,7 @@ async function watchCommand(sessionId, options, command) {
|
|
|
1794
1870
|
console.log(rendered.line);
|
|
1795
1871
|
}
|
|
1796
1872
|
if (receivedFullPage) continue;
|
|
1797
|
-
if (parsed.status?.
|
|
1873
|
+
if (parsed.status?.phase && isWatchTerminal(parsed.status.phase, parsed.status.sessionKind)) break;
|
|
1798
1874
|
await sleep(effectivePollIntervalMs);
|
|
1799
1875
|
}
|
|
1800
1876
|
} finally {
|
|
@@ -1827,12 +1903,13 @@ function buildPromptFailureError(sessionId, prompt, sessionUrl) {
|
|
|
1827
1903
|
hint: `Inspect with: arcanist sessions transcript ${sessionId}`
|
|
1828
1904
|
});
|
|
1829
1905
|
}
|
|
1830
|
-
function
|
|
1906
|
+
function extractSessionLifecycle(payload) {
|
|
1831
1907
|
const session = payload.session;
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1908
|
+
const root = session && typeof session === "object" ? session : payload;
|
|
1909
|
+
const phaseRaw = root.phase;
|
|
1910
|
+
const phase = typeof phaseRaw === "string" && VALID_PHASES.has(phaseRaw) ? phaseRaw : null;
|
|
1911
|
+
const sessionKind = typeof root.sessionKind === "string" ? root.sessionKind : null;
|
|
1912
|
+
return { phase, sessionKind };
|
|
1836
1913
|
}
|
|
1837
1914
|
async function fetchCreatedPromptStatus(config, sessionId, promptId) {
|
|
1838
1915
|
const promptList = await apiFetch(config, `/api/sessions/${sessionId}/prompts`);
|
|
@@ -1846,7 +1923,8 @@ async function waitForCreatedPromptToSettle(config, sessionId, promptId, pollInt
|
|
|
1846
1923
|
return createdPrompt;
|
|
1847
1924
|
}
|
|
1848
1925
|
const sessionPayload = await apiFetch(config, `/api/sessions/${sessionId}`);
|
|
1849
|
-
|
|
1926
|
+
const lifecycle = extractSessionLifecycle(sessionPayload);
|
|
1927
|
+
if (lifecycle.phase && isWatchTerminal(lifecycle.phase, lifecycle.sessionKind)) {
|
|
1850
1928
|
return fetchCreatedPromptStatus(config, sessionId, promptId);
|
|
1851
1929
|
}
|
|
1852
1930
|
await sleep(effectivePollIntervalMs);
|
|
@@ -2142,17 +2220,38 @@ async function usageCommand(sessionId, options, command) {
|
|
|
2142
2220
|
async function stopCommand(sessionId, options = {}, command) {
|
|
2143
2221
|
const runtime = getRuntimeOptions(command, options);
|
|
2144
2222
|
const config = requireConfig(runtime);
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2223
|
+
let status;
|
|
2224
|
+
try {
|
|
2225
|
+
const response = await apiFetch(config, `/api/sessions/${sessionId}/stop`, {
|
|
2226
|
+
method: "POST"
|
|
2227
|
+
});
|
|
2228
|
+
status = response.status ?? "stopping";
|
|
2229
|
+
} catch (err) {
|
|
2230
|
+
const parsed = parseStopBlocked(err);
|
|
2231
|
+
if (!parsed) throw err;
|
|
2232
|
+
status = parsed.reason;
|
|
2233
|
+
}
|
|
2149
2234
|
if (isJson(command, options)) {
|
|
2150
2235
|
writeJson({ sessionId, status });
|
|
2151
2236
|
return;
|
|
2152
2237
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
)
|
|
2238
|
+
if (status === "already_stopped") {
|
|
2239
|
+
console.log(`Session ${sessionId} is already stopped.`);
|
|
2240
|
+
} else if (status === "stopping" || status === "stopped") {
|
|
2241
|
+
console.log(`Stop requested for session ${sessionId}.`);
|
|
2242
|
+
} else {
|
|
2243
|
+
console.log(`Session ${sessionId} cannot be stopped from phase=${status}.`);
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
function parseStopBlocked(err) {
|
|
2247
|
+
if (!(err instanceof ApiError) || err.status !== 409) return null;
|
|
2248
|
+
try {
|
|
2249
|
+
const body = JSON.parse(err.body);
|
|
2250
|
+
if (body.error !== "session_not_stoppable") return null;
|
|
2251
|
+
return { reason: body.reason ?? "not_stoppable" };
|
|
2252
|
+
} catch {
|
|
2253
|
+
return null;
|
|
2254
|
+
}
|
|
2156
2255
|
}
|
|
2157
2256
|
|
|
2158
2257
|
// src/commands/test-creds.ts
|