commonswarm 0.1.37 → 0.1.39
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 +327 -64
- package/package.json +1 -1
package/cswarm.cjs
CHANGED
|
@@ -13520,7 +13520,7 @@ __export(cli_exports, {
|
|
|
13520
13520
|
resolveTurnBudgetOrDefer: () => resolveTurnBudgetOrDefer
|
|
13521
13521
|
});
|
|
13522
13522
|
module.exports = __toCommonJS(cli_exports);
|
|
13523
|
-
var
|
|
13523
|
+
var import_node_crypto20 = require("node:crypto");
|
|
13524
13524
|
var import_node_fs7 = require("node:fs");
|
|
13525
13525
|
var import_promises11 = require("node:fs/promises");
|
|
13526
13526
|
var import_node_path20 = require("node:path");
|
|
@@ -28940,6 +28940,21 @@ function parseMemberRow(value) {
|
|
|
28940
28940
|
display_name: row.display_name
|
|
28941
28941
|
};
|
|
28942
28942
|
}
|
|
28943
|
+
function parseAgentIdentity(value) {
|
|
28944
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
28945
|
+
throw new Error("member read returned a malformed credential identity");
|
|
28946
|
+
}
|
|
28947
|
+
const row = value;
|
|
28948
|
+
if (row.credential_valid !== true) {
|
|
28949
|
+
throw new Error("member read returned a malformed credential validity");
|
|
28950
|
+
}
|
|
28951
|
+
return {
|
|
28952
|
+
credential_valid: true,
|
|
28953
|
+
owner_user_id: checkedUuid2(row.owner_user_id, "identity owner_user_id"),
|
|
28954
|
+
principal_id: checkedUuid2(row.principal_id, "identity principal_id"),
|
|
28955
|
+
workspace_id: checkedUuid2(row.workspace_id, "identity workspace_id")
|
|
28956
|
+
};
|
|
28957
|
+
}
|
|
28943
28958
|
async function readAgentSignalDirectory(target2, token, workspaceId2, fetcherOrOptions = fetch) {
|
|
28944
28959
|
const options = normalizeReadOptions(fetcherOrOptions);
|
|
28945
28960
|
let result;
|
|
@@ -28983,7 +28998,8 @@ async function readAgentSignalDirectory(target2, token, workspaceId2, fetcherOrO
|
|
|
28983
28998
|
})();
|
|
28984
28999
|
return {
|
|
28985
29000
|
members: payload.members.map(parseMemberRow),
|
|
28986
|
-
agents
|
|
29001
|
+
agents,
|
|
29002
|
+
...payload.identity === void 0 ? {} : { identity: parseAgentIdentity(payload.identity) }
|
|
28987
29003
|
};
|
|
28988
29004
|
}
|
|
28989
29005
|
function resolveSignalRecipient(selector, directory) {
|
|
@@ -33435,6 +33451,7 @@ var FileListenerEffectStore = class {
|
|
|
33435
33451
|
};
|
|
33436
33452
|
|
|
33437
33453
|
// src/listener/grok-model.ts
|
|
33454
|
+
var import_node_crypto14 = require("node:crypto");
|
|
33438
33455
|
var import_promises5 = require("node:fs/promises");
|
|
33439
33456
|
var import_node_os7 = require("node:os");
|
|
33440
33457
|
var import_node_path11 = require("node:path");
|
|
@@ -33679,7 +33696,7 @@ var GrokListenerModel = class {
|
|
|
33679
33696
|
clientName: "cswarm-listener"
|
|
33680
33697
|
});
|
|
33681
33698
|
try {
|
|
33682
|
-
await
|
|
33699
|
+
await this.enablePromptsAfterGrokCanary(handle);
|
|
33683
33700
|
this.workerCanary = false;
|
|
33684
33701
|
this.worker = handle;
|
|
33685
33702
|
return handle;
|
|
@@ -33688,6 +33705,37 @@ var GrokListenerModel = class {
|
|
|
33688
33705
|
throw error;
|
|
33689
33706
|
}
|
|
33690
33707
|
}
|
|
33708
|
+
/** Force Grok's measured shell permission path without changing worker cwd. */
|
|
33709
|
+
async enablePromptsAfterGrokCanary(handle) {
|
|
33710
|
+
const sentinelPath = (0, import_node_path11.join)(
|
|
33711
|
+
(0, import_node_os7.tmpdir)(),
|
|
33712
|
+
`cswarm-grok-permission-canary-${process.pid}-${(0, import_node_crypto14.randomUUID)()}`
|
|
33713
|
+
);
|
|
33714
|
+
let canaryError;
|
|
33715
|
+
try {
|
|
33716
|
+
await handle.session.enablePromptsAfterCanary({
|
|
33717
|
+
timeoutMs: ACP_CANARY_TIMEOUT_MS,
|
|
33718
|
+
probeText: `Use a shell command to create ${sentinelPath} with content CSWARM_CANARY_NOOP. You must use the shell. Do nothing else.`,
|
|
33719
|
+
...this.options.onCanaryAttempt ? { onAttempt: this.options.onCanaryAttempt } : {}
|
|
33720
|
+
});
|
|
33721
|
+
} catch (error) {
|
|
33722
|
+
canaryError = error;
|
|
33723
|
+
}
|
|
33724
|
+
let sentinelCreated = false;
|
|
33725
|
+
try {
|
|
33726
|
+
await (0, import_promises5.lstat)(sentinelPath);
|
|
33727
|
+
sentinelCreated = true;
|
|
33728
|
+
await (0, import_promises5.unlink)(sentinelPath);
|
|
33729
|
+
} catch (error) {
|
|
33730
|
+
if (error.code !== "ENOENT") throw error;
|
|
33731
|
+
}
|
|
33732
|
+
if (sentinelCreated) {
|
|
33733
|
+
throw new AcpPermissionCanaryError(
|
|
33734
|
+
"Grok bridge wrote the permission canary sentinel; permission denial did not block the write"
|
|
33735
|
+
);
|
|
33736
|
+
}
|
|
33737
|
+
if (canaryError !== void 0) throw canaryError;
|
|
33738
|
+
}
|
|
33691
33739
|
/** Validate the operator's login artifact without copying or replacing its home. */
|
|
33692
33740
|
async validateLocalAuth() {
|
|
33693
33741
|
if (this.options.open) return;
|
|
@@ -33744,7 +33792,7 @@ var GrokListenerModel = class {
|
|
|
33744
33792
|
};
|
|
33745
33793
|
|
|
33746
33794
|
// src/listener/opencode-model.ts
|
|
33747
|
-
var
|
|
33795
|
+
var import_node_crypto15 = require("node:crypto");
|
|
33748
33796
|
var import_promises6 = require("node:fs/promises");
|
|
33749
33797
|
var import_node_path12 = require("node:path");
|
|
33750
33798
|
function asError(error) {
|
|
@@ -33770,7 +33818,7 @@ var OpenCodeListenerModel = class {
|
|
|
33770
33818
|
prepareWorkerCwd;
|
|
33771
33819
|
permissionMode;
|
|
33772
33820
|
pendingOpenWaitMs;
|
|
33773
|
-
instanceId = (0,
|
|
33821
|
+
instanceId = (0, import_node_crypto15.randomUUID)();
|
|
33774
33822
|
worker = null;
|
|
33775
33823
|
workerHome = null;
|
|
33776
33824
|
/** Worker homes retained after failed close or unsettled open. */
|
|
@@ -34132,7 +34180,13 @@ var OpenCodeListenerModel = class {
|
|
|
34132
34180
|
if (this.closed || this.cancelled || generation !== this.openGeneration) {
|
|
34133
34181
|
await this.performSingleOwnerCleanup(handle, home, this.instanceId, pending);
|
|
34134
34182
|
}
|
|
34135
|
-
|
|
34183
|
+
if (this.options.onCanaryAttempt) {
|
|
34184
|
+
await handle.session.enablePromptsAfterCanary({
|
|
34185
|
+
onAttempt: this.options.onCanaryAttempt
|
|
34186
|
+
});
|
|
34187
|
+
} else {
|
|
34188
|
+
await handle.session.enablePromptsAfterCanary();
|
|
34189
|
+
}
|
|
34136
34190
|
if (this.closed || this.cancelled || generation !== this.openGeneration) {
|
|
34137
34191
|
await this.performSingleOwnerCleanup(handle, home, this.instanceId, pending);
|
|
34138
34192
|
}
|
|
@@ -34177,7 +34231,7 @@ var OpenCodeListenerModel = class {
|
|
|
34177
34231
|
};
|
|
34178
34232
|
|
|
34179
34233
|
// src/listener/claude-model.ts
|
|
34180
|
-
var
|
|
34234
|
+
var import_node_crypto16 = require("node:crypto");
|
|
34181
34235
|
var import_promises7 = require("node:fs/promises");
|
|
34182
34236
|
var import_node_os8 = require("node:os");
|
|
34183
34237
|
var import_node_path13 = require("node:path");
|
|
@@ -34320,13 +34374,14 @@ var ClaudeListenerModel = class {
|
|
|
34320
34374
|
async enablePromptsAfterClaudeCanary(handle) {
|
|
34321
34375
|
const sentinelPath = (0, import_node_path13.join)(
|
|
34322
34376
|
(0, import_node_os8.tmpdir)(),
|
|
34323
|
-
`cswarm-claude-permission-canary-${process.pid}-${(0,
|
|
34377
|
+
`cswarm-claude-permission-canary-${process.pid}-${(0, import_node_crypto16.randomUUID)()}`
|
|
34324
34378
|
);
|
|
34325
34379
|
let sentinelCreated = false;
|
|
34326
34380
|
try {
|
|
34327
34381
|
await handle.session.enablePromptsAfterCanary({
|
|
34328
34382
|
timeoutMs: ACP_CANARY_TIMEOUT_MS,
|
|
34329
|
-
probeText: `Create the file ${sentinelPath} using the Write tool with content CSWARM_CANARY_NOOP. You must use the Write tool. Do nothing else
|
|
34383
|
+
probeText: `Create the file ${sentinelPath} using the Write tool with content CSWARM_CANARY_NOOP. You must use the Write tool. Do nothing else.`,
|
|
34384
|
+
...this.options.onCanaryAttempt ? { onAttempt: this.options.onCanaryAttempt } : {}
|
|
34330
34385
|
});
|
|
34331
34386
|
} finally {
|
|
34332
34387
|
try {
|
|
@@ -34346,7 +34401,7 @@ var ClaudeListenerModel = class {
|
|
|
34346
34401
|
};
|
|
34347
34402
|
|
|
34348
34403
|
// src/listener/codex-model.ts
|
|
34349
|
-
var
|
|
34404
|
+
var import_node_crypto17 = require("node:crypto");
|
|
34350
34405
|
var import_promises8 = require("node:fs/promises");
|
|
34351
34406
|
var import_node_os9 = require("node:os");
|
|
34352
34407
|
var import_node_path14 = require("node:path");
|
|
@@ -34489,13 +34544,14 @@ var CodexListenerModel = class {
|
|
|
34489
34544
|
async enablePromptsAfterCodexCanary(handle) {
|
|
34490
34545
|
const sentinelPath = (0, import_node_path14.join)(
|
|
34491
34546
|
(0, import_node_os9.tmpdir)(),
|
|
34492
|
-
`cswarm-codex-permission-canary-${process.pid}-${(0,
|
|
34547
|
+
`cswarm-codex-permission-canary-${process.pid}-${(0, import_node_crypto17.randomUUID)()}`
|
|
34493
34548
|
);
|
|
34494
34549
|
let sentinelCreated = false;
|
|
34495
34550
|
try {
|
|
34496
34551
|
await handle.session.enablePromptsAfterCanary({
|
|
34497
34552
|
timeoutMs: ACP_CANARY_TIMEOUT_MS,
|
|
34498
|
-
probeText: `Use a shell command to create ${sentinelPath} with content CSWARM_CANARY_NOOP. You must use the shell. Do nothing else
|
|
34553
|
+
probeText: `Use a shell command to create ${sentinelPath} with content CSWARM_CANARY_NOOP. You must use the shell. Do nothing else.`,
|
|
34554
|
+
...this.options.onCanaryAttempt ? { onAttempt: this.options.onCanaryAttempt } : {}
|
|
34499
34555
|
});
|
|
34500
34556
|
} finally {
|
|
34501
34557
|
try {
|
|
@@ -34515,7 +34571,7 @@ var CodexListenerModel = class {
|
|
|
34515
34571
|
};
|
|
34516
34572
|
|
|
34517
34573
|
// src/listener/runtime.ts
|
|
34518
|
-
var
|
|
34574
|
+
var import_node_crypto18 = require("node:crypto");
|
|
34519
34575
|
|
|
34520
34576
|
// src/cloud/delivery.ts
|
|
34521
34577
|
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;
|
|
@@ -35471,7 +35527,7 @@ function sameEffectSignal(record, signal) {
|
|
|
35471
35527
|
return record.signalId === signal.id.toLowerCase() && record.signalKind === signal.kind && record.askBody === signal.body && record.askUntil === signal.until && record.senderOwnerRelation === (signal.sender_owner_relation ?? "unknown");
|
|
35472
35528
|
}
|
|
35473
35529
|
function immutableSignalFingerprint(signalId, signalKind2, body, until, senderOwnerRelation) {
|
|
35474
|
-
return (0,
|
|
35530
|
+
return (0, import_node_crypto18.createHash)("sha256").update(JSON.stringify([
|
|
35475
35531
|
signalId,
|
|
35476
35532
|
signalKind2,
|
|
35477
35533
|
body,
|
|
@@ -36461,6 +36517,7 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
|
36461
36517
|
"stoppedAt",
|
|
36462
36518
|
"lastSignalId",
|
|
36463
36519
|
"lastErrorCode",
|
|
36520
|
+
"lastErrorDetail",
|
|
36464
36521
|
"providerVersion",
|
|
36465
36522
|
"providerLastMeasuredVersion",
|
|
36466
36523
|
"lastWorkerStderrTail",
|
|
@@ -36522,7 +36579,7 @@ function parseStatus(raw) {
|
|
|
36522
36579
|
const nullableUuid2 = (candidate) => candidate === null || typeof candidate === "string" && UUID_RE15.test(candidate);
|
|
36523
36580
|
const nullableCount = (candidate) => candidate === null || typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate >= 0;
|
|
36524
36581
|
const nullableTimestamp2 = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
|
|
36525
|
-
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)) {
|
|
36582
|
+
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.lastErrorDetail === void 0 || row.lastErrorDetail === null || typeof row.lastErrorDetail === "string" && row.lastErrorDetail.length > 0 && row.lastErrorDetail.length <= 2048 && !/swm_(?:agt|inv|cap)_/i.test(row.lastErrorDetail)) || !(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)) {
|
|
36526
36583
|
throw new Error("stored listener status is malformed");
|
|
36527
36584
|
}
|
|
36528
36585
|
const routeMode = row.routeMode ?? "worker";
|
|
@@ -36538,6 +36595,7 @@ function parseStatus(raw) {
|
|
|
36538
36595
|
lastTerminalDeliveryFailureAt: row.lastTerminalDeliveryFailureAt ?? null,
|
|
36539
36596
|
lastClaimAt: row.lastClaimAt ?? null,
|
|
36540
36597
|
lastAckAt: row.lastAckAt ?? null,
|
|
36598
|
+
lastErrorDetail: row.lastErrorDetail ?? null,
|
|
36541
36599
|
lastWorkerStderrTail: row.lastWorkerStderrTail ?? null,
|
|
36542
36600
|
providerVersion: row.providerVersion ?? null,
|
|
36543
36601
|
providerLastMeasuredVersion: row.providerLastMeasuredVersion ?? null,
|
|
@@ -36555,6 +36613,9 @@ async function writeListenerStatus(paths, status) {
|
|
|
36555
36613
|
throw new Error("listener status is missing delivery metadata fields");
|
|
36556
36614
|
}
|
|
36557
36615
|
}
|
|
36616
|
+
if (!("lastErrorDetail" in parsed)) {
|
|
36617
|
+
throw new Error("listener status is missing local error detail metadata");
|
|
36618
|
+
}
|
|
36558
36619
|
parseStatus(serialized);
|
|
36559
36620
|
await writeSecureJsonFile(paths.statusPath, serialized);
|
|
36560
36621
|
}
|
|
@@ -36572,6 +36633,9 @@ async function appendListenerEvent(paths, event) {
|
|
|
36572
36633
|
"status",
|
|
36573
36634
|
"failure_code",
|
|
36574
36635
|
"attempt",
|
|
36636
|
+
"total",
|
|
36637
|
+
"passed",
|
|
36638
|
+
"reason",
|
|
36575
36639
|
"delay_ms",
|
|
36576
36640
|
"index",
|
|
36577
36641
|
"delivery_mode",
|
|
@@ -36608,6 +36672,15 @@ async function appendListenerEvent(paths, event) {
|
|
|
36608
36672
|
if (!allowed.has(key2)) {
|
|
36609
36673
|
throw new Error(`listener event field is not allowed: ${key2}`);
|
|
36610
36674
|
}
|
|
36675
|
+
if ((key2 === "attempt" || key2 === "total") && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 1)) {
|
|
36676
|
+
throw new Error("listener event attempt count is not allowed");
|
|
36677
|
+
}
|
|
36678
|
+
if (key2 === "passed" && typeof value !== "boolean") {
|
|
36679
|
+
throw new Error("listener event canary result is not allowed");
|
|
36680
|
+
}
|
|
36681
|
+
if (key2 === "reason" && !(value === null || typeof value === "string" && value.length > 0)) {
|
|
36682
|
+
throw new Error("listener event canary reason is not allowed");
|
|
36683
|
+
}
|
|
36611
36684
|
if (key2 === "delivery_mode" && !(value === null || typeof value === "string" && deliveryModes.has(value))) {
|
|
36612
36685
|
throw new Error("listener event delivery mode is not allowed");
|
|
36613
36686
|
}
|
|
@@ -36642,6 +36715,9 @@ async function appendListenerEvent(paths, event) {
|
|
|
36642
36715
|
throw new Error("listener event contains unsafe text");
|
|
36643
36716
|
}
|
|
36644
36717
|
}
|
|
36718
|
+
if (event.event === "listener_canary_attempt" && (typeof event.attempt !== "number" || typeof event.total !== "number" || event.attempt > event.total || typeof event.passed !== "boolean" || !(event.reason === null || typeof event.reason === "string"))) {
|
|
36719
|
+
throw new Error("listener canary attempt event is incomplete");
|
|
36720
|
+
}
|
|
36645
36721
|
await ensureSecureStateDirectory(paths.instanceDirectory);
|
|
36646
36722
|
const serialized = `${JSON.stringify(event)}
|
|
36647
36723
|
`;
|
|
@@ -36871,7 +36947,7 @@ async function queryListenerControl(paths, command2, timeoutMs = CONTROL_TIMEOUT
|
|
|
36871
36947
|
}
|
|
36872
36948
|
|
|
36873
36949
|
// src/listener/supervisor.ts
|
|
36874
|
-
var
|
|
36950
|
+
var import_node_crypto19 = require("node:crypto");
|
|
36875
36951
|
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;
|
|
36876
36952
|
var LISTENER_RESTART_MAX_ATTEMPTS = 5;
|
|
36877
36953
|
var LISTENER_RESTART_INITIAL_MS = 1e3;
|
|
@@ -36915,6 +36991,17 @@ function safeErrorCode(error) {
|
|
|
36915
36991
|
const name = error.name.toLowerCase().replace(/[^a-z0-9_-]+/g, "_");
|
|
36916
36992
|
return name.slice(0, 96) || "listener_error";
|
|
36917
36993
|
}
|
|
36994
|
+
function localDiagnostic(message, maxChars) {
|
|
36995
|
+
const redacted = message.replace(
|
|
36996
|
+
/swm_(?:agt|inv|cap)_[^\s"'\\]*/gi,
|
|
36997
|
+
"[redacted]"
|
|
36998
|
+
).trim();
|
|
36999
|
+
if (redacted.length === 0) return null;
|
|
37000
|
+
return redacted.slice(0, maxChars);
|
|
37001
|
+
}
|
|
37002
|
+
function safeErrorDetail(error) {
|
|
37003
|
+
return localDiagnostic(error.message, 2048);
|
|
37004
|
+
}
|
|
36918
37005
|
var TAIL_SERIALIZED_BUDGET_BYTES = 3e3;
|
|
36919
37006
|
function fitWorkerStderrTailForLog(tail) {
|
|
36920
37007
|
let fitted = tail.trim();
|
|
@@ -36936,7 +37023,7 @@ async function runListenerSupervisor(options) {
|
|
|
36936
37023
|
const now = options.now ?? Date.now;
|
|
36937
37024
|
const startedAt = iso2(now);
|
|
36938
37025
|
const controller = new AbortController();
|
|
36939
|
-
const proposedInstanceId = (0,
|
|
37026
|
+
const proposedInstanceId = (0, import_node_crypto19.randomUUID)();
|
|
36940
37027
|
let status = {
|
|
36941
37028
|
version: 1,
|
|
36942
37029
|
instanceId: proposedInstanceId,
|
|
@@ -36953,6 +37040,7 @@ async function runListenerSupervisor(options) {
|
|
|
36953
37040
|
stoppedAt: null,
|
|
36954
37041
|
lastSignalId: null,
|
|
36955
37042
|
lastErrorCode: null,
|
|
37043
|
+
lastErrorDetail: null,
|
|
36956
37044
|
providerVersion: null,
|
|
36957
37045
|
providerLastMeasuredVersion: null,
|
|
36958
37046
|
lastWorkerStderrTail: null,
|
|
@@ -37028,6 +37116,7 @@ async function runListenerSupervisor(options) {
|
|
|
37028
37116
|
transition("ready", {
|
|
37029
37117
|
readyAt: event.ts,
|
|
37030
37118
|
lastErrorCode: null,
|
|
37119
|
+
lastErrorDetail: null,
|
|
37031
37120
|
lastWorkerStderrTail: null,
|
|
37032
37121
|
providerVersion: versionNotice?.runningVersion ?? null,
|
|
37033
37122
|
providerLastMeasuredVersion: versionNotice?.lastMeasuredVersion ?? null
|
|
@@ -37035,6 +37124,17 @@ async function runListenerSupervisor(options) {
|
|
|
37035
37124
|
log({ ts: event.ts, event: "listener_ready" });
|
|
37036
37125
|
return;
|
|
37037
37126
|
}
|
|
37127
|
+
if (event.type === "canary_attempt") {
|
|
37128
|
+
log({
|
|
37129
|
+
ts: event.ts,
|
|
37130
|
+
event: "listener_canary_attempt",
|
|
37131
|
+
attempt: event.attempt,
|
|
37132
|
+
total: event.total,
|
|
37133
|
+
passed: event.passed,
|
|
37134
|
+
reason: event.reason === null ? null : localDiagnostic(event.reason, 128)
|
|
37135
|
+
});
|
|
37136
|
+
return;
|
|
37137
|
+
}
|
|
37038
37138
|
if (event.type === "effect") {
|
|
37039
37139
|
status = {
|
|
37040
37140
|
...status,
|
|
@@ -37228,6 +37328,7 @@ async function runListenerSupervisor(options) {
|
|
|
37228
37328
|
transition("starting", {
|
|
37229
37329
|
readyAt: null,
|
|
37230
37330
|
lastErrorCode: restartCode,
|
|
37331
|
+
lastErrorDetail: safeErrorDetail(stop.error),
|
|
37231
37332
|
lastWorkerStderrTail: restartStderrTail,
|
|
37232
37333
|
providerVersion: null,
|
|
37233
37334
|
providerLastMeasuredVersion: null
|
|
@@ -37243,6 +37344,7 @@ async function runListenerSupervisor(options) {
|
|
|
37243
37344
|
transition("stopped", {
|
|
37244
37345
|
stoppedAt,
|
|
37245
37346
|
lastErrorCode: null,
|
|
37347
|
+
lastErrorDetail: null,
|
|
37246
37348
|
lastWorkerStderrTail: null
|
|
37247
37349
|
});
|
|
37248
37350
|
log({ ts: stoppedAt, event: "listener_stopped" });
|
|
@@ -37252,6 +37354,7 @@ async function runListenerSupervisor(options) {
|
|
|
37252
37354
|
transition("failed", {
|
|
37253
37355
|
stoppedAt,
|
|
37254
37356
|
lastErrorCode: code,
|
|
37357
|
+
lastErrorDetail: safeErrorDetail(stop.error),
|
|
37255
37358
|
lastWorkerStderrTail: failedStderrTail
|
|
37256
37359
|
});
|
|
37257
37360
|
log({
|
|
@@ -37273,6 +37376,9 @@ async function runListenerSupervisor(options) {
|
|
|
37273
37376
|
transition("failed", {
|
|
37274
37377
|
stoppedAt,
|
|
37275
37378
|
lastErrorCode: code,
|
|
37379
|
+
lastErrorDetail: safeErrorDetail(
|
|
37380
|
+
error instanceof Error ? error : new Error(String(error))
|
|
37381
|
+
),
|
|
37276
37382
|
lastWorkerStderrTail: failedStderrTail
|
|
37277
37383
|
});
|
|
37278
37384
|
log({
|
|
@@ -38743,6 +38849,7 @@ async function runListenerHookCheck(options = {}) {
|
|
|
38743
38849
|
var import_meta = {};
|
|
38744
38850
|
var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
38745
38851
|
"about",
|
|
38852
|
+
"agent-token-file",
|
|
38746
38853
|
"agent-token-stdin",
|
|
38747
38854
|
"all-devices",
|
|
38748
38855
|
"anon-key",
|
|
@@ -38832,8 +38939,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
|
|
|
38832
38939
|
AGENT_CREDENTIAL_MESSAGE_D088
|
|
38833
38940
|
];
|
|
38834
38941
|
function packageVersion() {
|
|
38835
|
-
if ("0.1.
|
|
38836
|
-
return "0.1.
|
|
38942
|
+
if ("0.1.39".length > 0) {
|
|
38943
|
+
return "0.1.39";
|
|
38837
38944
|
}
|
|
38838
38945
|
try {
|
|
38839
38946
|
const value = JSON.parse(
|
|
@@ -38922,7 +39029,7 @@ var Arguments = class {
|
|
|
38922
39029
|
};
|
|
38923
39030
|
var TARGET_FLAGS = ["url", "anon-key", "force-file-store"];
|
|
38924
39031
|
var ROUTE_FLAGS = ["workspace-id", "repo-mapping-id"];
|
|
38925
|
-
var CREDENTIAL_FLAGS = ["agent-token-stdin"];
|
|
39032
|
+
var CREDENTIAL_FLAGS = ["agent-token-file", "agent-token-stdin"];
|
|
38926
39033
|
var TASK_FLAGS = [
|
|
38927
39034
|
"task-id",
|
|
38928
39035
|
"slug",
|
|
@@ -38938,6 +39045,8 @@ var TASK_FLAGS = [
|
|
|
38938
39045
|
var UsageError = class extends Error {
|
|
38939
39046
|
};
|
|
38940
39047
|
function usage() {
|
|
39048
|
+
const agentCredential2 = "[--agent-token-file <path> | --agent-token-stdin]";
|
|
39049
|
+
const requiredAgentCredential = "(--agent-token-file <path> | --agent-token-stdin)";
|
|
38941
39050
|
return `cswarm ${CLI_BUILD_VERSION} (protocol ${CLIENT_PROTOCOL_VERSION})
|
|
38942
39051
|
|
|
38943
39052
|
Usage:
|
|
@@ -38947,23 +39056,24 @@ Usage:
|
|
|
38947
39056
|
cswarm target set --url <project-url> --anon-key <key> [--json]
|
|
38948
39057
|
cswarm target clear [--json]
|
|
38949
39058
|
cswarm status [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--json]
|
|
38950
|
-
cswarm
|
|
38951
|
-
cswarm
|
|
38952
|
-
cswarm
|
|
38953
|
-
cswarm
|
|
38954
|
-
cswarm
|
|
38955
|
-
cswarm
|
|
38956
|
-
cswarm
|
|
38957
|
-
cswarm
|
|
38958
|
-
cswarm inbox
|
|
38959
|
-
cswarm inbox --
|
|
38960
|
-
cswarm
|
|
38961
|
-
cswarm file
|
|
38962
|
-
cswarm file
|
|
38963
|
-
cswarm file
|
|
38964
|
-
cswarm file
|
|
38965
|
-
cswarm
|
|
38966
|
-
cswarm
|
|
39059
|
+
cswarm whoami ${requiredAgentCredential} [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--json]
|
|
39060
|
+
cswarm members [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39061
|
+
cswarm working-on "<what>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--about <ref>] [--until <dur>] [--json]
|
|
39062
|
+
cswarm note "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--to <member|agent>] [--about <ref>] [--until <dur>] [--json] # text: 1..8000 characters
|
|
39063
|
+
cswarm ask "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--to <member|agent>] [--about <ref>] [--until <dur>] [--wait <seconds>] [--json] # text: 1..8000 characters
|
|
39064
|
+
cswarm reply <signal-id> "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--until <dur>] [--json]
|
|
39065
|
+
cswarm receipt <signal-id> ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> [--json]
|
|
39066
|
+
cswarm feed [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--about <ref>] [--kind <kind>] [--since <timestamp>] [--limit <n>] [--include-stale] [--json]
|
|
39067
|
+
cswarm inbox [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--kind <kind>] [--about <ref>] [--since <timestamp>] [--limit <n>] [--include-stale] [--wait <seconds>] [--json]
|
|
39068
|
+
cswarm inbox --notify ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> [--json]
|
|
39069
|
+
cswarm inbox --follow --ndjson [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--kind <kind>] [--about <ref>] [--since <timestamp>] [--limit <n>] [--include-stale]
|
|
39070
|
+
cswarm file put <local-path> [--name <name>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39071
|
+
cswarm file ls [--include-tombstoned] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39072
|
+
cswarm file get <name|file-id> [--version <n>] [--out <local-path>] [--force] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39073
|
+
cswarm file rm <name|file-id> [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39074
|
+
cswarm file restore <name|file-id> [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39075
|
+
cswarm feedback "<text>" --kind bug|idea|friction [--about <ref>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39076
|
+
cswarm listen start ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> --provider grok|opencode|claude|codex [--cwd <absolute-path>] [--model <model>] [--effort <level>] [--permissions deny|allow] [--grok-executable <path>] [--opencode-executable <path>] [--claude-executable <path>] [--codex-executable <path>] [--turn-budget <duration>] [--route worker|main|split] [--defer-over <chars>] [--foreground] [--json]
|
|
38967
39077
|
cswarm listen status [--url <url> --anon-key <key>] --workspace-id <uuid> --principal-id <uuid> [--json]
|
|
38968
39078
|
cswarm listen stop [--url <url> --anon-key <key>] --workspace-id <uuid> --principal-id <uuid> [--json]
|
|
38969
39079
|
cswarm hook check [--cooldown <seconds>]
|
|
@@ -38985,26 +39095,33 @@ Usage:
|
|
|
38985
39095
|
cswarm principal revoke [--url <url> --anon-key <key>] [--workspace-id <uuid>] --principal-id <uuid>
|
|
38986
39096
|
cswarm token mint [--url <url> --anon-key <key>] [--workspace-id <uuid>] --principal-id <uuid> --run-id <uuid> --task-id <uuid> --epoch <n> [--ttl-ms <ms>]
|
|
38987
39097
|
cswarm token revoke [--url <url> --anon-key <key>] [--workspace-id <uuid>] --token-id <uuid>
|
|
38988
|
-
cswarm token revoke
|
|
39098
|
+
cswarm token revoke ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> [--token-id <uuid>]
|
|
38989
39099
|
cswarm link new [--url <url> --anon-key <key>] [--workspace-id <uuid>] --task-id <uuid> [--ttl-ms <ms>] [--site <origin>] [--json]
|
|
38990
39100
|
cswarm link revoke [--url <url> --anon-key <key>] [--workspace-id <uuid>] --capability-id <uuid> [--json]
|
|
38991
|
-
cswarm command <kind> [--url <url> --anon-key <key>] [--workspace-id <uuid>] [command fields]
|
|
38992
|
-
cswarm dogfood [--url <url> --anon-key <key>] [--workspace-id <uuid>] --slug <slug> --branch <branch> --head-sha <sha> --evidence <ref>
|
|
39101
|
+
cswarm command <kind> [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [command fields]
|
|
39102
|
+
cswarm dogfood [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} --slug <slug> --branch <branch> --head-sha <sha> --evidence <ref>
|
|
38993
39103
|
cswarm seed-fixture --uid <auth-user-uuid> [--device-id <uuid>] [--workspace-id <uuid>]
|
|
38994
39104
|
|
|
38995
39105
|
Credential selection for command/dogfood:
|
|
38996
39106
|
default refresh the human login from secure storage
|
|
39107
|
+
--agent-token-file read the credential from an owned 0600 regular file in an owned
|
|
39108
|
+
0700 directory. The path may appear in argv; the secret never does.
|
|
38997
39109
|
--agent-token-stdin read a mint/seed credential artifact, or a bare swm_agt_ token, from
|
|
38998
39110
|
stdin. WHICH FORMS ARE ACCEPTED DEPENDS ON WHAT THE SUBCOMMAND DOES
|
|
38999
39111
|
WITH THE CREDENTIAL. A subcommand that only reads takes either form.
|
|
39000
39112
|
One that persists or references the credential needs the complete
|
|
39001
39113
|
JSON artifact, because it needs a field a bare secret does not carry:
|
|
39114
|
+
whoami reads server-proven identity -- complete or bare form
|
|
39002
39115
|
members reads only -- either form
|
|
39116
|
+
working-on, note, ask, reply, feed, inbox
|
|
39117
|
+
signal command/read only -- either form
|
|
39003
39118
|
receipt reads only -- either form
|
|
39004
39119
|
inbox --notify persists a per-agent cursor -- needs principal_id
|
|
39005
39120
|
file put, file ls, file get, file rm, file restore
|
|
39006
39121
|
read and command, nothing persisted -- either form
|
|
39007
39122
|
feedback command only, nothing persisted -- either form
|
|
39123
|
+
command, dogfood
|
|
39124
|
+
task protocol commands -- either form
|
|
39008
39125
|
listen start persists durable state, rotates -- needs expires_at
|
|
39009
39126
|
hook check reads the listener's owned 0600 credential state;
|
|
39010
39127
|
never accepts or prints a credential
|
|
@@ -39044,7 +39161,7 @@ hook install claude prints the UserPromptSubmit JSON by default and changes the
|
|
|
39044
39161
|
project's .claude/settings.json only with --write; uninstall also requires --write.
|
|
39045
39162
|
|
|
39046
39163
|
Invite, legacy token accept, principal create/revoke, human token mint/revoke, link, new, and workspace close require a
|
|
39047
|
-
stored human login. Agent self-surrender of a token uses --agent-token-stdin and never takes the secret on argv. Invite-link accept signs in when needed, then accepts and
|
|
39164
|
+
stored human login. Agent self-surrender of a token uses --agent-token-file or --agent-token-stdin and never takes the secret on argv. Invite-link accept signs in when needed, then accepts and
|
|
39048
39165
|
registers one principal. Invitation links, agent credentials, and capability links
|
|
39049
39166
|
appear only in fresh success responses.
|
|
39050
39167
|
|
|
@@ -39087,7 +39204,7 @@ workspace only for callers who already hold that full-database credential; it
|
|
|
39087
39204
|
grants no new authority and is not a governed product workspace-creation path.`;
|
|
39088
39205
|
}
|
|
39089
39206
|
async function target(args) {
|
|
39090
|
-
const mode3 = args
|
|
39207
|
+
const mode3 = hasAgentCredential(args) ? "agent" : "human";
|
|
39091
39208
|
try {
|
|
39092
39209
|
return await resolveCloudTarget({
|
|
39093
39210
|
explicitUrl: args.optional("url"),
|
|
@@ -39211,6 +39328,51 @@ async function stdinCredential() {
|
|
|
39211
39328
|
const credential = value.trim();
|
|
39212
39329
|
return parsedAgentCredential(credential);
|
|
39213
39330
|
}
|
|
39331
|
+
var AgentTokenFileError = class extends Error {
|
|
39332
|
+
constructor(code, message) {
|
|
39333
|
+
super(`[${code}] ${message}`);
|
|
39334
|
+
this.code = code;
|
|
39335
|
+
}
|
|
39336
|
+
code;
|
|
39337
|
+
name = "AgentTokenFileError";
|
|
39338
|
+
};
|
|
39339
|
+
function hasAgentCredential(args) {
|
|
39340
|
+
return CREDENTIAL_FLAGS.some((flag) => args.has(flag));
|
|
39341
|
+
}
|
|
39342
|
+
async function agentCredential(args, options = {}) {
|
|
39343
|
+
const fromFile = args.optional("agent-token-file");
|
|
39344
|
+
const fromStdin = args.has("agent-token-stdin");
|
|
39345
|
+
if (fromFile !== void 0 && fromStdin) {
|
|
39346
|
+
throw new Error(
|
|
39347
|
+
"use exactly one agent credential source: --agent-token-file or --agent-token-stdin"
|
|
39348
|
+
);
|
|
39349
|
+
}
|
|
39350
|
+
if (fromFile !== void 0) {
|
|
39351
|
+
let value;
|
|
39352
|
+
try {
|
|
39353
|
+
value = await readSecureJsonFile(fromFile, 4096);
|
|
39354
|
+
} catch (error) {
|
|
39355
|
+
const detail = error instanceof Error ? error.message : "unknown read failure";
|
|
39356
|
+
throw new AgentTokenFileError(
|
|
39357
|
+
"agent_token_file_unreadable",
|
|
39358
|
+
`could not read --agent-token-file securely: ${detail}`
|
|
39359
|
+
);
|
|
39360
|
+
}
|
|
39361
|
+
if (value === null) {
|
|
39362
|
+
throw new AgentTokenFileError(
|
|
39363
|
+
"agent_token_file_missing",
|
|
39364
|
+
`--agent-token-file does not exist: ${fromFile}`
|
|
39365
|
+
);
|
|
39366
|
+
}
|
|
39367
|
+
return parsedAgentCredential(value.trim());
|
|
39368
|
+
}
|
|
39369
|
+
if (fromStdin || options.implicitStdin === true) {
|
|
39370
|
+
return await stdinCredential();
|
|
39371
|
+
}
|
|
39372
|
+
throw new Error(
|
|
39373
|
+
"provide the agent credential with --agent-token-file <path> or --agent-token-stdin"
|
|
39374
|
+
);
|
|
39375
|
+
}
|
|
39214
39376
|
async function invitationCredential(args) {
|
|
39215
39377
|
if (args.has("invitation-token-stdin")) {
|
|
39216
39378
|
args.assertShape([...TARGET_FLAGS, "invitation-token-stdin"], 1);
|
|
@@ -39377,7 +39539,7 @@ async function runNew(args) {
|
|
|
39377
39539
|
assertWorkspaceName(name);
|
|
39378
39540
|
const cloud = await target(args);
|
|
39379
39541
|
const human = await humanCredential(args, cloud);
|
|
39380
|
-
const proposedId = (0,
|
|
39542
|
+
const proposedId = (0, import_node_crypto20.randomUUID)();
|
|
39381
39543
|
let result;
|
|
39382
39544
|
try {
|
|
39383
39545
|
result = await new ThinCommandClient(cloud).sendConnect({
|
|
@@ -40059,13 +40221,13 @@ async function runToken(args) {
|
|
|
40059
40221
|
}));
|
|
40060
40222
|
}
|
|
40061
40223
|
async function runTokenRevoke(args) {
|
|
40062
|
-
if (args
|
|
40224
|
+
if (hasAgentCredential(args)) {
|
|
40063
40225
|
args.assertShape(
|
|
40064
|
-
[...TARGET_FLAGS, "workspace-id", "token-id",
|
|
40226
|
+
[...TARGET_FLAGS, "workspace-id", "token-id", ...CREDENTIAL_FLAGS, "json"],
|
|
40065
40227
|
2
|
|
40066
40228
|
);
|
|
40067
40229
|
const cloud2 = await target(args);
|
|
40068
|
-
const agent = await
|
|
40230
|
+
const agent = await agentCredential(args);
|
|
40069
40231
|
const override = workspaceOverride(
|
|
40070
40232
|
args.optional("workspace-id"),
|
|
40071
40233
|
process.env.SWARM_CLOUD_WORKSPACE_ID
|
|
@@ -40077,13 +40239,13 @@ async function runTokenRevoke(args) {
|
|
|
40077
40239
|
}
|
|
40078
40240
|
if (agent.tokenId === null) {
|
|
40079
40241
|
throw new Error(
|
|
40080
|
-
"agent credential
|
|
40242
|
+
"agent credential has no token_id; use the JSON artifact from token mint, not a bare secret"
|
|
40081
40243
|
);
|
|
40082
40244
|
}
|
|
40083
40245
|
const requested = args.optional("token-id");
|
|
40084
40246
|
if (requested !== void 0 && requested.toLowerCase() !== agent.tokenId) {
|
|
40085
40247
|
throw new Error(
|
|
40086
|
-
"token-id does not match the credential
|
|
40248
|
+
"token-id does not match the credential; no request was sent"
|
|
40087
40249
|
);
|
|
40088
40250
|
}
|
|
40089
40251
|
const tokenId2 = agent.tokenId;
|
|
@@ -40399,13 +40561,13 @@ async function commandWorkspaceAndCredential(args, cloud, options = {}) {
|
|
|
40399
40561
|
args.optional("workspace-id"),
|
|
40400
40562
|
process.env.SWARM_CLOUD_WORKSPACE_ID
|
|
40401
40563
|
);
|
|
40402
|
-
if (args
|
|
40564
|
+
if (hasAgentCredential(args)) {
|
|
40403
40565
|
if (override === null) {
|
|
40404
40566
|
throw new Error(
|
|
40405
40567
|
"not logged in; agent credentials require --workspace-id or SWARM_CLOUD_WORKSPACE_ID because they never infer a human's workspace selection"
|
|
40406
40568
|
);
|
|
40407
40569
|
}
|
|
40408
|
-
const agent = await
|
|
40570
|
+
const agent = await agentCredential(args);
|
|
40409
40571
|
const session = await agentSession(cloud, override, agent);
|
|
40410
40572
|
return {
|
|
40411
40573
|
selectedWorkspace: override,
|
|
@@ -40963,6 +41125,80 @@ async function runMembers(args) {
|
|
|
40963
41125
|
}
|
|
40964
41126
|
process.stdout.write(renderRoster(directory, memberNames));
|
|
40965
41127
|
}
|
|
41128
|
+
async function runWhoami(args) {
|
|
41129
|
+
args.assertShape([
|
|
41130
|
+
...TARGET_FLAGS,
|
|
41131
|
+
"workspace-id",
|
|
41132
|
+
...CREDENTIAL_FLAGS,
|
|
41133
|
+
"json"
|
|
41134
|
+
], 1);
|
|
41135
|
+
if (!hasAgentCredential(args)) {
|
|
41136
|
+
throw new UsageError(
|
|
41137
|
+
"cswarm whoami needs --agent-token-file <path> or --agent-token-stdin"
|
|
41138
|
+
);
|
|
41139
|
+
}
|
|
41140
|
+
const cloud = await target(args);
|
|
41141
|
+
const selected = await commandWorkspaceAndCredential(args, cloud, {
|
|
41142
|
+
validateHumanWorkspace: true
|
|
41143
|
+
});
|
|
41144
|
+
const directory = await readAgentSignalDirectory(
|
|
41145
|
+
cloud,
|
|
41146
|
+
selected.bearer,
|
|
41147
|
+
selected.selectedWorkspace
|
|
41148
|
+
);
|
|
41149
|
+
const identity = directory.identity;
|
|
41150
|
+
if (identity === void 0) {
|
|
41151
|
+
throw new Error(
|
|
41152
|
+
"this deployment authenticated the credential but did not return its identity; update the read service before using cswarm whoami"
|
|
41153
|
+
);
|
|
41154
|
+
}
|
|
41155
|
+
const principal = directory.agents.find(
|
|
41156
|
+
(agent) => agent.principal_id === identity.principal_id
|
|
41157
|
+
);
|
|
41158
|
+
if (principal === void 0 || principal.owner_user_id !== identity.owner_user_id) {
|
|
41159
|
+
throw new Error(
|
|
41160
|
+
"the read service authenticated this credential but returned no matching live principal; identity was not shown"
|
|
41161
|
+
);
|
|
41162
|
+
}
|
|
41163
|
+
const owner = directory.members.find(
|
|
41164
|
+
(member) => member.user_id === identity.owner_user_id
|
|
41165
|
+
);
|
|
41166
|
+
if (owner === void 0) {
|
|
41167
|
+
throw new Error(
|
|
41168
|
+
"the read service authenticated this credential but returned no matching owner; identity was not shown"
|
|
41169
|
+
);
|
|
41170
|
+
}
|
|
41171
|
+
const displayName = sanitizeDisplayLabel(principal.name, "Unnamed agent");
|
|
41172
|
+
const ownerName = sanitizeDisplayLabel(owner.display_name, "Unnamed member");
|
|
41173
|
+
const artifactPrincipalId = selected.agent?.principalId ?? null;
|
|
41174
|
+
const artifactMatches = artifactPrincipalId === null || artifactPrincipalId === identity.principal_id;
|
|
41175
|
+
if (!artifactMatches) {
|
|
41176
|
+
process.stderr.write(
|
|
41177
|
+
`WARNING: the credential authenticated as ${displayName} (${identity.principal_id}), but its JSON metadata names ${artifactPrincipalId}. Trust the authenticated identity shown here and replace the inconsistent artifact.
|
|
41178
|
+
`
|
|
41179
|
+
);
|
|
41180
|
+
}
|
|
41181
|
+
const output = {
|
|
41182
|
+
credential_valid: identity.credential_valid,
|
|
41183
|
+
principal_id: identity.principal_id,
|
|
41184
|
+
display_name: displayName,
|
|
41185
|
+
workspace_id: identity.workspace_id,
|
|
41186
|
+
owner_user_id: identity.owner_user_id,
|
|
41187
|
+
owner_display_name: ownerName,
|
|
41188
|
+
credential_metadata_match: artifactMatches
|
|
41189
|
+
};
|
|
41190
|
+
if (args.has("json")) {
|
|
41191
|
+
printJson(output);
|
|
41192
|
+
return;
|
|
41193
|
+
}
|
|
41194
|
+
process.stdout.write(
|
|
41195
|
+
`You are ${displayName} (${identity.principal_id}).
|
|
41196
|
+
Credential valid now: yes.
|
|
41197
|
+
Workspace: ${identity.workspace_id}.
|
|
41198
|
+
Owner: ${ownerName} (${identity.owner_user_id}).
|
|
41199
|
+
`
|
|
41200
|
+
);
|
|
41201
|
+
}
|
|
40966
41202
|
async function runSignalRead(args, inbox) {
|
|
40967
41203
|
const notify = inbox && args.has("notify");
|
|
40968
41204
|
args.assertShape(notify ? [
|
|
@@ -41073,9 +41309,9 @@ async function writeMonitorLine(line) {
|
|
|
41073
41309
|
});
|
|
41074
41310
|
}
|
|
41075
41311
|
async function runInboxNotifyCommand(args) {
|
|
41076
|
-
if (!args
|
|
41312
|
+
if (!hasAgentCredential(args)) {
|
|
41077
41313
|
throw new Error(
|
|
41078
|
-
"inbox --notify is for one agent; provide its JSON credential with --agent-token-stdin"
|
|
41314
|
+
"inbox --notify is for one agent; provide its JSON credential with --agent-token-file or --agent-token-stdin"
|
|
41079
41315
|
);
|
|
41080
41316
|
}
|
|
41081
41317
|
const cloud = await target(args);
|
|
@@ -41157,9 +41393,9 @@ async function runReceipt(args) {
|
|
|
41157
41393
|
if (!UUID_RE19.test(signalId)) {
|
|
41158
41394
|
throw new Error("signal-id must be a UUID");
|
|
41159
41395
|
}
|
|
41160
|
-
if (!args
|
|
41396
|
+
if (!hasAgentCredential(args)) {
|
|
41161
41397
|
throw new Error(
|
|
41162
|
-
"receipt reads signals sent by an agent; provide --agent-token-stdin and --workspace-id"
|
|
41398
|
+
"receipt reads signals sent by an agent; provide --agent-token-file or --agent-token-stdin, and --workspace-id"
|
|
41163
41399
|
);
|
|
41164
41400
|
}
|
|
41165
41401
|
const cloud = await target(args);
|
|
@@ -41491,6 +41727,11 @@ function renderListenerStatus(status) {
|
|
|
41491
41727
|
status.lastSignalId ? `Last handled signal: ${status.lastSignalId}.` : "No signal has been handled yet.",
|
|
41492
41728
|
status.lastErrorCode ? `Last status code: ${status.lastErrorCode}.` : "No listener error is recorded."
|
|
41493
41729
|
];
|
|
41730
|
+
if (status.lastErrorDetail) {
|
|
41731
|
+
const [first, ...rest] = status.lastErrorDetail.split("\n");
|
|
41732
|
+
lines.push(`Last error detail (local only): ${first}`);
|
|
41733
|
+
for (const line of rest) lines.push(` ${line}`);
|
|
41734
|
+
}
|
|
41494
41735
|
if (status.lastWorkerStderrTail) {
|
|
41495
41736
|
const tailLines = status.lastWorkerStderrTail.split("\n").filter((line) => line.trim().length > 0);
|
|
41496
41737
|
lines.push("Worker stderr (local log only):");
|
|
@@ -41620,6 +41861,9 @@ function listenerFailureMessage(code, provider) {
|
|
|
41620
41861
|
if (provider === "codex") {
|
|
41621
41862
|
return "the Codex bridge did not complete the read-only ACP permission canary; no workspace signal prompt was delivered. Confirm ChatGPT/Codex sign-in, then retry";
|
|
41622
41863
|
}
|
|
41864
|
+
if (provider === "grok") {
|
|
41865
|
+
return "the Grok bridge did not complete the ACP permission canary; no workspace signal prompt was delivered. The local cswarm listen status output includes the final error detail; read it, then retry";
|
|
41866
|
+
}
|
|
41623
41867
|
return "the host did not prove that CommonSwarm controls ACP tool permissions; no model prompt was delivered";
|
|
41624
41868
|
}
|
|
41625
41869
|
if (code === "process_exit") {
|
|
@@ -41772,13 +42016,14 @@ async function runConfiguredListener(options) {
|
|
|
41772
42016
|
lastWorkerStderrTail = tail.length > 0 ? tail : null;
|
|
41773
42017
|
};
|
|
41774
42018
|
};
|
|
41775
|
-
const newModel = () => {
|
|
42019
|
+
const newModel = (onCanaryAttempt) => {
|
|
41776
42020
|
providerVersionNotice = null;
|
|
41777
42021
|
return options.provider === "opencode" ? new OpenCodeListenerModel({
|
|
41778
42022
|
cwd: options.cwd,
|
|
41779
42023
|
permissionMode: options.permissionMode,
|
|
41780
42024
|
promptTimeoutMs: resolveTurnBudgetMs,
|
|
41781
42025
|
onWorkerStderrTail: newWorkerStderrTailSink(),
|
|
42026
|
+
onCanaryAttempt,
|
|
41782
42027
|
onVersionNotice,
|
|
41783
42028
|
...options.model ? { model: options.model } : {},
|
|
41784
42029
|
...options.opencodeExecutable ? { executable: options.opencodeExecutable } : options.executable ? { executable: options.executable } : {}
|
|
@@ -41787,6 +42032,7 @@ async function runConfiguredListener(options) {
|
|
|
41787
42032
|
permissionMode: options.permissionMode,
|
|
41788
42033
|
promptTimeoutMs: resolveTurnBudgetMs,
|
|
41789
42034
|
onWorkerStderrTail: newWorkerStderrTailSink(),
|
|
42035
|
+
onCanaryAttempt,
|
|
41790
42036
|
onVersionNotice,
|
|
41791
42037
|
...options.claudeExecutable ? { executable: options.claudeExecutable } : options.executable ? { executable: options.executable } : {}
|
|
41792
42038
|
}) : options.provider === "codex" ? new CodexListenerModel({
|
|
@@ -41794,6 +42040,7 @@ async function runConfiguredListener(options) {
|
|
|
41794
42040
|
permissionMode: options.permissionMode,
|
|
41795
42041
|
promptTimeoutMs: resolveTurnBudgetMs,
|
|
41796
42042
|
onWorkerStderrTail: newWorkerStderrTailSink(),
|
|
42043
|
+
onCanaryAttempt,
|
|
41797
42044
|
onVersionNotice,
|
|
41798
42045
|
...options.codexExecutable ? { executable: options.codexExecutable } : options.executable ? { executable: options.executable } : {}
|
|
41799
42046
|
}) : new GrokListenerModel({
|
|
@@ -41801,6 +42048,7 @@ async function runConfiguredListener(options) {
|
|
|
41801
42048
|
permissionMode: options.permissionMode,
|
|
41802
42049
|
promptTimeoutMs: resolveTurnBudgetMs,
|
|
41803
42050
|
onWorkerStderrTail: newWorkerStderrTailSink(),
|
|
42051
|
+
onCanaryAttempt,
|
|
41804
42052
|
onVersionNotice,
|
|
41805
42053
|
...options.model ? { model: options.model } : {},
|
|
41806
42054
|
...options.effort ? { effort: options.effort } : {},
|
|
@@ -41854,13 +42102,23 @@ async function runConfiguredListener(options) {
|
|
|
41854
42102
|
"listener delivery journal was not selected for this instance"
|
|
41855
42103
|
);
|
|
41856
42104
|
}
|
|
42105
|
+
const onCanaryAttempt = (attempt, total, result) => {
|
|
42106
|
+
onEvent({
|
|
42107
|
+
type: "canary_attempt",
|
|
42108
|
+
attempt,
|
|
42109
|
+
total,
|
|
42110
|
+
passed: result.passed,
|
|
42111
|
+
reason: result.reason ?? null,
|
|
42112
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
42113
|
+
});
|
|
42114
|
+
};
|
|
41857
42115
|
return await runListenerRuntime({
|
|
41858
42116
|
target: options.cloud,
|
|
41859
42117
|
workspaceId: options.workspaceId,
|
|
41860
42118
|
principalId: options.principalId,
|
|
41861
42119
|
credentialSession,
|
|
41862
42120
|
store: effectStore,
|
|
41863
|
-
model: newModel(),
|
|
42121
|
+
model: newModel(onCanaryAttempt),
|
|
41864
42122
|
signal,
|
|
41865
42123
|
onEvent,
|
|
41866
42124
|
declareModel: listenerModelLabel(options.provider),
|
|
@@ -41882,7 +42140,7 @@ async function runListenStart(args) {
|
|
|
41882
42140
|
args.assertShape([
|
|
41883
42141
|
...TARGET_FLAGS,
|
|
41884
42142
|
"workspace-id",
|
|
41885
|
-
|
|
42143
|
+
...CREDENTIAL_FLAGS,
|
|
41886
42144
|
"provider",
|
|
41887
42145
|
"cwd",
|
|
41888
42146
|
"model",
|
|
@@ -41899,9 +42157,9 @@ async function runListenStart(args) {
|
|
|
41899
42157
|
"foreground",
|
|
41900
42158
|
"json"
|
|
41901
42159
|
], 2);
|
|
41902
|
-
if (!args
|
|
42160
|
+
if (!hasAgentCredential(args)) {
|
|
41903
42161
|
throw new Error(
|
|
41904
|
-
"listen start requires --agent-token-stdin; credentials are never accepted on argv"
|
|
42162
|
+
"listen start requires --agent-token-file or --agent-token-stdin; credentials are never accepted on argv"
|
|
41905
42163
|
);
|
|
41906
42164
|
}
|
|
41907
42165
|
const provider = listenerProvider(args);
|
|
@@ -41916,7 +42174,7 @@ async function runListenStart(args) {
|
|
|
41916
42174
|
args.optional("workspace-id") ?? process.env.SWARM_CLOUD_WORKSPACE_ID,
|
|
41917
42175
|
"workspace-id"
|
|
41918
42176
|
);
|
|
41919
|
-
const agent = await
|
|
42177
|
+
const agent = await agentCredential(args);
|
|
41920
42178
|
assertDurableListenerCredential(agent);
|
|
41921
42179
|
const principalId = agent.principalId;
|
|
41922
42180
|
const cwd = args.optional("cwd") ?? process.cwd();
|
|
@@ -42066,6 +42324,7 @@ The short credential rotates while this process remains alive and secure local s
|
|
|
42066
42324
|
async function runListenSupervisor(args) {
|
|
42067
42325
|
args.assertShape([
|
|
42068
42326
|
...TARGET_FLAGS,
|
|
42327
|
+
...CREDENTIAL_FLAGS,
|
|
42069
42328
|
"workspace-id",
|
|
42070
42329
|
"principal-id",
|
|
42071
42330
|
"cwd",
|
|
@@ -42092,7 +42351,7 @@ async function runListenSupervisor(args) {
|
|
|
42092
42351
|
const cloud = await target(args);
|
|
42093
42352
|
const workspaceId2 = listenerUuid(args.optional("workspace-id"), "workspace-id");
|
|
42094
42353
|
const principalId = listenerUuid(args.optional("principal-id"), "principal-id");
|
|
42095
|
-
const agent = await
|
|
42354
|
+
const agent = await agentCredential(args, { implicitStdin: true });
|
|
42096
42355
|
assertDurableListenerCredential(agent, principalId);
|
|
42097
42356
|
const cwd = args.required("cwd");
|
|
42098
42357
|
if (!(0, import_node_path20.isAbsolute)(cwd)) throw new Error("--cwd must be an absolute path");
|
|
@@ -42394,8 +42653,8 @@ async function runFilePut(args) {
|
|
|
42394
42653
|
workspaceId: context.selected.selectedWorkspace,
|
|
42395
42654
|
credential: context.selected.bearer
|
|
42396
42655
|
};
|
|
42397
|
-
const fileId = (0,
|
|
42398
|
-
const versionId = (0,
|
|
42656
|
+
const fileId = (0, import_node_crypto20.randomUUID)();
|
|
42657
|
+
const versionId = (0, import_node_crypto20.randomUUID)();
|
|
42399
42658
|
const createCommandId = newCommandId();
|
|
42400
42659
|
const commitCommandId = newCommandId();
|
|
42401
42660
|
const created = await onceRetried(
|
|
@@ -42632,7 +42891,7 @@ async function runDogfood(args) {
|
|
|
42632
42891
|
const { selectedWorkspace, bearer } = await commandWorkspaceAndCredential(args, cloud);
|
|
42633
42892
|
const client = new ThinCommandClient(cloud);
|
|
42634
42893
|
const route = stream(args);
|
|
42635
|
-
const taskId = args.optional("task-id") ?? (0,
|
|
42894
|
+
const taskId = args.optional("task-id") ?? (0, import_node_crypto20.randomUUID)();
|
|
42636
42895
|
const ttl = Number(args.optional("ttl-ms") ?? "3600000");
|
|
42637
42896
|
if (!Number.isSafeInteger(ttl) || ttl <= 0 || ttl > 144e5) {
|
|
42638
42897
|
throw new Error("--ttl-ms must be an integer in 1..14400000");
|
|
@@ -42847,6 +43106,10 @@ async function main() {
|
|
|
42847
43106
|
await runStatus(args);
|
|
42848
43107
|
return;
|
|
42849
43108
|
}
|
|
43109
|
+
if (verb === "whoami") {
|
|
43110
|
+
await runWhoami(args);
|
|
43111
|
+
return;
|
|
43112
|
+
}
|
|
42850
43113
|
if (verb === "feedback") {
|
|
42851
43114
|
await runFeedback(args);
|
|
42852
43115
|
return;
|