commonswarm 0.1.56 → 0.1.58
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 +1040 -128
- package/package.json +1 -1
package/cswarm.cjs
CHANGED
|
@@ -13512,6 +13512,7 @@ __export(cli_exports, {
|
|
|
13512
13512
|
listenerFailureMessage: () => listenerFailureMessage,
|
|
13513
13513
|
listenerHostLimits: () => listenerHostLimits,
|
|
13514
13514
|
listenerPermissionMode: () => listenerPermissionMode,
|
|
13515
|
+
listenerPollIntervalMs: () => listenerPollIntervalMs,
|
|
13515
13516
|
listenerProviderInstallEvidence: () => listenerProviderInstallEvidence,
|
|
13516
13517
|
listenerRouteConfiguration: () => listenerRouteConfiguration,
|
|
13517
13518
|
listenerStatusJson: () => listenerStatusJson,
|
|
@@ -13528,10 +13529,10 @@ module.exports = __toCommonJS(cli_exports);
|
|
|
13528
13529
|
var import_node_crypto22 = require("node:crypto");
|
|
13529
13530
|
var import_node_child_process9 = require("node:child_process");
|
|
13530
13531
|
var import_node_fs7 = require("node:fs");
|
|
13531
|
-
var
|
|
13532
|
+
var import_promises13 = require("node:fs/promises");
|
|
13532
13533
|
var import_node_os10 = require("node:os");
|
|
13533
13534
|
var import_node_path21 = require("node:path");
|
|
13534
|
-
var
|
|
13535
|
+
var import_promises14 = require("node:readline/promises");
|
|
13535
13536
|
|
|
13536
13537
|
// src/protocol/events.ts
|
|
13537
13538
|
var SCHEMA_VERSION = 1;
|
|
@@ -26174,7 +26175,7 @@ var src_default = Postgres;
|
|
|
26174
26175
|
function Postgres(a, b2) {
|
|
26175
26176
|
const options = parseOptions(a, b2), subscribe = options.no_subscribe || Subscribe(Postgres, { ...options });
|
|
26176
26177
|
let ending = false;
|
|
26177
|
-
const queries = queue_default(), connecting = queue_default(), reserved = queue_default(), closed = queue_default(), ended = queue_default(),
|
|
26178
|
+
const queries = queue_default(), connecting = queue_default(), reserved = queue_default(), closed = queue_default(), ended = queue_default(), open7 = queue_default(), busy = queue_default(), full = queue_default(), queues = { connecting, reserved, closed, ended, open: open7, busy, full };
|
|
26178
26179
|
const connections = [...Array(options.max)].map(() => connection_default(options, queues, { onopen, onend, onclose }));
|
|
26179
26180
|
const sql = Sql(handler);
|
|
26180
26181
|
Object.assign(sql, {
|
|
@@ -26287,7 +26288,7 @@ function Postgres(a, b2) {
|
|
|
26287
26288
|
}
|
|
26288
26289
|
async function reserve() {
|
|
26289
26290
|
const queue = queue_default();
|
|
26290
|
-
const c =
|
|
26291
|
+
const c = open7.length ? open7.shift() : await new Promise((resolve3, reject) => {
|
|
26291
26292
|
const query = { reserve: resolve3, reject };
|
|
26292
26293
|
queries.push(query);
|
|
26293
26294
|
closed.length && connect(closed.shift(), query);
|
|
@@ -26360,7 +26361,7 @@ function Postgres(a, b2) {
|
|
|
26360
26361
|
c.queue.remove(c);
|
|
26361
26362
|
queue.push(c);
|
|
26362
26363
|
c.queue = queue;
|
|
26363
|
-
queue ===
|
|
26364
|
+
queue === open7 ? c.idleTimer.start() : c.idleTimer.cancel();
|
|
26364
26365
|
return c;
|
|
26365
26366
|
}
|
|
26366
26367
|
function json(x) {
|
|
@@ -26374,8 +26375,8 @@ function Postgres(a, b2) {
|
|
|
26374
26375
|
function handler(query) {
|
|
26375
26376
|
if (ending)
|
|
26376
26377
|
return query.reject(Errors.connection("CONNECTION_ENDED", options, options));
|
|
26377
|
-
if (
|
|
26378
|
-
return go(
|
|
26378
|
+
if (open7.length)
|
|
26379
|
+
return go(open7.shift(), query);
|
|
26379
26380
|
if (closed.length)
|
|
26380
26381
|
return connect(closed.shift(), query);
|
|
26381
26382
|
busy.length ? go(busy.shift(), query) : queries.push(query);
|
|
@@ -26420,7 +26421,7 @@ function Postgres(a, b2) {
|
|
|
26420
26421
|
}
|
|
26421
26422
|
function onopen(c) {
|
|
26422
26423
|
if (queries.length === 0)
|
|
26423
|
-
return move(c,
|
|
26424
|
+
return move(c, open7);
|
|
26424
26425
|
let max = Math.ceil(queries.length / (connecting.length + 1)), ready = true;
|
|
26425
26426
|
while (ready && queries.length && max-- > 0) {
|
|
26426
26427
|
const query = queries.shift();
|
|
@@ -27125,6 +27126,34 @@ function describeRenewalGrant(grant) {
|
|
|
27125
27126
|
return lines;
|
|
27126
27127
|
}
|
|
27127
27128
|
|
|
27129
|
+
// src/cloud/wake.ts
|
|
27130
|
+
var WAKE_EVENT = "wake";
|
|
27131
|
+
var WAKE_TOPIC_RE = /^cswarm-wake:[A-Za-z0-9_-]{43}$/;
|
|
27132
|
+
function isWakeTopic(value) {
|
|
27133
|
+
return WAKE_TOPIC_RE.test(value);
|
|
27134
|
+
}
|
|
27135
|
+
var WakeHintError = class extends Error {
|
|
27136
|
+
code = "malformed_wake";
|
|
27137
|
+
constructor(message) {
|
|
27138
|
+
super(message);
|
|
27139
|
+
this.name = "WakeHintError";
|
|
27140
|
+
}
|
|
27141
|
+
};
|
|
27142
|
+
function parseOptionalWakeHint(value) {
|
|
27143
|
+
if (value === void 0 || value === null) return void 0;
|
|
27144
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
27145
|
+
throw new WakeHintError("wake hint must be an object");
|
|
27146
|
+
}
|
|
27147
|
+
const row = value;
|
|
27148
|
+
if (typeof row.topic !== "string" || !isWakeTopic(row.topic)) {
|
|
27149
|
+
throw new WakeHintError("wake hint topic is malformed");
|
|
27150
|
+
}
|
|
27151
|
+
if (row.event !== WAKE_EVENT) {
|
|
27152
|
+
throw new WakeHintError("wake hint event is malformed");
|
|
27153
|
+
}
|
|
27154
|
+
return { topic: row.topic, event: WAKE_EVENT };
|
|
27155
|
+
}
|
|
27156
|
+
|
|
27128
27157
|
// src/cloud/renewal.ts
|
|
27129
27158
|
var AGENT_TOKEN_DEFAULT_TTL_MS2 = 60 * 60 * 1e3;
|
|
27130
27159
|
var AGENT_TOKEN_MAX_TTL_MS2 = 8 * 60 * 60 * 1e3;
|
|
@@ -27426,6 +27455,16 @@ async function requestSuccessor(options) {
|
|
|
27426
27455
|
"The deployment issued a successor credential that lasts longer than eight hours. cswarm refused to store it. Agent credentials stay short on purpose; renewal is what makes that survivable."
|
|
27427
27456
|
);
|
|
27428
27457
|
}
|
|
27458
|
+
let wake;
|
|
27459
|
+
try {
|
|
27460
|
+
wake = parseOptionalWakeHint(body.wake);
|
|
27461
|
+
} catch {
|
|
27462
|
+
throw new RenewalRefused(
|
|
27463
|
+
response.status,
|
|
27464
|
+
"malformed_wake",
|
|
27465
|
+
"The deployment returned a successor credential with a malformed wake hint. It was not stored."
|
|
27466
|
+
);
|
|
27467
|
+
}
|
|
27429
27468
|
return {
|
|
27430
27469
|
token,
|
|
27431
27470
|
tokenId: tokenId.toLowerCase(),
|
|
@@ -27434,7 +27473,8 @@ async function requestSuccessor(options) {
|
|
|
27434
27473
|
issuedAt,
|
|
27435
27474
|
expiresAt,
|
|
27436
27475
|
horizonExpiresAt: timestamp(body.horizon_expires_at),
|
|
27437
|
-
successorsRemaining: count(body.successors_remaining)
|
|
27476
|
+
successorsRemaining: count(body.successors_remaining),
|
|
27477
|
+
...wake === void 0 ? {} : { wake }
|
|
27438
27478
|
};
|
|
27439
27479
|
}
|
|
27440
27480
|
var AgentCredentialSession = class _AgentCredentialSession {
|
|
@@ -30009,6 +30049,12 @@ async function agentSignalPage(target2, credential, query, options, allowLegacyC
|
|
|
30009
30049
|
const rawRows = body.signals;
|
|
30010
30050
|
const parsedRows = parseSignalRows(rawRows, parseOptions2);
|
|
30011
30051
|
const ascending = query.ascending === true || query.after !== void 0;
|
|
30052
|
+
let wake;
|
|
30053
|
+
try {
|
|
30054
|
+
wake = parseOptionalWakeHint(body.wake);
|
|
30055
|
+
} catch {
|
|
30056
|
+
throw plainMalformedError("signal read returned a malformed wake hint");
|
|
30057
|
+
}
|
|
30012
30058
|
return {
|
|
30013
30059
|
signals: sortSignals(
|
|
30014
30060
|
rowsAfterCursor(parsedRows.signals, query.after),
|
|
@@ -30019,7 +30065,8 @@ async function agentSignalPage(target2, credential, query, options, allowLegacyC
|
|
|
30019
30065
|
rawCount: rawRows.length,
|
|
30020
30066
|
nextCursor: rawRows.length === 0 ? null : cursorFromUnknown(rawRows[rawRows.length - 1]),
|
|
30021
30067
|
malformedRows: parsedRows.malformedRows,
|
|
30022
|
-
pendingDeliveryCount
|
|
30068
|
+
pendingDeliveryCount,
|
|
30069
|
+
...wake === void 0 ? {} : { wake }
|
|
30023
30070
|
};
|
|
30024
30071
|
}
|
|
30025
30072
|
function parseAgentMemberRow(value) {
|
|
@@ -30720,10 +30767,86 @@ async function runInboxFollow(options) {
|
|
|
30720
30767
|
// src/cloud/arrival-watch.ts
|
|
30721
30768
|
var import_node_os4 = require("node:os");
|
|
30722
30769
|
var import_node_path4 = require("node:path");
|
|
30770
|
+
var import_promises4 = require("node:fs/promises");
|
|
30771
|
+
|
|
30772
|
+
// src/cloud/idle-poll.ts
|
|
30773
|
+
var IDLE_POLL_DEFAULT_MS = 15e3;
|
|
30774
|
+
var IDLE_POLL_MAX_MS = 6e4;
|
|
30775
|
+
var IDLE_POLL_MIN_MS = 1e3;
|
|
30776
|
+
var ARRIVAL_WATCH_POLL_MS = IDLE_POLL_MAX_MS;
|
|
30777
|
+
var DURATION_RE = /^([1-9]\d*)(s|m)$/;
|
|
30778
|
+
function formatIdlePollDuration(ms) {
|
|
30779
|
+
if (!Number.isSafeInteger(ms) || ms <= 0) {
|
|
30780
|
+
throw new Error("idle poll duration must be a positive number of milliseconds");
|
|
30781
|
+
}
|
|
30782
|
+
if (ms % 6e4 === 0) return `${ms / 6e4}m`;
|
|
30783
|
+
if (ms % 1e3 === 0) return `${ms / 1e3}s`;
|
|
30784
|
+
throw new Error("idle poll duration must be a whole number of seconds");
|
|
30785
|
+
}
|
|
30786
|
+
var IDLE_POLL_MIN_LABEL = formatIdlePollDuration(IDLE_POLL_MIN_MS);
|
|
30787
|
+
var IDLE_POLL_DEFAULT_LABEL = formatIdlePollDuration(IDLE_POLL_DEFAULT_MS);
|
|
30788
|
+
var IDLE_POLL_MAX_LABEL = formatIdlePollDuration(IDLE_POLL_MAX_MS);
|
|
30789
|
+
function idlePollDurationExamples() {
|
|
30790
|
+
const midMs = Math.min(IDLE_POLL_MAX_MS, IDLE_POLL_DEFAULT_MS * 2);
|
|
30791
|
+
const labels = [];
|
|
30792
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30793
|
+
for (const ms of [IDLE_POLL_DEFAULT_MS, midMs, IDLE_POLL_MAX_MS]) {
|
|
30794
|
+
const label = formatIdlePollDuration(ms);
|
|
30795
|
+
if (seen.has(label)) continue;
|
|
30796
|
+
seen.add(label);
|
|
30797
|
+
labels.push(label);
|
|
30798
|
+
}
|
|
30799
|
+
return labels;
|
|
30800
|
+
}
|
|
30801
|
+
var IDLE_POLL_DURATION_EXAMPLES = idlePollDurationExamples();
|
|
30802
|
+
function idlePollDurationHint() {
|
|
30803
|
+
return idlePollDurationExamples().join(", ");
|
|
30804
|
+
}
|
|
30805
|
+
function idlePollBoundSentence() {
|
|
30806
|
+
return `between ${IDLE_POLL_MIN_LABEL} and ${IDLE_POLL_MAX_LABEL}`;
|
|
30807
|
+
}
|
|
30808
|
+
function parseIdlePollIntervalMs(value, defaultMs = IDLE_POLL_DEFAULT_MS) {
|
|
30809
|
+
if (value === void 0) return defaultMs;
|
|
30810
|
+
const match = DURATION_RE.exec(value);
|
|
30811
|
+
if (!match) {
|
|
30812
|
+
throw new Error(
|
|
30813
|
+
`--poll-interval must be a duration such as ${idlePollDurationHint()}`
|
|
30814
|
+
);
|
|
30815
|
+
}
|
|
30816
|
+
const unit = match[2] === "s" ? 1e3 : 6e4;
|
|
30817
|
+
const milliseconds = Number(match[1]) * unit;
|
|
30818
|
+
if (!Number.isSafeInteger(milliseconds) || milliseconds < IDLE_POLL_MIN_MS || milliseconds > IDLE_POLL_MAX_MS) {
|
|
30819
|
+
throw new Error(`--poll-interval must be ${idlePollBoundSentence()}`);
|
|
30820
|
+
}
|
|
30821
|
+
return milliseconds;
|
|
30822
|
+
}
|
|
30823
|
+
function nextIdlePollMs(baseMs, emptyStreak, maxMs = IDLE_POLL_MAX_MS) {
|
|
30824
|
+
if (!Number.isSafeInteger(baseMs) || baseMs < 0) {
|
|
30825
|
+
throw new Error("idle poll base must be a non-negative number of milliseconds");
|
|
30826
|
+
}
|
|
30827
|
+
if (!Number.isSafeInteger(emptyStreak) || emptyStreak < 0) {
|
|
30828
|
+
throw new Error("idle poll empty streak must be a non-negative integer");
|
|
30829
|
+
}
|
|
30830
|
+
if (!Number.isSafeInteger(maxMs) || maxMs < 0) {
|
|
30831
|
+
throw new Error("idle poll max must be a non-negative number of milliseconds");
|
|
30832
|
+
}
|
|
30833
|
+
const shift = Math.min(emptyStreak, 16);
|
|
30834
|
+
const grown = baseMs * 2 ** shift;
|
|
30835
|
+
return Math.min(maxMs, grown);
|
|
30836
|
+
}
|
|
30837
|
+
function idlePollStatusSentence(currentMs) {
|
|
30838
|
+
return `Current idle poll interval: ${formatIdlePollDuration(currentMs)}.`;
|
|
30839
|
+
}
|
|
30840
|
+
function idlePollHelpSentence(defaultMs = IDLE_POLL_DEFAULT_MS) {
|
|
30841
|
+
return `listen start --poll-interval sets how long the listener waits after an empty claim (default ${formatIdlePollDuration(defaultMs)}). A whole number plus s or m (for example ${idlePollDurationHint()}), ${idlePollBoundSentence()}. Empty polls double that wait up to ${IDLE_POLL_MAX_LABEL}; any delivery resets it to the configured interval.`;
|
|
30842
|
+
}
|
|
30843
|
+
|
|
30844
|
+
// src/cloud/arrival-watch.ts
|
|
30723
30845
|
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;
|
|
30724
30846
|
var CURSOR_MAX_BYTES = 4 * 1024;
|
|
30725
30847
|
var ARRIVAL_SNIPPET_MAX = 180;
|
|
30726
|
-
var
|
|
30848
|
+
var WATCH_LOCK_MAX_BYTES = 512;
|
|
30849
|
+
var ARRIVAL_WATCH_POLL_MS2 = ARRIVAL_WATCH_POLL_MS;
|
|
30727
30850
|
var ARRIVAL_RETRY_NOTICE_THRESHOLD_MS = 6e4;
|
|
30728
30851
|
var EXIT_NOTIFY_ORPHANED = 74;
|
|
30729
30852
|
var NotifyStdoutClosedError = class extends Error {
|
|
@@ -30776,6 +30899,94 @@ function arrivalCursorPath(target2, workspaceId2, principalId, root = stateRoot(
|
|
|
30776
30899
|
`${target2.profileId}-${workspaceId2.toLowerCase()}-${principalId.toLowerCase()}.json`
|
|
30777
30900
|
);
|
|
30778
30901
|
}
|
|
30902
|
+
function arrivalWatchLockPath(target2, workspaceId2, principalId, root = stateRoot()) {
|
|
30903
|
+
return arrivalCursorPath(target2, workspaceId2, principalId, root).replace(
|
|
30904
|
+
/\.json$/u,
|
|
30905
|
+
".lock"
|
|
30906
|
+
);
|
|
30907
|
+
}
|
|
30908
|
+
function arrivalWatchAlreadyRunningSentence(pid) {
|
|
30909
|
+
return `inbox --notify is already running for this agent as pid ${pid}.`;
|
|
30910
|
+
}
|
|
30911
|
+
var ArrivalWatchAlreadyRunningError = class extends Error {
|
|
30912
|
+
code = "notify_already_running";
|
|
30913
|
+
pid;
|
|
30914
|
+
constructor(pid) {
|
|
30915
|
+
super(arrivalWatchAlreadyRunningSentence(pid));
|
|
30916
|
+
this.name = "ArrivalWatchAlreadyRunningError";
|
|
30917
|
+
this.pid = pid;
|
|
30918
|
+
}
|
|
30919
|
+
};
|
|
30920
|
+
function pidIsAlive(pid) {
|
|
30921
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
30922
|
+
try {
|
|
30923
|
+
process.kill(pid, 0);
|
|
30924
|
+
return true;
|
|
30925
|
+
} catch (error) {
|
|
30926
|
+
return error.code !== "ESRCH";
|
|
30927
|
+
}
|
|
30928
|
+
}
|
|
30929
|
+
function parseWatchLock(raw) {
|
|
30930
|
+
let value;
|
|
30931
|
+
try {
|
|
30932
|
+
value = JSON.parse(raw);
|
|
30933
|
+
} catch {
|
|
30934
|
+
return null;
|
|
30935
|
+
}
|
|
30936
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
30937
|
+
const row = value;
|
|
30938
|
+
if (row.version !== 1 || !Number.isSafeInteger(row.pid) || row.pid <= 0) {
|
|
30939
|
+
return null;
|
|
30940
|
+
}
|
|
30941
|
+
return { pid: row.pid };
|
|
30942
|
+
}
|
|
30943
|
+
async function acquireArrivalWatchLock(path, pid = process.pid) {
|
|
30944
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) {
|
|
30945
|
+
throw new Error("arrival watch lock pid must be a positive integer");
|
|
30946
|
+
}
|
|
30947
|
+
await ensureSecureStateDirectory((0, import_node_path4.dirname)(path));
|
|
30948
|
+
const payload = `${JSON.stringify({ version: 1, pid })}
|
|
30949
|
+
`;
|
|
30950
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
30951
|
+
try {
|
|
30952
|
+
const handle = await (0, import_promises4.open)(path, "wx", 384);
|
|
30953
|
+
try {
|
|
30954
|
+
await handle.writeFile(payload, "utf8");
|
|
30955
|
+
} finally {
|
|
30956
|
+
await handle.close();
|
|
30957
|
+
}
|
|
30958
|
+
return;
|
|
30959
|
+
} catch (error) {
|
|
30960
|
+
if (error.code !== "EEXIST") throw error;
|
|
30961
|
+
}
|
|
30962
|
+
let existing = null;
|
|
30963
|
+
try {
|
|
30964
|
+
const raw = await (0, import_promises4.readFile)(path, "utf8");
|
|
30965
|
+
if (Buffer.byteLength(raw, "utf8") <= WATCH_LOCK_MAX_BYTES) {
|
|
30966
|
+
existing = parseWatchLock(raw);
|
|
30967
|
+
}
|
|
30968
|
+
} catch (error) {
|
|
30969
|
+
if (error.code !== "ENOENT") throw error;
|
|
30970
|
+
continue;
|
|
30971
|
+
}
|
|
30972
|
+
if (existing !== null && pidIsAlive(existing.pid)) {
|
|
30973
|
+
throw new ArrivalWatchAlreadyRunningError(existing.pid);
|
|
30974
|
+
}
|
|
30975
|
+
await (0, import_promises4.unlink)(path).catch(() => void 0);
|
|
30976
|
+
}
|
|
30977
|
+
throw new Error("arrival watch lock could not be acquired");
|
|
30978
|
+
}
|
|
30979
|
+
async function releaseArrivalWatchLock(path, pid = process.pid) {
|
|
30980
|
+
try {
|
|
30981
|
+
const raw = await (0, import_promises4.readFile)(path, "utf8");
|
|
30982
|
+
const existing = parseWatchLock(raw);
|
|
30983
|
+
if (existing === null || existing.pid !== pid) return;
|
|
30984
|
+
await (0, import_promises4.unlink)(path);
|
|
30985
|
+
} catch (error) {
|
|
30986
|
+
if (error.code === "ENOENT") return;
|
|
30987
|
+
throw error;
|
|
30988
|
+
}
|
|
30989
|
+
}
|
|
30779
30990
|
function parseCursor(raw, workspaceId2, principalId) {
|
|
30780
30991
|
let value;
|
|
30781
30992
|
try {
|
|
@@ -30889,8 +31100,9 @@ function assertCursorPage(page) {
|
|
|
30889
31100
|
}
|
|
30890
31101
|
}
|
|
30891
31102
|
async function runArrivalWatch(options) {
|
|
30892
|
-
const pollMs = options.pollMs ??
|
|
31103
|
+
const pollMs = options.pollMs ?? ARRIVAL_WATCH_POLL_MS2;
|
|
30893
31104
|
const random = options.random ?? Math.random;
|
|
31105
|
+
let emptyIdleStreak = 0;
|
|
30894
31106
|
let cursor = await options.store.read();
|
|
30895
31107
|
let baseline = cursor === void 0;
|
|
30896
31108
|
let attempt = 0;
|
|
@@ -30915,6 +31127,12 @@ async function runArrivalWatch(options) {
|
|
|
30915
31127
|
timer2 = setTimeout(finish, ms);
|
|
30916
31128
|
});
|
|
30917
31129
|
};
|
|
31130
|
+
const idleWait = async (hadDelivery) => {
|
|
31131
|
+
if (hadDelivery) emptyIdleStreak = 0;
|
|
31132
|
+
const intervalMs = nextIdlePollMs(pollMs, emptyIdleStreak, IDLE_POLL_MAX_MS);
|
|
31133
|
+
if (!hadDelivery) emptyIdleStreak += 1;
|
|
31134
|
+
await wait(intervalMs);
|
|
31135
|
+
};
|
|
30918
31136
|
while (!cancelled()) {
|
|
30919
31137
|
try {
|
|
30920
31138
|
const page = await options.readPage({
|
|
@@ -30940,7 +31158,7 @@ async function runArrivalWatch(options) {
|
|
|
30940
31158
|
await options.store.write(cursor);
|
|
30941
31159
|
baseline = false;
|
|
30942
31160
|
if (cancelled()) break;
|
|
30943
|
-
await
|
|
31161
|
+
await idleWait(false);
|
|
30944
31162
|
continue;
|
|
30945
31163
|
}
|
|
30946
31164
|
const emittedSignals = [];
|
|
@@ -30956,7 +31174,8 @@ async function runArrivalWatch(options) {
|
|
|
30956
31174
|
}
|
|
30957
31175
|
if (cancelled()) break;
|
|
30958
31176
|
const fullPage = page.rawCount >= SIGNAL_FOLLOW_PAGE_LIMIT;
|
|
30959
|
-
|
|
31177
|
+
if (fullPage) await wait(0);
|
|
31178
|
+
else await idleWait(emittedSignals.length > 0);
|
|
30960
31179
|
} catch (error) {
|
|
30961
31180
|
if (cancelled()) break;
|
|
30962
31181
|
const http = followHttpDetails(error);
|
|
@@ -31903,11 +32122,18 @@ function parseClaimSuccess(body, expected, now) {
|
|
|
31903
32122
|
"delivery claim response returned more deliveries than its pending count"
|
|
31904
32123
|
);
|
|
31905
32124
|
}
|
|
32125
|
+
let wake;
|
|
32126
|
+
try {
|
|
32127
|
+
wake = parseOptionalWakeHint(row.wake);
|
|
32128
|
+
} catch {
|
|
32129
|
+
throw new DeliveryProtocolError("delivery claim response wake field is malformed");
|
|
32130
|
+
}
|
|
31906
32131
|
return {
|
|
31907
32132
|
capabilities,
|
|
31908
32133
|
deliveries,
|
|
31909
32134
|
pendingDeliveryCount,
|
|
31910
|
-
terminalDeliveryFailureCount
|
|
32135
|
+
terminalDeliveryFailureCount,
|
|
32136
|
+
...wake === void 0 ? {} : { wake }
|
|
31911
32137
|
};
|
|
31912
32138
|
}
|
|
31913
32139
|
function parseAckSuccess(body, expected) {
|
|
@@ -32121,7 +32347,8 @@ var DeliveryCommandClient = class {
|
|
|
32121
32347
|
capabilities: parsed.capabilities,
|
|
32122
32348
|
deliveries: parsed.deliveries,
|
|
32123
32349
|
pendingDeliveryCount: parsed.pendingDeliveryCount,
|
|
32124
|
-
terminalDeliveryFailureCount: parsed.terminalDeliveryFailureCount
|
|
32350
|
+
terminalDeliveryFailureCount: parsed.terminalDeliveryFailureCount,
|
|
32351
|
+
...parsed.wake === void 0 ? {} : { wake: parsed.wake }
|
|
32125
32352
|
};
|
|
32126
32353
|
}
|
|
32127
32354
|
/** Acknowledge one leased delivery with an exact terminal outcome. */
|
|
@@ -32288,12 +32515,13 @@ var CONTROL_AND_SEPARATOR_STRIP_RE = new RegExp(
|
|
|
32288
32515
|
"[\\u0000-\\u0008\\u000b-\\u001f\\u007f-\\u009f" + EXOTIC_SEPARATORS + "]",
|
|
32289
32516
|
"g"
|
|
32290
32517
|
);
|
|
32291
|
-
var
|
|
32292
|
-
`swm_(?:agt|inv|cap)_[^${SEPARATOR_CLASS_SOURCE}]
|
|
32293
|
-
"
|
|
32518
|
+
var SECRET_SHAPE_RE = new RegExp(
|
|
32519
|
+
`swm_(?:agt|inv|cap)_[^${SEPARATOR_CLASS_SOURCE}]*|cswarm-wake:[A-Za-z0-9_-]{43}`,
|
|
32520
|
+
"i"
|
|
32294
32521
|
);
|
|
32522
|
+
var SECRET_SHAPE_GLOBAL_RE = new RegExp(SECRET_SHAPE_RE.source, "gi");
|
|
32295
32523
|
function redactCredentialText(value) {
|
|
32296
|
-
return value.replace(ANSI_ESCAPE_GLOBAL_RE2, "").replace(CONTROL_AND_SEPARATOR_STRIP_RE, "").replace(
|
|
32524
|
+
return value.replace(ANSI_ESCAPE_GLOBAL_RE2, "").replace(CONTROL_AND_SEPARATOR_STRIP_RE, "").replace(SECRET_SHAPE_GLOBAL_RE, "[redacted-credential]");
|
|
32297
32525
|
}
|
|
32298
32526
|
|
|
32299
32527
|
// src/host/stderr-tail.ts
|
|
@@ -32368,7 +32596,7 @@ function attachStderrTailExitObserver(child, onStderrTail) {
|
|
|
32368
32596
|
|
|
32369
32597
|
// src/host/opencode.ts
|
|
32370
32598
|
var import_node_fs3 = require("node:fs");
|
|
32371
|
-
var
|
|
32599
|
+
var import_promises5 = require("node:fs/promises");
|
|
32372
32600
|
var import_node_os5 = require("node:os");
|
|
32373
32601
|
var import_node_path6 = require("node:path");
|
|
32374
32602
|
|
|
@@ -33712,18 +33940,18 @@ function buildOpenCodeHomeOwner(options) {
|
|
|
33712
33940
|
}
|
|
33713
33941
|
async function writeOpenCodeHomeOwner(home, owner) {
|
|
33714
33942
|
const path = (0, import_node_path6.join)(home, OPENCODE_HOME_OWNER_FILE);
|
|
33715
|
-
await (0,
|
|
33943
|
+
await (0, import_promises5.writeFile)(path, `${JSON.stringify(owner)}
|
|
33716
33944
|
`, {
|
|
33717
33945
|
flag: "wx",
|
|
33718
33946
|
mode: 384
|
|
33719
33947
|
});
|
|
33720
|
-
await (0,
|
|
33948
|
+
await (0, import_promises5.chmod)(path, 384);
|
|
33721
33949
|
}
|
|
33722
33950
|
async function readOpenCodeHomeOwner(home) {
|
|
33723
33951
|
const path = (0, import_node_path6.join)(home, OPENCODE_HOME_OWNER_FILE);
|
|
33724
33952
|
let raw;
|
|
33725
33953
|
try {
|
|
33726
|
-
raw = await (0,
|
|
33954
|
+
raw = await (0, import_promises5.readFile)(path, "utf8");
|
|
33727
33955
|
} catch {
|
|
33728
33956
|
return null;
|
|
33729
33957
|
}
|
|
@@ -33744,10 +33972,10 @@ async function releaseOpenCodeHome(home, instanceId) {
|
|
|
33744
33972
|
return;
|
|
33745
33973
|
}
|
|
33746
33974
|
try {
|
|
33747
|
-
await (0,
|
|
33975
|
+
await (0, import_promises5.rm)(home, { recursive: true, force: true });
|
|
33748
33976
|
} catch {
|
|
33749
|
-
await (0,
|
|
33750
|
-
await (0,
|
|
33977
|
+
await (0, import_promises5.chmod)(home, 448);
|
|
33978
|
+
await (0, import_promises5.rm)(home, { recursive: true, force: true });
|
|
33751
33979
|
}
|
|
33752
33980
|
}
|
|
33753
33981
|
function parseOpenCodeVersionOutput(stdout) {
|
|
@@ -33815,7 +34043,7 @@ function buildOpenCodeSafeConfigJson(options) {
|
|
|
33815
34043
|
async function readValidatedOpenCodeAuth(sourceAuthPath, options) {
|
|
33816
34044
|
let info;
|
|
33817
34045
|
try {
|
|
33818
|
-
info = await (0,
|
|
34046
|
+
info = await (0, import_promises5.lstat)(sourceAuthPath);
|
|
33819
34047
|
} catch (error) {
|
|
33820
34048
|
if (error.code === "ENOENT") {
|
|
33821
34049
|
if (options?.allowMissing) return null;
|
|
@@ -33844,7 +34072,7 @@ async function readValidatedOpenCodeAuth(sourceAuthPath, options) {
|
|
|
33844
34072
|
"OpenCode auth file exceeds the listener safety bound"
|
|
33845
34073
|
);
|
|
33846
34074
|
}
|
|
33847
|
-
const raw = await (0,
|
|
34075
|
+
const raw = await (0, import_promises5.readFile)(sourceAuthPath);
|
|
33848
34076
|
if (raw.byteLength > MAX_OPENCODE_AUTH_BYTES) {
|
|
33849
34077
|
throw new AcpHostError(
|
|
33850
34078
|
"opencode_auth_too_large",
|
|
@@ -33870,53 +34098,53 @@ function resolveOpenCodeAuthSourcePath(parent = process.env) {
|
|
|
33870
34098
|
return (0, import_node_path6.join)(home, ".local", "share", "opencode", "auth.json");
|
|
33871
34099
|
}
|
|
33872
34100
|
async function prepareOpenCodeIsolatedHome(options) {
|
|
33873
|
-
const home = options.home ?? await (0,
|
|
34101
|
+
const home = options.home ?? await (0, import_promises5.mkdtemp)((0, import_node_path6.join)((0, import_node_os5.tmpdir)(), OPENCODE_HOME_PREFIX));
|
|
33874
34102
|
if (!(0, import_node_path6.isAbsolute)(home)) {
|
|
33875
34103
|
throw new AcpHostError(
|
|
33876
34104
|
"isolated_home_invalid",
|
|
33877
34105
|
"isolated OpenCode home must be absolute"
|
|
33878
34106
|
);
|
|
33879
34107
|
}
|
|
33880
|
-
await (0,
|
|
34108
|
+
await (0, import_promises5.chmod)(home, 448);
|
|
33881
34109
|
try {
|
|
33882
34110
|
const xdgConfig = (0, import_node_path6.join)(home, "xdg-config");
|
|
33883
34111
|
const xdgData = (0, import_node_path6.join)(home, "xdg-data");
|
|
33884
34112
|
const xdgCache = (0, import_node_path6.join)(home, "xdg-cache");
|
|
33885
34113
|
const xdgState = (0, import_node_path6.join)(home, "xdg-state");
|
|
33886
34114
|
for (const dir of [xdgConfig, xdgData, xdgCache, xdgState]) {
|
|
33887
|
-
await (0,
|
|
33888
|
-
await (0,
|
|
34115
|
+
await (0, import_promises5.mkdir)(dir, { recursive: true, mode: 448 });
|
|
34116
|
+
await (0, import_promises5.chmod)(dir, 448);
|
|
33889
34117
|
}
|
|
33890
34118
|
const configDir = (0, import_node_path6.join)(xdgConfig, "opencode");
|
|
33891
34119
|
const dataDir = (0, import_node_path6.join)(xdgData, "opencode");
|
|
33892
|
-
await (0,
|
|
33893
|
-
await (0,
|
|
33894
|
-
await (0,
|
|
33895
|
-
await (0,
|
|
34120
|
+
await (0, import_promises5.mkdir)(configDir, { recursive: true, mode: 448 });
|
|
34121
|
+
await (0, import_promises5.mkdir)(dataDir, { recursive: true, mode: 448 });
|
|
34122
|
+
await (0, import_promises5.chmod)(configDir, 448);
|
|
34123
|
+
await (0, import_promises5.chmod)(dataDir, 448);
|
|
33896
34124
|
const configPath = (0, import_node_path6.join)(configDir, "opencode.json");
|
|
33897
|
-
await (0,
|
|
34125
|
+
await (0, import_promises5.writeFile)(
|
|
33898
34126
|
configPath,
|
|
33899
34127
|
buildOpenCodeSafeConfigJson(
|
|
33900
34128
|
options.model ? { model: options.model } : void 0
|
|
33901
34129
|
),
|
|
33902
34130
|
{ flag: "wx", mode: 384 }
|
|
33903
34131
|
);
|
|
33904
|
-
await (0,
|
|
34132
|
+
await (0, import_promises5.chmod)(configPath, 384);
|
|
33905
34133
|
const sourceAuth = resolveOpenCodeAuthSourcePath(options.env ?? process.env);
|
|
33906
34134
|
const authBytes = await readValidatedOpenCodeAuth(sourceAuth, {
|
|
33907
34135
|
allowMissing: options.allowMissingAuth === true
|
|
33908
34136
|
});
|
|
33909
34137
|
if (authBytes) {
|
|
33910
34138
|
const destAuth = (0, import_node_path6.join)(dataDir, "auth.json");
|
|
33911
|
-
await (0,
|
|
33912
|
-
await (0,
|
|
34139
|
+
await (0, import_promises5.writeFile)(destAuth, authBytes, { flag: "wx", mode: 384 });
|
|
34140
|
+
await (0, import_promises5.chmod)(destAuth, 384);
|
|
33913
34141
|
}
|
|
33914
34142
|
const owner = options.owner ?? buildOpenCodeHomeOwner({ role: "ephemeral" });
|
|
33915
34143
|
await writeOpenCodeHomeOwner(home, owner);
|
|
33916
34144
|
return home;
|
|
33917
34145
|
} catch (error) {
|
|
33918
34146
|
if (!options.home) {
|
|
33919
|
-
await (0,
|
|
34147
|
+
await (0, import_promises5.rm)(home, { recursive: true, force: true }).catch(() => void 0);
|
|
33920
34148
|
}
|
|
33921
34149
|
throw error;
|
|
33922
34150
|
}
|
|
@@ -33941,10 +34169,10 @@ function buildOpenCodeChildEnv(parent, home) {
|
|
|
33941
34169
|
};
|
|
33942
34170
|
}
|
|
33943
34171
|
async function assertOpenCodeEffectiveConfig(options) {
|
|
33944
|
-
const hostile = await (0,
|
|
34172
|
+
const hostile = await (0, import_promises5.mkdtemp)((0, import_node_path6.join)((0, import_node_os5.tmpdir)(), "cswarm-opencode-hostile-"));
|
|
33945
34173
|
try {
|
|
33946
|
-
await (0,
|
|
33947
|
-
await (0,
|
|
34174
|
+
await (0, import_promises5.chmod)(hostile, 448);
|
|
34175
|
+
await (0, import_promises5.writeFile)(
|
|
33948
34176
|
(0, import_node_path6.join)(hostile, "opencode.json"),
|
|
33949
34177
|
`${JSON.stringify({
|
|
33950
34178
|
permission: {
|
|
@@ -34007,7 +34235,7 @@ async function assertOpenCodeEffectiveConfig(options) {
|
|
|
34007
34235
|
assertForcedAskPermissionMap(map);
|
|
34008
34236
|
return { permission: map };
|
|
34009
34237
|
} finally {
|
|
34010
|
-
await (0,
|
|
34238
|
+
await (0, import_promises5.rm)(hostile, { recursive: true, force: true }).catch(() => void 0);
|
|
34011
34239
|
}
|
|
34012
34240
|
}
|
|
34013
34241
|
function assertForcedAskPermissionMap(map) {
|
|
@@ -34058,7 +34286,7 @@ async function sweepStaleOpenCodeHomes(options) {
|
|
|
34058
34286
|
let removed = 0;
|
|
34059
34287
|
let entries;
|
|
34060
34288
|
try {
|
|
34061
|
-
entries = await (0,
|
|
34289
|
+
entries = await (0, import_promises5.readdir)(root);
|
|
34062
34290
|
} catch {
|
|
34063
34291
|
return 0;
|
|
34064
34292
|
}
|
|
@@ -34066,7 +34294,7 @@ async function sweepStaleOpenCodeHomes(options) {
|
|
|
34066
34294
|
if (!name.startsWith(OPENCODE_HOME_PREFIX)) continue;
|
|
34067
34295
|
const full = (0, import_node_path6.join)(root, name);
|
|
34068
34296
|
try {
|
|
34069
|
-
const st = await (0,
|
|
34297
|
+
const st = await (0, import_promises5.lstat)(full);
|
|
34070
34298
|
if (!st.isDirectory() || st.isSymbolicLink()) continue;
|
|
34071
34299
|
if (selfUid !== null && typeof st.uid === "number" && st.uid !== selfUid) {
|
|
34072
34300
|
continue;
|
|
@@ -34080,12 +34308,12 @@ async function sweepStaleOpenCodeHomes(options) {
|
|
|
34080
34308
|
if (alive(owner.pid)) {
|
|
34081
34309
|
continue;
|
|
34082
34310
|
}
|
|
34083
|
-
await (0,
|
|
34311
|
+
await (0, import_promises5.rm)(full, { recursive: true, force: true });
|
|
34084
34312
|
removed += 1;
|
|
34085
34313
|
continue;
|
|
34086
34314
|
}
|
|
34087
34315
|
if (now - st.mtimeMs < maxAgeMs) continue;
|
|
34088
|
-
await (0,
|
|
34316
|
+
await (0, import_promises5.rm)(full, { recursive: true, force: true });
|
|
34089
34317
|
removed += 1;
|
|
34090
34318
|
} catch {
|
|
34091
34319
|
}
|
|
@@ -34146,10 +34374,10 @@ async function openOpenCodeAcpSession(options) {
|
|
|
34146
34374
|
const disposeHome = async () => {
|
|
34147
34375
|
if (createdHome) {
|
|
34148
34376
|
try {
|
|
34149
|
-
await (0,
|
|
34377
|
+
await (0, import_promises5.rm)(home, { recursive: true, force: true });
|
|
34150
34378
|
} catch {
|
|
34151
|
-
await (0,
|
|
34152
|
-
await (0,
|
|
34379
|
+
await (0, import_promises5.chmod)(home, 448);
|
|
34380
|
+
await (0, import_promises5.rm)(home, { recursive: true, force: true });
|
|
34153
34381
|
}
|
|
34154
34382
|
}
|
|
34155
34383
|
};
|
|
@@ -35827,7 +36055,7 @@ var FileListenerEffectStore = class {
|
|
|
35827
36055
|
|
|
35828
36056
|
// src/listener/grok-model.ts
|
|
35829
36057
|
var import_node_crypto14 = require("node:crypto");
|
|
35830
|
-
var
|
|
36058
|
+
var import_promises6 = require("node:fs/promises");
|
|
35831
36059
|
var import_node_os7 = require("node:os");
|
|
35832
36060
|
var import_node_path11 = require("node:path");
|
|
35833
36061
|
|
|
@@ -36099,9 +36327,9 @@ var GrokListenerModel = class {
|
|
|
36099
36327
|
}
|
|
36100
36328
|
let sentinelCreated = false;
|
|
36101
36329
|
try {
|
|
36102
|
-
await (0,
|
|
36330
|
+
await (0, import_promises6.lstat)(sentinelPath);
|
|
36103
36331
|
sentinelCreated = true;
|
|
36104
|
-
await (0,
|
|
36332
|
+
await (0, import_promises6.unlink)(sentinelPath);
|
|
36105
36333
|
} catch (error) {
|
|
36106
36334
|
if (error.code !== "ENOENT") throw error;
|
|
36107
36335
|
}
|
|
@@ -36123,7 +36351,7 @@ var GrokListenerModel = class {
|
|
|
36123
36351
|
const sourceAuth = (0, import_node_path11.join)(sourceHome, "auth.json");
|
|
36124
36352
|
let info;
|
|
36125
36353
|
try {
|
|
36126
|
-
info = await (0,
|
|
36354
|
+
info = await (0, import_promises6.lstat)(sourceAuth);
|
|
36127
36355
|
} catch (error) {
|
|
36128
36356
|
if (error.code !== "ENOENT") throw error;
|
|
36129
36357
|
throw new AcpHostError(
|
|
@@ -36149,7 +36377,7 @@ var GrokListenerModel = class {
|
|
|
36149
36377
|
"Grok auth file exceeds the listener safety bound"
|
|
36150
36378
|
);
|
|
36151
36379
|
}
|
|
36152
|
-
const raw = await (0,
|
|
36380
|
+
const raw = await (0, import_promises6.readFile)(sourceAuth);
|
|
36153
36381
|
if (raw.byteLength > MAX_GROK_AUTH_BYTES) {
|
|
36154
36382
|
throw new AcpHostError(
|
|
36155
36383
|
"grok_auth_too_large",
|
|
@@ -36169,7 +36397,7 @@ var GrokListenerModel = class {
|
|
|
36169
36397
|
|
|
36170
36398
|
// src/listener/opencode-model.ts
|
|
36171
36399
|
var import_node_crypto15 = require("node:crypto");
|
|
36172
|
-
var
|
|
36400
|
+
var import_promises7 = require("node:fs/promises");
|
|
36173
36401
|
var import_node_path12 = require("node:path");
|
|
36174
36402
|
function asError(error) {
|
|
36175
36403
|
return error instanceof Error ? error : new Error(String(error));
|
|
@@ -36181,8 +36409,8 @@ var OpenCodeListenerModel = class {
|
|
|
36181
36409
|
this.openSession = options.open ?? openOpenCodeAcpSession;
|
|
36182
36410
|
this.prepareHome = options.prepareHome ?? prepareOpenCodeIsolatedHome;
|
|
36183
36411
|
this.prepareWorkerCwd = options.prepareWorkerCwd ?? (async (home) => {
|
|
36184
|
-
const cwd = await (0,
|
|
36185
|
-
await (0,
|
|
36412
|
+
const cwd = await (0, import_promises7.mkdtemp)((0, import_node_path12.join)(home, "canary-cwd-"));
|
|
36413
|
+
await (0, import_promises7.chmod)(cwd, 448);
|
|
36186
36414
|
return cwd;
|
|
36187
36415
|
});
|
|
36188
36416
|
this.permissionMode = options.permissionMode ?? "deny";
|
|
@@ -36526,11 +36754,11 @@ var OpenCodeListenerModel = class {
|
|
|
36526
36754
|
await closeWorker();
|
|
36527
36755
|
} catch (error) {
|
|
36528
36756
|
if (error?.code !== "child_exit_timeout") {
|
|
36529
|
-
await (0,
|
|
36757
|
+
await (0, import_promises7.rm)(ownedCanaryCwd, { recursive: true, force: true }).catch(() => void 0);
|
|
36530
36758
|
}
|
|
36531
36759
|
throw error;
|
|
36532
36760
|
}
|
|
36533
|
-
await (0,
|
|
36761
|
+
await (0, import_promises7.rm)(ownedCanaryCwd, { recursive: true, force: true }).catch(() => void 0);
|
|
36534
36762
|
})();
|
|
36535
36763
|
return await closePromise;
|
|
36536
36764
|
};
|
|
@@ -36601,7 +36829,7 @@ var OpenCodeListenerModel = class {
|
|
|
36601
36829
|
throw finalErr;
|
|
36602
36830
|
} finally {
|
|
36603
36831
|
if (canaryCwd !== null && !canaryCwdOwnedByHandle && !retainCanaryCwd) {
|
|
36604
|
-
await (0,
|
|
36832
|
+
await (0, import_promises7.rm)(canaryCwd, { recursive: true, force: true }).catch(() => void 0);
|
|
36605
36833
|
}
|
|
36606
36834
|
}
|
|
36607
36835
|
}
|
|
@@ -36609,7 +36837,7 @@ var OpenCodeListenerModel = class {
|
|
|
36609
36837
|
|
|
36610
36838
|
// src/listener/claude-model.ts
|
|
36611
36839
|
var import_node_crypto16 = require("node:crypto");
|
|
36612
|
-
var
|
|
36840
|
+
var import_promises8 = require("node:fs/promises");
|
|
36613
36841
|
var import_node_os8 = require("node:os");
|
|
36614
36842
|
var import_node_path13 = require("node:path");
|
|
36615
36843
|
var CLAUDE_CODE_VERSION_REQUIRED_RE = /\bClaude Code (\d+\.\d+\.\d+) does not support this model; version (\d+\.\d+\.\d+) or newer is required\b/;
|
|
@@ -36798,9 +37026,9 @@ var ClaudeListenerModel = class {
|
|
|
36798
37026
|
canaryError = error;
|
|
36799
37027
|
} finally {
|
|
36800
37028
|
try {
|
|
36801
|
-
await (0,
|
|
37029
|
+
await (0, import_promises8.lstat)(sentinelPath);
|
|
36802
37030
|
sentinelCreated = true;
|
|
36803
|
-
await (0,
|
|
37031
|
+
await (0, import_promises8.unlink)(sentinelPath);
|
|
36804
37032
|
} catch (error) {
|
|
36805
37033
|
if (error.code !== "ENOENT") throw error;
|
|
36806
37034
|
}
|
|
@@ -36829,7 +37057,7 @@ var ClaudeListenerModel = class {
|
|
|
36829
37057
|
|
|
36830
37058
|
// src/listener/codex-model.ts
|
|
36831
37059
|
var import_node_crypto17 = require("node:crypto");
|
|
36832
|
-
var
|
|
37060
|
+
var import_promises9 = require("node:fs/promises");
|
|
36833
37061
|
var import_node_os9 = require("node:os");
|
|
36834
37062
|
var import_node_path14 = require("node:path");
|
|
36835
37063
|
var CodexListenerClosedDuringOpen = class extends Error {
|
|
@@ -36977,11 +37205,11 @@ var CodexListenerModel = class {
|
|
|
36977
37205
|
const configuredHome = this.options.env?.HOME;
|
|
36978
37206
|
const home = configuredHome && (0, import_node_path14.isAbsolute)(configuredHome) ? configuredHome : (0, import_node_os9.homedir)();
|
|
36979
37207
|
const sentinelDirectory = (0, import_node_path14.join)(home, ".cswarm", "canary");
|
|
36980
|
-
await (0,
|
|
36981
|
-
await (0,
|
|
37208
|
+
await (0, import_promises9.mkdir)(sentinelDirectory, { recursive: true, mode: 448 });
|
|
37209
|
+
await (0, import_promises9.chmod)(sentinelDirectory, 448);
|
|
36982
37210
|
const [workerCwd, canaryDirectory] = await Promise.all([
|
|
36983
|
-
(0,
|
|
36984
|
-
(0,
|
|
37211
|
+
(0, import_promises9.realpath)(this.options.cwd),
|
|
37212
|
+
(0, import_promises9.realpath)(sentinelDirectory)
|
|
36985
37213
|
]);
|
|
36986
37214
|
if (pathIsInsideOrEqual(workerCwd, canaryDirectory)) {
|
|
36987
37215
|
throw new AcpPermissionCanaryError(
|
|
@@ -37004,9 +37232,9 @@ var CodexListenerModel = class {
|
|
|
37004
37232
|
canaryError = error;
|
|
37005
37233
|
} finally {
|
|
37006
37234
|
try {
|
|
37007
|
-
await (0,
|
|
37235
|
+
await (0, import_promises9.lstat)(sentinelPath);
|
|
37008
37236
|
sentinelCreated = true;
|
|
37009
|
-
await (0,
|
|
37237
|
+
await (0, import_promises9.unlink)(sentinelPath);
|
|
37010
37238
|
} catch (error) {
|
|
37011
37239
|
if (error.code !== "ENOENT") throw error;
|
|
37012
37240
|
}
|
|
@@ -37238,9 +37466,350 @@ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
|
|
|
37238
37466
|
}, true);
|
|
37239
37467
|
}
|
|
37240
37468
|
|
|
37469
|
+
// src/listener/wake.ts
|
|
37470
|
+
var LISTENER_RECONCILE_POLL_MS = 3e5;
|
|
37471
|
+
var WAKE_COALESCE_MS = 1e3;
|
|
37472
|
+
var WAKE_CLAIMS_PER_MINUTE_BUDGET = 50;
|
|
37473
|
+
var WAKE_RATE_LIMIT_POLL_MS = 6e4;
|
|
37474
|
+
var REALTIME_SUBSCRIBE_STATUS = {
|
|
37475
|
+
SUBSCRIBED: "SUBSCRIBED",
|
|
37476
|
+
CHANNEL_ERROR: "CHANNEL_ERROR",
|
|
37477
|
+
CLOSED: "CLOSED",
|
|
37478
|
+
TIMED_OUT: "TIMED_OUT"
|
|
37479
|
+
};
|
|
37480
|
+
var LISTENER_WAKE_MODES = ["push", "poll"];
|
|
37481
|
+
var LISTENER_WAKE_MODE_PUSH = LISTENER_WAKE_MODES[0];
|
|
37482
|
+
var LISTENER_WAKE_MODE_POLL = LISTENER_WAKE_MODES[1];
|
|
37483
|
+
var LISTENER_WAKE_MODE_SET = new Set(
|
|
37484
|
+
LISTENER_WAKE_MODES
|
|
37485
|
+
);
|
|
37486
|
+
var WAKE_ERROR_CODES = [
|
|
37487
|
+
"channel_error",
|
|
37488
|
+
"closed",
|
|
37489
|
+
"timed_out",
|
|
37490
|
+
"rate_limited",
|
|
37491
|
+
"wake_budget"
|
|
37492
|
+
];
|
|
37493
|
+
var WAKE_ERROR_CODE_SET = new Set(WAKE_ERROR_CODES);
|
|
37494
|
+
var WAKE_ERROR_CODE_WAKE_BUDGET = WAKE_ERROR_CODES.find(
|
|
37495
|
+
(code) => code === "wake_budget"
|
|
37496
|
+
);
|
|
37497
|
+
var LISTENER_WAKE_STATUS_KEYS = [
|
|
37498
|
+
"mode",
|
|
37499
|
+
"subscribedAt",
|
|
37500
|
+
"reconnects",
|
|
37501
|
+
"lastWakeAt",
|
|
37502
|
+
"lastReconcileAt",
|
|
37503
|
+
"errorCode",
|
|
37504
|
+
"topicRotatedAt",
|
|
37505
|
+
"rateLimited"
|
|
37506
|
+
];
|
|
37507
|
+
var LISTENER_WAKE_SENSITIVE_KEYS = [
|
|
37508
|
+
"topic",
|
|
37509
|
+
"wakeTopic",
|
|
37510
|
+
"wake_topic"
|
|
37511
|
+
];
|
|
37512
|
+
function defaultRealtime(target2) {
|
|
37513
|
+
const client = createClient(target2.url, target2.anonKey, {
|
|
37514
|
+
auth: { persistSession: false, autoRefreshToken: false }
|
|
37515
|
+
});
|
|
37516
|
+
return client.realtime;
|
|
37517
|
+
}
|
|
37518
|
+
function isSubscribeStatus(value) {
|
|
37519
|
+
return value === REALTIME_SUBSCRIBE_STATUS.SUBSCRIBED || value === REALTIME_SUBSCRIBE_STATUS.CHANNEL_ERROR || value === REALTIME_SUBSCRIBE_STATUS.CLOSED || value === REALTIME_SUBSCRIBE_STATUS.TIMED_OUT;
|
|
37520
|
+
}
|
|
37521
|
+
function wakeErrorCodeFromSubscribeStatus(status) {
|
|
37522
|
+
switch (status) {
|
|
37523
|
+
case REALTIME_SUBSCRIBE_STATUS.CHANNEL_ERROR:
|
|
37524
|
+
return "channel_error";
|
|
37525
|
+
case REALTIME_SUBSCRIBE_STATUS.CLOSED:
|
|
37526
|
+
return "closed";
|
|
37527
|
+
case REALTIME_SUBSCRIBE_STATUS.TIMED_OUT:
|
|
37528
|
+
return "timed_out";
|
|
37529
|
+
default:
|
|
37530
|
+
return null;
|
|
37531
|
+
}
|
|
37532
|
+
}
|
|
37533
|
+
function emptyListenerWakeStatus() {
|
|
37534
|
+
return {
|
|
37535
|
+
mode: LISTENER_WAKE_MODE_POLL,
|
|
37536
|
+
subscribedAt: null,
|
|
37537
|
+
reconnects: 0,
|
|
37538
|
+
lastWakeAt: null,
|
|
37539
|
+
lastReconcileAt: null,
|
|
37540
|
+
errorCode: null,
|
|
37541
|
+
topicRotatedAt: null,
|
|
37542
|
+
rateLimited: false
|
|
37543
|
+
};
|
|
37544
|
+
}
|
|
37545
|
+
function listenerWakePersistWorthy(previous, next, lastPersistMs, nowMs) {
|
|
37546
|
+
if (previous === void 0) return true;
|
|
37547
|
+
if (previous.mode !== next.mode || previous.rateLimited !== next.rateLimited || previous.errorCode !== next.errorCode || previous.reconnects !== next.reconnects || previous.subscribedAt !== next.subscribedAt || previous.topicRotatedAt !== next.topicRotatedAt || previous.lastReconcileAt !== next.lastReconcileAt) {
|
|
37548
|
+
return true;
|
|
37549
|
+
}
|
|
37550
|
+
if (previous.lastWakeAt !== next.lastWakeAt) {
|
|
37551
|
+
return nowMs - lastPersistMs >= WAKE_COALESCE_MS;
|
|
37552
|
+
}
|
|
37553
|
+
return false;
|
|
37554
|
+
}
|
|
37555
|
+
function listenerWakeStatusSentence(wake, pollIntervalMs, lastWakeLabel) {
|
|
37556
|
+
if (wake.mode === LISTENER_WAKE_MODE_PUSH) {
|
|
37557
|
+
const last = lastWakeLabel === null ? "no wake yet" : `last wake ${lastWakeLabel}`;
|
|
37558
|
+
return `${LISTENER_WAKE_MODE_PUSH} (Realtime), ${last}, reconcile every ${formatIdlePollDuration(LISTENER_RECONCILE_POLL_MS)}.`;
|
|
37559
|
+
}
|
|
37560
|
+
if (wake.errorCode === WAKE_ERROR_CODE_WAKE_BUDGET) {
|
|
37561
|
+
return `Subscribed; claims paused until the minute clears (${WAKE_ERROR_CODE_WAKE_BUDGET}); polling every ${formatIdlePollDuration(pollIntervalMs)} meanwhile.`;
|
|
37562
|
+
}
|
|
37563
|
+
const code = wake.errorCode ?? "disconnected";
|
|
37564
|
+
return `${LISTENER_WAKE_MODE_POLL} every ${formatIdlePollDuration(pollIntervalMs)}. Realtime not connected (${code}).`;
|
|
37565
|
+
}
|
|
37566
|
+
var WakeSubscriber = class {
|
|
37567
|
+
now;
|
|
37568
|
+
target;
|
|
37569
|
+
createRealtime;
|
|
37570
|
+
realtime = null;
|
|
37571
|
+
channel = null;
|
|
37572
|
+
topic = null;
|
|
37573
|
+
waiter = null;
|
|
37574
|
+
pending = null;
|
|
37575
|
+
connectionState = "disconnected";
|
|
37576
|
+
subscribedAt = null;
|
|
37577
|
+
reconnects = 0;
|
|
37578
|
+
lastWakeAt = null;
|
|
37579
|
+
lastReconcileAt = null;
|
|
37580
|
+
lastErrorCode = null;
|
|
37581
|
+
topicRotatedAt = null;
|
|
37582
|
+
rateLimitedUntil = 0;
|
|
37583
|
+
lastClaimAt = 0;
|
|
37584
|
+
wakeClaimTimes = [];
|
|
37585
|
+
closed = false;
|
|
37586
|
+
everSubscribed = false;
|
|
37587
|
+
constructor(options) {
|
|
37588
|
+
this.target = options.target;
|
|
37589
|
+
this.now = options.now ?? Date.now;
|
|
37590
|
+
this.createRealtime = options.createRealtime ?? defaultRealtime;
|
|
37591
|
+
}
|
|
37592
|
+
get state() {
|
|
37593
|
+
return this.connectionState;
|
|
37594
|
+
}
|
|
37595
|
+
get hasTopic() {
|
|
37596
|
+
return this.topic !== null;
|
|
37597
|
+
}
|
|
37598
|
+
snapshot(nowMs = this.now()) {
|
|
37599
|
+
const serverLimited = nowMs < this.rateLimitedUntil;
|
|
37600
|
+
const overBudget = this.overWakeBudget(nowMs);
|
|
37601
|
+
const rateLimited = serverLimited || overBudget;
|
|
37602
|
+
const mode3 = this.connectionState === "subscribed" && !rateLimited ? LISTENER_WAKE_MODE_PUSH : LISTENER_WAKE_MODE_POLL;
|
|
37603
|
+
const errorCode = serverLimited ? "rate_limited" : overBudget ? WAKE_ERROR_CODE_WAKE_BUDGET : this.lastErrorCode;
|
|
37604
|
+
return {
|
|
37605
|
+
mode: mode3,
|
|
37606
|
+
subscribedAt: this.subscribedAt,
|
|
37607
|
+
reconnects: this.reconnects,
|
|
37608
|
+
lastWakeAt: this.lastWakeAt,
|
|
37609
|
+
lastReconcileAt: this.lastReconcileAt,
|
|
37610
|
+
errorCode,
|
|
37611
|
+
topicRotatedAt: this.topicRotatedAt,
|
|
37612
|
+
rateLimited
|
|
37613
|
+
};
|
|
37614
|
+
}
|
|
37615
|
+
noteReconcile(nowMs = this.now()) {
|
|
37616
|
+
this.lastReconcileAt = new Date(nowMs).toISOString();
|
|
37617
|
+
}
|
|
37618
|
+
noteClaim(nowMs = this.now()) {
|
|
37619
|
+
this.lastClaimAt = nowMs;
|
|
37620
|
+
}
|
|
37621
|
+
noteWakeClaim(nowMs = this.now()) {
|
|
37622
|
+
this.noteClaim(nowMs);
|
|
37623
|
+
this.wakeClaimTimes.push(nowMs);
|
|
37624
|
+
this.trimWakeClaims(nowMs);
|
|
37625
|
+
}
|
|
37626
|
+
coalescingRemainingMs(nowMs = this.now()) {
|
|
37627
|
+
if (this.lastClaimAt <= 0) return 0;
|
|
37628
|
+
return Math.max(0, this.lastClaimAt + WAKE_COALESCE_MS - nowMs);
|
|
37629
|
+
}
|
|
37630
|
+
overWakeBudget(nowMs = this.now()) {
|
|
37631
|
+
this.trimWakeClaims(nowMs);
|
|
37632
|
+
return this.wakeClaimTimes.length >= WAKE_CLAIMS_PER_MINUTE_BUDGET;
|
|
37633
|
+
}
|
|
37634
|
+
canClaimOnWake(nowMs = this.now()) {
|
|
37635
|
+
if (nowMs < this.rateLimitedUntil) return false;
|
|
37636
|
+
if (this.coalescingRemainingMs(nowMs) > 0) return false;
|
|
37637
|
+
return !this.overWakeBudget(nowMs);
|
|
37638
|
+
}
|
|
37639
|
+
markRateLimited(nowMs = this.now()) {
|
|
37640
|
+
this.rateLimitedUntil = nowMs + WAKE_RATE_LIMIT_POLL_MS;
|
|
37641
|
+
this.lastErrorCode = "rate_limited";
|
|
37642
|
+
this.emitPending("state");
|
|
37643
|
+
}
|
|
37644
|
+
setTopic(topic) {
|
|
37645
|
+
if (this.closed) return;
|
|
37646
|
+
if (!isWakeTopic(topic)) {
|
|
37647
|
+
throw new Error("wake topic is malformed");
|
|
37648
|
+
}
|
|
37649
|
+
if (this.topic === topic) return;
|
|
37650
|
+
const rotated = this.topic !== null;
|
|
37651
|
+
void this.detachChannel();
|
|
37652
|
+
this.topic = topic;
|
|
37653
|
+
if (rotated) {
|
|
37654
|
+
this.topicRotatedAt = new Date(this.now()).toISOString();
|
|
37655
|
+
}
|
|
37656
|
+
this.connect();
|
|
37657
|
+
}
|
|
37658
|
+
next(options) {
|
|
37659
|
+
if (this.waiter !== null) {
|
|
37660
|
+
throw new Error("wake next() already has a waiter");
|
|
37661
|
+
}
|
|
37662
|
+
if (options.signal?.aborted) {
|
|
37663
|
+
return Promise.resolve("deadline");
|
|
37664
|
+
}
|
|
37665
|
+
const nowMs = this.now();
|
|
37666
|
+
if (this.pending !== null) {
|
|
37667
|
+
if (!(this.pending === "wake" && this.wakeClaimPaused(nowMs))) {
|
|
37668
|
+
const reason = this.pending;
|
|
37669
|
+
this.pending = null;
|
|
37670
|
+
return Promise.resolve(reason);
|
|
37671
|
+
}
|
|
37672
|
+
}
|
|
37673
|
+
if (nowMs >= options.until) {
|
|
37674
|
+
return Promise.resolve("deadline");
|
|
37675
|
+
}
|
|
37676
|
+
return new Promise((resolve3) => {
|
|
37677
|
+
const finish = (reason) => {
|
|
37678
|
+
if (this.waiter === null) return;
|
|
37679
|
+
const current = this.waiter;
|
|
37680
|
+
this.waiter = null;
|
|
37681
|
+
if (current.timer !== null) clearTimeout(current.timer);
|
|
37682
|
+
if (current.signal && current.onAbort) {
|
|
37683
|
+
current.signal.removeEventListener("abort", current.onAbort);
|
|
37684
|
+
}
|
|
37685
|
+
resolve3(reason);
|
|
37686
|
+
};
|
|
37687
|
+
const delay2 = Math.max(0, options.until - this.now());
|
|
37688
|
+
const timer2 = setTimeout(() => finish("deadline"), delay2);
|
|
37689
|
+
const onAbort = () => finish("deadline");
|
|
37690
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
37691
|
+
this.waiter = {
|
|
37692
|
+
resolve: finish,
|
|
37693
|
+
timer: timer2,
|
|
37694
|
+
onAbort,
|
|
37695
|
+
signal: options.signal
|
|
37696
|
+
};
|
|
37697
|
+
});
|
|
37698
|
+
}
|
|
37699
|
+
async close() {
|
|
37700
|
+
this.closed = true;
|
|
37701
|
+
this.topic = null;
|
|
37702
|
+
this.finishWait("deadline");
|
|
37703
|
+
await this.detachChannel();
|
|
37704
|
+
try {
|
|
37705
|
+
this.realtime?.disconnect?.();
|
|
37706
|
+
} catch {
|
|
37707
|
+
}
|
|
37708
|
+
this.realtime = null;
|
|
37709
|
+
this.connectionState = "disconnected";
|
|
37710
|
+
}
|
|
37711
|
+
trimWakeClaims(nowMs) {
|
|
37712
|
+
const minuteStart = Math.floor(nowMs / 6e4) * 6e4;
|
|
37713
|
+
this.wakeClaimTimes = this.wakeClaimTimes.filter((ts) => ts >= minuteStart);
|
|
37714
|
+
}
|
|
37715
|
+
/** Client budget or a server 429: do not claim on wake; poll covers the window. */
|
|
37716
|
+
wakeClaimPaused(nowMs) {
|
|
37717
|
+
return nowMs < this.rateLimitedUntil || this.overWakeBudget(nowMs);
|
|
37718
|
+
}
|
|
37719
|
+
emitPending(reason) {
|
|
37720
|
+
if (this.waiter !== null) {
|
|
37721
|
+
if (reason === "wake" && this.wakeClaimPaused(this.now())) {
|
|
37722
|
+
this.pending = "wake";
|
|
37723
|
+
return;
|
|
37724
|
+
}
|
|
37725
|
+
this.finishWait(reason);
|
|
37726
|
+
return;
|
|
37727
|
+
}
|
|
37728
|
+
if (reason === "wake" || this.pending !== "wake") {
|
|
37729
|
+
this.pending = reason;
|
|
37730
|
+
}
|
|
37731
|
+
}
|
|
37732
|
+
finishWait(reason) {
|
|
37733
|
+
const waiter = this.waiter;
|
|
37734
|
+
if (waiter === null) return;
|
|
37735
|
+
this.waiter = null;
|
|
37736
|
+
if (waiter.timer !== null) clearTimeout(waiter.timer);
|
|
37737
|
+
if (waiter.signal && waiter.onAbort) {
|
|
37738
|
+
waiter.signal.removeEventListener("abort", waiter.onAbort);
|
|
37739
|
+
}
|
|
37740
|
+
waiter.resolve(reason);
|
|
37741
|
+
}
|
|
37742
|
+
connect() {
|
|
37743
|
+
if (this.closed || this.topic === null) return;
|
|
37744
|
+
if (this.realtime === null) {
|
|
37745
|
+
this.realtime = this.createRealtime(this.target);
|
|
37746
|
+
void this.realtime.setAuth(this.target.anonKey);
|
|
37747
|
+
}
|
|
37748
|
+
const topic = this.topic;
|
|
37749
|
+
this.connectionState = "connecting";
|
|
37750
|
+
this.lastErrorCode = null;
|
|
37751
|
+
const channel = this.realtime.channel(topic, {
|
|
37752
|
+
config: { private: true }
|
|
37753
|
+
});
|
|
37754
|
+
this.channel = channel;
|
|
37755
|
+
channel.on("broadcast", { event: WAKE_EVENT }, () => {
|
|
37756
|
+
this.lastWakeAt = new Date(this.now()).toISOString();
|
|
37757
|
+
this.emitPending("wake");
|
|
37758
|
+
});
|
|
37759
|
+
channel.subscribe((status) => {
|
|
37760
|
+
this.onSubscribeStatus(status);
|
|
37761
|
+
});
|
|
37762
|
+
}
|
|
37763
|
+
onSubscribeStatus(status) {
|
|
37764
|
+
if (this.closed) return;
|
|
37765
|
+
if (!isSubscribeStatus(status)) return;
|
|
37766
|
+
const wasSubscribed = this.connectionState === "subscribed";
|
|
37767
|
+
if (status === REALTIME_SUBSCRIBE_STATUS.SUBSCRIBED) {
|
|
37768
|
+
this.connectionState = "subscribed";
|
|
37769
|
+
this.subscribedAt = new Date(this.now()).toISOString();
|
|
37770
|
+
this.lastErrorCode = null;
|
|
37771
|
+
if (this.everSubscribed && !wasSubscribed) this.reconnects += 1;
|
|
37772
|
+
this.everSubscribed = true;
|
|
37773
|
+
if (!wasSubscribed) this.emitPending("state");
|
|
37774
|
+
return;
|
|
37775
|
+
}
|
|
37776
|
+
const code = wakeErrorCodeFromSubscribeStatus(status);
|
|
37777
|
+
if (status === REALTIME_SUBSCRIBE_STATUS.CLOSED) {
|
|
37778
|
+
this.connectionState = "disconnected";
|
|
37779
|
+
} else {
|
|
37780
|
+
this.connectionState = "errored";
|
|
37781
|
+
}
|
|
37782
|
+
this.lastErrorCode = code;
|
|
37783
|
+
this.subscribedAt = null;
|
|
37784
|
+
if (wasSubscribed) this.emitPending("state");
|
|
37785
|
+
}
|
|
37786
|
+
async detachChannel() {
|
|
37787
|
+
const channel = this.channel;
|
|
37788
|
+
this.channel = null;
|
|
37789
|
+
if (channel === null) return;
|
|
37790
|
+
try {
|
|
37791
|
+
await channel.unsubscribe();
|
|
37792
|
+
} catch {
|
|
37793
|
+
}
|
|
37794
|
+
try {
|
|
37795
|
+
await this.realtime?.removeChannel?.(channel);
|
|
37796
|
+
} catch {
|
|
37797
|
+
}
|
|
37798
|
+
if (this.channel !== null) return;
|
|
37799
|
+
if (this.connectionState === "subscribed") {
|
|
37800
|
+
this.connectionState = "disconnected";
|
|
37801
|
+
this.subscribedAt = null;
|
|
37802
|
+
}
|
|
37803
|
+
}
|
|
37804
|
+
};
|
|
37805
|
+
function createWakeSubscriber(options) {
|
|
37806
|
+
return new WakeSubscriber(options);
|
|
37807
|
+
}
|
|
37808
|
+
|
|
37241
37809
|
// src/listener/runtime.ts
|
|
37242
37810
|
var LISTENER_PAGE_LIMIT = 100;
|
|
37243
|
-
var LISTENER_IDLE_POLL_MS =
|
|
37811
|
+
var LISTENER_IDLE_POLL_MS = IDLE_POLL_DEFAULT_MS;
|
|
37812
|
+
var LISTENER_IDLE_POLL_MAX_MS = IDLE_POLL_MAX_MS;
|
|
37244
37813
|
var LISTENER_DELIVERY_SAFETY_MARGIN_MS = 3e4;
|
|
37245
37814
|
var LISTENER_ACK_ONLY_MINIMUM_MS = DELIVERY_REQUEST_TIMEOUT_MS + LISTENER_DELIVERY_SAFETY_MARGIN_MS;
|
|
37246
37815
|
var LISTENER_REPLY_ONLY_MINIMUM_MS = SIGNAL_REQUEST_TIMEOUT_MS + LISTENER_ACK_ONLY_MINIMUM_MS;
|
|
@@ -37525,10 +38094,22 @@ async function runListenerRuntime(options) {
|
|
|
37525
38094
|
const random = options.random ?? Math.random;
|
|
37526
38095
|
const pageLimit = options.pageLimit ?? LISTENER_PAGE_LIMIT;
|
|
37527
38096
|
const pollMs = options.pollMs ?? LISTENER_IDLE_POLL_MS;
|
|
38097
|
+
let emptyIdleStreak = 0;
|
|
37528
38098
|
const routeMode = options.routeMode ?? "worker";
|
|
37529
38099
|
const deferOverChars = options.deferOverChars ?? null;
|
|
37530
38100
|
const deliveryHoldBudgetMs = options.deliveryHoldBudgetMs ?? LISTENER_DELIVERY_HOLD_BUDGET_MS;
|
|
37531
38101
|
const abort = options.signal;
|
|
38102
|
+
const idleSleep = async (hadDelivery) => {
|
|
38103
|
+
if (hadDelivery) emptyIdleStreak = 0;
|
|
38104
|
+
const intervalMs = nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
|
|
38105
|
+
if (!hadDelivery) emptyIdleStreak += 1;
|
|
38106
|
+
options.onEvent?.({
|
|
38107
|
+
type: "idle_poll",
|
|
38108
|
+
intervalMs,
|
|
38109
|
+
ts: eventTime(now)
|
|
38110
|
+
});
|
|
38111
|
+
await sleep2(intervalMs, abort);
|
|
38112
|
+
};
|
|
37532
38113
|
const hasInstanceId = options.listenerInstanceId !== void 0;
|
|
37533
38114
|
const hasJournal = options.deliveryJournal !== void 0;
|
|
37534
38115
|
if (hasInstanceId !== hasJournal) {
|
|
@@ -37714,6 +38295,35 @@ async function runListenerRuntime(options) {
|
|
|
37714
38295
|
abort.addEventListener("abort", onAbort);
|
|
37715
38296
|
}
|
|
37716
38297
|
let stop;
|
|
38298
|
+
let wakeSubscriber = options.wake ?? null;
|
|
38299
|
+
let reconcileDueAt = now();
|
|
38300
|
+
const ensureWake = () => {
|
|
38301
|
+
if (wakeSubscriber === null) {
|
|
38302
|
+
wakeSubscriber = options.createWake ? options.createWake(options.target) : createWakeSubscriber({ target: options.target, now });
|
|
38303
|
+
}
|
|
38304
|
+
return wakeSubscriber;
|
|
38305
|
+
};
|
|
38306
|
+
const applyWakeHint = (hint) => {
|
|
38307
|
+
if (hint === void 0) return;
|
|
38308
|
+
try {
|
|
38309
|
+
ensureWake().setTopic(hint.topic);
|
|
38310
|
+
} catch {
|
|
38311
|
+
}
|
|
38312
|
+
};
|
|
38313
|
+
const emitWake = () => {
|
|
38314
|
+
if (wakeSubscriber === null) return;
|
|
38315
|
+
options.onEvent?.({
|
|
38316
|
+
type: "wake",
|
|
38317
|
+
wake: wakeSubscriber.snapshot(now()),
|
|
38318
|
+
ts: eventTime(now)
|
|
38319
|
+
});
|
|
38320
|
+
};
|
|
38321
|
+
const waitCapMs = () => {
|
|
38322
|
+
if (wakeSubscriber !== null && wakeSubscriber.snapshot(now()).mode === LISTENER_WAKE_MODE_PUSH) {
|
|
38323
|
+
return LISTENER_RECONCILE_POLL_MS;
|
|
38324
|
+
}
|
|
38325
|
+
return nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
|
|
38326
|
+
};
|
|
37717
38327
|
const sendPreparedAck = async (active) => {
|
|
37718
38328
|
if (active.phase !== "ack_pending" || active.signalId === null || active.leaseId === null || active.leasedUntil === null || active.ack === null) {
|
|
37719
38329
|
return {
|
|
@@ -37780,8 +38390,33 @@ async function runListenerRuntime(options) {
|
|
|
37780
38390
|
stop = { reason: "cancelled" };
|
|
37781
38391
|
break;
|
|
37782
38392
|
}
|
|
37783
|
-
let
|
|
37784
|
-
|
|
38393
|
+
let skipRead = false;
|
|
38394
|
+
if (ready && deliveryMode === "durable_claim" && wakeSubscriber !== null && wakeSubscriber.hasTopic) {
|
|
38395
|
+
const until = Math.min(reconcileDueAt, now() + waitCapMs());
|
|
38396
|
+
const reason = await wakeSubscriber.next({
|
|
38397
|
+
until,
|
|
38398
|
+
...abort ? { signal: abort } : {}
|
|
38399
|
+
});
|
|
38400
|
+
emitWake();
|
|
38401
|
+
if (abort?.aborted) {
|
|
38402
|
+
stop = { reason: "cancelled" };
|
|
38403
|
+
break;
|
|
38404
|
+
}
|
|
38405
|
+
if (reason === "wake" && wakeSubscriber.snapshot(now()).mode === LISTENER_WAKE_MODE_PUSH) {
|
|
38406
|
+
const coalesceMs = wakeSubscriber.coalescingRemainingMs(now());
|
|
38407
|
+
if (coalesceMs > 0) await sleep2(coalesceMs, abort);
|
|
38408
|
+
if (abort?.aborted) {
|
|
38409
|
+
stop = { reason: "cancelled" };
|
|
38410
|
+
break;
|
|
38411
|
+
}
|
|
38412
|
+
if (wakeSubscriber.snapshot(now()).mode === LISTENER_WAKE_MODE_PUSH) {
|
|
38413
|
+
skipRead = true;
|
|
38414
|
+
}
|
|
38415
|
+
}
|
|
38416
|
+
}
|
|
38417
|
+
let page = null;
|
|
38418
|
+
if (skipRead) {
|
|
38419
|
+
} else try {
|
|
37785
38420
|
const token = await options.credentialSession.bearer();
|
|
37786
38421
|
page = await readPage({
|
|
37787
38422
|
token,
|
|
@@ -37799,6 +38434,8 @@ async function runListenerRuntime(options) {
|
|
|
37799
38434
|
}
|
|
37800
38435
|
});
|
|
37801
38436
|
requireCapabilities(page);
|
|
38437
|
+
applyWakeHint(page.wake);
|
|
38438
|
+
emitWake();
|
|
37802
38439
|
if (ready && readEpisodeStartedAtMs !== null) {
|
|
37803
38440
|
const recoveredAtMs = now();
|
|
37804
38441
|
options.onEvent?.({
|
|
@@ -37926,7 +38563,7 @@ async function runListenerRuntime(options) {
|
|
|
37926
38563
|
break;
|
|
37927
38564
|
}
|
|
37928
38565
|
}
|
|
37929
|
-
if (page
|
|
38566
|
+
if ((page?.capabilities.deliveryAck === true || skipRead) && now() < horizon && !preparedNeedsMainRoute) {
|
|
37930
38567
|
const ackStop = await sendPreparedAck(recovery);
|
|
37931
38568
|
if (ackStop !== null) {
|
|
37932
38569
|
stop = ackStop;
|
|
@@ -37936,7 +38573,7 @@ async function runListenerRuntime(options) {
|
|
|
37936
38573
|
stop = { reason: "cancelled" };
|
|
37937
38574
|
break;
|
|
37938
38575
|
}
|
|
37939
|
-
await
|
|
38576
|
+
await idleSleep(true);
|
|
37940
38577
|
continue;
|
|
37941
38578
|
}
|
|
37942
38579
|
await sleep2(Math.max(0, horizon - now()), abort);
|
|
@@ -37956,7 +38593,7 @@ async function runListenerRuntime(options) {
|
|
|
37956
38593
|
continue;
|
|
37957
38594
|
}
|
|
37958
38595
|
if (recovery?.phase === "leased") {
|
|
37959
|
-
if (page
|
|
38596
|
+
if (page?.capabilities.deliveryAck === true || skipRead) {
|
|
37960
38597
|
let terminal = null;
|
|
37961
38598
|
if (recovery.signalId !== null) {
|
|
37962
38599
|
try {
|
|
@@ -38066,6 +38703,10 @@ async function runListenerRuntime(options) {
|
|
|
38066
38703
|
stop = { reason: "credential", error: asError2(error) };
|
|
38067
38704
|
break;
|
|
38068
38705
|
}
|
|
38706
|
+
if (error instanceof DeliveryHttpError && error.code === "rate_limited") {
|
|
38707
|
+
wakeSubscriber?.markRateLimited(now());
|
|
38708
|
+
emitWake();
|
|
38709
|
+
}
|
|
38069
38710
|
if (!isRetryableDeliveryError(error)) {
|
|
38070
38711
|
stop = { reason: "fatal", error: asError2(error) };
|
|
38071
38712
|
break;
|
|
@@ -38084,6 +38725,14 @@ async function runListenerRuntime(options) {
|
|
|
38084
38725
|
stop = { reason: "fatal", error: new Error("delivery claim did not settle") };
|
|
38085
38726
|
break;
|
|
38086
38727
|
}
|
|
38728
|
+
applyWakeHint(result.wake);
|
|
38729
|
+
if (!skipRead && wakeSubscriber !== null && wakeSubscriber.hasTopic) {
|
|
38730
|
+
wakeSubscriber.noteReconcile(now());
|
|
38731
|
+
reconcileDueAt = now() + LISTENER_RECONCILE_POLL_MS;
|
|
38732
|
+
}
|
|
38733
|
+
if (skipRead) wakeSubscriber?.noteWakeClaim(now());
|
|
38734
|
+
else wakeSubscriber?.noteClaim(now());
|
|
38735
|
+
emitWake();
|
|
38087
38736
|
const claimed = result.deliveries[0] ?? null;
|
|
38088
38737
|
options.onEvent?.({
|
|
38089
38738
|
type: "delivery_claim",
|
|
@@ -38114,9 +38763,28 @@ async function runListenerRuntime(options) {
|
|
|
38114
38763
|
stop = { reason: "fatal", error: asError2(error) };
|
|
38115
38764
|
break;
|
|
38116
38765
|
}
|
|
38117
|
-
|
|
38766
|
+
if (wakeSubscriber !== null && wakeSubscriber.hasTopic) {
|
|
38767
|
+
const snap = wakeSubscriber.snapshot(now());
|
|
38768
|
+
const intervalMs = snap.mode === LISTENER_WAKE_MODE_PUSH ? LISTENER_RECONCILE_POLL_MS : nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
|
|
38769
|
+
if (snap.mode === LISTENER_WAKE_MODE_PUSH) emptyIdleStreak = 0;
|
|
38770
|
+
else emptyIdleStreak += 1;
|
|
38771
|
+
options.onEvent?.({
|
|
38772
|
+
type: "idle_poll",
|
|
38773
|
+
intervalMs,
|
|
38774
|
+
ts: eventTime(now)
|
|
38775
|
+
});
|
|
38776
|
+
emitWake();
|
|
38777
|
+
continue;
|
|
38778
|
+
}
|
|
38779
|
+
await idleSleep(false);
|
|
38118
38780
|
continue;
|
|
38119
38781
|
}
|
|
38782
|
+
emptyIdleStreak = 0;
|
|
38783
|
+
options.onEvent?.({
|
|
38784
|
+
type: "idle_poll",
|
|
38785
|
+
intervalMs: pollMs,
|
|
38786
|
+
ts: eventTime(now)
|
|
38787
|
+
});
|
|
38120
38788
|
const leasedUntilMs = Date.parse(claimed.leasedUntil);
|
|
38121
38789
|
if (!Number.isFinite(leasedUntilMs) || leasedUntilMs > now() + LISTENER_DELIVERY_MAX_LEASE_MS) {
|
|
38122
38790
|
stop = { reason: "fatal", error: new Error("delivery lease deadline is invalid") };
|
|
@@ -38302,6 +38970,7 @@ async function runListenerRuntime(options) {
|
|
|
38302
38970
|
}
|
|
38303
38971
|
continue;
|
|
38304
38972
|
}
|
|
38973
|
+
if (page === null) continue;
|
|
38305
38974
|
for (const signal of page.signals) {
|
|
38306
38975
|
if (abort?.aborted) {
|
|
38307
38976
|
stop = { reason: "cancelled" };
|
|
@@ -38389,11 +39058,15 @@ async function runListenerRuntime(options) {
|
|
|
38389
39058
|
continue;
|
|
38390
39059
|
}
|
|
38391
39060
|
after = null;
|
|
38392
|
-
await
|
|
39061
|
+
await idleSleep(page.signals.length > 0);
|
|
38393
39062
|
}
|
|
38394
39063
|
} finally {
|
|
38395
39064
|
abort?.removeEventListener("abort", onAbort);
|
|
38396
39065
|
options.model.cancel();
|
|
39066
|
+
try {
|
|
39067
|
+
await wakeSubscriber?.close();
|
|
39068
|
+
} catch {
|
|
39069
|
+
}
|
|
38397
39070
|
try {
|
|
38398
39071
|
await options.model.close();
|
|
38399
39072
|
} catch (error) {
|
|
@@ -38411,6 +39084,7 @@ var LISTENER_READ_RETRY_HOUR_CAP = 25;
|
|
|
38411
39084
|
var LISTENER_READ_RETRY_MINUTE_CAP = 61;
|
|
38412
39085
|
var LISTENER_CLAIM_HOUR_CAP = 25;
|
|
38413
39086
|
var LISTENER_THROUGHPUT_LAPSE_RATIO = 0.5;
|
|
39087
|
+
var LISTENER_MODE_CHANGE_SKIP_MAX = 1;
|
|
38414
39088
|
var FAILURE_CODES = /* @__PURE__ */ new Set([
|
|
38415
39089
|
"http_status",
|
|
38416
39090
|
"no_response",
|
|
@@ -38507,12 +39181,52 @@ function recordListenerReadRecovery(health, input) {
|
|
|
38507
39181
|
retryHours: trimNewest(retryHours, LISTENER_READ_RETRY_HOUR_CAP)
|
|
38508
39182
|
};
|
|
38509
39183
|
}
|
|
38510
|
-
function
|
|
38511
|
-
return
|
|
39184
|
+
function freezeClosedHourExpectedClaims(rows3, currentHourStart) {
|
|
39185
|
+
return rows3.map((row) => {
|
|
39186
|
+
if (row.hourStart === currentHourStart) return row;
|
|
39187
|
+
if (row.expectedClaims !== void 0) return row;
|
|
39188
|
+
if (row.cadenceMs === void 0 || row.cadenceMs < 1) return row;
|
|
39189
|
+
return { ...row, expectedClaims: HOUR_MS / row.cadenceMs };
|
|
39190
|
+
});
|
|
39191
|
+
}
|
|
39192
|
+
function recordListenerClaimCadence(health, cadenceMs, ts) {
|
|
39193
|
+
const hourStart = bucketStart(ts, HOUR_MS);
|
|
39194
|
+
const claimHours = freezeClosedHourExpectedClaims(
|
|
39195
|
+
health.claimHours.map((row) => ({ ...row })),
|
|
39196
|
+
hourStart
|
|
39197
|
+
);
|
|
39198
|
+
const hour = claimHours.find((row) => row.hourStart === hourStart);
|
|
39199
|
+
if (hour) {
|
|
39200
|
+
hour.cadenceMs = hour.cadenceMs === void 0 ? cadenceMs : Math.max(hour.cadenceMs, cadenceMs);
|
|
39201
|
+
} else {
|
|
39202
|
+
claimHours.push({ hourStart, claims: 0, cadenceMs });
|
|
39203
|
+
}
|
|
39204
|
+
return {
|
|
39205
|
+
...health,
|
|
39206
|
+
claimCadenceMs: cadenceMs,
|
|
39207
|
+
claimHours: trimNewest(claimHours, LISTENER_CLAIM_HOUR_CAP)
|
|
39208
|
+
};
|
|
39209
|
+
}
|
|
39210
|
+
function recordListenerWakeModeChange(health, ts) {
|
|
39211
|
+
const hourStart = bucketStart(ts, HOUR_MS);
|
|
39212
|
+
const claimHours = freezeClosedHourExpectedClaims(
|
|
39213
|
+
health.claimHours.map((row) => ({ ...row })),
|
|
39214
|
+
hourStart
|
|
39215
|
+
);
|
|
39216
|
+
const hour = claimHours.find((row) => row.hourStart === hourStart);
|
|
39217
|
+
if (hour) hour.modeChanged = true;
|
|
39218
|
+
else claimHours.push({ hourStart, claims: 0, modeChanged: true });
|
|
39219
|
+
return {
|
|
39220
|
+
...health,
|
|
39221
|
+
claimHours: trimNewest(claimHours, LISTENER_CLAIM_HOUR_CAP)
|
|
39222
|
+
};
|
|
38512
39223
|
}
|
|
38513
39224
|
function recordListenerClaim(health, ts) {
|
|
38514
39225
|
const hourStart = bucketStart(ts, HOUR_MS);
|
|
38515
|
-
const claimHours =
|
|
39226
|
+
const claimHours = freezeClosedHourExpectedClaims(
|
|
39227
|
+
health.claimHours.map((row) => ({ ...row })),
|
|
39228
|
+
hourStart
|
|
39229
|
+
);
|
|
38516
39230
|
const hour = claimHours.find((row) => row.hourStart === hourStart);
|
|
38517
39231
|
if (hour) hour.claims += 1;
|
|
38518
39232
|
else claimHours.push({ hourStart, claims: 1 });
|
|
@@ -38567,7 +39281,15 @@ function parseListenerReadHealth(value, rejectUnknownKeys = false) {
|
|
|
38567
39281
|
for (const value2 of row.claimHours) {
|
|
38568
39282
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
|
|
38569
39283
|
const hour = value2;
|
|
38570
|
-
if (!hasExpectedKeys(hour, ["hourStart", "claims"],
|
|
39284
|
+
if (!hasExpectedKeys(hour, ["hourStart", "claims"], false) || !validTimestamp(hour.hourStart) || !validCount(hour.claims)) return null;
|
|
39285
|
+
if (rejectUnknownKeys && Object.keys(hour).some(
|
|
39286
|
+
(key2) => key2 !== "hourStart" && key2 !== "claims" && key2 !== "cadenceMs" && key2 !== "expectedClaims" && key2 !== "modeChanged"
|
|
39287
|
+
)) return null;
|
|
39288
|
+
if (hour.cadenceMs !== void 0 && !(typeof hour.cadenceMs === "number" && Number.isSafeInteger(hour.cadenceMs) && hour.cadenceMs >= 1)) return null;
|
|
39289
|
+
if (hour.expectedClaims !== void 0 && !(typeof hour.expectedClaims === "number" && Number.isFinite(hour.expectedClaims) && hour.expectedClaims > 0)) return null;
|
|
39290
|
+
if (hour.modeChanged !== void 0 && typeof hour.modeChanged !== "boolean") {
|
|
39291
|
+
return null;
|
|
39292
|
+
}
|
|
38571
39293
|
}
|
|
38572
39294
|
return {
|
|
38573
39295
|
currentEpisodeStartedAt: row.currentEpisodeStartedAt,
|
|
@@ -38587,10 +39309,18 @@ function parseListenerReadHealth(value, rejectUnknownKeys = false) {
|
|
|
38587
39309
|
retries: minute.retries
|
|
38588
39310
|
})),
|
|
38589
39311
|
claimCadenceMs: row.claimCadenceMs,
|
|
38590
|
-
claimHours: row.claimHours.map((hour) =>
|
|
38591
|
-
|
|
38592
|
-
|
|
38593
|
-
|
|
39312
|
+
claimHours: row.claimHours.map((hour) => {
|
|
39313
|
+
const cadenceMs = hour.cadenceMs;
|
|
39314
|
+
const expectedClaims = hour.expectedClaims;
|
|
39315
|
+
const modeChanged = hour.modeChanged;
|
|
39316
|
+
return {
|
|
39317
|
+
hourStart: hour.hourStart,
|
|
39318
|
+
claims: hour.claims,
|
|
39319
|
+
...typeof cadenceMs === "number" ? { cadenceMs } : {},
|
|
39320
|
+
...typeof expectedClaims === "number" ? { expectedClaims } : {},
|
|
39321
|
+
...modeChanged === true ? { modeChanged: true } : {}
|
|
39322
|
+
};
|
|
39323
|
+
})
|
|
38594
39324
|
};
|
|
38595
39325
|
}
|
|
38596
39326
|
function summarizeListenerReadHealth(health, readyAt, nowMs) {
|
|
@@ -38624,10 +39354,19 @@ function summarizeListenerReadHealth(health, readyAt, nowMs) {
|
|
|
38624
39354
|
const claimsByHour = new Map(
|
|
38625
39355
|
health.claimHours.map((row) => [row.hourStart, row.claims])
|
|
38626
39356
|
);
|
|
38627
|
-
const
|
|
39357
|
+
const cadenceByHour = /* @__PURE__ */ new Map();
|
|
39358
|
+
const expectedByHour = /* @__PURE__ */ new Map();
|
|
39359
|
+
for (const row of health.claimHours) {
|
|
39360
|
+
if (row.cadenceMs !== void 0) cadenceByHour.set(row.hourStart, row.cadenceMs);
|
|
39361
|
+
if (row.expectedClaims !== void 0) {
|
|
39362
|
+
expectedByHour.set(row.hourStart, row.expectedClaims);
|
|
39363
|
+
}
|
|
39364
|
+
}
|
|
38628
39365
|
for (let hour = first; hour < currentHour; hour += HOUR_MS) {
|
|
38629
39366
|
const hourStart = new Date(hour).toISOString();
|
|
38630
39367
|
const claims = claimsByHour.get(hourStart) ?? 0;
|
|
39368
|
+
const cadenceMs = cadenceByHour.get(hourStart) ?? health.claimCadenceMs;
|
|
39369
|
+
const expectedClaims = expectedByHour.get(hourStart) ?? HOUR_MS / cadenceMs;
|
|
38631
39370
|
claimThroughputHours.push({
|
|
38632
39371
|
hourStart,
|
|
38633
39372
|
claims,
|
|
@@ -38636,9 +39375,25 @@ function summarizeListenerReadHealth(health, readyAt, nowMs) {
|
|
|
38636
39375
|
});
|
|
38637
39376
|
}
|
|
38638
39377
|
}
|
|
38639
|
-
const
|
|
38640
|
-
|
|
38641
|
-
|
|
39378
|
+
const consecutiveModeChangedHours = (hourStart) => {
|
|
39379
|
+
let count2 = 0;
|
|
39380
|
+
let t = Date.parse(hourStart);
|
|
39381
|
+
if (!Number.isFinite(t)) return 0;
|
|
39382
|
+
while (true) {
|
|
39383
|
+
const start = new Date(t).toISOString();
|
|
39384
|
+
const row = health.claimHours.find((hour) => hour.hourStart === start);
|
|
39385
|
+
if (row?.modeChanged !== true) break;
|
|
39386
|
+
count2 += 1;
|
|
39387
|
+
t -= HOUR_MS;
|
|
39388
|
+
}
|
|
39389
|
+
return count2;
|
|
39390
|
+
};
|
|
39391
|
+
const throughputLapseHours = claimThroughputHours.filter((hour) => {
|
|
39392
|
+
if (hour.ratio >= LISTENER_THROUGHPUT_LAPSE_RATIO) return false;
|
|
39393
|
+
const consecutive = consecutiveModeChangedHours(hour.hourStart);
|
|
39394
|
+
if (consecutive === 0) return true;
|
|
39395
|
+
return consecutive > LISTENER_MODE_CHANGE_SKIP_MAX;
|
|
39396
|
+
});
|
|
38642
39397
|
return {
|
|
38643
39398
|
currentEpisodeDurationMs,
|
|
38644
39399
|
episodesLast24h,
|
|
@@ -38654,7 +39409,7 @@ function summarizeListenerReadHealth(health, readyAt, nowMs) {
|
|
|
38654
39409
|
// src/listener/control.ts
|
|
38655
39410
|
var import_node_crypto19 = require("node:crypto");
|
|
38656
39411
|
var import_node_net = require("node:net");
|
|
38657
|
-
var
|
|
39412
|
+
var import_promises10 = require("node:fs/promises");
|
|
38658
39413
|
var import_node_path16 = require("node:path");
|
|
38659
39414
|
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;
|
|
38660
39415
|
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-]+)*)?$/;
|
|
@@ -38741,7 +39496,9 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
|
38741
39496
|
"connectionsOpened",
|
|
38742
39497
|
"connectionReuseRatio",
|
|
38743
39498
|
"activityPublishFailures",
|
|
38744
|
-
"activityLastErrorCode"
|
|
39499
|
+
"activityLastErrorCode",
|
|
39500
|
+
"idlePollMs",
|
|
39501
|
+
"wake"
|
|
38745
39502
|
]);
|
|
38746
39503
|
var STATUS_ACTIVITY_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
38747
39504
|
"activity_credential_failed",
|
|
@@ -38764,7 +39521,10 @@ var STATUS_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
|
38764
39521
|
"reply",
|
|
38765
39522
|
"owner",
|
|
38766
39523
|
"ownerId",
|
|
38767
|
-
"owner_id"
|
|
39524
|
+
"owner_id",
|
|
39525
|
+
"topic",
|
|
39526
|
+
"wakeTopic",
|
|
39527
|
+
"wake_topic"
|
|
38768
39528
|
]);
|
|
38769
39529
|
var STATUS_DELIVERY_KEYS = [
|
|
38770
39530
|
"deliveryMode",
|
|
@@ -38801,6 +39561,37 @@ function parseHeldBackDeliveries(value) {
|
|
|
38801
39561
|
}
|
|
38802
39562
|
return parsed;
|
|
38803
39563
|
}
|
|
39564
|
+
var WAKE_STATUS_KEY_SET = new Set(LISTENER_WAKE_STATUS_KEYS);
|
|
39565
|
+
var WAKE_SENSITIVE_KEY_SET = new Set(LISTENER_WAKE_SENSITIVE_KEYS);
|
|
39566
|
+
function nullableIso(value) {
|
|
39567
|
+
return value === null || typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
39568
|
+
}
|
|
39569
|
+
function parseListenerWake(value, rejectUnknownKeys = false) {
|
|
39570
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
39571
|
+
const row = value;
|
|
39572
|
+
for (const key2 of Object.keys(row)) {
|
|
39573
|
+
if (WAKE_SENSITIVE_KEY_SET.has(key2)) return null;
|
|
39574
|
+
if (rejectUnknownKeys && !WAKE_STATUS_KEY_SET.has(key2)) return null;
|
|
39575
|
+
}
|
|
39576
|
+
for (const key2 of LISTENER_WAKE_STATUS_KEYS) {
|
|
39577
|
+
if (!(key2 in row)) return null;
|
|
39578
|
+
}
|
|
39579
|
+
const mode3 = LISTENER_WAKE_MODES.find((item) => item === row.mode);
|
|
39580
|
+
if (mode3 === void 0 || !nullableIso(row.subscribedAt) || !(typeof row.reconnects === "number" && Number.isSafeInteger(row.reconnects) && row.reconnects >= 0) || !nullableIso(row.lastWakeAt) || !nullableIso(row.lastReconcileAt) || !(row.errorCode === null || typeof row.errorCode === "string" && WAKE_ERROR_CODE_SET.has(row.errorCode)) || !nullableIso(row.topicRotatedAt) || typeof row.rateLimited !== "boolean") {
|
|
39581
|
+
return null;
|
|
39582
|
+
}
|
|
39583
|
+
if (mode3 === LISTENER_WAKE_MODE_PUSH && row.subscribedAt === null) return null;
|
|
39584
|
+
return {
|
|
39585
|
+
mode: mode3,
|
|
39586
|
+
subscribedAt: row.subscribedAt,
|
|
39587
|
+
reconnects: row.reconnects,
|
|
39588
|
+
lastWakeAt: row.lastWakeAt,
|
|
39589
|
+
lastReconcileAt: row.lastReconcileAt,
|
|
39590
|
+
errorCode: row.errorCode,
|
|
39591
|
+
topicRotatedAt: row.topicRotatedAt,
|
|
39592
|
+
rateLimited: row.rateLimited
|
|
39593
|
+
};
|
|
39594
|
+
}
|
|
38804
39595
|
function parseStatus(raw, rejectUnknownKeys = false) {
|
|
38805
39596
|
let value;
|
|
38806
39597
|
try {
|
|
@@ -38825,9 +39616,10 @@ function parseStatus(raw, rejectUnknownKeys = false) {
|
|
|
38825
39616
|
const nullableTimestamp3 = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
|
|
38826
39617
|
const readHealth = row.readHealth === void 0 ? void 0 : parseListenerReadHealth(row.readHealth, rejectUnknownKeys);
|
|
38827
39618
|
const heldBackDeliveries = row.heldBackDeliveries === void 0 ? void 0 : parseHeldBackDeliveries(row.heldBackDeliveries);
|
|
38828
|
-
|
|
39619
|
+
const wake = row.wake === void 0 ? void 0 : parseListenerWake(row.wake, rejectUnknownKeys);
|
|
39620
|
+
if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE18.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE18.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE18.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))) || !nullableUuid3(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 && !SECRET_SHAPE_RE.test(row.lastErrorDetail)) || !(row.lastErrorReasonCode === void 0 || row.lastErrorReasonCode === null || typeof row.lastErrorReasonCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorReasonCode)) || !(row.providerExecutable === void 0 || row.providerExecutable === null || typeof row.providerExecutable === "string" && (0, import_node_path16.isAbsolute)(row.providerExecutable)) || !(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.providerBundledAgentSdkVersion === void 0 || row.providerBundledAgentSdkVersion === null || typeof row.providerBundledAgentSdkVersion === "string" && SEMVER_RE2.test(row.providerBundledAgentSdkVersion)) || !(row.providerBundledClaudeCodeVersion === void 0 || row.providerBundledClaudeCodeVersion === null || typeof row.providerBundledClaudeCodeVersion === "string" && SEMVER_RE2.test(row.providerBundledClaudeCodeVersion)) || !(row.providerMinimumRequiredVersion === void 0 || row.providerMinimumRequiredVersion === null || typeof row.providerMinimumRequiredVersion === "string" && SEMVER_RE2.test(row.providerMinimumRequiredVersion)) || !(row.cswarmVersion === void 0 || row.cswarmVersion === null || typeof row.cswarmVersion === "string" && SEMVER_RE2.test(row.cswarmVersion)) || (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 && !SECRET_SHAPE_RE.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 || nullableTimestamp3(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp3(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp3(row.lastAckAt)) || !(row.lastAckOutcome === void 0 || row.lastAckOutcome === null || typeof row.lastAckOutcome === "string" && deliveryOutcomes.has(row.lastAckOutcome)) || !(row.consecutiveAckFailureCount === void 0 || nullableCount(row.consecutiveAckFailureCount)) || !(row.lastAckSignalId === void 0 || row.lastAckSignalId === null || typeof row.lastAckSignalId === "string" && UUID_RE18.test(row.lastAckSignalId)) || !(row.currentDeliverySignalId === void 0 || row.currentDeliverySignalId === null || typeof row.currentDeliverySignalId === "string" && UUID_RE18.test(row.currentDeliverySignalId)) || !(row.currentDeliverySince === void 0 || nullableTimestamp3(row.currentDeliverySince)) || heldBackDeliveries === null || !(row.pendingDeliveryCountAt === void 0 || nullableTimestamp3(row.pendingDeliveryCountAt)) || !(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) || readHealth === null || wake === null || !(row.connectionsOpened === void 0 || typeof row.connectionsOpened === "number" && Number.isSafeInteger(row.connectionsOpened) && row.connectionsOpened >= 0) || !(row.connectionReuseRatio === void 0 || typeof row.connectionReuseRatio === "number" && Number.isFinite(row.connectionReuseRatio) && row.connectionReuseRatio >= 0) || !(row.activityPublishFailures === void 0 || typeof row.activityPublishFailures === "number" && Number.isSafeInteger(row.activityPublishFailures) && row.activityPublishFailures >= 0) || !(row.activityLastErrorCode === void 0 || row.activityLastErrorCode === null || typeof row.activityLastErrorCode === "string" && STATUS_ACTIVITY_ERROR_CODES.has(
|
|
38829
39621
|
row.activityLastErrorCode
|
|
38830
|
-
))) {
|
|
39622
|
+
)) || !(row.idlePollMs === void 0 || row.idlePollMs === null || typeof row.idlePollMs === "number" && Number.isSafeInteger(row.idlePollMs) && row.idlePollMs >= 0)) {
|
|
38831
39623
|
throw new Error("stored listener status is malformed");
|
|
38832
39624
|
}
|
|
38833
39625
|
const routeMode = row.routeMode ?? "worker";
|
|
@@ -38870,7 +39662,9 @@ function parseStatus(raw, rejectUnknownKeys = false) {
|
|
|
38870
39662
|
deferOverChars,
|
|
38871
39663
|
pendingForMainCount: row.pendingForMainCount ?? 0,
|
|
38872
39664
|
droppedForMainCount: row.droppedForMainCount ?? 0,
|
|
38873
|
-
...readHealth === void 0 ? {} : { readHealth }
|
|
39665
|
+
...readHealth === void 0 ? {} : { readHealth },
|
|
39666
|
+
...row.idlePollMs === void 0 ? {} : { idlePollMs: row.idlePollMs ?? null },
|
|
39667
|
+
...wake === void 0 ? {} : { wake }
|
|
38874
39668
|
};
|
|
38875
39669
|
}
|
|
38876
39670
|
async function writeListenerStatus(paths, status) {
|
|
@@ -38940,7 +39734,12 @@ async function appendListenerEvent(paths, event) {
|
|
|
38940
39734
|
"dropped_count",
|
|
38941
39735
|
// How long one delivery held the worker seat, and why it gave it back.
|
|
38942
39736
|
"held_ms",
|
|
38943
|
-
"release_reason"
|
|
39737
|
+
"release_reason",
|
|
39738
|
+
"idle_poll_ms",
|
|
39739
|
+
"wake_mode",
|
|
39740
|
+
"wake_error_code",
|
|
39741
|
+
"wake_reconnects",
|
|
39742
|
+
"rate_limited"
|
|
38944
39743
|
]);
|
|
38945
39744
|
const deliveryModes = /* @__PURE__ */ new Set(["durable_claim", "cursor_fallback"]);
|
|
38946
39745
|
const routeModes = /* @__PURE__ */ new Set(["worker", "main", "split"]);
|
|
@@ -39005,6 +39804,21 @@ async function appendListenerEvent(paths, event) {
|
|
|
39005
39804
|
if (key2 === "held_ms" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
|
|
39006
39805
|
throw new Error("listener event hold duration is not allowed");
|
|
39007
39806
|
}
|
|
39807
|
+
if (key2 === "idle_poll_ms" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
|
|
39808
|
+
throw new Error("listener event idle poll interval is not allowed");
|
|
39809
|
+
}
|
|
39810
|
+
if (key2 === "wake_mode" && !(typeof value === "string" && LISTENER_WAKE_MODE_SET.has(value))) {
|
|
39811
|
+
throw new Error("listener event wake mode is not allowed");
|
|
39812
|
+
}
|
|
39813
|
+
if (key2 === "wake_error_code" && !(value === null || typeof value === "string" && WAKE_ERROR_CODE_SET.has(value))) {
|
|
39814
|
+
throw new Error("listener event wake error code is not allowed");
|
|
39815
|
+
}
|
|
39816
|
+
if (key2 === "wake_reconnects" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
|
|
39817
|
+
throw new Error("listener event wake reconnect count is not allowed");
|
|
39818
|
+
}
|
|
39819
|
+
if (key2 === "rate_limited" && typeof value !== "boolean") {
|
|
39820
|
+
throw new Error("listener event rate-limited flag is not allowed");
|
|
39821
|
+
}
|
|
39008
39822
|
if (key2 === "release_reason" && !(typeof value === "string" && LISTENER_DELIVERY_HOLD_RELEASE_REASONS.includes(
|
|
39009
39823
|
value
|
|
39010
39824
|
))) {
|
|
@@ -39019,7 +39833,7 @@ async function appendListenerEvent(paths, event) {
|
|
|
39019
39833
|
if (typeof value === "string" && // worker_stderr_tail is deliberately exempt from the generic 128-char
|
|
39020
39834
|
// cap (its own bound is 2048, above); the secret scan still applies to
|
|
39021
39835
|
// every string, the tail included.
|
|
39022
|
-
(key2 !== "worker_stderr_tail" && value.length > 128 ||
|
|
39836
|
+
(key2 !== "worker_stderr_tail" && value.length > 128 || SECRET_SHAPE_RE.test(value))) {
|
|
39023
39837
|
throw new Error("listener event contains unsafe text");
|
|
39024
39838
|
}
|
|
39025
39839
|
}
|
|
@@ -39039,7 +39853,7 @@ async function appendListenerEvent(paths, event) {
|
|
|
39039
39853
|
throw new Error("listener event is too large");
|
|
39040
39854
|
}
|
|
39041
39855
|
try {
|
|
39042
|
-
const info = await (0,
|
|
39856
|
+
const info = await (0, import_promises10.lstat)(paths.logPath);
|
|
39043
39857
|
if (!info.isFile() || info.isSymbolicLink() || (info.mode & 511) !== 384) {
|
|
39044
39858
|
throw new Error("listener event log is not a secure regular file");
|
|
39045
39859
|
}
|
|
@@ -39049,14 +39863,14 @@ async function appendListenerEvent(paths, event) {
|
|
|
39049
39863
|
} catch (error) {
|
|
39050
39864
|
if (error.code !== "ENOENT") throw error;
|
|
39051
39865
|
}
|
|
39052
|
-
const handle = await (0,
|
|
39866
|
+
const handle = await (0, import_promises10.open)(paths.logPath, "a", 384);
|
|
39053
39867
|
try {
|
|
39054
39868
|
await handle.writeFile(serialized, "utf8");
|
|
39055
39869
|
await handle.sync();
|
|
39056
39870
|
} finally {
|
|
39057
39871
|
await handle.close();
|
|
39058
39872
|
}
|
|
39059
|
-
await (0,
|
|
39873
|
+
await (0, import_promises10.chmod)(paths.logPath, 384);
|
|
39060
39874
|
}
|
|
39061
39875
|
function parseControlRequest(raw) {
|
|
39062
39876
|
let value;
|
|
@@ -39091,7 +39905,7 @@ async function startupLock(paths) {
|
|
|
39091
39905
|
while (Date.now() < deadline) {
|
|
39092
39906
|
let handle;
|
|
39093
39907
|
try {
|
|
39094
|
-
handle = await (0,
|
|
39908
|
+
handle = await (0, import_promises10.open)(lockPath, "wx", 384);
|
|
39095
39909
|
} catch (error) {
|
|
39096
39910
|
if (error.code !== "EEXIST") throw error;
|
|
39097
39911
|
try {
|
|
@@ -39100,9 +39914,9 @@ async function startupLock(paths) {
|
|
|
39100
39914
|
} catch (queryError) {
|
|
39101
39915
|
if (queryError instanceof ListenerAlreadyRunningError) throw queryError;
|
|
39102
39916
|
}
|
|
39103
|
-
const info = await (0,
|
|
39917
|
+
const info = await (0, import_promises10.lstat)(lockPath).catch(() => null);
|
|
39104
39918
|
if (info && Date.now() - info.mtimeMs >= START_LOCK_STALE_MS) {
|
|
39105
|
-
await (0,
|
|
39919
|
+
await (0, import_promises10.unlink)(lockPath).catch(() => void 0);
|
|
39106
39920
|
continue;
|
|
39107
39921
|
}
|
|
39108
39922
|
await new Promise((resolve3) => setTimeout(resolve3, 25));
|
|
@@ -39114,12 +39928,12 @@ async function startupLock(paths) {
|
|
|
39114
39928
|
await handle.sync();
|
|
39115
39929
|
} catch (error) {
|
|
39116
39930
|
await handle.close().catch(() => void 0);
|
|
39117
|
-
await (0,
|
|
39931
|
+
await (0, import_promises10.unlink)(lockPath).catch(() => void 0);
|
|
39118
39932
|
throw error;
|
|
39119
39933
|
}
|
|
39120
39934
|
return async () => {
|
|
39121
39935
|
await handle.close().catch(() => void 0);
|
|
39122
|
-
await (0,
|
|
39936
|
+
await (0, import_promises10.unlink)(lockPath).catch(() => void 0);
|
|
39123
39937
|
};
|
|
39124
39938
|
}
|
|
39125
39939
|
throw new ListenerAlreadyRunningError();
|
|
@@ -39136,7 +39950,7 @@ async function prepareSocket(paths) {
|
|
|
39136
39950
|
} catch (error) {
|
|
39137
39951
|
if (error instanceof ListenerAlreadyRunningError) throw error;
|
|
39138
39952
|
if (process.platform !== "win32") {
|
|
39139
|
-
await (0,
|
|
39953
|
+
await (0, import_promises10.unlink)(paths.socketPath).catch((unlinkError) => {
|
|
39140
39954
|
if (unlinkError.code !== "ENOENT") {
|
|
39141
39955
|
throw unlinkError;
|
|
39142
39956
|
}
|
|
@@ -39193,13 +40007,13 @@ async function startListenerControlServer(options) {
|
|
|
39193
40007
|
server.listen(options.paths.socketPath);
|
|
39194
40008
|
});
|
|
39195
40009
|
if (process.platform !== "win32") {
|
|
39196
|
-
await (0,
|
|
40010
|
+
await (0, import_promises10.chmod)(options.paths.socketPath, 384);
|
|
39197
40011
|
}
|
|
39198
40012
|
} catch (error) {
|
|
39199
40013
|
if (server.listening) {
|
|
39200
40014
|
await new Promise((resolve3) => server.close(() => resolve3()));
|
|
39201
40015
|
if (process.platform !== "win32") {
|
|
39202
|
-
await (0,
|
|
40016
|
+
await (0, import_promises10.unlink)(options.paths.socketPath).catch(() => void 0);
|
|
39203
40017
|
}
|
|
39204
40018
|
}
|
|
39205
40019
|
throw error;
|
|
@@ -39210,7 +40024,7 @@ async function startListenerControlServer(options) {
|
|
|
39210
40024
|
close: async () => {
|
|
39211
40025
|
await new Promise((resolve3) => server.close(() => resolve3()));
|
|
39212
40026
|
if (process.platform !== "win32") {
|
|
39213
|
-
await (0,
|
|
40027
|
+
await (0, import_promises10.unlink)(options.paths.socketPath).catch(() => void 0);
|
|
39214
40028
|
}
|
|
39215
40029
|
}
|
|
39216
40030
|
};
|
|
@@ -39307,7 +40121,7 @@ function safeErrorCode(error) {
|
|
|
39307
40121
|
}
|
|
39308
40122
|
function localDiagnostic(message, maxChars) {
|
|
39309
40123
|
const redacted = message.replace(
|
|
39310
|
-
|
|
40124
|
+
new RegExp(SECRET_SHAPE_RE.source, "gi"),
|
|
39311
40125
|
"[redacted]"
|
|
39312
40126
|
).trim();
|
|
39313
40127
|
if (redacted.length === 0) return null;
|
|
@@ -39415,6 +40229,8 @@ async function runListenerSupervisor(options) {
|
|
|
39415
40229
|
connectionReuseRatio: 0,
|
|
39416
40230
|
activityPublishFailures: 0,
|
|
39417
40231
|
activityLastErrorCode: null,
|
|
40232
|
+
idlePollMs: null,
|
|
40233
|
+
wake: emptyListenerWakeStatus(),
|
|
39418
40234
|
logPath: options.paths.logPath
|
|
39419
40235
|
};
|
|
39420
40236
|
let writes = Promise.resolve();
|
|
@@ -39487,7 +40303,69 @@ async function runListenerSupervisor(options) {
|
|
|
39487
40303
|
const fitted = fitWorkerStderrTailForLog(tail);
|
|
39488
40304
|
return fitted.length > 0 ? fitted : null;
|
|
39489
40305
|
};
|
|
40306
|
+
let lastWakePersistMs = 0;
|
|
39490
40307
|
const onEvent = (event) => {
|
|
40308
|
+
if (event.type === "wake") {
|
|
40309
|
+
const previousWake = status.wake;
|
|
40310
|
+
const previous = previousWake?.mode;
|
|
40311
|
+
let readHealth = status.readHealth ?? emptyListenerReadHealth();
|
|
40312
|
+
if (previous !== void 0 && previous !== event.wake.mode) {
|
|
40313
|
+
readHealth = recordListenerWakeModeChange(readHealth, event.ts);
|
|
40314
|
+
}
|
|
40315
|
+
const cadenceMs = event.wake.mode === LISTENER_WAKE_MODE_PUSH ? LISTENER_RECONCILE_POLL_MS : status.idlePollMs && status.idlePollMs > 0 ? status.idlePollMs : null;
|
|
40316
|
+
if (cadenceMs !== null) {
|
|
40317
|
+
readHealth = recordListenerClaimCadence(
|
|
40318
|
+
readHealth,
|
|
40319
|
+
cadenceMs,
|
|
40320
|
+
event.ts
|
|
40321
|
+
);
|
|
40322
|
+
}
|
|
40323
|
+
status = {
|
|
40324
|
+
...status,
|
|
40325
|
+
wake: event.wake,
|
|
40326
|
+
readHealth,
|
|
40327
|
+
updatedAt: event.ts
|
|
40328
|
+
};
|
|
40329
|
+
const eventMs = Date.parse(event.ts);
|
|
40330
|
+
const nowMs = Number.isFinite(eventMs) ? eventMs : Date.now();
|
|
40331
|
+
if (listenerWakePersistWorthy(
|
|
40332
|
+
previousWake,
|
|
40333
|
+
event.wake,
|
|
40334
|
+
lastWakePersistMs,
|
|
40335
|
+
nowMs
|
|
40336
|
+
)) {
|
|
40337
|
+
lastWakePersistMs = nowMs;
|
|
40338
|
+
persist();
|
|
40339
|
+
log({
|
|
40340
|
+
ts: event.ts,
|
|
40341
|
+
event: "listener_wake",
|
|
40342
|
+
wake_mode: event.wake.mode,
|
|
40343
|
+
wake_error_code: event.wake.errorCode,
|
|
40344
|
+
wake_reconnects: event.wake.reconnects,
|
|
40345
|
+
rate_limited: event.wake.rateLimited
|
|
40346
|
+
});
|
|
40347
|
+
}
|
|
40348
|
+
return;
|
|
40349
|
+
}
|
|
40350
|
+
if (event.type === "idle_poll") {
|
|
40351
|
+
status = {
|
|
40352
|
+
...status,
|
|
40353
|
+
idlePollMs: event.intervalMs,
|
|
40354
|
+
readHealth: recordListenerClaimCadence(
|
|
40355
|
+
status.readHealth ?? emptyListenerReadHealth(),
|
|
40356
|
+
event.intervalMs > 0 ? event.intervalMs : 1,
|
|
40357
|
+
event.ts
|
|
40358
|
+
),
|
|
40359
|
+
updatedAt: event.ts
|
|
40360
|
+
};
|
|
40361
|
+
persist();
|
|
40362
|
+
log({
|
|
40363
|
+
ts: event.ts,
|
|
40364
|
+
event: "listener_idle_poll",
|
|
40365
|
+
idle_poll_ms: event.intervalMs
|
|
40366
|
+
});
|
|
40367
|
+
return;
|
|
40368
|
+
}
|
|
39491
40369
|
if (event.type === "ready") {
|
|
39492
40370
|
const versionNotice = options.getProviderVersionNotice?.() ?? null;
|
|
39493
40371
|
transition("ready", {
|
|
@@ -39512,7 +40390,8 @@ async function runListenerSupervisor(options) {
|
|
|
39512
40390
|
...event.cadenceMs === void 0 ? {} : {
|
|
39513
40391
|
readHealth: recordListenerClaimCadence(
|
|
39514
40392
|
status.readHealth ?? emptyListenerReadHealth(),
|
|
39515
|
-
event.cadenceMs
|
|
40393
|
+
event.cadenceMs,
|
|
40394
|
+
event.ts
|
|
39516
40395
|
)
|
|
39517
40396
|
}
|
|
39518
40397
|
});
|
|
@@ -40785,6 +41664,7 @@ function buildListenerChildArgs(spec) {
|
|
|
40785
41664
|
...spec.model ? ["--model", spec.model] : [],
|
|
40786
41665
|
...provider === "grok" && spec.effort ? ["--effort", spec.effort] : [],
|
|
40787
41666
|
...spec.turnBudget ? ["--turn-budget", spec.turnBudget] : [],
|
|
41667
|
+
...spec.pollInterval ? ["--poll-interval", spec.pollInterval] : [],
|
|
40788
41668
|
...spec.route && spec.route !== "worker" ? ["--route", spec.route] : [],
|
|
40789
41669
|
...spec.deferOver !== void 0 ? ["--defer-over", String(spec.deferOver)] : []
|
|
40790
41670
|
];
|
|
@@ -40820,7 +41700,7 @@ async function spawnDetachedListener(options) {
|
|
|
40820
41700
|
}
|
|
40821
41701
|
|
|
40822
41702
|
// src/listener/hook.ts
|
|
40823
|
-
var
|
|
41703
|
+
var import_promises11 = require("node:fs/promises");
|
|
40824
41704
|
var import_node_path20 = require("node:path");
|
|
40825
41705
|
|
|
40826
41706
|
// src/listener/brain-digest.ts
|
|
@@ -41218,7 +42098,7 @@ async function listenerIsLive(context) {
|
|
|
41218
42098
|
async function discoverStoredStatusContexts(stateDirectory2) {
|
|
41219
42099
|
let entries;
|
|
41220
42100
|
try {
|
|
41221
|
-
entries = await (0,
|
|
42101
|
+
entries = await (0, import_promises11.readdir)(stateDirectory2, { withFileTypes: true });
|
|
41222
42102
|
} catch (error) {
|
|
41223
42103
|
if (error.code === "ENOENT") return [];
|
|
41224
42104
|
throw error;
|
|
@@ -41618,7 +42498,7 @@ async function runListenerHookCheck(options = {}) {
|
|
|
41618
42498
|
}
|
|
41619
42499
|
|
|
41620
42500
|
// src/listener/attendance-canary.ts
|
|
41621
|
-
var
|
|
42501
|
+
var import_promises12 = require("node:fs/promises");
|
|
41622
42502
|
var LOG_TAIL_BYTES = 256 * 1024;
|
|
41623
42503
|
function agentReceipt(receipts, principalId) {
|
|
41624
42504
|
for (const receipt of receipts) {
|
|
@@ -41631,7 +42511,7 @@ function agentReceipt(receipts, principalId) {
|
|
|
41631
42511
|
async function readLogTail(path) {
|
|
41632
42512
|
let handle;
|
|
41633
42513
|
try {
|
|
41634
|
-
handle = await (0,
|
|
42514
|
+
handle = await (0, import_promises12.open)(path, "r");
|
|
41635
42515
|
} catch (error) {
|
|
41636
42516
|
if (error.code === "ENOENT") return "";
|
|
41637
42517
|
throw error;
|
|
@@ -42735,6 +43615,7 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
42735
43615
|
"slug",
|
|
42736
43616
|
"state-dir",
|
|
42737
43617
|
"thread",
|
|
43618
|
+
"poll-interval",
|
|
42738
43619
|
"renewal-horizon-days",
|
|
42739
43620
|
"standing",
|
|
42740
43621
|
"task-id",
|
|
@@ -42781,8 +43662,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
42781
43662
|
]);
|
|
42782
43663
|
var UUID_RE23 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
42783
43664
|
function packageVersion() {
|
|
42784
|
-
if ("0.1.
|
|
42785
|
-
return "0.1.
|
|
43665
|
+
if ("0.1.58".length > 0) {
|
|
43666
|
+
return "0.1.58";
|
|
42786
43667
|
}
|
|
42787
43668
|
try {
|
|
42788
43669
|
const value = JSON.parse(
|
|
@@ -42923,7 +43804,7 @@ Usage:
|
|
|
42923
43804
|
cswarm brain get <topic>[@<version>] [--version <n>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
42924
43805
|
cswarm brain put <topic> [<markdown-path>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--if-version <n>] [--json] # without a path, reads Markdown from stdin; --if-version refuses the write unless the live version is still <n>
|
|
42925
43806
|
cswarm feedback "<text>" --kind bug|idea|friction [--about <ref>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
42926
|
-
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>] [--allow-unattended] [--foreground] [--json]
|
|
43807
|
+
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>] [--poll-interval <duration>] [--route worker|main|split] [--defer-over <chars>] [--allow-unattended] [--foreground] [--json]
|
|
42927
43808
|
cswarm listen canary ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> [--state-dir <path>] [--wait <seconds>] [--json]
|
|
42928
43809
|
cswarm listen status ${agentCredential2} [--url <url> --anon-key <key>] --workspace-id <uuid> [--principal-id <uuid>] [--json]
|
|
42929
43810
|
cswarm listen stop ${agentCredential2} [--url <url> --anon-key <key>] --workspace-id <uuid> [--principal-id <uuid>] [--json]
|
|
@@ -43013,6 +43894,8 @@ Place -- before signal text that itself begins with -- to stop option parsing.
|
|
|
43013
43894
|
Signal text is at most 8000 characters and --about at most 500; a longer body is
|
|
43014
43895
|
refused locally before any network call, so compose within the limit.
|
|
43015
43896
|
|
|
43897
|
+
${idlePollHelpSentence()}
|
|
43898
|
+
|
|
43016
43899
|
listen start --turn-budget bounds ONE worker prompt turn (default 10m): how long
|
|
43017
43900
|
the worker may think and use tools on a single message before the turn times out
|
|
43018
43901
|
and durable delivery retries it. A whole number plus s, m, or h (for example
|
|
@@ -43251,7 +44134,7 @@ async function stdinInviteLink() {
|
|
|
43251
44134
|
return link;
|
|
43252
44135
|
}
|
|
43253
44136
|
async function confirmationLine(prompt) {
|
|
43254
|
-
const reader = (0,
|
|
44137
|
+
const reader = (0, import_promises14.createInterface)({
|
|
43255
44138
|
input: process.stdin,
|
|
43256
44139
|
output: process.stderr,
|
|
43257
44140
|
terminal: Boolean(process.stdin.isTTY)
|
|
@@ -44555,6 +45438,9 @@ function listenerTurnBudgetMs(value) {
|
|
|
44555
45438
|
}
|
|
44556
45439
|
return milliseconds;
|
|
44557
45440
|
}
|
|
45441
|
+
function listenerPollIntervalMs(value) {
|
|
45442
|
+
return parseIdlePollIntervalMs(value);
|
|
45443
|
+
}
|
|
44558
45444
|
function listenerRouteConfiguration(routeValue, deferOverValue) {
|
|
44559
45445
|
const routeMode = routeValue ?? "worker";
|
|
44560
45446
|
if (routeMode !== "worker" && routeMode !== "main" && routeMode !== "split") {
|
|
@@ -45535,6 +46421,12 @@ async function runInboxNotifyCommand(args) {
|
|
|
45535
46421
|
const stop = () => controller.abort();
|
|
45536
46422
|
process.on("SIGINT", stop);
|
|
45537
46423
|
process.on("SIGTERM", stop);
|
|
46424
|
+
const lockPath = arrivalWatchLockPath(
|
|
46425
|
+
cloud,
|
|
46426
|
+
selected.selectedWorkspace,
|
|
46427
|
+
principalId
|
|
46428
|
+
);
|
|
46429
|
+
await acquireArrivalWatchLock(lockPath);
|
|
45538
46430
|
try {
|
|
45539
46431
|
const retryNotices = createArrivalRetryNoticePolicy();
|
|
45540
46432
|
let renderedBearer = selected.bearer;
|
|
@@ -45608,6 +46500,7 @@ async function runInboxNotifyCommand(args) {
|
|
|
45608
46500
|
process.off("SIGINT", stop);
|
|
45609
46501
|
process.off("SIGTERM", stop);
|
|
45610
46502
|
httpClient.close();
|
|
46503
|
+
await releaseArrivalWatchLock(lockPath);
|
|
45611
46504
|
}
|
|
45612
46505
|
}
|
|
45613
46506
|
async function runReceipt(args) {
|
|
@@ -46107,6 +47000,10 @@ function listenerStatusJson(status, permissionMode, evidence = {
|
|
|
46107
47000
|
readRetriesLastHour: readSummary.retriesLastHour,
|
|
46108
47001
|
readRetryHours: readSummary.retryHours,
|
|
46109
47002
|
claimCadenceMs: readHealth.claimCadenceMs,
|
|
47003
|
+
idlePollMs: status.idlePollMs ?? null,
|
|
47004
|
+
idlePollSentence: status.idlePollMs === void 0 || status.idlePollMs === null ? null : idlePollStatusSentence(status.idlePollMs),
|
|
47005
|
+
wake: status.wake ?? emptyListenerWakeStatus(),
|
|
47006
|
+
mode: (status.wake ?? emptyListenerWakeStatus()).mode,
|
|
46110
47007
|
claimThroughputHours: readSummary.claimThroughputHours,
|
|
46111
47008
|
listenerLapse: lapseNotices.length > 0,
|
|
46112
47009
|
listenerLapseCodes: lapseNotices.map((notice) => notice.code),
|
|
@@ -46177,7 +47074,13 @@ function renderListenerStatus(status, evidence = {
|
|
|
46177
47074
|
`Read retry episodes in the last 24h: ${readSummary.episodesLast24h}; retries in the rolling hour: ${readSummary.retriesLastHour}.`,
|
|
46178
47075
|
readSummary.longestEpisodeAttemptsLast24h === 0 ? "Longest read retry episode in the last 24h: none recorded." : `Longest read retry episode in the last 24h: ${readSummary.longestEpisodeAttemptsLast24h} attempts over ${Math.floor(readSummary.longestEpisodeDurationMsLast24h / 1e3)}s.`,
|
|
46179
47076
|
readSummary.retryHours.length === 0 ? "Read retries by hour in the last 24h: none." : `Read retries by hour in the last 24h: ${readSummary.retryHours.map((hour) => `${hour.hourStart}=${hour.retries}`).join("; ")}.`,
|
|
46180
|
-
readSummary.claimThroughputHours.length === 0 ? "Claim throughput by full hour: no complete listener hour is available yet." : `Claim throughput by full hour: ${readSummary.claimThroughputHours.map((hour) => `${hour.hourStart} ${hour.claims}/${Math.round(hour.expectedClaims)} (${hour.ratio.toFixed(3)})`).join("; ")}
|
|
47077
|
+
readSummary.claimThroughputHours.length === 0 ? "Claim throughput by full hour: no complete listener hour is available yet." : `Claim throughput by full hour: ${readSummary.claimThroughputHours.map((hour) => `${hour.hourStart} ${hour.claims}/${Math.round(hour.expectedClaims)} (${hour.ratio.toFixed(3)})`).join("; ")}.`,
|
|
47078
|
+
status.idlePollMs === void 0 || status.idlePollMs === null ? "Current idle poll interval has not been reported yet." : idlePollStatusSentence(status.idlePollMs),
|
|
47079
|
+
listenerWakeStatusSentence(
|
|
47080
|
+
status.wake ?? emptyListenerWakeStatus(),
|
|
47081
|
+
status.idlePollMs && status.idlePollMs > 0 ? status.idlePollMs : IDLE_POLL_DEFAULT_MS,
|
|
47082
|
+
status.wake?.lastWakeAt ? relativeAge(status.wake.lastWakeAt, nowMs) : null
|
|
47083
|
+
)
|
|
46181
47084
|
];
|
|
46182
47085
|
for (const notice of lapseNotices) {
|
|
46183
47086
|
lines.push(`WARNING [${notice.code}]: ${notice.message}`);
|
|
@@ -46811,6 +47714,7 @@ async function runConfiguredListener(options) {
|
|
|
46811
47714
|
/* One delivery may hold the seat for one turn budget, not for the
|
|
46812
47715
|
whole 15-minute lease. Same lever, so the two cannot drift. */
|
|
46813
47716
|
deliveryHoldBudgetMs: turnBudgetMs,
|
|
47717
|
+
...options.pollMs === void 0 ? {} : { pollMs: options.pollMs },
|
|
46814
47718
|
pendingMainQueue,
|
|
46815
47719
|
fetcher: httpClient.fetch
|
|
46816
47720
|
});
|
|
@@ -46841,6 +47745,7 @@ async function runListenStart(args) {
|
|
|
46841
47745
|
"codex-executable",
|
|
46842
47746
|
"state-dir",
|
|
46843
47747
|
"turn-budget",
|
|
47748
|
+
"poll-interval",
|
|
46844
47749
|
"route",
|
|
46845
47750
|
"defer-over",
|
|
46846
47751
|
"allow-unattended",
|
|
@@ -46855,6 +47760,7 @@ async function runListenStart(args) {
|
|
|
46855
47760
|
const provider = listenerProvider(args);
|
|
46856
47761
|
validateListenerProviderFlags(args, provider);
|
|
46857
47762
|
const turnBudgetMs = listenerTurnBudgetMs(args.optional("turn-budget"));
|
|
47763
|
+
const pollMs = listenerPollIntervalMs(args.optional("poll-interval"));
|
|
46858
47764
|
const routing = listenerRouteConfiguration(
|
|
46859
47765
|
args.optional("route"),
|
|
46860
47766
|
args.optional("defer-over")
|
|
@@ -46897,6 +47803,7 @@ async function runListenStart(args) {
|
|
|
46897
47803
|
permissionMode,
|
|
46898
47804
|
provider,
|
|
46899
47805
|
turnBudgetMs,
|
|
47806
|
+
pollMs,
|
|
46900
47807
|
...routing,
|
|
46901
47808
|
...args.optional("model") ? { model: args.required("model") } : {},
|
|
46902
47809
|
...args.optional("effort") ? { effort: args.required("effort") } : {},
|
|
@@ -46951,6 +47858,7 @@ async function runListenStart(args) {
|
|
|
46951
47858
|
...args.optional("model") ? { model: args.required("model") } : {},
|
|
46952
47859
|
...args.optional("effort") ? { effort: args.required("effort") } : {},
|
|
46953
47860
|
...args.optional("turn-budget") ? { turnBudget: args.required("turn-budget") } : {},
|
|
47861
|
+
...args.optional("poll-interval") ? { pollInterval: args.required("poll-interval") } : {},
|
|
46954
47862
|
...args.optional("grok-executable") ? { executable: args.required("grok-executable") } : {},
|
|
46955
47863
|
...opencodeExecutable ? { opencodeExecutable } : {},
|
|
46956
47864
|
...claudeExecutable ? { claudeExecutable } : {},
|
|
@@ -47059,12 +47967,14 @@ async function runListenSupervisor(args) {
|
|
|
47059
47967
|
"codex-executable",
|
|
47060
47968
|
"state-dir",
|
|
47061
47969
|
"turn-budget",
|
|
47970
|
+
"poll-interval",
|
|
47062
47971
|
"route",
|
|
47063
47972
|
"defer-over"
|
|
47064
47973
|
], 1);
|
|
47065
47974
|
const provider = listenerProvider(args);
|
|
47066
47975
|
validateListenerProviderFlags(args, provider);
|
|
47067
47976
|
const turnBudgetMs = listenerTurnBudgetMs(args.optional("turn-budget"));
|
|
47977
|
+
const pollMs = listenerPollIntervalMs(args.optional("poll-interval"));
|
|
47068
47978
|
const routing = listenerRouteConfiguration(
|
|
47069
47979
|
args.optional("route"),
|
|
47070
47980
|
args.optional("defer-over")
|
|
@@ -47085,6 +47995,7 @@ async function runListenSupervisor(args) {
|
|
|
47085
47995
|
permissionMode: listenerPermissionMode(args.optional("permissions")),
|
|
47086
47996
|
provider,
|
|
47087
47997
|
turnBudgetMs,
|
|
47998
|
+
pollMs,
|
|
47088
47999
|
...routing,
|
|
47089
48000
|
...args.optional("model") ? { model: args.required("model") } : {},
|
|
47090
48001
|
...args.optional("effort") ? { effort: args.required("effort") } : {},
|
|
@@ -48337,7 +49248,7 @@ async function runSeed(args) {
|
|
|
48337
49248
|
if (!tokenOut || !(0, import_node_path21.isAbsolute)(tokenOut)) {
|
|
48338
49249
|
throw new Error("SEED_TOKEN_OUT must be an absolute path");
|
|
48339
49250
|
}
|
|
48340
|
-
const tokenFile = await (0,
|
|
49251
|
+
const tokenFile = await (0, import_promises13.open)(tokenOut, "wx", 384).catch((error) => {
|
|
48341
49252
|
if (error.code === "EEXIST") {
|
|
48342
49253
|
throw new Error("SEED_TOKEN_OUT already exists; refusing to overwrite it");
|
|
48343
49254
|
}
|
|
@@ -48376,7 +49287,7 @@ async function runSeed(args) {
|
|
|
48376
49287
|
tokenWritten = true;
|
|
48377
49288
|
}
|
|
48378
49289
|
await tokenFile.close();
|
|
48379
|
-
if (!tokenWritten) await (0,
|
|
49290
|
+
if (!tokenWritten) await (0, import_promises13.unlink)(tokenOut);
|
|
48380
49291
|
process.stdout.write(`${JSON.stringify({
|
|
48381
49292
|
userId: result.userId,
|
|
48382
49293
|
membershipRole: result.membershipRole,
|
|
@@ -48389,7 +49300,7 @@ async function runSeed(args) {
|
|
|
48389
49300
|
`);
|
|
48390
49301
|
} catch (error) {
|
|
48391
49302
|
await tokenFile.close().catch(() => void 0);
|
|
48392
|
-
if (!tokenWritten) await (0,
|
|
49303
|
+
if (!tokenWritten) await (0, import_promises13.unlink)(tokenOut).catch(() => void 0);
|
|
48393
49304
|
throw error;
|
|
48394
49305
|
}
|
|
48395
49306
|
}
|
|
@@ -48653,6 +49564,7 @@ ${usage()}
|
|
|
48653
49564
|
listenerFailureMessage,
|
|
48654
49565
|
listenerHostLimits,
|
|
48655
49566
|
listenerPermissionMode,
|
|
49567
|
+
listenerPollIntervalMs,
|
|
48656
49568
|
listenerProviderInstallEvidence,
|
|
48657
49569
|
listenerRouteConfiguration,
|
|
48658
49570
|
listenerStatusJson,
|