commonswarm 0.1.33 → 0.1.35
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/cswarm.cjs +594 -289
- package/package.json +1 -1
package/cswarm.cjs
CHANGED
|
@@ -4381,8 +4381,8 @@ var require_RealtimeChannel = __commonJS({
|
|
|
4381
4381
|
}
|
|
4382
4382
|
/** @internal */
|
|
4383
4383
|
_notThisChannelEvent(event, ref) {
|
|
4384
|
-
const { close, error, leave, join:
|
|
4385
|
-
const events = [close, error, leave,
|
|
4384
|
+
const { close, error, leave, join: join18 } = constants_1.CHANNEL_EVENTS;
|
|
4385
|
+
const events = [close, error, leave, join18];
|
|
4386
4386
|
return ref && events.includes(event) && ref !== this.joinPush.ref;
|
|
4387
4387
|
}
|
|
4388
4388
|
/** @internal */
|
|
@@ -13523,7 +13523,7 @@ module.exports = __toCommonJS(cli_exports);
|
|
|
13523
13523
|
var import_node_crypto19 = require("node:crypto");
|
|
13524
13524
|
var import_node_fs7 = require("node:fs");
|
|
13525
13525
|
var import_promises11 = require("node:fs/promises");
|
|
13526
|
-
var
|
|
13526
|
+
var import_node_path20 = require("node:path");
|
|
13527
13527
|
var import_promises12 = require("node:readline/promises");
|
|
13528
13528
|
|
|
13529
13529
|
// src/cloud/auth.ts
|
|
@@ -29571,8 +29571,195 @@ async function runInboxFollow(options) {
|
|
|
29571
29571
|
}
|
|
29572
29572
|
}
|
|
29573
29573
|
|
|
29574
|
-
// src/cloud/
|
|
29574
|
+
// src/cloud/arrival-watch.ts
|
|
29575
|
+
var import_node_os4 = require("node:os");
|
|
29576
|
+
var import_node_path4 = require("node:path");
|
|
29575
29577
|
var UUID_RE8 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
29578
|
+
var CURSOR_MAX_BYTES = 4 * 1024;
|
|
29579
|
+
var ARRIVAL_SNIPPET_MAX = 180;
|
|
29580
|
+
var ARRIVAL_WATCH_POLL_MS = 25e3;
|
|
29581
|
+
function stateRoot() {
|
|
29582
|
+
return process.env.XDG_STATE_HOME ? (0, import_node_path4.join)(process.env.XDG_STATE_HOME, "cswarm", "arrival-cursors") : (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".cswarm", "arrival-cursors");
|
|
29583
|
+
}
|
|
29584
|
+
function arrivalCursorPath(target2, workspaceId2, principalId, root = stateRoot()) {
|
|
29585
|
+
return (0, import_node_path4.join)(
|
|
29586
|
+
root,
|
|
29587
|
+
`${target2.profileId}-${workspaceId2.toLowerCase()}-${principalId.toLowerCase()}.json`
|
|
29588
|
+
);
|
|
29589
|
+
}
|
|
29590
|
+
function parseCursor(raw, workspaceId2, principalId) {
|
|
29591
|
+
let value;
|
|
29592
|
+
try {
|
|
29593
|
+
value = JSON.parse(raw);
|
|
29594
|
+
} catch {
|
|
29595
|
+
throw new Error("stored arrival cursor is malformed");
|
|
29596
|
+
}
|
|
29597
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
29598
|
+
throw new Error("stored arrival cursor is malformed");
|
|
29599
|
+
}
|
|
29600
|
+
const row = value;
|
|
29601
|
+
const keys = Object.keys(row).sort();
|
|
29602
|
+
const cursor = row.cursor;
|
|
29603
|
+
if (keys.join(",") !== "cursor,principal_id,version,workspace_id" || row.version !== 1 || row.workspace_id !== workspaceId2.toLowerCase() || row.principal_id !== principalId.toLowerCase() || !(cursor === null || typeof cursor === "object" && !Array.isArray(cursor) && Object.keys(cursor).sort().join(",") === "created_at,id" && typeof cursor.created_at === "string" && Number.isFinite(Date.parse(cursor.created_at)) && typeof cursor.id === "string" && UUID_RE8.test(cursor.id))) {
|
|
29604
|
+
throw new Error("stored arrival cursor is malformed");
|
|
29605
|
+
}
|
|
29606
|
+
if (cursor === null) return null;
|
|
29607
|
+
return {
|
|
29608
|
+
created_at: cursor.created_at,
|
|
29609
|
+
id: cursor.id.toLowerCase()
|
|
29610
|
+
};
|
|
29611
|
+
}
|
|
29612
|
+
function fileArrivalCursorStore(options) {
|
|
29613
|
+
const workspaceId2 = options.workspaceId.toLowerCase();
|
|
29614
|
+
const principalId = options.principalId.toLowerCase();
|
|
29615
|
+
if (!UUID_RE8.test(workspaceId2) || !UUID_RE8.test(principalId)) {
|
|
29616
|
+
throw new Error("arrival cursor identity must use workspace and principal UUIDs");
|
|
29617
|
+
}
|
|
29618
|
+
const location2 = arrivalCursorPath(
|
|
29619
|
+
options.target,
|
|
29620
|
+
workspaceId2,
|
|
29621
|
+
principalId,
|
|
29622
|
+
options.stateDirectory
|
|
29623
|
+
);
|
|
29624
|
+
return {
|
|
29625
|
+
location: location2,
|
|
29626
|
+
async read() {
|
|
29627
|
+
const raw = await readSecureJsonFile(location2, CURSOR_MAX_BYTES);
|
|
29628
|
+
return raw === null ? void 0 : parseCursor(raw, workspaceId2, principalId);
|
|
29629
|
+
},
|
|
29630
|
+
async write(cursor) {
|
|
29631
|
+
const record = {
|
|
29632
|
+
version: 1,
|
|
29633
|
+
workspace_id: workspaceId2,
|
|
29634
|
+
principal_id: principalId,
|
|
29635
|
+
cursor
|
|
29636
|
+
};
|
|
29637
|
+
await writeSecureJsonFile(location2, JSON.stringify(record));
|
|
29638
|
+
}
|
|
29639
|
+
};
|
|
29640
|
+
}
|
|
29641
|
+
function arrivalSnippet(body) {
|
|
29642
|
+
const oneLine = body.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ").replace(/\s+/g, " ").trim();
|
|
29643
|
+
if (oneLine.length <= ARRIVAL_SNIPPET_MAX) return oneLine;
|
|
29644
|
+
return `${oneLine.slice(0, ARRIVAL_SNIPPET_MAX - 1).trimEnd()}\u2026`;
|
|
29645
|
+
}
|
|
29646
|
+
function arrivalReplyCommand(signalId, workspaceId2, target2) {
|
|
29647
|
+
return `cswarm reply ${signalId} "<answer>" --agent-token-stdin --url ${target2.url} --anon-key ${target2.anonKey} --workspace-id ${workspaceId2}`;
|
|
29648
|
+
}
|
|
29649
|
+
function arrivalNotification(signal, workspaceId2, target2) {
|
|
29650
|
+
return {
|
|
29651
|
+
type: "arrival",
|
|
29652
|
+
workspace_id: workspaceId2,
|
|
29653
|
+
signal_id: signal.id,
|
|
29654
|
+
sender: signal.from,
|
|
29655
|
+
sender_kind: signal.from_kind,
|
|
29656
|
+
kind: signal.kind,
|
|
29657
|
+
snippet: arrivalSnippet(signal.body),
|
|
29658
|
+
reply_command: arrivalReplyCommand(signal.id, workspaceId2, target2)
|
|
29659
|
+
};
|
|
29660
|
+
}
|
|
29661
|
+
function formatArrivalNotification(notification) {
|
|
29662
|
+
return `CommonSwarm from ${notification.sender_kind} ${notification.sender}: ${notification.snippet} \u2014 reply: ${notification.reply_command}`;
|
|
29663
|
+
}
|
|
29664
|
+
function cursorOf(signal) {
|
|
29665
|
+
return { created_at: signal.created_at, id: signal.id };
|
|
29666
|
+
}
|
|
29667
|
+
function assertCursorPage(page) {
|
|
29668
|
+
if (!page.capabilities.cursorAfter || page.legacyCursorFallback) {
|
|
29669
|
+
throw new Error(
|
|
29670
|
+
"arrival watch needs a read service with durable cursor support"
|
|
29671
|
+
);
|
|
29672
|
+
}
|
|
29673
|
+
}
|
|
29674
|
+
async function runArrivalWatch(options) {
|
|
29675
|
+
const pollMs = options.pollMs ?? ARRIVAL_WATCH_POLL_MS;
|
|
29676
|
+
const random = options.random ?? Math.random;
|
|
29677
|
+
let cursor = await options.store.read();
|
|
29678
|
+
let baseline = cursor === void 0;
|
|
29679
|
+
let attempt = 0;
|
|
29680
|
+
const cancelled = () => options.signal?.aborted === true;
|
|
29681
|
+
const wait = async (ms) => {
|
|
29682
|
+
if (options.sleep) {
|
|
29683
|
+
await options.sleep(ms);
|
|
29684
|
+
return;
|
|
29685
|
+
}
|
|
29686
|
+
await new Promise((resolve) => {
|
|
29687
|
+
let timer2;
|
|
29688
|
+
const finish = () => {
|
|
29689
|
+
if (timer2 !== void 0) clearTimeout(timer2);
|
|
29690
|
+
options.signal?.removeEventListener("abort", finish);
|
|
29691
|
+
resolve();
|
|
29692
|
+
};
|
|
29693
|
+
if (cancelled() || ms <= 0) {
|
|
29694
|
+
resolve();
|
|
29695
|
+
return;
|
|
29696
|
+
}
|
|
29697
|
+
options.signal?.addEventListener("abort", finish, { once: true });
|
|
29698
|
+
timer2 = setTimeout(finish, ms);
|
|
29699
|
+
});
|
|
29700
|
+
};
|
|
29701
|
+
while (!cancelled()) {
|
|
29702
|
+
try {
|
|
29703
|
+
const page = await options.readPage({
|
|
29704
|
+
after: cursor ?? null,
|
|
29705
|
+
baseline,
|
|
29706
|
+
limit: baseline ? 1 : SIGNAL_FOLLOW_PAGE_LIMIT
|
|
29707
|
+
});
|
|
29708
|
+
assertCursorPage(page);
|
|
29709
|
+
if (page.signals.some(
|
|
29710
|
+
(row) => row.workspace_id !== options.workspaceId || row.to_agent !== options.principalId
|
|
29711
|
+
)) {
|
|
29712
|
+
throw new Error(
|
|
29713
|
+
"arrival read returned a message directed to another workspace or agent"
|
|
29714
|
+
);
|
|
29715
|
+
}
|
|
29716
|
+
attempt = 0;
|
|
29717
|
+
if (baseline) {
|
|
29718
|
+
if (page.rawCount > 0 && page.nextCursor === null) {
|
|
29719
|
+
throw new Error("arrival baseline returned no safe terminal cursor");
|
|
29720
|
+
}
|
|
29721
|
+
cursor = page.nextCursor;
|
|
29722
|
+
await options.store.write(cursor);
|
|
29723
|
+
baseline = false;
|
|
29724
|
+
if (cancelled()) break;
|
|
29725
|
+
await wait(pollMs);
|
|
29726
|
+
continue;
|
|
29727
|
+
}
|
|
29728
|
+
for (const row of page.signals) {
|
|
29729
|
+
if (cancelled()) break;
|
|
29730
|
+
await options.emit(row);
|
|
29731
|
+
cursor = cursorOf(row);
|
|
29732
|
+
await options.store.write(cursor);
|
|
29733
|
+
}
|
|
29734
|
+
if (cancelled()) break;
|
|
29735
|
+
const fullPage = page.rawCount >= SIGNAL_FOLLOW_PAGE_LIMIT;
|
|
29736
|
+
await wait(fullPage ? 0 : pollMs);
|
|
29737
|
+
} catch (error) {
|
|
29738
|
+
if (cancelled()) break;
|
|
29739
|
+
const http = followHttpDetails(error);
|
|
29740
|
+
const retryable = isRetryableFollowError(error) || http?.status === 429 || http !== null && http.status >= 500;
|
|
29741
|
+
if (!retryable) {
|
|
29742
|
+
return {
|
|
29743
|
+
reason: "error",
|
|
29744
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
29745
|
+
};
|
|
29746
|
+
}
|
|
29747
|
+
attempt += 1;
|
|
29748
|
+
const delayMs = nextFollowBackoffMs(
|
|
29749
|
+
attempt,
|
|
29750
|
+
http?.retryAfterMs ?? null,
|
|
29751
|
+
random
|
|
29752
|
+
);
|
|
29753
|
+
const typed = error instanceof Error ? error : new Error(String(error));
|
|
29754
|
+
options.onRetry?.(typed, delayMs);
|
|
29755
|
+
await wait(delayMs);
|
|
29756
|
+
}
|
|
29757
|
+
}
|
|
29758
|
+
return { reason: "cancelled" };
|
|
29759
|
+
}
|
|
29760
|
+
|
|
29761
|
+
// src/cloud/delivery-receipts.ts
|
|
29762
|
+
var UUID_RE9 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
29576
29763
|
var DeliveryReceiptReadError = class extends Error {
|
|
29577
29764
|
constructor(code, message, status = null) {
|
|
29578
29765
|
super(message);
|
|
@@ -29591,7 +29778,7 @@ var ACK_OUTCOMES = /* @__PURE__ */ new Set([
|
|
|
29591
29778
|
"failed_terminal"
|
|
29592
29779
|
]);
|
|
29593
29780
|
function uuid3(value, field) {
|
|
29594
|
-
if (typeof value !== "string" || !
|
|
29781
|
+
if (typeof value !== "string" || !UUID_RE9.test(value)) {
|
|
29595
29782
|
throw new DeliveryReceiptReadError(
|
|
29596
29783
|
"protocol",
|
|
29597
29784
|
`delivery receipt returned a malformed ${field}`
|
|
@@ -29980,8 +30167,8 @@ function attachStderrTailExitObserver(child, onStderrTail) {
|
|
|
29980
30167
|
// src/host/opencode.ts
|
|
29981
30168
|
var import_node_fs3 = require("node:fs");
|
|
29982
30169
|
var import_promises4 = require("node:fs/promises");
|
|
29983
|
-
var
|
|
29984
|
-
var
|
|
30170
|
+
var import_node_os5 = require("node:os");
|
|
30171
|
+
var import_node_path6 = require("node:path");
|
|
29985
30172
|
|
|
29986
30173
|
// src/host/bounds.ts
|
|
29987
30174
|
var ACP_MAX_LINE_BYTES = 1048576;
|
|
@@ -30063,7 +30250,7 @@ function sanitizeChildEnv(parent = process.env) {
|
|
|
30063
30250
|
|
|
30064
30251
|
// src/host/session.ts
|
|
30065
30252
|
var import_node_fs2 = require("node:fs");
|
|
30066
|
-
var
|
|
30253
|
+
var import_node_path5 = require("node:path");
|
|
30067
30254
|
|
|
30068
30255
|
// src/host/permission.ts
|
|
30069
30256
|
function allowOnceOrDeny(request) {
|
|
@@ -30544,7 +30731,7 @@ function assertAbsoluteExistingCwd(cwd) {
|
|
|
30544
30731
|
if (!cwd || typeof cwd !== "string") {
|
|
30545
30732
|
throw new AcpProtocolError("cwd is required", "invalid_cwd");
|
|
30546
30733
|
}
|
|
30547
|
-
if (!(0,
|
|
30734
|
+
if (!(0, import_node_path5.isAbsolute)(cwd)) {
|
|
30548
30735
|
throw new AcpProtocolError("cwd must be an absolute path", "invalid_cwd");
|
|
30549
30736
|
}
|
|
30550
30737
|
let st;
|
|
@@ -31254,8 +31441,8 @@ function isProcessAlive(pid) {
|
|
|
31254
31441
|
}
|
|
31255
31442
|
}
|
|
31256
31443
|
function resolveOpenCodeExecutable(executable = "opencode", pathEnv) {
|
|
31257
|
-
if ((0,
|
|
31258
|
-
const abs = (0,
|
|
31444
|
+
if ((0, import_node_path6.isAbsolute)(executable) || executable.includes("/")) {
|
|
31445
|
+
const abs = (0, import_node_path6.resolve)(executable);
|
|
31259
31446
|
try {
|
|
31260
31447
|
(0, import_node_fs3.accessSync)(abs, import_node_fs3.constants.X_OK);
|
|
31261
31448
|
} catch {
|
|
@@ -31273,7 +31460,7 @@ function resolveOpenCodeExecutable(executable = "opencode", pathEnv) {
|
|
|
31273
31460
|
const pathValue = pathEnv ?? process.env.PATH ?? "";
|
|
31274
31461
|
for (const dir of pathValue.split(":")) {
|
|
31275
31462
|
if (!dir) continue;
|
|
31276
|
-
const candidate = (0,
|
|
31463
|
+
const candidate = (0, import_node_path6.join)(dir, executable);
|
|
31277
31464
|
try {
|
|
31278
31465
|
(0, import_node_fs3.accessSync)(candidate, import_node_fs3.constants.X_OK);
|
|
31279
31466
|
try {
|
|
@@ -31305,7 +31492,7 @@ function buildOpenCodeHomeOwner(options) {
|
|
|
31305
31492
|
};
|
|
31306
31493
|
}
|
|
31307
31494
|
async function writeOpenCodeHomeOwner(home, owner) {
|
|
31308
|
-
const path = (0,
|
|
31495
|
+
const path = (0, import_node_path6.join)(home, OPENCODE_HOME_OWNER_FILE);
|
|
31309
31496
|
await (0, import_promises4.writeFile)(path, `${JSON.stringify(owner)}
|
|
31310
31497
|
`, {
|
|
31311
31498
|
flag: "wx",
|
|
@@ -31314,7 +31501,7 @@ async function writeOpenCodeHomeOwner(home, owner) {
|
|
|
31314
31501
|
await (0, import_promises4.chmod)(path, 384);
|
|
31315
31502
|
}
|
|
31316
31503
|
async function readOpenCodeHomeOwner(home) {
|
|
31317
|
-
const path = (0,
|
|
31504
|
+
const path = (0, import_node_path6.join)(home, OPENCODE_HOME_OWNER_FILE);
|
|
31318
31505
|
let raw;
|
|
31319
31506
|
try {
|
|
31320
31507
|
raw = await (0, import_promises4.readFile)(path, "utf8");
|
|
@@ -31332,7 +31519,7 @@ async function readOpenCodeHomeOwner(home) {
|
|
|
31332
31519
|
}
|
|
31333
31520
|
}
|
|
31334
31521
|
async function releaseOpenCodeHome(home, instanceId) {
|
|
31335
|
-
if (!(0,
|
|
31522
|
+
if (!(0, import_node_path6.isAbsolute)(home)) return;
|
|
31336
31523
|
const owner = await readOpenCodeHomeOwner(home);
|
|
31337
31524
|
if (owner && owner.instanceId !== instanceId) {
|
|
31338
31525
|
return;
|
|
@@ -31457,15 +31644,15 @@ async function readValidatedOpenCodeAuth(sourceAuthPath, options) {
|
|
|
31457
31644
|
}
|
|
31458
31645
|
function resolveOpenCodeAuthSourcePath(parent = process.env) {
|
|
31459
31646
|
const xdgData = parent.XDG_DATA_HOME;
|
|
31460
|
-
if (typeof xdgData === "string" && (0,
|
|
31461
|
-
return (0,
|
|
31647
|
+
if (typeof xdgData === "string" && (0, import_node_path6.isAbsolute)(xdgData)) {
|
|
31648
|
+
return (0, import_node_path6.join)(xdgData, "opencode", "auth.json");
|
|
31462
31649
|
}
|
|
31463
|
-
const home = parent.HOME ?? (0,
|
|
31464
|
-
return (0,
|
|
31650
|
+
const home = parent.HOME ?? (0, import_node_os5.homedir)();
|
|
31651
|
+
return (0, import_node_path6.join)(home, ".local", "share", "opencode", "auth.json");
|
|
31465
31652
|
}
|
|
31466
31653
|
async function prepareOpenCodeIsolatedHome(options) {
|
|
31467
|
-
const home = options.home ?? await (0, import_promises4.mkdtemp)((0,
|
|
31468
|
-
if (!(0,
|
|
31654
|
+
const home = options.home ?? await (0, import_promises4.mkdtemp)((0, import_node_path6.join)((0, import_node_os5.tmpdir)(), OPENCODE_HOME_PREFIX));
|
|
31655
|
+
if (!(0, import_node_path6.isAbsolute)(home)) {
|
|
31469
31656
|
throw new AcpHostError(
|
|
31470
31657
|
"isolated_home_invalid",
|
|
31471
31658
|
"isolated OpenCode home must be absolute"
|
|
@@ -31473,21 +31660,21 @@ async function prepareOpenCodeIsolatedHome(options) {
|
|
|
31473
31660
|
}
|
|
31474
31661
|
await (0, import_promises4.chmod)(home, 448);
|
|
31475
31662
|
try {
|
|
31476
|
-
const xdgConfig = (0,
|
|
31477
|
-
const xdgData = (0,
|
|
31478
|
-
const xdgCache = (0,
|
|
31479
|
-
const xdgState = (0,
|
|
31663
|
+
const xdgConfig = (0, import_node_path6.join)(home, "xdg-config");
|
|
31664
|
+
const xdgData = (0, import_node_path6.join)(home, "xdg-data");
|
|
31665
|
+
const xdgCache = (0, import_node_path6.join)(home, "xdg-cache");
|
|
31666
|
+
const xdgState = (0, import_node_path6.join)(home, "xdg-state");
|
|
31480
31667
|
for (const dir of [xdgConfig, xdgData, xdgCache, xdgState]) {
|
|
31481
31668
|
await (0, import_promises4.mkdir)(dir, { recursive: true, mode: 448 });
|
|
31482
31669
|
await (0, import_promises4.chmod)(dir, 448);
|
|
31483
31670
|
}
|
|
31484
|
-
const configDir = (0,
|
|
31485
|
-
const dataDir = (0,
|
|
31671
|
+
const configDir = (0, import_node_path6.join)(xdgConfig, "opencode");
|
|
31672
|
+
const dataDir = (0, import_node_path6.join)(xdgData, "opencode");
|
|
31486
31673
|
await (0, import_promises4.mkdir)(configDir, { recursive: true, mode: 448 });
|
|
31487
31674
|
await (0, import_promises4.mkdir)(dataDir, { recursive: true, mode: 448 });
|
|
31488
31675
|
await (0, import_promises4.chmod)(configDir, 448);
|
|
31489
31676
|
await (0, import_promises4.chmod)(dataDir, 448);
|
|
31490
|
-
const configPath = (0,
|
|
31677
|
+
const configPath = (0, import_node_path6.join)(configDir, "opencode.json");
|
|
31491
31678
|
await (0, import_promises4.writeFile)(
|
|
31492
31679
|
configPath,
|
|
31493
31680
|
buildOpenCodeSafeConfigJson(
|
|
@@ -31501,7 +31688,7 @@ async function prepareOpenCodeIsolatedHome(options) {
|
|
|
31501
31688
|
allowMissing: options.allowMissingAuth === true
|
|
31502
31689
|
});
|
|
31503
31690
|
if (authBytes) {
|
|
31504
|
-
const destAuth = (0,
|
|
31691
|
+
const destAuth = (0, import_node_path6.join)(dataDir, "auth.json");
|
|
31505
31692
|
await (0, import_promises4.writeFile)(destAuth, authBytes, { flag: "wx", mode: 384 });
|
|
31506
31693
|
await (0, import_promises4.chmod)(destAuth, 384);
|
|
31507
31694
|
}
|
|
@@ -31516,7 +31703,7 @@ async function prepareOpenCodeIsolatedHome(options) {
|
|
|
31516
31703
|
}
|
|
31517
31704
|
}
|
|
31518
31705
|
function buildOpenCodeChildEnv(parent, home) {
|
|
31519
|
-
if (!(0,
|
|
31706
|
+
if (!(0, import_node_path6.isAbsolute)(home)) {
|
|
31520
31707
|
throw new AcpHostError(
|
|
31521
31708
|
"isolated_home_invalid",
|
|
31522
31709
|
"isolated OpenCode home must be absolute"
|
|
@@ -31526,20 +31713,20 @@ function buildOpenCodeChildEnv(parent, home) {
|
|
|
31526
31713
|
return {
|
|
31527
31714
|
...base,
|
|
31528
31715
|
HOME: home,
|
|
31529
|
-
XDG_CONFIG_HOME: (0,
|
|
31530
|
-
XDG_DATA_HOME: (0,
|
|
31531
|
-
XDG_CACHE_HOME: (0,
|
|
31532
|
-
XDG_STATE_HOME: (0,
|
|
31716
|
+
XDG_CONFIG_HOME: (0, import_node_path6.join)(home, "xdg-config"),
|
|
31717
|
+
XDG_DATA_HOME: (0, import_node_path6.join)(home, "xdg-data"),
|
|
31718
|
+
XDG_CACHE_HOME: (0, import_node_path6.join)(home, "xdg-cache"),
|
|
31719
|
+
XDG_STATE_HOME: (0, import_node_path6.join)(home, "xdg-state"),
|
|
31533
31720
|
// Measured 1.18.10: private home alone still merges project opencode.json.
|
|
31534
31721
|
OPENCODE_DISABLE_PROJECT_CONFIG: "1"
|
|
31535
31722
|
};
|
|
31536
31723
|
}
|
|
31537
31724
|
async function assertOpenCodeEffectiveConfig(options) {
|
|
31538
|
-
const hostile = await (0, import_promises4.mkdtemp)((0,
|
|
31725
|
+
const hostile = await (0, import_promises4.mkdtemp)((0, import_node_path6.join)((0, import_node_os5.tmpdir)(), "cswarm-opencode-hostile-"));
|
|
31539
31726
|
try {
|
|
31540
31727
|
await (0, import_promises4.chmod)(hostile, 448);
|
|
31541
31728
|
await (0, import_promises4.writeFile)(
|
|
31542
|
-
(0,
|
|
31729
|
+
(0, import_node_path6.join)(hostile, "opencode.json"),
|
|
31543
31730
|
`${JSON.stringify({
|
|
31544
31731
|
permission: {
|
|
31545
31732
|
bash: "allow",
|
|
@@ -31647,7 +31834,7 @@ async function sweepStaleOpenCodeHomes(options) {
|
|
|
31647
31834
|
const maxAgeMs = options?.maxAgeMs ?? STALE_HOME_MAX_AGE_MS;
|
|
31648
31835
|
const now = options?.now ?? Date.now();
|
|
31649
31836
|
const alive = options?.isAlive ?? isProcessAlive;
|
|
31650
|
-
const root = options?.root ?? (0,
|
|
31837
|
+
const root = options?.root ?? (0, import_node_os5.tmpdir)();
|
|
31651
31838
|
const selfUid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
31652
31839
|
let removed = 0;
|
|
31653
31840
|
let entries;
|
|
@@ -31658,7 +31845,7 @@ async function sweepStaleOpenCodeHomes(options) {
|
|
|
31658
31845
|
}
|
|
31659
31846
|
for (const name of entries) {
|
|
31660
31847
|
if (!name.startsWith(OPENCODE_HOME_PREFIX)) continue;
|
|
31661
|
-
const full = (0,
|
|
31848
|
+
const full = (0, import_node_path6.join)(root, name);
|
|
31662
31849
|
try {
|
|
31663
31850
|
const st = await (0, import_promises4.lstat)(full);
|
|
31664
31851
|
if (!st.isDirectory() || st.isSymbolicLink()) continue;
|
|
@@ -31836,7 +32023,7 @@ async function openOpenCodeAcpSession(options) {
|
|
|
31836
32023
|
// src/host/claude.ts
|
|
31837
32024
|
var import_node_child_process4 = require("node:child_process");
|
|
31838
32025
|
var import_node_fs4 = require("node:fs");
|
|
31839
|
-
var
|
|
32026
|
+
var import_node_path7 = require("node:path");
|
|
31840
32027
|
var CHILD_EXIT_WAIT_MS2 = 3e3;
|
|
31841
32028
|
var CHILD_KILL_WAIT_MS2 = 1e3;
|
|
31842
32029
|
var WINDOWS_NPM_SHIM_MAX_BYTES = 64 * 1024;
|
|
@@ -31856,11 +32043,11 @@ function isPackagedClaudeBridge(executable) {
|
|
|
31856
32043
|
function resolvePackagedClaudeBridge(pathEnv, platform = process.platform) {
|
|
31857
32044
|
const pathValue = pathEnv ?? process.env.PATH ?? "";
|
|
31858
32045
|
const names = platform === "win32" ? ["claude-agent-acp.cmd"] : ["claude-agent-acp"];
|
|
31859
|
-
for (const dir of pathValue.split(
|
|
32046
|
+
for (const dir of pathValue.split(import_node_path7.delimiter)) {
|
|
31860
32047
|
if (!dir) continue;
|
|
31861
32048
|
for (const name of names) {
|
|
31862
32049
|
try {
|
|
31863
|
-
const candidate = resolvedClaudeCandidate((0,
|
|
32050
|
+
const candidate = resolvedClaudeCandidate((0, import_node_path7.join)(dir, name), platform);
|
|
31864
32051
|
if (isPackagedClaudeBridge(candidate)) return candidate;
|
|
31865
32052
|
} catch {
|
|
31866
32053
|
}
|
|
@@ -31889,7 +32076,7 @@ function resolveWindowsNpmShim(shim) {
|
|
|
31889
32076
|
`unrecognized claude-agent-acp npm shim: ${shim}`
|
|
31890
32077
|
);
|
|
31891
32078
|
}
|
|
31892
|
-
const target2 = (0,
|
|
32079
|
+
const target2 = (0, import_node_path7.join)((0, import_node_path7.dirname)(shim), ...WINDOWS_NPM_ENTRYPOINT);
|
|
31893
32080
|
try {
|
|
31894
32081
|
(0, import_node_fs4.accessSync)(target2, import_node_fs4.constants.R_OK);
|
|
31895
32082
|
return (0, import_node_fs4.realpathSync)(target2);
|
|
@@ -31903,12 +32090,12 @@ function resolveWindowsNpmShim(shim) {
|
|
|
31903
32090
|
function resolvedClaudeCandidate(candidate, platform) {
|
|
31904
32091
|
(0, import_node_fs4.accessSync)(candidate, import_node_fs4.constants.X_OK);
|
|
31905
32092
|
const real = (0, import_node_fs4.realpathSync)(candidate);
|
|
31906
|
-
return platform === "win32" && (0,
|
|
32093
|
+
return platform === "win32" && (0, import_node_path7.extname)(real).toLowerCase() === ".cmd" ? resolveWindowsNpmShim(real) : real;
|
|
31907
32094
|
}
|
|
31908
32095
|
function resolveClaudeExecutable(executable = "claude-agent-acp", pathEnv, platform = process.platform) {
|
|
31909
|
-
if ((0,
|
|
31910
|
-
const abs = (0,
|
|
31911
|
-
const candidates = platform === "win32" && (0,
|
|
32096
|
+
if ((0, import_node_path7.isAbsolute)(executable) || executable.includes("/") || executable.includes("\\")) {
|
|
32097
|
+
const abs = (0, import_node_path7.resolve)(executable);
|
|
32098
|
+
const candidates = platform === "win32" && (0, import_node_path7.extname)(abs) === "" ? [`${abs}.cmd`] : [abs];
|
|
31912
32099
|
for (const candidate of candidates) {
|
|
31913
32100
|
try {
|
|
31914
32101
|
return resolvedClaudeCandidate(candidate, platform);
|
|
@@ -31919,11 +32106,11 @@ function resolveClaudeExecutable(executable = "claude-agent-acp", pathEnv, platf
|
|
|
31919
32106
|
throw new AcpHostError("executable_missing", `not executable: ${abs}`);
|
|
31920
32107
|
}
|
|
31921
32108
|
const pathValue = pathEnv ?? process.env.PATH ?? "";
|
|
31922
|
-
const names = platform === "win32" && (0,
|
|
31923
|
-
for (const dir of pathValue.split(
|
|
32109
|
+
const names = platform === "win32" && (0, import_node_path7.extname)(executable) === "" ? [`${executable}.cmd`] : [executable];
|
|
32110
|
+
for (const dir of pathValue.split(import_node_path7.delimiter)) {
|
|
31924
32111
|
if (!dir) continue;
|
|
31925
32112
|
for (const name of names) {
|
|
31926
|
-
const candidate = (0,
|
|
32113
|
+
const candidate = (0, import_node_path7.join)(dir, name);
|
|
31927
32114
|
try {
|
|
31928
32115
|
return resolvedClaudeCandidate(candidate, platform);
|
|
31929
32116
|
} catch (error) {
|
|
@@ -31937,7 +32124,7 @@ function resolveClaudeExecutable(executable = "claude-agent-acp", pathEnv, platf
|
|
|
31937
32124
|
);
|
|
31938
32125
|
}
|
|
31939
32126
|
function buildClaudeLaunch(executable, args, platform = process.platform) {
|
|
31940
|
-
return platform === "win32" && (0,
|
|
32127
|
+
return platform === "win32" && (0, import_node_path7.extname)(executable).toLowerCase() === ".js" ? { command: process.execPath, args: [executable, ...args] } : { command: executable, args: [...args] };
|
|
31941
32128
|
}
|
|
31942
32129
|
function parseClaudeVersionOutput(stdout) {
|
|
31943
32130
|
return parseProviderVersionOutput(stdout, /\bclaude-agent-acp\b/i);
|
|
@@ -32190,7 +32377,7 @@ async function openClaudeAcpSession(options) {
|
|
|
32190
32377
|
// src/host/codex.ts
|
|
32191
32378
|
var import_node_child_process5 = require("node:child_process");
|
|
32192
32379
|
var import_node_fs5 = require("node:fs");
|
|
32193
|
-
var
|
|
32380
|
+
var import_node_path8 = require("node:path");
|
|
32194
32381
|
var CHILD_EXIT_WAIT_MS3 = 3e3;
|
|
32195
32382
|
var CHILD_KILL_WAIT_MS3 = 1e3;
|
|
32196
32383
|
var WINDOWS_NPM_SHIM_MAX_BYTES2 = 64 * 1024;
|
|
@@ -32219,7 +32406,7 @@ function resolveWindowsNpmShim2(shim) {
|
|
|
32219
32406
|
`unrecognized codex-acp npm shim: ${shim}`
|
|
32220
32407
|
);
|
|
32221
32408
|
}
|
|
32222
|
-
const target2 = (0,
|
|
32409
|
+
const target2 = (0, import_node_path8.join)((0, import_node_path8.dirname)(shim), ...WINDOWS_NPM_ENTRYPOINT2);
|
|
32223
32410
|
try {
|
|
32224
32411
|
(0, import_node_fs5.accessSync)(target2, import_node_fs5.constants.R_OK);
|
|
32225
32412
|
return (0, import_node_fs5.realpathSync)(target2);
|
|
@@ -32233,12 +32420,12 @@ function resolveWindowsNpmShim2(shim) {
|
|
|
32233
32420
|
function resolvedCodexCandidate(candidate, platform) {
|
|
32234
32421
|
(0, import_node_fs5.accessSync)(candidate, import_node_fs5.constants.X_OK);
|
|
32235
32422
|
const real = (0, import_node_fs5.realpathSync)(candidate);
|
|
32236
|
-
return platform === "win32" && (0,
|
|
32423
|
+
return platform === "win32" && (0, import_node_path8.extname)(real).toLowerCase() === ".cmd" ? resolveWindowsNpmShim2(real) : real;
|
|
32237
32424
|
}
|
|
32238
32425
|
function resolveCodexExecutable(executable = "codex-acp", pathEnv, platform = process.platform) {
|
|
32239
|
-
if ((0,
|
|
32240
|
-
const abs = (0,
|
|
32241
|
-
const candidates = platform === "win32" && (0,
|
|
32426
|
+
if ((0, import_node_path8.isAbsolute)(executable) || executable.includes("/") || executable.includes("\\")) {
|
|
32427
|
+
const abs = (0, import_node_path8.resolve)(executable);
|
|
32428
|
+
const candidates = platform === "win32" && (0, import_node_path8.extname)(abs) === "" ? [`${abs}.cmd`] : [abs];
|
|
32242
32429
|
for (const candidate of candidates) {
|
|
32243
32430
|
try {
|
|
32244
32431
|
return resolvedCodexCandidate(candidate, platform);
|
|
@@ -32249,11 +32436,11 @@ function resolveCodexExecutable(executable = "codex-acp", pathEnv, platform = pr
|
|
|
32249
32436
|
throw new AcpHostError("executable_missing", `not executable: ${abs}`);
|
|
32250
32437
|
}
|
|
32251
32438
|
const pathValue = pathEnv ?? process.env.PATH ?? "";
|
|
32252
|
-
const names = platform === "win32" && (0,
|
|
32253
|
-
for (const dir of pathValue.split(
|
|
32439
|
+
const names = platform === "win32" && (0, import_node_path8.extname)(executable) === "" ? [`${executable}.cmd`] : [executable];
|
|
32440
|
+
for (const dir of pathValue.split(import_node_path8.delimiter)) {
|
|
32254
32441
|
if (!dir) continue;
|
|
32255
32442
|
for (const name of names) {
|
|
32256
|
-
const candidate = (0,
|
|
32443
|
+
const candidate = (0, import_node_path8.join)(dir, name);
|
|
32257
32444
|
try {
|
|
32258
32445
|
return resolvedCodexCandidate(candidate, platform);
|
|
32259
32446
|
} catch (error) {
|
|
@@ -32267,7 +32454,7 @@ function resolveCodexExecutable(executable = "codex-acp", pathEnv, platform = pr
|
|
|
32267
32454
|
);
|
|
32268
32455
|
}
|
|
32269
32456
|
function buildCodexLaunch(executable, args, platform = process.platform) {
|
|
32270
|
-
return platform === "win32" && (0,
|
|
32457
|
+
return platform === "win32" && (0, import_node_path8.extname)(executable).toLowerCase() === ".js" ? { command: process.execPath, args: [executable, ...args] } : { command: executable, args: [...args] };
|
|
32271
32458
|
}
|
|
32272
32459
|
function parseCodexVersionOutput(stdout) {
|
|
32273
32460
|
return parseProviderVersionOutput(stdout, /@agentclientprotocol\/codex-acp\b/i);
|
|
@@ -32481,7 +32668,7 @@ async function resolveBudgetAndPrompt(session, prompt, budget) {
|
|
|
32481
32668
|
}
|
|
32482
32669
|
|
|
32483
32670
|
// src/listener/engine.ts
|
|
32484
|
-
var
|
|
32671
|
+
var UUID_RE10 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
32485
32672
|
var TERMINAL_STATES = /* @__PURE__ */ new Set(["done", "expired", "failed"]);
|
|
32486
32673
|
var REPLY_MAX_CODE_UNITS = 2e3;
|
|
32487
32674
|
var TRUNCATION_SUFFIX = "\n[Reply truncated by CommonSwarm]";
|
|
@@ -32489,7 +32676,7 @@ var UNSAFE_CONTROLS_RE = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u
|
|
|
32489
32676
|
var LISTENER_MAX_PROMPT_ATTEMPTS = 3;
|
|
32490
32677
|
var LISTENER_MAX_POST_ATTEMPTS = 5;
|
|
32491
32678
|
function listenerReplyCommandId(signalId, effectOrdinal = 0) {
|
|
32492
|
-
if (!
|
|
32679
|
+
if (!UUID_RE10.test(signalId)) {
|
|
32493
32680
|
throw new Error("listener signal id must be a UUID");
|
|
32494
32681
|
}
|
|
32495
32682
|
if (!Number.isSafeInteger(effectOrdinal) || effectOrdinal < 0) {
|
|
@@ -32950,10 +33137,10 @@ var ListenerEngine = class {
|
|
|
32950
33137
|
|
|
32951
33138
|
// src/listener/file-store.ts
|
|
32952
33139
|
var import_node_crypto13 = require("node:crypto");
|
|
32953
|
-
var
|
|
32954
|
-
var
|
|
33140
|
+
var import_node_os6 = require("node:os");
|
|
33141
|
+
var import_node_path9 = require("node:path");
|
|
32955
33142
|
var import_node_util = require("node:util");
|
|
32956
|
-
var
|
|
33143
|
+
var UUID_RE11 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
32957
33144
|
var COMMAND_ID_RE2 = /^[A-Za-z0-9_-]{8,72}$/;
|
|
32958
33145
|
var MAX_EFFECT_BYTES = 1024 * 1024;
|
|
32959
33146
|
var STATES = /* @__PURE__ */ new Set([
|
|
@@ -32988,10 +33175,10 @@ var V1_EFFECT_KEYS = /* @__PURE__ */ new Set([
|
|
|
32988
33175
|
]);
|
|
32989
33176
|
var V2_EFFECT_KEYS = /* @__PURE__ */ new Set([...V1_EFFECT_KEYS, "signalKind"]);
|
|
32990
33177
|
function defaultListenerStateDirectory() {
|
|
32991
|
-
return process.env.XDG_STATE_HOME ? (0,
|
|
33178
|
+
return process.env.XDG_STATE_HOME ? (0, import_node_path9.join)(process.env.XDG_STATE_HOME, "cswarm", "listeners") : (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".cswarm", "listeners");
|
|
32992
33179
|
}
|
|
32993
33180
|
function listenerInstanceKey(input) {
|
|
32994
|
-
if (!
|
|
33181
|
+
if (!UUID_RE11.test(input.workspaceId) || !UUID_RE11.test(input.principalId)) {
|
|
32995
33182
|
throw new Error("listener workspace and principal ids must be UUIDs");
|
|
32996
33183
|
}
|
|
32997
33184
|
if (!input.profileId || input.profileId.includes("\0")) {
|
|
@@ -33023,7 +33210,7 @@ function parseListenerEffectRecord(raw, expectedId) {
|
|
|
33023
33210
|
throw new Error("stored listener effect is malformed");
|
|
33024
33211
|
}
|
|
33025
33212
|
const row = value;
|
|
33026
|
-
if (typeof row.version !== "number" || row.version !== 1 && row.version !== 2 || typeof row.signalId !== "string" || row.signalId.toLowerCase() !== expectedId || !
|
|
33213
|
+
if (typeof row.version !== "number" || row.version !== 1 && row.version !== 2 || typeof row.signalId !== "string" || row.signalId.toLowerCase() !== expectedId || !UUID_RE11.test(row.signalId)) {
|
|
33027
33214
|
throw new Error("stored listener effect is malformed");
|
|
33028
33215
|
}
|
|
33029
33216
|
if (row.version === 1) {
|
|
@@ -33040,7 +33227,7 @@ function upcastV1Ask(row) {
|
|
|
33040
33227
|
if (row.effectOrdinal !== 0 || typeof row.commandId !== "string" || !COMMAND_ID_RE2.test(row.commandId) || typeof row.askBody !== "string" || row.askBody.length < 1 || typeof row.askUntil !== "string" || !Number.isFinite(Date.parse(row.askUntil)) || typeof row.senderOwnerRelation !== "string" || !RELATIONS.has(row.senderOwnerRelation) || typeof row.state !== "string" || !STATES.has(row.state) || !integer(row.promptAttempts) || !integer(row.postAttempts) || !nullableString(row.replyBody, 2e3) || typeof row.replyTruncated !== "boolean" || !nullableString(row.replySignalId, 64) || !nullableString(row.failureCode, 96) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt))) {
|
|
33041
33228
|
throw new Error("stored listener effect is malformed");
|
|
33042
33229
|
}
|
|
33043
|
-
if (row.replySignalId !== null && !
|
|
33230
|
+
if (row.replySignalId !== null && !UUID_RE11.test(row.replySignalId)) {
|
|
33044
33231
|
throw new Error("stored listener effect is malformed");
|
|
33045
33232
|
}
|
|
33046
33233
|
return {
|
|
@@ -33068,7 +33255,7 @@ function parseV2Record(row) {
|
|
|
33068
33255
|
throw new Error("stored listener effect is malformed");
|
|
33069
33256
|
}
|
|
33070
33257
|
if (signalKind2 === "note") {
|
|
33071
|
-
if (typeof row.commandId !== "string" || row.commandId !== "" || row.state !== "observed" || row.promptAttempts !== 0 || row.postAttempts !== 0 || row.replyBody !== null || row.replyTruncated !== false || row.replySignalId !== null || row.failureCode !== null) {
|
|
33258
|
+
if (typeof row.commandId !== "string" || row.commandId !== "" || row.state !== "observed" && row.state !== "routed_main" || row.promptAttempts !== 0 || row.postAttempts !== 0 || row.replyBody !== null || row.replyTruncated !== false || row.replySignalId !== null || row.failureCode !== null) {
|
|
33072
33259
|
throw new Error("stored listener effect is malformed");
|
|
33073
33260
|
}
|
|
33074
33261
|
} else if (row.state === "routed_main") {
|
|
@@ -33079,7 +33266,7 @@ function parseV2Record(row) {
|
|
|
33079
33266
|
if (typeof row.commandId !== "string" || !COMMAND_ID_RE2.test(row.commandId) || row.state === "observed") {
|
|
33080
33267
|
throw new Error("stored listener effect is malformed");
|
|
33081
33268
|
}
|
|
33082
|
-
if (row.replySignalId !== null && !
|
|
33269
|
+
if (row.replySignalId !== null && !UUID_RE11.test(row.replySignalId)) {
|
|
33083
33270
|
throw new Error("stored listener effect is malformed");
|
|
33084
33271
|
}
|
|
33085
33272
|
}
|
|
@@ -33103,7 +33290,7 @@ function parseV2Record(row) {
|
|
|
33103
33290
|
};
|
|
33104
33291
|
}
|
|
33105
33292
|
function newObservedNoteRecord(input) {
|
|
33106
|
-
if (!
|
|
33293
|
+
if (!UUID_RE11.test(input.signalId)) {
|
|
33107
33294
|
throw new Error("listener note signal id must be a UUID");
|
|
33108
33295
|
}
|
|
33109
33296
|
if (input.body.length < 1) {
|
|
@@ -33137,11 +33324,11 @@ function newObservedNoteRecord(input) {
|
|
|
33137
33324
|
updatedAt: input.updatedAt
|
|
33138
33325
|
};
|
|
33139
33326
|
}
|
|
33140
|
-
function
|
|
33327
|
+
function newRoutedMainRecord(input) {
|
|
33141
33328
|
const base = newObservedNoteRecord(input);
|
|
33142
33329
|
return {
|
|
33143
33330
|
...base,
|
|
33144
|
-
signalKind:
|
|
33331
|
+
signalKind: input.signalKind,
|
|
33145
33332
|
state: "routed_main"
|
|
33146
33333
|
};
|
|
33147
33334
|
}
|
|
@@ -33210,16 +33397,16 @@ var FileListenerEffectStore = class {
|
|
|
33210
33397
|
effectsDirectory;
|
|
33211
33398
|
constructor(options) {
|
|
33212
33399
|
const root = options.stateDirectory ?? defaultListenerStateDirectory();
|
|
33213
|
-
if (!(0,
|
|
33400
|
+
if (!(0, import_node_path9.isAbsolute)(root)) {
|
|
33214
33401
|
throw new Error("listener state directory must be absolute");
|
|
33215
33402
|
}
|
|
33216
|
-
this.instanceDirectory = (0,
|
|
33217
|
-
this.effectsDirectory = (0,
|
|
33403
|
+
this.instanceDirectory = (0, import_node_path9.join)(root, listenerInstanceKey(options));
|
|
33404
|
+
this.effectsDirectory = (0, import_node_path9.join)(this.instanceDirectory, "effects");
|
|
33218
33405
|
}
|
|
33219
33406
|
async read(signalId) {
|
|
33220
33407
|
const id = this.checkedId(signalId);
|
|
33221
33408
|
const raw = await readSecureJsonFile(
|
|
33222
|
-
(0,
|
|
33409
|
+
(0, import_node_path9.join)(this.effectsDirectory, `${id}.json`),
|
|
33223
33410
|
MAX_EFFECT_BYTES
|
|
33224
33411
|
);
|
|
33225
33412
|
return raw === null ? null : parseListenerEffectRecord(raw, id);
|
|
@@ -33232,12 +33419,12 @@ var FileListenerEffectStore = class {
|
|
|
33232
33419
|
throw new Error("listener effect is too large");
|
|
33233
33420
|
}
|
|
33234
33421
|
await writeSecureJsonFile(
|
|
33235
|
-
(0,
|
|
33422
|
+
(0, import_node_path9.join)(this.effectsDirectory, `${id}.json`),
|
|
33236
33423
|
serialized
|
|
33237
33424
|
);
|
|
33238
33425
|
}
|
|
33239
33426
|
checkedId(signalId) {
|
|
33240
|
-
if (!
|
|
33427
|
+
if (!UUID_RE11.test(signalId)) {
|
|
33241
33428
|
throw new Error("listener signal id must be a UUID");
|
|
33242
33429
|
}
|
|
33243
33430
|
return signalId.toLowerCase();
|
|
@@ -33246,18 +33433,18 @@ var FileListenerEffectStore = class {
|
|
|
33246
33433
|
|
|
33247
33434
|
// src/listener/grok-model.ts
|
|
33248
33435
|
var import_promises5 = require("node:fs/promises");
|
|
33249
|
-
var
|
|
33250
|
-
var
|
|
33436
|
+
var import_node_os7 = require("node:os");
|
|
33437
|
+
var import_node_path11 = require("node:path");
|
|
33251
33438
|
|
|
33252
33439
|
// src/host/grok.ts
|
|
33253
33440
|
var import_node_child_process6 = require("node:child_process");
|
|
33254
33441
|
var import_node_fs6 = require("node:fs");
|
|
33255
|
-
var
|
|
33442
|
+
var import_node_path10 = require("node:path");
|
|
33256
33443
|
var CHILD_EXIT_WAIT_MS4 = 3e3;
|
|
33257
33444
|
var CHILD_KILL_WAIT_MS4 = 1e3;
|
|
33258
33445
|
function resolveGrokExecutable(executable = "grok") {
|
|
33259
|
-
if ((0,
|
|
33260
|
-
const abs = (0,
|
|
33446
|
+
if ((0, import_node_path10.isAbsolute)(executable) || executable.includes("/")) {
|
|
33447
|
+
const abs = (0, import_node_path10.resolve)(executable);
|
|
33261
33448
|
try {
|
|
33262
33449
|
(0, import_node_fs6.accessSync)(abs, import_node_fs6.constants.X_OK);
|
|
33263
33450
|
} catch {
|
|
@@ -33502,11 +33689,11 @@ var GrokListenerModel = class {
|
|
|
33502
33689
|
async validateLocalAuth() {
|
|
33503
33690
|
if (this.options.open) return;
|
|
33504
33691
|
const parent = this.options.env ?? process.env;
|
|
33505
|
-
const sourceHome = parent.GROK_HOME ?? (0,
|
|
33506
|
-
if (!(0,
|
|
33692
|
+
const sourceHome = parent.GROK_HOME ?? (0, import_node_path11.join)(parent.HOME ?? (0, import_node_os7.homedir)(), ".grok");
|
|
33693
|
+
if (!(0, import_node_path11.isAbsolute)(sourceHome)) {
|
|
33507
33694
|
throw new Error("Grok home must be an absolute path");
|
|
33508
33695
|
}
|
|
33509
|
-
const sourceAuth = (0,
|
|
33696
|
+
const sourceAuth = (0, import_node_path11.join)(sourceHome, "auth.json");
|
|
33510
33697
|
let info;
|
|
33511
33698
|
try {
|
|
33512
33699
|
info = await (0, import_promises5.lstat)(sourceAuth);
|
|
@@ -33556,7 +33743,7 @@ var GrokListenerModel = class {
|
|
|
33556
33743
|
// src/listener/opencode-model.ts
|
|
33557
33744
|
var import_node_crypto14 = require("node:crypto");
|
|
33558
33745
|
var import_promises6 = require("node:fs/promises");
|
|
33559
|
-
var
|
|
33746
|
+
var import_node_path12 = require("node:path");
|
|
33560
33747
|
function asError(error) {
|
|
33561
33748
|
return error instanceof Error ? error : new Error(String(error));
|
|
33562
33749
|
}
|
|
@@ -33567,7 +33754,7 @@ var OpenCodeListenerModel = class {
|
|
|
33567
33754
|
this.openSession = options.open ?? openOpenCodeAcpSession;
|
|
33568
33755
|
this.prepareHome = options.prepareHome ?? prepareOpenCodeIsolatedHome;
|
|
33569
33756
|
this.prepareWorkerCwd = options.prepareWorkerCwd ?? (async (home) => {
|
|
33570
|
-
const cwd = await (0, import_promises6.mkdtemp)((0,
|
|
33757
|
+
const cwd = await (0, import_promises6.mkdtemp)((0, import_node_path12.join)(home, "canary-cwd-"));
|
|
33571
33758
|
await (0, import_promises6.chmod)(cwd, 448);
|
|
33572
33759
|
return cwd;
|
|
33573
33760
|
});
|
|
@@ -33989,8 +34176,8 @@ var OpenCodeListenerModel = class {
|
|
|
33989
34176
|
// src/listener/claude-model.ts
|
|
33990
34177
|
var import_node_crypto15 = require("node:crypto");
|
|
33991
34178
|
var import_promises7 = require("node:fs/promises");
|
|
33992
|
-
var
|
|
33993
|
-
var
|
|
34179
|
+
var import_node_os8 = require("node:os");
|
|
34180
|
+
var import_node_path13 = require("node:path");
|
|
33994
34181
|
var ClaudeListenerClosedDuringOpen = class extends Error {
|
|
33995
34182
|
constructor() {
|
|
33996
34183
|
super("listener model closed while the Claude worker was opening");
|
|
@@ -34128,8 +34315,8 @@ var ClaudeListenerModel = class {
|
|
|
34128
34315
|
}
|
|
34129
34316
|
/** Force Claude's measured Write permission path without changing worker cwd. */
|
|
34130
34317
|
async enablePromptsAfterClaudeCanary(handle) {
|
|
34131
|
-
const sentinelPath = (0,
|
|
34132
|
-
(0,
|
|
34318
|
+
const sentinelPath = (0, import_node_path13.join)(
|
|
34319
|
+
(0, import_node_os8.tmpdir)(),
|
|
34133
34320
|
`cswarm-claude-permission-canary-${process.pid}-${(0, import_node_crypto15.randomUUID)()}`
|
|
34134
34321
|
);
|
|
34135
34322
|
let sentinelCreated = false;
|
|
@@ -34158,8 +34345,8 @@ var ClaudeListenerModel = class {
|
|
|
34158
34345
|
// src/listener/codex-model.ts
|
|
34159
34346
|
var import_node_crypto16 = require("node:crypto");
|
|
34160
34347
|
var import_promises8 = require("node:fs/promises");
|
|
34161
|
-
var
|
|
34162
|
-
var
|
|
34348
|
+
var import_node_os9 = require("node:os");
|
|
34349
|
+
var import_node_path14 = require("node:path");
|
|
34163
34350
|
var CodexListenerClosedDuringOpen = class extends Error {
|
|
34164
34351
|
constructor() {
|
|
34165
34352
|
super("listener model closed while the Codex worker was opening");
|
|
@@ -34297,8 +34484,8 @@ var CodexListenerModel = class {
|
|
|
34297
34484
|
}
|
|
34298
34485
|
/** Force Codex's measured shell permission path without changing worker cwd. */
|
|
34299
34486
|
async enablePromptsAfterCodexCanary(handle) {
|
|
34300
|
-
const sentinelPath = (0,
|
|
34301
|
-
(0,
|
|
34487
|
+
const sentinelPath = (0, import_node_path14.join)(
|
|
34488
|
+
(0, import_node_os9.tmpdir)(),
|
|
34302
34489
|
`cswarm-codex-permission-canary-${process.pid}-${(0, import_node_crypto16.randomUUID)()}`
|
|
34303
34490
|
);
|
|
34304
34491
|
let sentinelCreated = false;
|
|
@@ -34328,7 +34515,7 @@ var CodexListenerModel = class {
|
|
|
34328
34515
|
var import_node_crypto17 = require("node:crypto");
|
|
34329
34516
|
|
|
34330
34517
|
// src/cloud/delivery.ts
|
|
34331
|
-
var
|
|
34518
|
+
var UUID_RE12 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
34332
34519
|
var RFC3339_TIMESTAMP_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|([+-]\d{2}):(\d{2}))$/i;
|
|
34333
34520
|
var DELIVERY_KINDS = /* @__PURE__ */ new Set(["ask", "note"]);
|
|
34334
34521
|
var SENDER_OWNER_RELATIONS2 = /* @__PURE__ */ new Set([
|
|
@@ -34415,7 +34602,7 @@ var DeliveryProtocolError = class extends Error {
|
|
|
34415
34602
|
}
|
|
34416
34603
|
};
|
|
34417
34604
|
function checkedUuid3(value, field) {
|
|
34418
|
-
if (typeof value !== "string" || !
|
|
34605
|
+
if (typeof value !== "string" || !UUID_RE12.test(value)) {
|
|
34419
34606
|
throw new DeliveryProtocolError(
|
|
34420
34607
|
`delivery response returned a malformed ${field}`
|
|
34421
34608
|
);
|
|
@@ -34526,7 +34713,7 @@ function checkedClaimCapabilities(value) {
|
|
|
34526
34713
|
}
|
|
34527
34714
|
function checkedOptionalUuidArray(value, field) {
|
|
34528
34715
|
if (value === void 0) return;
|
|
34529
|
-
if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !
|
|
34716
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !UUID_RE12.test(item))) {
|
|
34530
34717
|
throw new DeliveryProtocolError(
|
|
34531
34718
|
`delivery response returned a malformed ${field}`
|
|
34532
34719
|
);
|
|
@@ -34673,7 +34860,7 @@ function checkedCommandId(value) {
|
|
|
34673
34860
|
return value;
|
|
34674
34861
|
}
|
|
34675
34862
|
function checkedUuidRequest(value, field) {
|
|
34676
|
-
if (!
|
|
34863
|
+
if (!UUID_RE12.test(value)) {
|
|
34677
34864
|
throw new Error(`${field} must be a UUID for an agent delivery command`);
|
|
34678
34865
|
}
|
|
34679
34866
|
}
|
|
@@ -34909,8 +35096,8 @@ var DeliveryCommandClient = class {
|
|
|
34909
35096
|
};
|
|
34910
35097
|
|
|
34911
35098
|
// src/listener/main-routing.ts
|
|
34912
|
-
var
|
|
34913
|
-
var
|
|
35099
|
+
var import_node_path15 = require("node:path");
|
|
35100
|
+
var UUID_RE13 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
34914
35101
|
var MAX_QUEUE_BYTES = 1024 * 1024;
|
|
34915
35102
|
var QUEUE_FILE = "pending-for-main.json";
|
|
34916
35103
|
var QUEUE_LOCK = "pending-for-main";
|
|
@@ -34965,7 +35152,7 @@ function parseEntry(value) {
|
|
|
34965
35152
|
if (Object.keys(row).some((key2) => !allowed.has(key2))) {
|
|
34966
35153
|
throw new Error("stored pending-for-main entry is malformed");
|
|
34967
35154
|
}
|
|
34968
|
-
if (typeof row.signalId !== "string" || !
|
|
35155
|
+
if (typeof row.signalId !== "string" || !UUID_RE13.test(row.signalId) || typeof row.workspaceId !== "string" || !UUID_RE13.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE13.test(row.principalId) || typeof row.fromId !== "string" || !UUID_RE13.test(row.fromId) || row.fromKind !== "user" && row.fromKind !== "agent" || !(row.kind === void 0 || row.kind === "ask" || row.kind === "note") || !(row.senderName === null || typeof row.senderName === "string" && row.senderName.length <= 200) || typeof row.body !== "string" || row.body.length < 1 || !checkedTimestamp2(row.createdAt) || !checkedTimestamp2(row.queuedAt) || !(row.observationPending === void 0 || row.observationPending === true)) {
|
|
34969
35156
|
throw new Error("stored pending-for-main entry is malformed");
|
|
34970
35157
|
}
|
|
34971
35158
|
return {
|
|
@@ -35008,11 +35195,11 @@ var FilePendingMainQueue = class {
|
|
|
35008
35195
|
path;
|
|
35009
35196
|
directory;
|
|
35010
35197
|
constructor(instanceDirectory) {
|
|
35011
|
-
if (!(0,
|
|
35198
|
+
if (!(0, import_node_path15.isAbsolute)(instanceDirectory)) {
|
|
35012
35199
|
throw new Error("pending-for-main directory must be absolute");
|
|
35013
35200
|
}
|
|
35014
35201
|
this.directory = instanceDirectory;
|
|
35015
|
-
this.path = (0,
|
|
35202
|
+
this.path = (0, import_node_path15.join)(instanceDirectory, QUEUE_FILE);
|
|
35016
35203
|
}
|
|
35017
35204
|
async readUnlocked() {
|
|
35018
35205
|
const raw = await readSecureJsonFile(this.path, MAX_QUEUE_BYTES);
|
|
@@ -35076,8 +35263,8 @@ var FilePendingMainQueue = class {
|
|
|
35076
35263
|
}
|
|
35077
35264
|
};
|
|
35078
35265
|
function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
|
|
35079
|
-
if (signal.kind !== "ask") {
|
|
35080
|
-
throw new Error("only directed asks can enter the pending-for-main queue");
|
|
35266
|
+
if (signal.kind !== "ask" && signal.kind !== "note") {
|
|
35267
|
+
throw new Error("only directed asks and notes can enter the pending-for-main queue");
|
|
35081
35268
|
}
|
|
35082
35269
|
return parseEntry({
|
|
35083
35270
|
signalId: signal.id,
|
|
@@ -35085,6 +35272,7 @@ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
|
|
|
35085
35272
|
principalId,
|
|
35086
35273
|
fromId: signal.from,
|
|
35087
35274
|
fromKind: signal.from_kind,
|
|
35275
|
+
kind: signal.kind,
|
|
35088
35276
|
senderName: provenance.senderName,
|
|
35089
35277
|
body: signal.body,
|
|
35090
35278
|
createdAt: signal.created_at,
|
|
@@ -35103,7 +35291,7 @@ var LISTENER_REPLY_ONLY_MINIMUM_MS = SIGNAL_REQUEST_TIMEOUT_MS + LISTENER_ACK_ON
|
|
|
35103
35291
|
var LISTENER_PROMPT_START_MINIMUM_MS = SIGNAL_READ_TIMEOUT_MS + ACP_DEFAULT_REQUEST_TIMEOUT_MS + LISTENER_REPLY_ONLY_MINIMUM_MS;
|
|
35104
35292
|
var LISTENER_DELIVERY_RETRY_INITIAL_MS = 500;
|
|
35105
35293
|
var LISTENER_DELIVERY_RETRY_MAX_MS = 3e4;
|
|
35106
|
-
var
|
|
35294
|
+
var UUID_RE14 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
35107
35295
|
var ListenerCapabilityError = class extends Error {
|
|
35108
35296
|
code;
|
|
35109
35297
|
constructor(code, message) {
|
|
@@ -35182,7 +35370,7 @@ function ackForTerminalEffect(record, now) {
|
|
|
35182
35370
|
if (record.state === "observed" && record.signalKind === "note") {
|
|
35183
35371
|
return { outcome: "observed", lastErrorCode: null };
|
|
35184
35372
|
}
|
|
35185
|
-
if (record.state === "routed_main"
|
|
35373
|
+
if (record.state === "routed_main") {
|
|
35186
35374
|
return { outcome: "queued", lastErrorCode: null };
|
|
35187
35375
|
}
|
|
35188
35376
|
if (record.state === "expired" && record.signalKind === "ask" && Date.parse(record.askUntil) <= now()) {
|
|
@@ -35204,7 +35392,7 @@ function ackForTerminalEffect(record, now) {
|
|
|
35204
35392
|
return { outcome: "failed_terminal", lastErrorCode: "local_effect_failed" };
|
|
35205
35393
|
}
|
|
35206
35394
|
function isAckableTerminalEffect(record, now) {
|
|
35207
|
-
return record.state === "done" && record.signalKind === "ask" && !!record.replySignalId || record.state === "observed" && record.signalKind === "note" || record.state === "routed_main"
|
|
35395
|
+
return record.state === "done" && record.signalKind === "ask" && !!record.replySignalId || record.state === "observed" && record.signalKind === "note" || record.state === "routed_main" || record.state === "expired" && record.signalKind === "ask" && Date.parse(record.askUntil) <= now() || record.state === "failed" && record.signalKind === "ask";
|
|
35208
35396
|
}
|
|
35209
35397
|
function effectPhaseBudget(record) {
|
|
35210
35398
|
if (record === null || record.state === "received" || record.state === "prompting") {
|
|
@@ -35215,6 +35403,9 @@ function effectPhaseBudget(record) {
|
|
|
35215
35403
|
}
|
|
35216
35404
|
return LISTENER_ACK_ONLY_MINIMUM_MS;
|
|
35217
35405
|
}
|
|
35406
|
+
function observedNoteNeedsMainRoute(record, routeMode, deferOverChars) {
|
|
35407
|
+
return record?.state === "observed" && record.signalKind === "note" && decideListenerRoute(routeMode, deferOverChars, record.askBody.length) === "main";
|
|
35408
|
+
}
|
|
35218
35409
|
function verifyPreparedAckEffect(record, active, now) {
|
|
35219
35410
|
if (record === null || record.signalId !== active.signalId || active.ack === null) {
|
|
35220
35411
|
throw new Error("prepared delivery ACK has no matching terminal effect");
|
|
@@ -35378,7 +35569,7 @@ async function runListenerRuntime(options) {
|
|
|
35378
35569
|
new Error("listener instance id and delivery journal must be configured together")
|
|
35379
35570
|
);
|
|
35380
35571
|
}
|
|
35381
|
-
if (hasInstanceId && !
|
|
35572
|
+
if (hasInstanceId && !UUID_RE14.test(options.listenerInstanceId)) {
|
|
35382
35573
|
return await closeBeforeStart(
|
|
35383
35574
|
options.model,
|
|
35384
35575
|
new Error("listener instance id must be a UUID")
|
|
@@ -35450,7 +35641,11 @@ async function runListenerRuntime(options) {
|
|
|
35450
35641
|
...options.resolveSenderProvenance === void 0 ? {} : { resolveSenderProvenance: options.resolveSenderProvenance },
|
|
35451
35642
|
isCredentialFailure: isCredentialLoss
|
|
35452
35643
|
});
|
|
35453
|
-
const
|
|
35644
|
+
const routeSignalToMain = async (signal) => {
|
|
35645
|
+
if (signal.kind !== "ask" && signal.kind !== "note") {
|
|
35646
|
+
throw new Error("only directed asks and notes can route to the main session");
|
|
35647
|
+
}
|
|
35648
|
+
const signalKind2 = signal.kind;
|
|
35454
35649
|
let provenance = {
|
|
35455
35650
|
senderName: null,
|
|
35456
35651
|
operatorId: null,
|
|
@@ -35480,13 +35675,19 @@ async function runListenerRuntime(options) {
|
|
|
35480
35675
|
});
|
|
35481
35676
|
const existing = await options.store.read(signal.id);
|
|
35482
35677
|
if (existing !== null) {
|
|
35483
|
-
if (!sameEffectSignal(existing, signal)
|
|
35484
|
-
throw new Error("stored listener effect does not match the main-routed
|
|
35678
|
+
if (!sameEffectSignal(existing, signal)) {
|
|
35679
|
+
throw new Error("stored listener effect does not match the main-routed message");
|
|
35680
|
+
}
|
|
35681
|
+
if (existing.state === "routed_main") {
|
|
35682
|
+
return existing;
|
|
35683
|
+
}
|
|
35684
|
+
if (!(existing.signalKind === "note" && existing.state === "observed")) {
|
|
35685
|
+
throw new Error("stored listener effect does not match the main-routed message");
|
|
35485
35686
|
}
|
|
35486
|
-
return existing;
|
|
35487
35687
|
}
|
|
35488
|
-
await options.store.write(
|
|
35688
|
+
await options.store.write(newRoutedMainRecord({
|
|
35489
35689
|
signalId: signal.id,
|
|
35690
|
+
signalKind: signalKind2,
|
|
35490
35691
|
body: signal.body,
|
|
35491
35692
|
until: signal.until,
|
|
35492
35693
|
senderOwnerRelation: signal.sender_owner_relation ?? "unknown",
|
|
@@ -35494,7 +35695,7 @@ async function runListenerRuntime(options) {
|
|
|
35494
35695
|
}));
|
|
35495
35696
|
const persisted = await options.store.read(signal.id);
|
|
35496
35697
|
if (persisted === null || !sameEffectSignal(persisted, signal) || persisted.state !== "routed_main") {
|
|
35497
|
-
throw new Error("main-routed
|
|
35698
|
+
throw new Error("main-routed message effect could not be verified");
|
|
35498
35699
|
}
|
|
35499
35700
|
return persisted;
|
|
35500
35701
|
};
|
|
@@ -35716,7 +35917,20 @@ async function runListenerRuntime(options) {
|
|
|
35716
35917
|
const recovery = currentJournalRecord?.active ?? null;
|
|
35717
35918
|
if (recovery?.phase === "ack_pending") {
|
|
35718
35919
|
const horizon = Date.parse(recovery.leasedUntil) + LISTENER_DELIVERY_SAFETY_MARGIN_MS;
|
|
35719
|
-
|
|
35920
|
+
let preparedNeedsMainRoute = false;
|
|
35921
|
+
if (recovery.signalId !== null) {
|
|
35922
|
+
try {
|
|
35923
|
+
preparedNeedsMainRoute = observedNoteNeedsMainRoute(
|
|
35924
|
+
await options.store.read(recovery.signalId),
|
|
35925
|
+
routeMode,
|
|
35926
|
+
deferOverChars
|
|
35927
|
+
);
|
|
35928
|
+
} catch (error) {
|
|
35929
|
+
stop = { reason: "fatal", error: asError2(error) };
|
|
35930
|
+
break;
|
|
35931
|
+
}
|
|
35932
|
+
}
|
|
35933
|
+
if (page.capabilities.deliveryAck && now() < horizon && !preparedNeedsMainRoute) {
|
|
35720
35934
|
const ackStop = await sendPreparedAck(recovery);
|
|
35721
35935
|
if (ackStop !== null) {
|
|
35722
35936
|
stop = ackStop;
|
|
@@ -35755,7 +35969,7 @@ async function runListenerRuntime(options) {
|
|
|
35755
35969
|
terminal = null;
|
|
35756
35970
|
}
|
|
35757
35971
|
}
|
|
35758
|
-
if (terminal !== null && sameRecoveredEffect(recovery, terminal) && isAckableTerminalEffect(terminal, now)) {
|
|
35972
|
+
if (terminal !== null && sameRecoveredEffect(recovery, terminal) && !observedNoteNeedsMainRoute(terminal, routeMode, deferOverChars) && isAckableTerminalEffect(terminal, now)) {
|
|
35759
35973
|
try {
|
|
35760
35974
|
const mapped = ackForTerminalEffect(terminal, now);
|
|
35761
35975
|
await options.deliveryJournal.prepareAck({
|
|
@@ -35949,7 +36163,33 @@ async function runListenerRuntime(options) {
|
|
|
35949
36163
|
if (existing !== null && !sameEffectSignal(existing, signal)) {
|
|
35950
36164
|
throw new Error("stored listener effect does not match the authoritative delivery");
|
|
35951
36165
|
}
|
|
35952
|
-
if (signal.kind
|
|
36166
|
+
if (signal.kind !== "ask" && signal.kind !== "note") {
|
|
36167
|
+
throw new Error("claimed delivery has an unsupported signal kind");
|
|
36168
|
+
}
|
|
36169
|
+
const decision = decideListenerRoute(
|
|
36170
|
+
routeMode,
|
|
36171
|
+
deferOverChars,
|
|
36172
|
+
signal.body.length
|
|
36173
|
+
);
|
|
36174
|
+
options.onEvent?.({
|
|
36175
|
+
type: "routing_decision",
|
|
36176
|
+
signalId: signal.id,
|
|
36177
|
+
routeMode,
|
|
36178
|
+
decision,
|
|
36179
|
+
threshold: deferOverChars,
|
|
36180
|
+
bodyLength: signal.body.length,
|
|
36181
|
+
ts: eventTime(now)
|
|
36182
|
+
});
|
|
36183
|
+
if (decision === "main") {
|
|
36184
|
+
terminal = await routeSignalToMain(signal);
|
|
36185
|
+
options.onEvent?.({
|
|
36186
|
+
type: "effect",
|
|
36187
|
+
signalId: signal.id,
|
|
36188
|
+
status: "routed_main",
|
|
36189
|
+
failureCode: null,
|
|
36190
|
+
ts: eventTime(now)
|
|
36191
|
+
});
|
|
36192
|
+
} else if (signal.kind === "note") {
|
|
35953
36193
|
if (existing === null) {
|
|
35954
36194
|
await options.store.write(newObservedNoteRecord({
|
|
35955
36195
|
signalId: signal.id,
|
|
@@ -35970,85 +36210,58 @@ async function runListenerRuntime(options) {
|
|
|
35970
36210
|
failureCode: null,
|
|
35971
36211
|
ts: eventTime(now)
|
|
35972
36212
|
});
|
|
35973
|
-
} else
|
|
35974
|
-
|
|
35975
|
-
|
|
35976
|
-
|
|
35977
|
-
signal
|
|
35978
|
-
|
|
35979
|
-
|
|
35980
|
-
|
|
35981
|
-
|
|
35982
|
-
|
|
35983
|
-
|
|
35984
|
-
|
|
35985
|
-
|
|
35986
|
-
|
|
35987
|
-
|
|
35988
|
-
|
|
35989
|
-
|
|
36213
|
+
} else {
|
|
36214
|
+
let processAttempt = 0;
|
|
36215
|
+
while (terminal === null) {
|
|
36216
|
+
const before = await options.store.read(signal.id);
|
|
36217
|
+
if (before !== null && !sameEffectSignal(before, signal)) {
|
|
36218
|
+
throw new Error("stored listener effect does not match the authoritative delivery");
|
|
36219
|
+
}
|
|
36220
|
+
const requiredBudget = effectPhaseBudget(before);
|
|
36221
|
+
if (leasedUntilMs <= now() + requiredBudget) {
|
|
36222
|
+
await sleep2(
|
|
36223
|
+
Math.max(
|
|
36224
|
+
0,
|
|
36225
|
+
leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS - now()
|
|
36226
|
+
),
|
|
36227
|
+
abort
|
|
36228
|
+
);
|
|
36229
|
+
if (abort?.aborted) {
|
|
36230
|
+
stop = { reason: "cancelled" };
|
|
36231
|
+
break;
|
|
36232
|
+
}
|
|
36233
|
+
if (now() >= leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS) {
|
|
36234
|
+
await journal.clearActive(eventTime(now));
|
|
36235
|
+
after = null;
|
|
36236
|
+
}
|
|
36237
|
+
break;
|
|
36238
|
+
}
|
|
36239
|
+
const processed = await engine.process(signal);
|
|
36240
|
+
const effect = "record" in processed ? processed.record : null;
|
|
35990
36241
|
options.onEvent?.({
|
|
35991
36242
|
type: "effect",
|
|
35992
36243
|
signalId: signal.id,
|
|
35993
|
-
status:
|
|
35994
|
-
failureCode: null,
|
|
36244
|
+
status: processed.status,
|
|
36245
|
+
failureCode: effect?.failureCode ?? null,
|
|
35995
36246
|
ts: eventTime(now)
|
|
35996
36247
|
});
|
|
35997
|
-
|
|
35998
|
-
|
|
35999
|
-
|
|
36000
|
-
|
|
36001
|
-
|
|
36002
|
-
|
|
36003
|
-
|
|
36004
|
-
|
|
36005
|
-
|
|
36006
|
-
|
|
36007
|
-
|
|
36008
|
-
0,
|
|
36009
|
-
leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS - now()
|
|
36010
|
-
),
|
|
36011
|
-
abort
|
|
36012
|
-
);
|
|
36013
|
-
if (abort?.aborted) {
|
|
36014
|
-
stop = { reason: "cancelled" };
|
|
36015
|
-
break;
|
|
36016
|
-
}
|
|
36017
|
-
if (now() >= leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS) {
|
|
36018
|
-
await journal.clearActive(eventTime(now));
|
|
36019
|
-
after = null;
|
|
36020
|
-
}
|
|
36248
|
+
if (processed.status === "ignored") {
|
|
36249
|
+
throw new Error("claimed delivery was ignored by the listener engine");
|
|
36250
|
+
}
|
|
36251
|
+
if (processed.status === "retry_pending") {
|
|
36252
|
+
processAttempt += 1;
|
|
36253
|
+
await sleep2(
|
|
36254
|
+
deliveryRetryDelay(processAttempt, null, random),
|
|
36255
|
+
abort
|
|
36256
|
+
);
|
|
36257
|
+
if (abort?.aborted) {
|
|
36258
|
+
stop = { reason: "cancelled" };
|
|
36021
36259
|
break;
|
|
36022
36260
|
}
|
|
36023
|
-
|
|
36024
|
-
const effect = "record" in processed ? processed.record : null;
|
|
36025
|
-
options.onEvent?.({
|
|
36026
|
-
type: "effect",
|
|
36027
|
-
signalId: signal.id,
|
|
36028
|
-
status: processed.status,
|
|
36029
|
-
failureCode: effect?.failureCode ?? null,
|
|
36030
|
-
ts: eventTime(now)
|
|
36031
|
-
});
|
|
36032
|
-
if (processed.status === "ignored") {
|
|
36033
|
-
throw new Error("claimed delivery was ignored by the listener engine");
|
|
36034
|
-
}
|
|
36035
|
-
if (processed.status === "retry_pending") {
|
|
36036
|
-
processAttempt += 1;
|
|
36037
|
-
await sleep2(
|
|
36038
|
-
deliveryRetryDelay(processAttempt, null, random),
|
|
36039
|
-
abort
|
|
36040
|
-
);
|
|
36041
|
-
if (abort?.aborted) {
|
|
36042
|
-
stop = { reason: "cancelled" };
|
|
36043
|
-
break;
|
|
36044
|
-
}
|
|
36045
|
-
continue;
|
|
36046
|
-
}
|
|
36047
|
-
terminal = processed.record;
|
|
36261
|
+
continue;
|
|
36048
36262
|
}
|
|
36263
|
+
terminal = processed.record;
|
|
36049
36264
|
}
|
|
36050
|
-
} else {
|
|
36051
|
-
throw new Error("claimed delivery has an unsupported signal kind");
|
|
36052
36265
|
}
|
|
36053
36266
|
} catch (error) {
|
|
36054
36267
|
if (abort?.aborted) {
|
|
@@ -36097,24 +36310,7 @@ async function runListenerRuntime(options) {
|
|
|
36097
36310
|
stop = { reason: "cancelled" };
|
|
36098
36311
|
break;
|
|
36099
36312
|
}
|
|
36100
|
-
if (signal.kind
|
|
36101
|
-
try {
|
|
36102
|
-
await readOrReplaceUnreadableEffect(options.store, signal, now);
|
|
36103
|
-
const record2 = await observeFallbackNote(options.store, signal, now);
|
|
36104
|
-
options.onEvent?.({
|
|
36105
|
-
type: "effect",
|
|
36106
|
-
signalId: signal.id,
|
|
36107
|
-
status: "observed",
|
|
36108
|
-
failureCode: record2.failureCode,
|
|
36109
|
-
ts: eventTime(now)
|
|
36110
|
-
});
|
|
36111
|
-
} catch (error) {
|
|
36112
|
-
stop = { reason: "fatal", error: asError2(error) };
|
|
36113
|
-
break;
|
|
36114
|
-
}
|
|
36115
|
-
continue;
|
|
36116
|
-
}
|
|
36117
|
-
if (signal.kind !== "ask") continue;
|
|
36313
|
+
if (signal.kind !== "ask" && signal.kind !== "note") continue;
|
|
36118
36314
|
let result;
|
|
36119
36315
|
try {
|
|
36120
36316
|
await readOrReplaceUnreadableEffect(options.store, signal, now);
|
|
@@ -36133,7 +36329,7 @@ async function runListenerRuntime(options) {
|
|
|
36133
36329
|
ts: eventTime(now)
|
|
36134
36330
|
});
|
|
36135
36331
|
if (decision === "main") {
|
|
36136
|
-
|
|
36332
|
+
await routeSignalToMain(signal);
|
|
36137
36333
|
options.onEvent?.({
|
|
36138
36334
|
type: "effect",
|
|
36139
36335
|
signalId: signal.id,
|
|
@@ -36143,6 +36339,17 @@ async function runListenerRuntime(options) {
|
|
|
36143
36339
|
});
|
|
36144
36340
|
continue;
|
|
36145
36341
|
}
|
|
36342
|
+
if (signal.kind === "note") {
|
|
36343
|
+
const record2 = await observeFallbackNote(options.store, signal, now);
|
|
36344
|
+
options.onEvent?.({
|
|
36345
|
+
type: "effect",
|
|
36346
|
+
signalId: signal.id,
|
|
36347
|
+
status: "observed",
|
|
36348
|
+
failureCode: record2.failureCode,
|
|
36349
|
+
ts: eventTime(now)
|
|
36350
|
+
});
|
|
36351
|
+
continue;
|
|
36352
|
+
}
|
|
36146
36353
|
result = await engine.process(signal);
|
|
36147
36354
|
} catch (error) {
|
|
36148
36355
|
if (abort?.aborted) {
|
|
@@ -36202,8 +36409,8 @@ async function runListenerRuntime(options) {
|
|
|
36202
36409
|
// src/listener/control.ts
|
|
36203
36410
|
var import_node_net = require("node:net");
|
|
36204
36411
|
var import_promises9 = require("node:fs/promises");
|
|
36205
|
-
var
|
|
36206
|
-
var
|
|
36412
|
+
var import_node_path16 = require("node:path");
|
|
36413
|
+
var UUID_RE15 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
36207
36414
|
var SEMVER_RE2 = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
36208
36415
|
var MAX_STATUS_BYTES = 16 * 1024;
|
|
36209
36416
|
var MAX_CONTROL_BYTES = 8 * 1024;
|
|
@@ -36218,19 +36425,19 @@ var ListenerAlreadyRunningError = class extends Error {
|
|
|
36218
36425
|
};
|
|
36219
36426
|
function listenerPaths(options) {
|
|
36220
36427
|
const root = options.stateDirectory ?? defaultListenerStateDirectory();
|
|
36221
|
-
if (!(0,
|
|
36428
|
+
if (!(0, import_node_path16.isAbsolute)(root)) {
|
|
36222
36429
|
throw new Error("listener state directory must be absolute");
|
|
36223
36430
|
}
|
|
36224
36431
|
const key2 = listenerInstanceKey(options);
|
|
36225
|
-
const instanceDirectory = (0,
|
|
36432
|
+
const instanceDirectory = (0, import_node_path16.join)(root, key2);
|
|
36226
36433
|
const uid2 = typeof process.getuid === "function" ? process.getuid() : process.pid;
|
|
36227
|
-
const controlDirectory = process.platform === "win32" ? "" : (0,
|
|
36228
|
-
const socketPath = process.platform === "win32" ? `\\\\.\\pipe\\cswarm-${key2}` : (0,
|
|
36434
|
+
const controlDirectory = process.platform === "win32" ? "" : (0, import_node_path16.join)("/tmp", `cswarm-control-${uid2}`);
|
|
36435
|
+
const socketPath = process.platform === "win32" ? `\\\\.\\pipe\\cswarm-${key2}` : (0, import_node_path16.join)(controlDirectory, `${key2.slice(0, 32)}.sock`);
|
|
36229
36436
|
return {
|
|
36230
36437
|
key: key2,
|
|
36231
36438
|
instanceDirectory,
|
|
36232
|
-
statusPath: (0,
|
|
36233
|
-
logPath: (0,
|
|
36439
|
+
statusPath: (0, import_node_path16.join)(instanceDirectory, "status.json"),
|
|
36440
|
+
logPath: (0, import_node_path16.join)(instanceDirectory, "events.ndjson"),
|
|
36234
36441
|
socketPath
|
|
36235
36442
|
};
|
|
36236
36443
|
}
|
|
@@ -36309,10 +36516,10 @@ function parseStatus(raw) {
|
|
|
36309
36516
|
throw new Error("stored listener status is malformed");
|
|
36310
36517
|
}
|
|
36311
36518
|
}
|
|
36312
|
-
const nullableUuid2 = (candidate) => candidate === null || typeof candidate === "string" &&
|
|
36519
|
+
const nullableUuid2 = (candidate) => candidate === null || typeof candidate === "string" && UUID_RE15.test(candidate);
|
|
36313
36520
|
const nullableCount = (candidate) => candidate === null || typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate >= 0;
|
|
36314
36521
|
const nullableTimestamp2 = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
|
|
36315
|
-
if (row.version !== 1 || typeof row.instanceId !== "string" || !
|
|
36522
|
+
if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE15.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE15.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE15.test(row.principalId) || !Number.isSafeInteger(row.pid) || row.pid < 1 || typeof row.state !== "string" || !["starting", "ready", "stopping", "stopped", "failed"].includes(row.state) || typeof row.startedAt !== "string" || !Number.isFinite(Date.parse(row.startedAt)) || !(row.readyAt === null || typeof row.readyAt === "string" && Number.isFinite(Date.parse(row.readyAt))) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt)) || !(row.stoppedAt === null || typeof row.stoppedAt === "string" && Number.isFinite(Date.parse(row.stoppedAt))) || !nullableUuid2(row.lastSignalId) || !(row.lastErrorCode === null || typeof row.lastErrorCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorCode)) || !(row.providerVersion === void 0 || row.providerVersion === null || typeof row.providerVersion === "string" && SEMVER_RE2.test(row.providerVersion)) || !(row.providerLastMeasuredVersion === void 0 || row.providerLastMeasuredVersion === null || typeof row.providerLastMeasuredVersion === "string" && SEMVER_RE2.test(row.providerLastMeasuredVersion)) || (row.providerVersion === null || row.providerVersion === void 0) !== (row.providerLastMeasuredVersion === null || row.providerLastMeasuredVersion === void 0) || !(row.lastWorkerStderrTail === void 0 || row.lastWorkerStderrTail === null || typeof row.lastWorkerStderrTail === "string" && row.lastWorkerStderrTail.length > 0 && row.lastWorkerStderrTail.length <= 2048 && !/swm_(?:agt|inv|cap)_/i.test(row.lastWorkerStderrTail)) || typeof row.logPath !== "string" || !(0, import_node_path16.isAbsolute)(row.logPath) || !(row.deliveryMode === void 0 || row.deliveryMode === null || typeof row.deliveryMode === "string" && STATUS_DELIVERY_MODES.has(row.deliveryMode)) || !(row.pendingDeliveryCount === void 0 || nullableCount(row.pendingDeliveryCount)) || !(row.lastTerminalDeliveryFailureCount === void 0 || nullableCount(row.lastTerminalDeliveryFailureCount)) || !(row.lastTerminalDeliveryFailureAt === void 0 || nullableTimestamp2(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp2(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp2(row.lastAckAt)) || !(row.routeMode === void 0 || row.routeMode === "worker" || row.routeMode === "main" || row.routeMode === "split") || !(row.deferOverChars === void 0 || row.deferOverChars === null || typeof row.deferOverChars === "number" && Number.isSafeInteger(row.deferOverChars) && row.deferOverChars >= 1 && row.deferOverChars <= 1e4) || !(row.pendingForMainCount === void 0 || typeof row.pendingForMainCount === "number" && Number.isSafeInteger(row.pendingForMainCount) && row.pendingForMainCount >= 0) || !(row.droppedForMainCount === void 0 || typeof row.droppedForMainCount === "number" && Number.isSafeInteger(row.droppedForMainCount) && row.droppedForMainCount >= 0)) {
|
|
36316
36523
|
throw new Error("stored listener status is malformed");
|
|
36317
36524
|
}
|
|
36318
36525
|
const routeMode = row.routeMode ?? "worker";
|
|
@@ -36486,7 +36693,7 @@ function writeResponse(socket, response) {
|
|
|
36486
36693
|
}
|
|
36487
36694
|
async function startupLock(paths) {
|
|
36488
36695
|
await ensureSecureStateDirectory(paths.instanceDirectory);
|
|
36489
|
-
const lockPath = (0,
|
|
36696
|
+
const lockPath = (0, import_node_path16.join)(paths.instanceDirectory, "starting.lock");
|
|
36490
36697
|
const deadline = Date.now() + START_LOCK_WAIT_MS;
|
|
36491
36698
|
while (Date.now() < deadline) {
|
|
36492
36699
|
let handle;
|
|
@@ -36527,7 +36734,7 @@ async function startupLock(paths) {
|
|
|
36527
36734
|
async function prepareSocket(paths) {
|
|
36528
36735
|
if (process.platform !== "win32") {
|
|
36529
36736
|
const uid2 = typeof process.getuid === "function" ? process.getuid() : process.pid;
|
|
36530
|
-
const directory = (0,
|
|
36737
|
+
const directory = (0, import_node_path16.join)("/tmp", `cswarm-control-${uid2}`);
|
|
36531
36738
|
await ensureSecureStateDirectory(directory);
|
|
36532
36739
|
}
|
|
36533
36740
|
try {
|
|
@@ -36662,7 +36869,7 @@ async function queryListenerControl(paths, command2, timeoutMs = CONTROL_TIMEOUT
|
|
|
36662
36869
|
|
|
36663
36870
|
// src/listener/supervisor.ts
|
|
36664
36871
|
var import_node_crypto18 = require("node:crypto");
|
|
36665
|
-
var
|
|
36872
|
+
var UUID_RE16 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
36666
36873
|
var LISTENER_RESTART_MAX_ATTEMPTS = 5;
|
|
36667
36874
|
var LISTENER_RESTART_INITIAL_MS = 1e3;
|
|
36668
36875
|
var LISTENER_RESTART_MAX_MS = 6e4;
|
|
@@ -36791,7 +36998,7 @@ async function runListenerSupervisor(options) {
|
|
|
36791
36998
|
// before the socket can answer, before any status/event persistence.
|
|
36792
36999
|
initialize: prepare ? async () => {
|
|
36793
37000
|
const selected = await prepare(proposedInstanceId);
|
|
36794
|
-
if (!selected || typeof selected !== "object" || typeof selected.instanceId !== "string" || !
|
|
37001
|
+
if (!selected || typeof selected !== "object" || typeof selected.instanceId !== "string" || !UUID_RE16.test(selected.instanceId)) {
|
|
36795
37002
|
throw new Error("listener prepare returned an invalid instance id");
|
|
36796
37003
|
}
|
|
36797
37004
|
status = { ...status, instanceId: selected.instanceId };
|
|
@@ -37147,9 +37354,9 @@ async function waitForListenerReady(paths, options = {}) {
|
|
|
37147
37354
|
}
|
|
37148
37355
|
|
|
37149
37356
|
// src/listener/delivery-journal.ts
|
|
37150
|
-
var
|
|
37357
|
+
var import_node_path17 = require("node:path");
|
|
37151
37358
|
var import_node_util2 = require("node:util");
|
|
37152
|
-
var
|
|
37359
|
+
var UUID_RE17 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
37153
37360
|
var COMMAND_ID_RE3 = /^[A-Za-z0-9_-]{8,72}$/;
|
|
37154
37361
|
var SIGNAL_FINGERPRINT_RE = /^[0-9a-f]{64}$/;
|
|
37155
37362
|
var MAX_JOURNAL_BYTES = 8192;
|
|
@@ -37244,7 +37451,7 @@ var ALLOWED_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
37244
37451
|
"credential_unavailable"
|
|
37245
37452
|
]);
|
|
37246
37453
|
function claimCommandId(listenerInstanceId, claimOrdinal) {
|
|
37247
|
-
if (!
|
|
37454
|
+
if (!UUID_RE17.test(listenerInstanceId)) {
|
|
37248
37455
|
throw new Error("stored delivery journal is malformed");
|
|
37249
37456
|
}
|
|
37250
37457
|
if (!Number.isSafeInteger(claimOrdinal) || claimOrdinal < 0) {
|
|
@@ -37259,7 +37466,7 @@ function claimCommandId(listenerInstanceId, claimOrdinal) {
|
|
|
37259
37466
|
return id;
|
|
37260
37467
|
}
|
|
37261
37468
|
function ackCommandId(leaseId) {
|
|
37262
|
-
if (!
|
|
37469
|
+
if (!UUID_RE17.test(leaseId)) {
|
|
37263
37470
|
throw new Error("stored delivery journal is malformed");
|
|
37264
37471
|
}
|
|
37265
37472
|
const cleanLease = leaseId.toLowerCase().replace(/-/g, "");
|
|
@@ -37342,19 +37549,19 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
|
|
|
37342
37549
|
if (row.version !== 1) {
|
|
37343
37550
|
throw new Error("stored delivery journal is malformed");
|
|
37344
37551
|
}
|
|
37345
|
-
if (typeof row.workspaceId !== "string" || !
|
|
37552
|
+
if (typeof row.workspaceId !== "string" || !UUID_RE17.test(row.workspaceId) || row.workspaceId !== row.workspaceId.toLowerCase()) {
|
|
37346
37553
|
throw new Error("stored delivery journal is malformed");
|
|
37347
37554
|
}
|
|
37348
37555
|
if (expectedWorkspaceId && row.workspaceId !== expectedWorkspaceId.toLowerCase()) {
|
|
37349
37556
|
throw new Error("stored delivery journal is malformed");
|
|
37350
37557
|
}
|
|
37351
|
-
if (typeof row.principalId !== "string" || !
|
|
37558
|
+
if (typeof row.principalId !== "string" || !UUID_RE17.test(row.principalId) || row.principalId !== row.principalId.toLowerCase()) {
|
|
37352
37559
|
throw new Error("stored delivery journal is malformed");
|
|
37353
37560
|
}
|
|
37354
37561
|
if (expectedPrincipalId && row.principalId !== expectedPrincipalId.toLowerCase()) {
|
|
37355
37562
|
throw new Error("stored delivery journal is malformed");
|
|
37356
37563
|
}
|
|
37357
|
-
if (typeof row.listenerInstanceId !== "string" || !
|
|
37564
|
+
if (typeof row.listenerInstanceId !== "string" || !UUID_RE17.test(row.listenerInstanceId) || row.listenerInstanceId !== row.listenerInstanceId.toLowerCase()) {
|
|
37358
37565
|
throw new Error("stored delivery journal is malformed");
|
|
37359
37566
|
}
|
|
37360
37567
|
if (!Number.isSafeInteger(row.nextClaimOrdinal) || row.nextClaimOrdinal < 0) {
|
|
@@ -37418,10 +37625,10 @@ function parseJournalRecord(raw, expectedWorkspaceId, expectedPrincipalId) {
|
|
|
37418
37625
|
if (active.claimLastAttemptAt === null) {
|
|
37419
37626
|
throw new Error("stored delivery journal is malformed");
|
|
37420
37627
|
}
|
|
37421
|
-
if (typeof active.signalId !== "string" || !
|
|
37628
|
+
if (typeof active.signalId !== "string" || !UUID_RE17.test(active.signalId) || active.signalId !== active.signalId.toLowerCase()) {
|
|
37422
37629
|
throw new Error("stored delivery journal is malformed");
|
|
37423
37630
|
}
|
|
37424
|
-
if (typeof active.leaseId !== "string" || !
|
|
37631
|
+
if (typeof active.leaseId !== "string" || !UUID_RE17.test(active.leaseId) || active.leaseId !== active.leaseId.toLowerCase()) {
|
|
37425
37632
|
throw new Error("stored delivery journal is malformed");
|
|
37426
37633
|
}
|
|
37427
37634
|
if (!isValidIsoTimestamp(active.leasedUntil) || Date.parse(active.leasedUntil) <= Date.parse(active.claimCreatedAt)) {
|
|
@@ -37493,7 +37700,7 @@ var FileListenerDeliveryJournal = class {
|
|
|
37493
37700
|
["profileId", "workspaceId", "principalId"],
|
|
37494
37701
|
"delivery journal configuration rejected"
|
|
37495
37702
|
);
|
|
37496
|
-
if (typeof options.profileId !== "string" || !options.profileId || options.profileId.includes("\0") || typeof options.workspaceId !== "string" || !
|
|
37703
|
+
if (typeof options.profileId !== "string" || !options.profileId || options.profileId.includes("\0") || typeof options.workspaceId !== "string" || !UUID_RE17.test(options.workspaceId) || typeof options.principalId !== "string" || !UUID_RE17.test(options.principalId)) {
|
|
37497
37704
|
throw new Error("delivery journal configuration rejected");
|
|
37498
37705
|
}
|
|
37499
37706
|
if (options.stateDirectory !== void 0) {
|
|
@@ -37508,11 +37715,11 @@ var FileListenerDeliveryJournal = class {
|
|
|
37508
37715
|
stateDirectory: options.stateDirectory
|
|
37509
37716
|
});
|
|
37510
37717
|
const root = this.options.stateDirectory ?? defaultListenerStateDirectory();
|
|
37511
|
-
if (!(0,
|
|
37718
|
+
if (!(0, import_node_path17.isAbsolute)(root)) {
|
|
37512
37719
|
throw new Error("delivery journal configuration rejected");
|
|
37513
37720
|
}
|
|
37514
|
-
this.instanceDirectory = (0,
|
|
37515
|
-
this.journalPath = (0,
|
|
37721
|
+
this.instanceDirectory = (0, import_node_path17.join)(root, listenerInstanceKey(this.options));
|
|
37722
|
+
this.journalPath = (0, import_node_path17.join)(this.instanceDirectory, "delivery-journal.json");
|
|
37516
37723
|
}
|
|
37517
37724
|
async readRecordUnlocked() {
|
|
37518
37725
|
let raw;
|
|
@@ -37614,7 +37821,7 @@ var FileListenerDeliveryJournal = class {
|
|
|
37614
37821
|
["signalId", "leaseId", "leasedUntil"],
|
|
37615
37822
|
"delivery journal mutation rejected"
|
|
37616
37823
|
);
|
|
37617
|
-
if (typeof input.signalId !== "string" || !
|
|
37824
|
+
if (typeof input.signalId !== "string" || !UUID_RE17.test(input.signalId) || typeof input.leaseId !== "string" || !UUID_RE17.test(input.leaseId) || !isValidIsoTimestamp(input.leasedUntil) || input.signalFingerprint !== void 0 && (typeof input.signalFingerprint !== "string" || !SIGNAL_FINGERPRINT_RE.test(input.signalFingerprint))) {
|
|
37618
37825
|
throw new Error("delivery journal mutation rejected");
|
|
37619
37826
|
}
|
|
37620
37827
|
const canonicalSignalId = input.signalId.toLowerCase();
|
|
@@ -37746,7 +37953,7 @@ async function openListenerDeliveryJournal(options) {
|
|
|
37746
37953
|
["profileId", "workspaceId", "principalId", "proposedListenerInstanceId"],
|
|
37747
37954
|
"delivery journal configuration rejected"
|
|
37748
37955
|
);
|
|
37749
|
-
if (typeof options.profileId !== "string" || !options.profileId || options.profileId.includes("\0") || typeof options.workspaceId !== "string" || !
|
|
37956
|
+
if (typeof options.profileId !== "string" || !options.profileId || options.profileId.includes("\0") || typeof options.workspaceId !== "string" || !UUID_RE17.test(options.workspaceId) || typeof options.principalId !== "string" || !UUID_RE17.test(options.principalId) || typeof options.proposedListenerInstanceId !== "string" || !UUID_RE17.test(options.proposedListenerInstanceId)) {
|
|
37750
37957
|
throw new Error("delivery journal configuration rejected");
|
|
37751
37958
|
}
|
|
37752
37959
|
if (options.stateDirectory !== void 0) {
|
|
@@ -37841,9 +38048,9 @@ async function openListenerDeliveryJournal(options) {
|
|
|
37841
38048
|
|
|
37842
38049
|
// src/listener/detach.ts
|
|
37843
38050
|
var import_node_child_process7 = require("node:child_process");
|
|
37844
|
-
var
|
|
38051
|
+
var import_node_path18 = require("node:path");
|
|
37845
38052
|
function isNativeAbsolutePath(value, platform = process.platform) {
|
|
37846
|
-
return platform === "win32" ?
|
|
38053
|
+
return platform === "win32" ? import_node_path18.win32.isAbsolute(value) : import_node_path18.posix.isAbsolute(value);
|
|
37847
38054
|
}
|
|
37848
38055
|
function listenerNodeExecArgv(values2) {
|
|
37849
38056
|
const safe = [];
|
|
@@ -37960,8 +38167,8 @@ async function spawnDetachedListener(options) {
|
|
|
37960
38167
|
|
|
37961
38168
|
// src/listener/hook.ts
|
|
37962
38169
|
var import_promises10 = require("node:fs/promises");
|
|
37963
|
-
var
|
|
37964
|
-
var
|
|
38170
|
+
var import_node_path19 = require("node:path");
|
|
38171
|
+
var UUID_RE18 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
37965
38172
|
var TOKEN_RE = /^swm_agt_[A-Za-z0-9_-]{43}$/;
|
|
37966
38173
|
var INSTANCE_KEY_RE = /^[0-9a-f]{64}$/;
|
|
37967
38174
|
var MAX_HOOK_CREDENTIAL_BYTES = 8 * 1024;
|
|
@@ -38002,7 +38209,7 @@ function parseListenerCredential(raw) {
|
|
|
38002
38209
|
"principalId",
|
|
38003
38210
|
"credential",
|
|
38004
38211
|
"updatedAt"
|
|
38005
|
-
]) || row.version !== 1 || typeof row.profileId !== "string" || !/^[0-9a-f]{24}$/.test(row.profileId) || typeof row.targetUrl !== "string" || typeof row.anonKey !== "string" || row.anonKey.length < 1 || row.anonKey.length > 4096 || typeof row.workspaceId !== "string" || !
|
|
38212
|
+
]) || row.version !== 1 || typeof row.profileId !== "string" || !/^[0-9a-f]{24}$/.test(row.profileId) || typeof row.targetUrl !== "string" || typeof row.anonKey !== "string" || row.anonKey.length < 1 || row.anonKey.length > 4096 || typeof row.workspaceId !== "string" || !UUID_RE18.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE18.test(row.principalId) || typeof row.credential !== "string" || !TOKEN_RE.test(row.credential) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt))) {
|
|
38006
38213
|
throw new Error("stored listener hook credential is malformed");
|
|
38007
38214
|
}
|
|
38008
38215
|
const target2 = cloudTarget(row.targetUrl, row.anonKey);
|
|
@@ -38021,7 +38228,7 @@ function parseListenerCredential(raw) {
|
|
|
38021
38228
|
};
|
|
38022
38229
|
}
|
|
38023
38230
|
async function writeListenerCredentialState(instanceDirectory, input) {
|
|
38024
|
-
if (!(0,
|
|
38231
|
+
if (!(0, import_node_path19.isAbsolute)(instanceDirectory)) {
|
|
38025
38232
|
throw new Error("listener hook state directory must be absolute");
|
|
38026
38233
|
}
|
|
38027
38234
|
const record = parseListenerCredential(JSON.stringify({
|
|
@@ -38035,16 +38242,16 @@ async function writeListenerCredentialState(instanceDirectory, input) {
|
|
|
38035
38242
|
updatedAt: new Date(input.now ?? Date.now()).toISOString()
|
|
38036
38243
|
}));
|
|
38037
38244
|
await writeSecureJsonFile(
|
|
38038
|
-
(0,
|
|
38245
|
+
(0, import_node_path19.join)(instanceDirectory, LISTENER_CREDENTIAL_FILE),
|
|
38039
38246
|
JSON.stringify(record)
|
|
38040
38247
|
);
|
|
38041
38248
|
await deleteSecureJsonFile(
|
|
38042
|
-
(0,
|
|
38249
|
+
(0, import_node_path19.join)(instanceDirectory, RETIRED_HOOK_CREDENTIAL_FILE)
|
|
38043
38250
|
).catch(() => void 0);
|
|
38044
38251
|
}
|
|
38045
38252
|
async function readListenerCredentialState(instanceDirectory) {
|
|
38046
38253
|
const raw = await readSecureJsonFile(
|
|
38047
|
-
(0,
|
|
38254
|
+
(0, import_node_path19.join)(instanceDirectory, LISTENER_CREDENTIAL_FILE),
|
|
38048
38255
|
MAX_HOOK_CREDENTIAL_BYTES
|
|
38049
38256
|
);
|
|
38050
38257
|
return raw === null ? null : parseListenerCredential(raw);
|
|
@@ -38062,7 +38269,7 @@ function parseSurface(raw) {
|
|
|
38062
38269
|
const row = value;
|
|
38063
38270
|
if (Object.keys(row).some(
|
|
38064
38271
|
(key2) => key2 !== "version" && key2 !== "surfacedSignalIds" && key2 !== "reportedDroppedCount" && key2 !== "credentialFailureReported"
|
|
38065
|
-
) || row.version !== 1 || !Array.isArray(row.surfacedSignalIds) || row.surfacedSignalIds.length > HOOK_SURFACED_IDS_MAX || row.surfacedSignalIds.some((id) => typeof id !== "string" || !
|
|
38272
|
+
) || row.version !== 1 || !Array.isArray(row.surfacedSignalIds) || row.surfacedSignalIds.length > HOOK_SURFACED_IDS_MAX || row.surfacedSignalIds.some((id) => typeof id !== "string" || !UUID_RE18.test(id)) || !(row.reportedDroppedCount === void 0 || typeof row.reportedDroppedCount === "number" && Number.isSafeInteger(row.reportedDroppedCount) && row.reportedDroppedCount >= 0) || !(row.credentialFailureReported === void 0 || typeof row.credentialFailureReported === "boolean")) {
|
|
38066
38273
|
throw new Error("stored listener hook surface state is malformed");
|
|
38067
38274
|
}
|
|
38068
38275
|
const ids = row.surfacedSignalIds.map((id) => String(id).toLowerCase());
|
|
@@ -38079,10 +38286,10 @@ function parseSurface(raw) {
|
|
|
38079
38286
|
var FileHookSurfaceStore = class {
|
|
38080
38287
|
constructor(instanceDirectory) {
|
|
38081
38288
|
this.instanceDirectory = instanceDirectory;
|
|
38082
|
-
if (!(0,
|
|
38289
|
+
if (!(0, import_node_path19.isAbsolute)(instanceDirectory)) {
|
|
38083
38290
|
throw new Error("listener hook surface directory must be absolute");
|
|
38084
38291
|
}
|
|
38085
|
-
this.path = (0,
|
|
38292
|
+
this.path = (0, import_node_path19.join)(instanceDirectory, HOOK_SURFACE_FILE);
|
|
38086
38293
|
}
|
|
38087
38294
|
instanceDirectory;
|
|
38088
38295
|
path;
|
|
@@ -38099,7 +38306,7 @@ var FileHookSurfaceStore = class {
|
|
|
38099
38306
|
const unseen = [];
|
|
38100
38307
|
for (const item of items) {
|
|
38101
38308
|
const signalId = item.signalId.toLowerCase();
|
|
38102
|
-
if (!
|
|
38309
|
+
if (!UUID_RE18.test(signalId) || seen.has(signalId)) continue;
|
|
38103
38310
|
seen.add(signalId);
|
|
38104
38311
|
unseen.push(item);
|
|
38105
38312
|
}
|
|
@@ -38122,7 +38329,7 @@ var FileHookSurfaceStore = class {
|
|
|
38122
38329
|
const seen = new Set(state.surfacedSignalIds);
|
|
38123
38330
|
for (const signalId of options.signalIds ?? []) {
|
|
38124
38331
|
const checked = signalId.toLowerCase();
|
|
38125
|
-
if (
|
|
38332
|
+
if (UUID_RE18.test(checked)) seen.add(checked);
|
|
38126
38333
|
}
|
|
38127
38334
|
await writeSecureJsonFile(
|
|
38128
38335
|
this.path,
|
|
@@ -38161,7 +38368,7 @@ function parseGlobalState(raw) {
|
|
|
38161
38368
|
}
|
|
38162
38369
|
async function reserveCheck(stateDirectory2, cooldownMs, now) {
|
|
38163
38370
|
return await withFileLock(stateDirectory2, GLOBAL_STATE_LOCK, async () => {
|
|
38164
|
-
const path = (0,
|
|
38371
|
+
const path = (0, import_node_path19.join)(stateDirectory2, GLOBAL_STATE_FILE);
|
|
38165
38372
|
const raw = await readSecureJsonFile(path, MAX_GLOBAL_STATE_BYTES);
|
|
38166
38373
|
const previous = raw === null ? null : parseGlobalState(raw);
|
|
38167
38374
|
if (previous !== null && now - previous.lastCheckAt < cooldownMs) return false;
|
|
@@ -38181,8 +38388,8 @@ async function statusContext(stateDirectory2, key2, instanceDirectory) {
|
|
|
38181
38388
|
const provisional = {
|
|
38182
38389
|
key: key2,
|
|
38183
38390
|
instanceDirectory,
|
|
38184
|
-
statusPath: (0,
|
|
38185
|
-
logPath: (0,
|
|
38391
|
+
statusPath: (0, import_node_path19.join)(instanceDirectory, "status.json"),
|
|
38392
|
+
logPath: (0, import_node_path19.join)(instanceDirectory, "events.ndjson"),
|
|
38186
38393
|
socketPath: ""
|
|
38187
38394
|
};
|
|
38188
38395
|
const status = await readListenerStatus(provisional).catch(() => null);
|
|
@@ -38218,7 +38425,7 @@ async function discoverContexts(stateDirectory2, isListenerLive = listenerIsLive
|
|
|
38218
38425
|
const contexts = [];
|
|
38219
38426
|
for (const entry of entries) {
|
|
38220
38427
|
if (!entry.isDirectory() || !INSTANCE_KEY_RE.test(entry.name)) continue;
|
|
38221
|
-
const instanceDirectory = (0,
|
|
38428
|
+
const instanceDirectory = (0, import_node_path19.join)(stateDirectory2, entry.name);
|
|
38222
38429
|
const storedStatus = await statusContext(
|
|
38223
38430
|
stateDirectory2,
|
|
38224
38431
|
entry.name,
|
|
@@ -38237,7 +38444,7 @@ async function discoverContexts(stateDirectory2, isListenerLive = listenerIsLive
|
|
|
38237
38444
|
continue;
|
|
38238
38445
|
}
|
|
38239
38446
|
await deleteSecureJsonFile(
|
|
38240
|
-
(0,
|
|
38447
|
+
(0, import_node_path19.join)(instanceDirectory, RETIRED_HOOK_CREDENTIAL_FILE)
|
|
38241
38448
|
).catch(() => void 0);
|
|
38242
38449
|
try {
|
|
38243
38450
|
const credential = await readListenerCredentialState(instanceDirectory);
|
|
@@ -38388,7 +38595,7 @@ async function recordQueuedObservations(check, signalIds, options, now) {
|
|
|
38388
38595
|
async function checkListenerHooks(options) {
|
|
38389
38596
|
try {
|
|
38390
38597
|
const stateDirectory2 = options.stateDirectory ?? defaultListenerStateDirectory();
|
|
38391
|
-
if (!(0,
|
|
38598
|
+
if (!(0, import_node_path19.isAbsolute)(stateDirectory2)) return "";
|
|
38392
38599
|
const now = options.now ?? Date.now;
|
|
38393
38600
|
const cooldownSeconds = options.cooldownSeconds ?? HOOK_DEFAULT_COOLDOWN_SECONDS;
|
|
38394
38601
|
if (!Number.isSafeInteger(cooldownSeconds) || cooldownSeconds < 0 || cooldownSeconds > 86400) {
|
|
@@ -38569,6 +38776,7 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
38569
38776
|
"name",
|
|
38570
38777
|
"ndjson",
|
|
38571
38778
|
"no-browser",
|
|
38779
|
+
"notify",
|
|
38572
38780
|
"opencode-executable",
|
|
38573
38781
|
"out",
|
|
38574
38782
|
"permissions",
|
|
@@ -38608,11 +38816,12 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
38608
38816
|
"link-stdin",
|
|
38609
38817
|
"local",
|
|
38610
38818
|
"ndjson",
|
|
38819
|
+
"notify",
|
|
38611
38820
|
"no-browser",
|
|
38612
38821
|
"reveal-anon-key",
|
|
38613
38822
|
"write"
|
|
38614
38823
|
]);
|
|
38615
|
-
var
|
|
38824
|
+
var UUID_RE19 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
38616
38825
|
var AGENT_CREDENTIAL_MESSAGE = "Agent credential minted. It is bound to this task and run so the agent's work stays scoped and attributable.";
|
|
38617
38826
|
var AGENT_CREDENTIAL_MESSAGE_D088 = "Agent credential minted. It is bound to this run, so the agent's work is attributable to it.";
|
|
38618
38827
|
var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
|
|
@@ -38620,8 +38829,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
|
|
|
38620
38829
|
AGENT_CREDENTIAL_MESSAGE_D088
|
|
38621
38830
|
];
|
|
38622
38831
|
function packageVersion() {
|
|
38623
|
-
if ("0.1.
|
|
38624
|
-
return "0.1.
|
|
38832
|
+
if ("0.1.35".length > 0) {
|
|
38833
|
+
return "0.1.35";
|
|
38625
38834
|
}
|
|
38626
38835
|
try {
|
|
38627
38836
|
const value = JSON.parse(
|
|
@@ -38743,6 +38952,7 @@ Usage:
|
|
|
38743
38952
|
cswarm receipt <signal-id> --agent-token-stdin [--url <url> --anon-key <key>] --workspace-id <uuid> [--json]
|
|
38744
38953
|
cswarm feed [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--about <ref>] [--kind <kind>] [--since <timestamp>] [--limit <n>] [--include-stale] [--json]
|
|
38745
38954
|
cswarm inbox [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--kind <kind>] [--about <ref>] [--since <timestamp>] [--limit <n>] [--include-stale] [--wait <seconds>] [--json]
|
|
38955
|
+
cswarm inbox --notify --agent-token-stdin [--url <url> --anon-key <key>] --workspace-id <uuid> [--json]
|
|
38746
38956
|
cswarm inbox --follow --ndjson [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--kind <kind>] [--about <ref>] [--since <timestamp>] [--limit <n>] [--include-stale]
|
|
38747
38957
|
cswarm file put <local-path> [--name <name>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--agent-token-stdin] [--json]
|
|
38748
38958
|
cswarm file ls [--include-tombstoned] [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--agent-token-stdin] [--json]
|
|
@@ -38788,6 +38998,7 @@ Credential selection for command/dogfood:
|
|
|
38788
38998
|
JSON artifact, because it needs a field a bare secret does not carry:
|
|
38789
38999
|
members reads only -- either form
|
|
38790
39000
|
receipt reads only -- either form
|
|
39001
|
+
inbox --notify persists a per-agent cursor -- needs principal_id
|
|
38791
39002
|
file put, file ls, file get, file rm, file restore
|
|
38792
39003
|
read and command, nothing persisted -- either form
|
|
38793
39004
|
feedback command only, nothing persisted -- either form
|
|
@@ -38820,11 +39031,11 @@ TTL); a turn that lands just before a rotation can be clamped to the ~5m
|
|
|
38820
39031
|
renewal lead, and if it times out there, durable delivery retries it on the
|
|
38821
39032
|
fresh credential.
|
|
38822
39033
|
|
|
38823
|
-
listen start --route worker|main|split chooses where directed
|
|
38824
|
-
is the unchanged default. main queues every ask for the interactive session.
|
|
38825
|
-
split queues
|
|
38826
|
-
1..10000 and an equal-length
|
|
38827
|
-
to surface queued
|
|
39034
|
+
listen start --route worker|main|split chooses where directed messages go. worker
|
|
39035
|
+
is the unchanged default. main queues every ask or note for the interactive session.
|
|
39036
|
+
split queues messages whose body is longer than --defer-over <chars>; the bound is
|
|
39037
|
+
1..10000 and an equal-length message stays on the worker path. Run cswarm hook check
|
|
39038
|
+
to surface queued messages. hook check has its own 3s ceiling, exits 0 on every
|
|
38828
39039
|
outcome, and skips network checks made within --cooldown seconds (default 30).
|
|
38829
39040
|
hook install claude prints the UserPromptSubmit JSON by default and changes the
|
|
38830
39041
|
project's .claude/settings.json only with --write; uninstall also requires --write.
|
|
@@ -38957,7 +39168,7 @@ function parsedAgentCredential(value) {
|
|
|
38957
39168
|
const withExpiry = [...requiredKeys, "expires_at"].sort();
|
|
38958
39169
|
const actualKeys = Object.keys(artifact).sort();
|
|
38959
39170
|
const shape = actualKeys.length === requiredKeys.length ? requiredKeys : withExpiry;
|
|
38960
|
-
if (actualKeys.length !== shape.length || !actualKeys.every((key2, index) => key2 === shape[index]) || !ACCEPTED_AGENT_CREDENTIAL_MESSAGES.includes(artifact.message) || artifact.status !== "accepted" || typeof artifact.principal_id !== "string" || !
|
|
39171
|
+
if (actualKeys.length !== shape.length || !actualKeys.every((key2, index) => key2 === shape[index]) || !ACCEPTED_AGENT_CREDENTIAL_MESSAGES.includes(artifact.message) || artifact.status !== "accepted" || typeof artifact.principal_id !== "string" || !UUID_RE19.test(artifact.principal_id) || typeof artifact.token_id !== "string" || !UUID_RE19.test(artifact.token_id) || typeof artifact.run_id !== "string" || !UUID_RE19.test(artifact.run_id) || typeof artifact.agent_token !== "string") {
|
|
38961
39172
|
throw new Error("agent credential JSON is malformed");
|
|
38962
39173
|
}
|
|
38963
39174
|
let expiresAt = null;
|
|
@@ -39195,7 +39406,7 @@ async function runNew(args) {
|
|
|
39195
39406
|
project: {
|
|
39196
39407
|
workspace_id: created,
|
|
39197
39408
|
name,
|
|
39198
|
-
stream_id: typeof response.stream_id === "string" &&
|
|
39409
|
+
stream_id: typeof response.stream_id === "string" && UUID_RE19.test(response.stream_id) ? response.stream_id : null
|
|
39199
39410
|
}
|
|
39200
39411
|
});
|
|
39201
39412
|
return;
|
|
@@ -39969,7 +40180,7 @@ async function runLinkNew(args) {
|
|
|
39969
40180
|
2
|
|
39970
40181
|
);
|
|
39971
40182
|
const taskId = args.required("task-id");
|
|
39972
|
-
if (!
|
|
40183
|
+
if (!UUID_RE19.test(taskId)) {
|
|
39973
40184
|
throw new Error("--task-id must be the work item's UUID");
|
|
39974
40185
|
}
|
|
39975
40186
|
const site = capabilitySiteOrigin(
|
|
@@ -40029,7 +40240,7 @@ async function runLinkRevoke(args) {
|
|
|
40029
40240
|
2
|
|
40030
40241
|
);
|
|
40031
40242
|
const capabilityId = args.required("capability-id");
|
|
40032
|
-
if (!
|
|
40243
|
+
if (!UUID_RE19.test(capabilityId)) {
|
|
40033
40244
|
throw new Error(
|
|
40034
40245
|
"--capability-id must be the id printed when the link was created"
|
|
40035
40246
|
);
|
|
@@ -40608,7 +40819,7 @@ async function runReply(args) {
|
|
|
40608
40819
|
"json"
|
|
40609
40820
|
], 3);
|
|
40610
40821
|
const signalId = args.positionals[1];
|
|
40611
|
-
if (signalId === void 0 || !
|
|
40822
|
+
if (signalId === void 0 || !UUID_RE19.test(signalId)) {
|
|
40612
40823
|
throw new Error("reply requires the signal UUID being answered");
|
|
40613
40824
|
}
|
|
40614
40825
|
const body = args.positionals[2];
|
|
@@ -40750,18 +40961,29 @@ async function runMembers(args) {
|
|
|
40750
40961
|
process.stdout.write(renderRoster(directory, memberNames));
|
|
40751
40962
|
}
|
|
40752
40963
|
async function runSignalRead(args, inbox) {
|
|
40753
|
-
args.
|
|
40964
|
+
const notify = inbox && args.has("notify");
|
|
40965
|
+
args.assertShape(notify ? [
|
|
40966
|
+
...TARGET_FLAGS,
|
|
40967
|
+
"workspace-id",
|
|
40968
|
+
...CREDENTIAL_FLAGS,
|
|
40969
|
+
"notify",
|
|
40970
|
+
"json"
|
|
40971
|
+
] : [
|
|
40754
40972
|
...TARGET_FLAGS,
|
|
40755
40973
|
"workspace-id",
|
|
40756
40974
|
...CREDENTIAL_FLAGS,
|
|
40757
40975
|
"about",
|
|
40758
40976
|
"kind",
|
|
40759
|
-
...inbox ? ["wait", "follow", "ndjson"] : [],
|
|
40977
|
+
...inbox ? ["wait", "follow", "ndjson", "notify"] : [],
|
|
40760
40978
|
"since",
|
|
40761
40979
|
"limit",
|
|
40762
40980
|
"include-stale",
|
|
40763
40981
|
"json"
|
|
40764
40982
|
], 1);
|
|
40983
|
+
if (notify) {
|
|
40984
|
+
await runInboxNotifyCommand(args);
|
|
40985
|
+
return;
|
|
40986
|
+
}
|
|
40765
40987
|
if (inbox && args.has("follow")) {
|
|
40766
40988
|
if (!args.has("ndjson")) {
|
|
40767
40989
|
throw new Error("inbox --follow requires --ndjson");
|
|
@@ -40838,6 +41060,89 @@ async function runSignalRead(args, inbox) {
|
|
|
40838
41060
|
})}
|
|
40839
41061
|
`);
|
|
40840
41062
|
}
|
|
41063
|
+
async function writeMonitorLine(line) {
|
|
41064
|
+
await new Promise((resolve, reject) => {
|
|
41065
|
+
process.stdout.write(`${line}
|
|
41066
|
+
`, (error) => {
|
|
41067
|
+
if (error) reject(error);
|
|
41068
|
+
else resolve();
|
|
41069
|
+
});
|
|
41070
|
+
});
|
|
41071
|
+
}
|
|
41072
|
+
async function runInboxNotifyCommand(args) {
|
|
41073
|
+
if (!args.has("agent-token-stdin")) {
|
|
41074
|
+
throw new Error(
|
|
41075
|
+
"inbox --notify is for one agent; provide its JSON credential with --agent-token-stdin"
|
|
41076
|
+
);
|
|
41077
|
+
}
|
|
41078
|
+
const cloud = await target(args);
|
|
41079
|
+
const selected = await commandWorkspaceAndCredential(args, cloud, {
|
|
41080
|
+
validateHumanWorkspace: true
|
|
41081
|
+
});
|
|
41082
|
+
const principalId = selected.agent?.principalId ?? null;
|
|
41083
|
+
if (selected.kind !== "agent" || principalId === null) {
|
|
41084
|
+
throw new Error(
|
|
41085
|
+
"inbox --notify needs the full JSON agent credential so its durable cursor is tied to one agent"
|
|
41086
|
+
);
|
|
41087
|
+
}
|
|
41088
|
+
const controller = new AbortController();
|
|
41089
|
+
const stop = () => controller.abort();
|
|
41090
|
+
process.on("SIGINT", stop);
|
|
41091
|
+
process.on("SIGTERM", stop);
|
|
41092
|
+
try {
|
|
41093
|
+
const cursorStore = fileArrivalCursorStore({
|
|
41094
|
+
target: cloud,
|
|
41095
|
+
workspaceId: selected.selectedWorkspace,
|
|
41096
|
+
principalId
|
|
41097
|
+
});
|
|
41098
|
+
const result = await runArrivalWatch({
|
|
41099
|
+
workspaceId: selected.selectedWorkspace,
|
|
41100
|
+
principalId,
|
|
41101
|
+
store: cursorStore,
|
|
41102
|
+
signal: controller.signal,
|
|
41103
|
+
readPage: async ({ after, baseline, limit }) => {
|
|
41104
|
+
const token = selected.session ? await selected.session.bearer() : selected.bearer;
|
|
41105
|
+
return await readAgentSignalPage(
|
|
41106
|
+
cloud,
|
|
41107
|
+
{ kind: "agent", token },
|
|
41108
|
+
{
|
|
41109
|
+
workspaceId: selected.selectedWorkspace,
|
|
41110
|
+
inbox: true,
|
|
41111
|
+
limit,
|
|
41112
|
+
includeStale: false,
|
|
41113
|
+
...baseline ? {} : {
|
|
41114
|
+
ascending: true,
|
|
41115
|
+
...after === null ? {} : { after }
|
|
41116
|
+
}
|
|
41117
|
+
},
|
|
41118
|
+
{ signal: controller.signal }
|
|
41119
|
+
);
|
|
41120
|
+
},
|
|
41121
|
+
emit: async (signal) => {
|
|
41122
|
+
const notification = arrivalNotification(
|
|
41123
|
+
signal,
|
|
41124
|
+
selected.selectedWorkspace,
|
|
41125
|
+
cloud
|
|
41126
|
+
);
|
|
41127
|
+
await writeMonitorLine(
|
|
41128
|
+
args.has("json") ? JSON.stringify(notification) : formatArrivalNotification(notification)
|
|
41129
|
+
);
|
|
41130
|
+
},
|
|
41131
|
+
onRetry: (_error, delayMs) => {
|
|
41132
|
+
process.stderr.write(
|
|
41133
|
+
`cswarm: arrival read failed; still watching and retrying in ${delayMs}ms.
|
|
41134
|
+
`
|
|
41135
|
+
);
|
|
41136
|
+
}
|
|
41137
|
+
});
|
|
41138
|
+
if (result.reason === "error") {
|
|
41139
|
+
throw result.error ?? new Error("arrival watch stopped");
|
|
41140
|
+
}
|
|
41141
|
+
} finally {
|
|
41142
|
+
process.off("SIGINT", stop);
|
|
41143
|
+
process.off("SIGTERM", stop);
|
|
41144
|
+
}
|
|
41145
|
+
}
|
|
40841
41146
|
async function runReceipt(args) {
|
|
40842
41147
|
args.assertShape([
|
|
40843
41148
|
...TARGET_FLAGS,
|
|
@@ -40846,7 +41151,7 @@ async function runReceipt(args) {
|
|
|
40846
41151
|
"json"
|
|
40847
41152
|
], 2);
|
|
40848
41153
|
const signalId = args.positionals[1];
|
|
40849
|
-
if (!
|
|
41154
|
+
if (!UUID_RE19.test(signalId)) {
|
|
40850
41155
|
throw new Error("signal-id must be a UUID");
|
|
40851
41156
|
}
|
|
40852
41157
|
if (!args.has("agent-token-stdin")) {
|
|
@@ -40984,7 +41289,7 @@ async function runInboxFollowCommand(args) {
|
|
|
40984
41289
|
}
|
|
40985
41290
|
}
|
|
40986
41291
|
function listenerUuid(value, flag) {
|
|
40987
|
-
if (!value || !
|
|
41292
|
+
if (!value || !UUID_RE19.test(value)) {
|
|
40988
41293
|
throw new Error(`--${flag} must be a UUID`);
|
|
40989
41294
|
}
|
|
40990
41295
|
return value.toLowerCase();
|
|
@@ -40997,7 +41302,7 @@ function listenerPermissionMode(value) {
|
|
|
40997
41302
|
function listenerStateDirectory(args) {
|
|
40998
41303
|
const value = args.optional("state-dir");
|
|
40999
41304
|
if (value === void 0) return void 0;
|
|
41000
|
-
if (!(0,
|
|
41305
|
+
if (!(0, import_node_path20.isAbsolute)(value)) {
|
|
41001
41306
|
throw new Error("--state-dir must be an absolute path");
|
|
41002
41307
|
}
|
|
41003
41308
|
return value;
|
|
@@ -41328,7 +41633,7 @@ function resolveDetachedClaudeExecutable(executable = "claude-agent-acp", pathEn
|
|
|
41328
41633
|
} catch (error) {
|
|
41329
41634
|
const code = error.code;
|
|
41330
41635
|
if (typeof code === "string") {
|
|
41331
|
-
if ((0,
|
|
41636
|
+
if ((0, import_node_path20.isAbsolute)(executable) || executable.includes("/") || executable.includes("\\")) {
|
|
41332
41637
|
const detail = error instanceof Error ? error.message : code;
|
|
41333
41638
|
throw new Error(
|
|
41334
41639
|
`could not use --claude-executable: ${detail}; install the current bridge with npm install -g @agentclientprotocol/claude-agent-acp@latest if this path should be replaced`
|
|
@@ -41345,7 +41650,7 @@ function resolveDetachedCodexExecutable(executable = "codex-acp", pathEnv = proc
|
|
|
41345
41650
|
} catch (error) {
|
|
41346
41651
|
const code = error.code;
|
|
41347
41652
|
if (typeof code === "string") {
|
|
41348
|
-
if ((0,
|
|
41653
|
+
if ((0, import_node_path20.isAbsolute)(executable) || executable.includes("/") || executable.includes("\\")) {
|
|
41349
41654
|
const detail = error instanceof Error ? error.message : code;
|
|
41350
41655
|
throw new Error(
|
|
41351
41656
|
`could not use --codex-executable: ${detail}; install the current bridge with npm install -g @agentclientprotocol/codex-acp@latest if this path should be replaced`
|
|
@@ -41612,7 +41917,7 @@ async function runListenStart(args) {
|
|
|
41612
41917
|
assertDurableListenerCredential(agent);
|
|
41613
41918
|
const principalId = agent.principalId;
|
|
41614
41919
|
const cwd = args.optional("cwd") ?? process.cwd();
|
|
41615
|
-
if (!(0,
|
|
41920
|
+
if (!(0, import_node_path20.isAbsolute)(cwd)) throw new Error("--cwd must be an absolute path");
|
|
41616
41921
|
const permissionMode = listenerPermissionMode(args.optional("permissions"));
|
|
41617
41922
|
const stateDirectory2 = listenerStateDirectory(args);
|
|
41618
41923
|
const paths = listenerPaths({
|
|
@@ -41649,7 +41954,7 @@ async function runListenStart(args) {
|
|
|
41649
41954
|
});
|
|
41650
41955
|
} else {
|
|
41651
41956
|
const entrypoint = process.argv[1];
|
|
41652
|
-
if (!entrypoint || !(0,
|
|
41957
|
+
if (!entrypoint || !(0, import_node_path20.isAbsolute)(entrypoint)) {
|
|
41653
41958
|
throw new Error("cannot locate the cswarm executable for detached start");
|
|
41654
41959
|
}
|
|
41655
41960
|
const artifact = JSON.stringify(agentCredentialArtifact({
|
|
@@ -41787,7 +42092,7 @@ async function runListenSupervisor(args) {
|
|
|
41787
42092
|
const agent = await stdinCredential();
|
|
41788
42093
|
assertDurableListenerCredential(agent, principalId);
|
|
41789
42094
|
const cwd = args.required("cwd");
|
|
41790
|
-
if (!(0,
|
|
42095
|
+
if (!(0, import_node_path20.isAbsolute)(cwd)) throw new Error("--cwd must be an absolute path");
|
|
41791
42096
|
const status = await runConfiguredListener({
|
|
41792
42097
|
cloud,
|
|
41793
42098
|
workspaceId: workspaceId2,
|
|
@@ -41889,7 +42194,7 @@ function claudeUserPromptHookSnippet() {
|
|
|
41889
42194
|
};
|
|
41890
42195
|
}
|
|
41891
42196
|
function projectClaudeSettingsPath() {
|
|
41892
|
-
return (0,
|
|
42197
|
+
return (0, import_node_path20.join)(process.cwd(), ".claude", "settings.json");
|
|
41893
42198
|
}
|
|
41894
42199
|
function readProjectSettings(path) {
|
|
41895
42200
|
let raw;
|
|
@@ -42001,7 +42306,7 @@ async function runHook(args) {
|
|
|
42001
42306
|
const path = projectClaudeSettingsPath();
|
|
42002
42307
|
const settings = readProjectSettings(path);
|
|
42003
42308
|
const updated = command2 === "install" ? installClaudeHook(settings) : uninstallClaudeHook(settings);
|
|
42004
|
-
(0, import_node_fs7.mkdirSync)((0,
|
|
42309
|
+
(0, import_node_fs7.mkdirSync)((0, import_node_path20.dirname)(path), { recursive: true });
|
|
42005
42310
|
(0, import_node_fs7.writeFileSync)(path, `${JSON.stringify(updated, null, 2)}
|
|
42006
42311
|
`, {
|
|
42007
42312
|
encoding: "utf8",
|
|
@@ -42047,7 +42352,7 @@ async function fileRows(context) {
|
|
|
42047
42352
|
);
|
|
42048
42353
|
}
|
|
42049
42354
|
async function resolveFileSelector(context, selector) {
|
|
42050
|
-
if (
|
|
42355
|
+
if (UUID_RE19.test(selector)) return selector.toLowerCase();
|
|
42051
42356
|
const rows3 = await fileRows(context);
|
|
42052
42357
|
const match = rows3.find(
|
|
42053
42358
|
(row) => row.name.toLowerCase() === selector.toLowerCase()
|
|
@@ -42069,7 +42374,7 @@ async function runFilePut(args) {
|
|
|
42069
42374
|
} catch {
|
|
42070
42375
|
throw new Error(`could not read ${localPath}; check the path and permissions`);
|
|
42071
42376
|
}
|
|
42072
|
-
const name = args.optional("name") ?? (0,
|
|
42377
|
+
const name = args.optional("name") ?? (0, import_node_path20.basename)(localPath);
|
|
42073
42378
|
if (bytes.byteLength > FILE_MAX_VERSION_BYTES) {
|
|
42074
42379
|
throw new Error(
|
|
42075
42380
|
`this file is ${formatFileSize(bytes.byteLength)}; the per-file limit is ${formatFileSize(FILE_MAX_VERSION_BYTES)}, so the upload was not started`
|
|
@@ -42171,7 +42476,7 @@ async function runFileGet(args) {
|
|
|
42171
42476
|
credential: context.selected.bearer
|
|
42172
42477
|
};
|
|
42173
42478
|
const grant = await fileDownloadUrl(send, { fileId, versionN });
|
|
42174
|
-
const destination = args.optional("out") ?? (0,
|
|
42479
|
+
const destination = args.optional("out") ?? (0, import_node_path20.basename)(grant.name);
|
|
42175
42480
|
const bytes = await getObject(context.cloud, grant.download_path);
|
|
42176
42481
|
writeDestination(destination, bytes, args.has("force"), import_node_fs7.writeFileSync);
|
|
42177
42482
|
if (args.has("json")) {
|
|
@@ -42387,7 +42692,7 @@ async function runSeed(args) {
|
|
|
42387
42692
|
throw new Error("DATABASE_URL is required for the fixture bridge");
|
|
42388
42693
|
}
|
|
42389
42694
|
const tokenOut = process.env.SEED_TOKEN_OUT;
|
|
42390
|
-
if (!tokenOut || !(0,
|
|
42695
|
+
if (!tokenOut || !(0, import_node_path20.isAbsolute)(tokenOut)) {
|
|
42391
42696
|
throw new Error("SEED_TOKEN_OUT must be an absolute path");
|
|
42392
42697
|
}
|
|
42393
42698
|
const tokenFile = await (0, import_promises11.open)(tokenOut, "wx", 384).catch((error) => {
|