@alook/daemon 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +1004 -150
- package/dist/index.js +893 -549
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -57,7 +57,7 @@ function cliCommandsSection() {
|
|
|
57
57
|
"### Messaging",
|
|
58
58
|
"",
|
|
59
59
|
`1. \`${CLI} inbox pull\` — fetch unread messages (advances your read waterline by default, ` + `so they won't re-pull; \`--no-ack\` to peek without advancing).`,
|
|
60
|
-
`2. \`${CLI} message send\` — send to a channel, DM, or thread. Attach with ` + `\`--attachment <id>\` (repeatable, order matters)
|
|
60
|
+
`2. \`${CLI} message send\` — send to a channel, DM, or thread. Attach with ` + `\`--attachment <id>\` (repeatable, order matters). Optionally add ` + `\`--remind-after <duration>\` to be reminded if the same channel, thread, or DM receives no ` + `newer message after your send. Use a whole number of minutes or hours from \`1m\` to \`24h\`, ` + `such as \`15m\` or \`2h\`.`,
|
|
61
61
|
`3. \`${CLI} message attachment upload --target <ref> --file <path>\` — upload a file; ` + `returns an id stable across pending→persisted. Feed it into ` + `\`message send --attachment <id>\`.`,
|
|
62
62
|
`4. \`${CLI} message attachment download --id <id> [--out <path>]\` — download any ` + `attachment you can see (or your own pending uploads).`,
|
|
63
63
|
`5. \`${CLI} message emoji --target <ref> --emoji <e>\` — react with a single emoji. ` + `Works on channel messages (\`/<server>/<channel>#N\`), DM messages ` + `(\`/.dm/<peer>#N\`), and thread-reply messages (\`/<server>/<channel>/#N#M\`).`,
|
|
@@ -206,7 +206,13 @@ function utilsSection() {
|
|
|
206
206
|
"",
|
|
207
207
|
"### Join a new server",
|
|
208
208
|
"",
|
|
209
|
-
`If a message contains a \`/c/invite/...\` link, just run \`${CLI} server join --invite <link>\`. ` + "The server enforces owner-only: it accepts only invites your owner created and rejects the " + "rest with a reason. Safe to attempt without reasoning about who sent it."
|
|
209
|
+
`If a message contains a \`/c/invite/...\` link, just run \`${CLI} server join --invite <link>\`. ` + "The server enforces owner-only: it accepts only invites your owner created and rejects the " + "rest with a reason. Safe to attempt without reasoning about who sent it.",
|
|
210
|
+
"",
|
|
211
|
+
"### Follow up when a conversation goes quiet",
|
|
212
|
+
"",
|
|
213
|
+
`Use \`message send --remind-after <duration>\` when you send something that may need a later ` + "follow-up — for example, a question, approval request, handoff, or blocker — and silence would " + "leave the work unfinished. If no newer message appears in that channel, thread, or DM during " + "the duration, you'll receive a reminder to return and decide what to do next. Don't add it to " + "ordinary messages that need no follow-up.",
|
|
214
|
+
"",
|
|
215
|
+
`Example: \`${CLI} message send --target /demo#1234/team --text "Please review the rollout plan" ` + `--remind-after 1m\`.`
|
|
210
216
|
].join(`
|
|
211
217
|
`);
|
|
212
218
|
}
|
|
@@ -1286,13 +1292,18 @@ function codexSessionRootCandidates(homeDirOrCodexRoot) {
|
|
|
1286
1292
|
// src/version.ts
|
|
1287
1293
|
import { createRequire } from "module";
|
|
1288
1294
|
var requireFromHere = createRequire(import.meta.url);
|
|
1295
|
+
var PACKAGE_JSON_CANDIDATES = ["../package.json", "../../package.json"];
|
|
1289
1296
|
function readDaemonVersion() {
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1297
|
+
for (const candidate of PACKAGE_JSON_CANDIDATES) {
|
|
1298
|
+
try {
|
|
1299
|
+
const pkg = requireFromHere(candidate);
|
|
1300
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0)
|
|
1301
|
+
return pkg.version;
|
|
1302
|
+
} catch {
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1295
1305
|
}
|
|
1306
|
+
return "";
|
|
1296
1307
|
}
|
|
1297
1308
|
function getDaemonClientInfo() {
|
|
1298
1309
|
return { name: "alook-daemon", version: readDaemonVersion() };
|
|
@@ -5168,519 +5179,13 @@ function createTypingScopeTracker() {
|
|
|
5168
5179
|
};
|
|
5169
5180
|
}
|
|
5170
5181
|
// src/credentials/credentialProxy.ts
|
|
5171
|
-
import * as
|
|
5182
|
+
import * as crypto2 from "crypto";
|
|
5172
5183
|
import * as fs5 from "fs";
|
|
5173
5184
|
import * as http from "http";
|
|
5174
5185
|
import * as https from "https";
|
|
5175
5186
|
import * as os2 from "os";
|
|
5176
5187
|
import * as path7 from "path";
|
|
5177
5188
|
import { URL as URL2 } from "url";
|
|
5178
|
-
var DEFAULT_HEADER_NAMES = {
|
|
5179
|
-
agentId: "X-Agent-Id",
|
|
5180
|
-
client: "X-Client",
|
|
5181
|
-
capabilities: "X-Agent-Active-Capabilities"
|
|
5182
|
-
};
|
|
5183
|
-
function randomVoucher(prefix) {
|
|
5184
|
-
return prefix + crypto.randomBytes(32).toString("base64url");
|
|
5185
|
-
}
|
|
5186
|
-
function sanitizeIdSegment(id) {
|
|
5187
|
-
return id.replace(/[^A-Za-z0-9._-]/g, "_") || "_";
|
|
5188
|
-
}
|
|
5189
|
-
|
|
5190
|
-
class CredentialBroker {
|
|
5191
|
-
registrations = new Map;
|
|
5192
|
-
voucherPrefix;
|
|
5193
|
-
voucherDir;
|
|
5194
|
-
upstreamBaseUrl;
|
|
5195
|
-
clientLabel;
|
|
5196
|
-
headerNames;
|
|
5197
|
-
constructor(config) {
|
|
5198
|
-
if (!config.upstreamBaseUrl)
|
|
5199
|
-
throw new Error("CredentialBroker: upstreamBaseUrl is required");
|
|
5200
|
-
this.upstreamBaseUrl = config.upstreamBaseUrl.replace(/\/+$/, "");
|
|
5201
|
-
this.voucherPrefix = config.voucherPrefix ?? "vch_";
|
|
5202
|
-
this.clientLabel = config.clientLabel ?? "cli";
|
|
5203
|
-
this.headerNames = config.headerNames ?? DEFAULT_HEADER_NAMES;
|
|
5204
|
-
this.voucherDir = config.voucherDir ?? path7.join(os2.tmpdir(), "agent-vouchers");
|
|
5205
|
-
}
|
|
5206
|
-
mint(agentId, launchId, capabilities, runnerKey) {
|
|
5207
|
-
if (!runnerKey)
|
|
5208
|
-
throw new Error("CredentialBroker.mint: runnerKey is required (per-agent tier-2 credential)");
|
|
5209
|
-
const voucher = randomVoucher(this.voucherPrefix);
|
|
5210
|
-
const dir = path7.join(this.voucherDir, sanitizeIdSegment(agentId));
|
|
5211
|
-
fs5.mkdirSync(dir, { recursive: true });
|
|
5212
|
-
const voucherFile = path7.join(dir, `${sanitizeIdSegment(launchId)}.token`);
|
|
5213
|
-
fs5.writeFileSync(voucherFile, voucher, { mode: 384 });
|
|
5214
|
-
this.registrations.set(voucher, {
|
|
5215
|
-
agentId,
|
|
5216
|
-
launchId,
|
|
5217
|
-
capabilities: new Set(capabilities),
|
|
5218
|
-
voucherFile,
|
|
5219
|
-
runnerKey
|
|
5220
|
-
});
|
|
5221
|
-
return { voucher, agentId, launchId, capabilities: [...capabilities], voucherFile };
|
|
5222
|
-
}
|
|
5223
|
-
revoke(voucher) {
|
|
5224
|
-
const reg = this.registrations.get(voucher);
|
|
5225
|
-
if (!reg)
|
|
5226
|
-
return false;
|
|
5227
|
-
this.registrations.delete(voucher);
|
|
5228
|
-
try {
|
|
5229
|
-
fs5.rmSync(reg.voucherFile, { force: true });
|
|
5230
|
-
} catch {}
|
|
5231
|
-
return true;
|
|
5232
|
-
}
|
|
5233
|
-
revokeAgent(agentId) {
|
|
5234
|
-
let n = 0;
|
|
5235
|
-
for (const [voucher, reg] of this.registrations) {
|
|
5236
|
-
if (reg.agentId === agentId && this.revoke(voucher))
|
|
5237
|
-
n++;
|
|
5238
|
-
}
|
|
5239
|
-
return n;
|
|
5240
|
-
}
|
|
5241
|
-
get size() {
|
|
5242
|
-
return this.registrations.size;
|
|
5243
|
-
}
|
|
5244
|
-
check(authHeader, requiredCapability) {
|
|
5245
|
-
const voucher = parseBearer(authHeader);
|
|
5246
|
-
if (!voucher) {
|
|
5247
|
-
return { ok: false, status: 401, code: "missing_voucher", error: "missing bearer voucher" };
|
|
5248
|
-
}
|
|
5249
|
-
const reg = this.registrations.get(voucher);
|
|
5250
|
-
if (!reg) {
|
|
5251
|
-
return { ok: false, status: 401, code: "invalid_proxy_token", error: "invalid local agent proxy token" };
|
|
5252
|
-
}
|
|
5253
|
-
if (requiredCapability && !reg.capabilities.has(requiredCapability)) {
|
|
5254
|
-
return {
|
|
5255
|
-
ok: false,
|
|
5256
|
-
status: 403,
|
|
5257
|
-
code: "capability_denied",
|
|
5258
|
-
error: `capability '${requiredCapability}' not granted to this voucher`
|
|
5259
|
-
};
|
|
5260
|
-
}
|
|
5261
|
-
return { ok: true, reg };
|
|
5262
|
-
}
|
|
5263
|
-
}
|
|
5264
|
-
function parseBearer(authHeader) {
|
|
5265
|
-
if (!authHeader)
|
|
5266
|
-
return null;
|
|
5267
|
-
const m = /^Bearer\s+(.+)$/i.exec(authHeader.trim());
|
|
5268
|
-
return m ? m[1].trim() : null;
|
|
5269
|
-
}
|
|
5270
|
-
var DEFAULT_CAPABILITY_RESOLVER = (method, pathname) => {
|
|
5271
|
-
if (pathname.includes("/attachment"))
|
|
5272
|
-
return "attach";
|
|
5273
|
-
if (/\/friends(\/|$|\?)/.test(pathname))
|
|
5274
|
-
return "friend";
|
|
5275
|
-
const isMessagesDoor = /\/channels\/[^/]+\/messages(\/|$|\?)/.test(pathname);
|
|
5276
|
-
if (isMessagesDoor)
|
|
5277
|
-
return method === "GET" ? "read" : "send";
|
|
5278
|
-
if (/\/messages\/[^/]+\/reactions\//.test(pathname))
|
|
5279
|
-
return "send";
|
|
5280
|
-
if ((method === "PUT" || method === "DELETE") && /\/messages\/[^/]+\/marks(\/|$|\?)/.test(pathname))
|
|
5281
|
-
return "send";
|
|
5282
|
-
if (method === "GET" && /\/users\/me\/marks(\/|$|\?)/.test(pathname))
|
|
5283
|
-
return "read";
|
|
5284
|
-
if (method === "GET" && /\/messages\/[^/]+(\?|$)/.test(pathname))
|
|
5285
|
-
return "read";
|
|
5286
|
-
if (pathname.includes("/history") || pathname.includes("/search") || pathname.includes("/inbox"))
|
|
5287
|
-
return "read";
|
|
5288
|
-
if (pathname.includes("/server") || pathname.includes("/channel"))
|
|
5289
|
-
return "server";
|
|
5290
|
-
return;
|
|
5291
|
-
};
|
|
5292
|
-
var DEFAULT_UPSTREAM_TIMEOUT_MS = 20000;
|
|
5293
|
-
async function startCredentialProxy(broker, options = {}) {
|
|
5294
|
-
const host = options.host ?? "127.0.0.1";
|
|
5295
|
-
const resolveCap = options.capabilityResolver ?? DEFAULT_CAPABILITY_RESOLVER;
|
|
5296
|
-
const upstream = new URL2(broker.upstreamBaseUrl);
|
|
5297
|
-
const upstreamClient = upstream.protocol === "https:" ? https : http;
|
|
5298
|
-
const onPull = options.onInboxPullResponse;
|
|
5299
|
-
const onProxyRequest = options.onProxyRequest;
|
|
5300
|
-
const server = http.createServer((req, res) => {
|
|
5301
|
-
const pathname = new URL2(req.url ?? "/", "http://placeholder").pathname;
|
|
5302
|
-
const requiredCap = resolveCap(req.method ?? "GET", pathname);
|
|
5303
|
-
const verdict = broker.check(req.headers["authorization"], requiredCap);
|
|
5304
|
-
if (!verdict.ok) {
|
|
5305
|
-
res.writeHead(verdict.status, { "content-type": "application/json" });
|
|
5306
|
-
res.end(JSON.stringify({ error: verdict.error, code: verdict.code }));
|
|
5307
|
-
req.resume();
|
|
5308
|
-
return;
|
|
5309
|
-
}
|
|
5310
|
-
const canonicalCommunityDoor = /^\/api\/community\/(channels|messages|servers|invites|friends|users|bots)(\/|$)/.test(pathname);
|
|
5311
|
-
if (pathname === "/api" || pathname.startsWith("/api/") && !canonicalCommunityDoor) {
|
|
5312
|
-
res.writeHead(404, { "content-type": "application/json" });
|
|
5313
|
-
res.end(JSON.stringify({ error: "unknown API route", code: "not_found" }));
|
|
5314
|
-
req.resume();
|
|
5315
|
-
return;
|
|
5316
|
-
}
|
|
5317
|
-
const reg = verdict.reg;
|
|
5318
|
-
const isInboxPull = onPull && pathname === "/api/community/users/me/inbox/pull";
|
|
5319
|
-
if (onProxyRequest) {
|
|
5320
|
-
try {
|
|
5321
|
-
onProxyRequest(reg.agentId, req.method ?? "GET", pathname);
|
|
5322
|
-
} catch {}
|
|
5323
|
-
}
|
|
5324
|
-
const outHeaders = { ...req.headers };
|
|
5325
|
-
delete outHeaders["authorization"];
|
|
5326
|
-
delete outHeaders["host"];
|
|
5327
|
-
outHeaders["authorization"] = `Bearer ${reg.runnerKey}`;
|
|
5328
|
-
outHeaders[broker.headerNames.agentId.toLowerCase()] = reg.agentId;
|
|
5329
|
-
outHeaders[broker.headerNames.client.toLowerCase()] = broker.clientLabel;
|
|
5330
|
-
outHeaders[broker.headerNames.capabilities.toLowerCase()] = [...reg.capabilities].join(",");
|
|
5331
|
-
let responded = false;
|
|
5332
|
-
let upstreamRes;
|
|
5333
|
-
const upstreamReq = upstreamClient.request({
|
|
5334
|
-
protocol: upstream.protocol,
|
|
5335
|
-
hostname: upstream.hostname,
|
|
5336
|
-
port: upstream.port || (upstream.protocol === "https:" ? 443 : 80),
|
|
5337
|
-
method: req.method,
|
|
5338
|
-
path: joinPath(upstream.pathname, req.url ?? "/"),
|
|
5339
|
-
headers: outHeaders
|
|
5340
|
-
}, (res_) => {
|
|
5341
|
-
responded = true;
|
|
5342
|
-
upstreamRes = res_;
|
|
5343
|
-
const destroyResIfIncomplete = () => {
|
|
5344
|
-
if (!res_.complete)
|
|
5345
|
-
res.destroy();
|
|
5346
|
-
};
|
|
5347
|
-
res_.on("error", destroyResIfIncomplete);
|
|
5348
|
-
res_.on("close", destroyResIfIncomplete);
|
|
5349
|
-
if (isInboxPull && res_.statusCode && res_.statusCode < 300) {
|
|
5350
|
-
const chunks = [];
|
|
5351
|
-
res_.on("data", (chunk) => chunks.push(chunk));
|
|
5352
|
-
res_.on("end", () => {
|
|
5353
|
-
const body = Buffer.concat(chunks);
|
|
5354
|
-
res.writeHead(res_.statusCode, res_.headers);
|
|
5355
|
-
res.end(body);
|
|
5356
|
-
try {
|
|
5357
|
-
const parsed = JSON.parse(body.toString());
|
|
5358
|
-
if (parsed.messages)
|
|
5359
|
-
onPull(reg.agentId, parsed.messages);
|
|
5360
|
-
} catch {}
|
|
5361
|
-
});
|
|
5362
|
-
} else {
|
|
5363
|
-
res.writeHead(res_.statusCode ?? 502, res_.headers);
|
|
5364
|
-
res_.pipe(res);
|
|
5365
|
-
}
|
|
5366
|
-
});
|
|
5367
|
-
upstreamReq.on("error", (err) => {
|
|
5368
|
-
if (responded)
|
|
5369
|
-
return;
|
|
5370
|
-
responded = true;
|
|
5371
|
-
res.writeHead(502, { "content-type": "application/json" });
|
|
5372
|
-
res.end(JSON.stringify({ error: `upstream error: ${err.message}`, code: "upstream_error" }));
|
|
5373
|
-
});
|
|
5374
|
-
const upstreamTimeoutMs = options.upstreamTimeoutMs ?? DEFAULT_UPSTREAM_TIMEOUT_MS;
|
|
5375
|
-
upstreamReq.setTimeout(upstreamTimeoutMs, () => {
|
|
5376
|
-
upstreamReq.destroy();
|
|
5377
|
-
upstreamRes?.destroy();
|
|
5378
|
-
if (responded) {
|
|
5379
|
-
res.destroy();
|
|
5380
|
-
return;
|
|
5381
|
-
}
|
|
5382
|
-
responded = true;
|
|
5383
|
-
res.writeHead(504, { "content-type": "application/json" });
|
|
5384
|
-
res.end(JSON.stringify({ error: `upstream request timed out after ${upstreamTimeoutMs}ms`, code: "upstream_timeout" }));
|
|
5385
|
-
});
|
|
5386
|
-
res.on("close", () => {
|
|
5387
|
-
upstreamReq.destroy();
|
|
5388
|
-
upstreamRes?.destroy();
|
|
5389
|
-
});
|
|
5390
|
-
req.pipe(upstreamReq);
|
|
5391
|
-
});
|
|
5392
|
-
await new Promise((resolve3, reject) => {
|
|
5393
|
-
server.once("error", reject);
|
|
5394
|
-
server.listen(options.port ?? 0, host, () => {
|
|
5395
|
-
server.removeListener("error", reject);
|
|
5396
|
-
resolve3();
|
|
5397
|
-
});
|
|
5398
|
-
});
|
|
5399
|
-
const addr = server.address();
|
|
5400
|
-
const port = typeof addr === "object" && addr ? addr.port : options.port ?? 0;
|
|
5401
|
-
const url = `http://${host}:${port}`;
|
|
5402
|
-
return {
|
|
5403
|
-
url,
|
|
5404
|
-
port,
|
|
5405
|
-
close: () => new Promise((resolve3) => {
|
|
5406
|
-
server.close(() => resolve3());
|
|
5407
|
-
})
|
|
5408
|
-
};
|
|
5409
|
-
}
|
|
5410
|
-
function joinPath(basePath, reqUrl) {
|
|
5411
|
-
const base = basePath.replace(/\/+$/, "");
|
|
5412
|
-
const reqPath = reqUrl.startsWith("/") ? reqUrl : `/${reqUrl}`;
|
|
5413
|
-
return base + reqPath || "/";
|
|
5414
|
-
}
|
|
5415
|
-
// src/daemon/createDaemon.ts
|
|
5416
|
-
import { homedir as homedir3 } from "os";
|
|
5417
|
-
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
5418
|
-
|
|
5419
|
-
// src/util/rotatingFileSink.ts
|
|
5420
|
-
import {
|
|
5421
|
-
appendFileSync,
|
|
5422
|
-
chmodSync,
|
|
5423
|
-
closeSync,
|
|
5424
|
-
constants,
|
|
5425
|
-
existsSync as existsSync5,
|
|
5426
|
-
fstatSync,
|
|
5427
|
-
lstatSync as lstatSync2,
|
|
5428
|
-
openSync,
|
|
5429
|
-
renameSync,
|
|
5430
|
-
statSync,
|
|
5431
|
-
unlinkSync as unlinkSync3
|
|
5432
|
-
} from "node:fs";
|
|
5433
|
-
function createRotatingFileSink(path8, maxBytes, opts = {}) {
|
|
5434
|
-
const report = (operation, error) => {
|
|
5435
|
-
try {
|
|
5436
|
-
opts.onError?.({ operation, error });
|
|
5437
|
-
} catch {}
|
|
5438
|
-
};
|
|
5439
|
-
const secureGeneration = (filePath) => {
|
|
5440
|
-
if (!existsSync5(filePath))
|
|
5441
|
-
return true;
|
|
5442
|
-
try {
|
|
5443
|
-
const stat = lstatSync2(filePath);
|
|
5444
|
-
if (!stat.isFile()) {
|
|
5445
|
-
if (opts.mode === undefined && !opts.hardMaxBytes)
|
|
5446
|
-
return true;
|
|
5447
|
-
report("unsafe_generation", new Error("log generation is not a regular file"));
|
|
5448
|
-
return false;
|
|
5449
|
-
}
|
|
5450
|
-
if (opts.mode !== undefined)
|
|
5451
|
-
chmodSync(filePath, opts.mode);
|
|
5452
|
-
if (opts.hardMaxBytes && maxBytes > 0 && stat.size > maxBytes) {
|
|
5453
|
-
try {
|
|
5454
|
-
unlinkSync3(filePath);
|
|
5455
|
-
} catch (error) {
|
|
5456
|
-
report("oversize_generation", error);
|
|
5457
|
-
return false;
|
|
5458
|
-
}
|
|
5459
|
-
report("oversize_generation", new Error(`removed generation larger than ${maxBytes} bytes`));
|
|
5460
|
-
}
|
|
5461
|
-
return true;
|
|
5462
|
-
} catch (error) {
|
|
5463
|
-
report("chmod", error);
|
|
5464
|
-
return false;
|
|
5465
|
-
}
|
|
5466
|
-
};
|
|
5467
|
-
const rotate = () => {
|
|
5468
|
-
try {
|
|
5469
|
-
renameSync(path8, `${path8}.1`);
|
|
5470
|
-
if (opts.mode !== undefined)
|
|
5471
|
-
chmodSync(`${path8}.1`, opts.mode);
|
|
5472
|
-
return true;
|
|
5473
|
-
} catch (error) {
|
|
5474
|
-
report("rotate", error);
|
|
5475
|
-
return false;
|
|
5476
|
-
}
|
|
5477
|
-
};
|
|
5478
|
-
const currentSize = () => {
|
|
5479
|
-
try {
|
|
5480
|
-
return existsSync5(path8) ? statSync(path8).size : 0;
|
|
5481
|
-
} catch (error) {
|
|
5482
|
-
report("stat", error);
|
|
5483
|
-
return null;
|
|
5484
|
-
}
|
|
5485
|
-
};
|
|
5486
|
-
const sink = {
|
|
5487
|
-
secure() {
|
|
5488
|
-
return secureGeneration(`${path8}.1`) && secureGeneration(path8);
|
|
5489
|
-
},
|
|
5490
|
-
write(line) {
|
|
5491
|
-
try {
|
|
5492
|
-
if (!sink.secure())
|
|
5493
|
-
return;
|
|
5494
|
-
const serialized = line + `
|
|
5495
|
-
`;
|
|
5496
|
-
const serializedBytes = Buffer.byteLength(serialized, "utf8");
|
|
5497
|
-
if (opts.hardMaxBytes && maxBytes > 0 && serializedBytes > maxBytes) {
|
|
5498
|
-
report("oversize", new Error(`record exceeds ${maxBytes} bytes`));
|
|
5499
|
-
return;
|
|
5500
|
-
}
|
|
5501
|
-
const measuredBytes = currentSize();
|
|
5502
|
-
if (measuredBytes === null && opts.hardMaxBytes)
|
|
5503
|
-
return;
|
|
5504
|
-
const currentBytes = measuredBytes ?? 0;
|
|
5505
|
-
const shouldRotate = maxBytes > 0 && (opts.hardMaxBytes ? currentBytes > 0 && currentBytes + serializedBytes > maxBytes : currentBytes >= maxBytes);
|
|
5506
|
-
if (shouldRotate && !rotate() && opts.hardMaxBytes)
|
|
5507
|
-
return;
|
|
5508
|
-
appendFileSync(path8, serialized, opts.mode === undefined ? undefined : { mode: opts.mode });
|
|
5509
|
-
} catch (error) {
|
|
5510
|
-
report("append", error);
|
|
5511
|
-
}
|
|
5512
|
-
},
|
|
5513
|
-
openSnapshot() {
|
|
5514
|
-
const files = [];
|
|
5515
|
-
try {
|
|
5516
|
-
for (const candidate of [`${path8}.1`, path8]) {
|
|
5517
|
-
if (!existsSync5(candidate))
|
|
5518
|
-
continue;
|
|
5519
|
-
if (!secureGeneration(candidate))
|
|
5520
|
-
continue;
|
|
5521
|
-
const noFollow = "O_NOFOLLOW" in constants ? constants.O_NOFOLLOW : 0;
|
|
5522
|
-
const fd = openSync(candidate, constants.O_RDONLY | noFollow);
|
|
5523
|
-
try {
|
|
5524
|
-
const stat = fstatSync(fd);
|
|
5525
|
-
if (!stat.isFile())
|
|
5526
|
-
throw new Error("snapshot source is not a regular file");
|
|
5527
|
-
files.push({ path: candidate, fd, size: stat.size });
|
|
5528
|
-
} catch (error) {
|
|
5529
|
-
closeSync(fd);
|
|
5530
|
-
throw error;
|
|
5531
|
-
}
|
|
5532
|
-
}
|
|
5533
|
-
} catch (error) {
|
|
5534
|
-
for (const file of files) {
|
|
5535
|
-
try {
|
|
5536
|
-
closeSync(file.fd);
|
|
5537
|
-
} catch {}
|
|
5538
|
-
}
|
|
5539
|
-
report("snapshot", error);
|
|
5540
|
-
return { files: [], close: () => {} };
|
|
5541
|
-
}
|
|
5542
|
-
let closed = false;
|
|
5543
|
-
return {
|
|
5544
|
-
files,
|
|
5545
|
-
close() {
|
|
5546
|
-
if (closed)
|
|
5547
|
-
return;
|
|
5548
|
-
closed = true;
|
|
5549
|
-
for (const file of files) {
|
|
5550
|
-
try {
|
|
5551
|
-
closeSync(file.fd);
|
|
5552
|
-
} catch {}
|
|
5553
|
-
}
|
|
5554
|
-
}
|
|
5555
|
-
};
|
|
5556
|
-
}
|
|
5557
|
-
};
|
|
5558
|
-
return sink;
|
|
5559
|
-
}
|
|
5560
|
-
|
|
5561
|
-
// src/util/traceSampler.ts
|
|
5562
|
-
var SAMPLEABLE_EVENTS = new Set(["tick", "progress", "runtime_signal"]);
|
|
5563
|
-
var SACRED_TURN_SPAN_EVENTS = new Set(["turn_begin", "turn_end", "turn_abort"]);
|
|
5564
|
-
var DEFAULT_TRACE_SAMPLE_MS = 30000;
|
|
5565
|
-
var DEFAULT_TRACE_FILE_MAX_BYTES = 32 * 1024 * 1024;
|
|
5566
|
-
function newAgentState() {
|
|
5567
|
-
return {
|
|
5568
|
-
lastTickKey: null,
|
|
5569
|
-
lastTickEmitAt: Number.NEGATIVE_INFINITY,
|
|
5570
|
-
pendingTick: null,
|
|
5571
|
-
lastProgressEmitAt: Number.NEGATIVE_INFINITY,
|
|
5572
|
-
lastSignalPhase: null,
|
|
5573
|
-
lastSignalEmitAt: Number.NEGATIVE_INFINITY
|
|
5574
|
-
};
|
|
5575
|
-
}
|
|
5576
|
-
function tickKey(rec) {
|
|
5577
|
-
return [
|
|
5578
|
-
rec.status,
|
|
5579
|
-
rec.turnActive,
|
|
5580
|
-
rec.inbox,
|
|
5581
|
-
rec.resetting,
|
|
5582
|
-
rec.stoppingSince == null ? "s0" : "s1"
|
|
5583
|
-
].join("|");
|
|
5584
|
-
}
|
|
5585
|
-
function hasEffects(rec) {
|
|
5586
|
-
const e = rec.effects;
|
|
5587
|
-
return Array.isArray(e) && e.length > 0;
|
|
5588
|
-
}
|
|
5589
|
-
function nowOf(rec) {
|
|
5590
|
-
return typeof rec.nowMs === "number" ? rec.nowMs : 0;
|
|
5591
|
-
}
|
|
5592
|
-
function createTraceSampler(emit, throttleMs = DEFAULT_TRACE_SAMPLE_MS) {
|
|
5593
|
-
const perAgent = new Map;
|
|
5594
|
-
const stateFor = (agentId) => {
|
|
5595
|
-
let s = perAgent.get(agentId);
|
|
5596
|
-
if (!s) {
|
|
5597
|
-
s = newAgentState();
|
|
5598
|
-
perAgent.set(agentId, s);
|
|
5599
|
-
}
|
|
5600
|
-
return s;
|
|
5601
|
-
};
|
|
5602
|
-
const flushPending = (s) => {
|
|
5603
|
-
if (s.pendingTick) {
|
|
5604
|
-
const p = s.pendingTick;
|
|
5605
|
-
s.pendingTick = null;
|
|
5606
|
-
s.lastTickEmitAt = nowOf(p);
|
|
5607
|
-
emit(p);
|
|
5608
|
-
}
|
|
5609
|
-
};
|
|
5610
|
-
return {
|
|
5611
|
-
offer(rec) {
|
|
5612
|
-
const agentId = typeof rec.agentId === "string" ? rec.agentId : null;
|
|
5613
|
-
if (!agentId) {
|
|
5614
|
-
emit(rec);
|
|
5615
|
-
return;
|
|
5616
|
-
}
|
|
5617
|
-
const s = stateFor(agentId);
|
|
5618
|
-
const event = rec.event;
|
|
5619
|
-
if (rec.recordKind === "turn_span" && SACRED_TURN_SPAN_EVENTS.has(String(event))) {
|
|
5620
|
-
flushPending(s);
|
|
5621
|
-
emit(rec);
|
|
5622
|
-
return;
|
|
5623
|
-
}
|
|
5624
|
-
if (!SAMPLEABLE_EVENTS.has(event) || hasEffects(rec)) {
|
|
5625
|
-
flushPending(s);
|
|
5626
|
-
if (event === "tick") {
|
|
5627
|
-
s.lastTickKey = tickKey(rec);
|
|
5628
|
-
s.lastTickEmitAt = nowOf(rec);
|
|
5629
|
-
}
|
|
5630
|
-
emit(rec);
|
|
5631
|
-
return;
|
|
5632
|
-
}
|
|
5633
|
-
if (event === "tick") {
|
|
5634
|
-
const key = tickKey(rec);
|
|
5635
|
-
const now = nowOf(rec);
|
|
5636
|
-
if (key !== s.lastTickKey) {
|
|
5637
|
-
flushPending(s);
|
|
5638
|
-
s.lastTickKey = key;
|
|
5639
|
-
s.lastTickEmitAt = now;
|
|
5640
|
-
emit(rec);
|
|
5641
|
-
return;
|
|
5642
|
-
}
|
|
5643
|
-
if (now - s.lastTickEmitAt >= throttleMs) {
|
|
5644
|
-
s.pendingTick = null;
|
|
5645
|
-
s.lastTickEmitAt = now;
|
|
5646
|
-
emit(rec);
|
|
5647
|
-
} else {
|
|
5648
|
-
s.pendingTick = rec;
|
|
5649
|
-
}
|
|
5650
|
-
return;
|
|
5651
|
-
}
|
|
5652
|
-
if (event === "progress") {
|
|
5653
|
-
const now = nowOf(rec);
|
|
5654
|
-
if (now - s.lastProgressEmitAt >= throttleMs) {
|
|
5655
|
-
s.lastProgressEmitAt = now;
|
|
5656
|
-
emit(rec);
|
|
5657
|
-
}
|
|
5658
|
-
return;
|
|
5659
|
-
}
|
|
5660
|
-
if (event === "runtime_signal") {
|
|
5661
|
-
const phase = typeof rec.apmPhase === "string" ? rec.apmPhase : "";
|
|
5662
|
-
const now = nowOf(rec);
|
|
5663
|
-
if (phase !== s.lastSignalPhase || now - s.lastSignalEmitAt >= throttleMs) {
|
|
5664
|
-
s.lastSignalPhase = phase;
|
|
5665
|
-
s.lastSignalEmitAt = now;
|
|
5666
|
-
emit(rec);
|
|
5667
|
-
}
|
|
5668
|
-
return;
|
|
5669
|
-
}
|
|
5670
|
-
emit(rec);
|
|
5671
|
-
}
|
|
5672
|
-
};
|
|
5673
|
-
}
|
|
5674
|
-
|
|
5675
|
-
// src/util/statusFile.ts
|
|
5676
|
-
import { writeFileSync as writeFileSync4, renameSync as renameSync2 } from "node:fs";
|
|
5677
|
-
function writeStatusFile(path8, snapshot) {
|
|
5678
|
-
try {
|
|
5679
|
-
const tmp = `${path8}.tmp`;
|
|
5680
|
-
writeFileSync4(tmp, JSON.stringify(snapshot));
|
|
5681
|
-
renameSync2(tmp, path8);
|
|
5682
|
-
} catch {}
|
|
5683
|
-
}
|
|
5684
5189
|
|
|
5685
5190
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
5686
5191
|
var exports_external = {};
|
|
@@ -6446,10 +5951,10 @@ function mergeDefs(...defs) {
|
|
|
6446
5951
|
function cloneDef(schema) {
|
|
6447
5952
|
return mergeDefs(schema._zod.def);
|
|
6448
5953
|
}
|
|
6449
|
-
function getElementAtPath(obj,
|
|
6450
|
-
if (!
|
|
5954
|
+
function getElementAtPath(obj, path7) {
|
|
5955
|
+
if (!path7)
|
|
6451
5956
|
return obj;
|
|
6452
|
-
return
|
|
5957
|
+
return path7.reduce((acc, key) => acc?.[key], obj);
|
|
6453
5958
|
}
|
|
6454
5959
|
function promiseAllObject(promisesObj) {
|
|
6455
5960
|
const keys = Object.keys(promisesObj);
|
|
@@ -6857,11 +6362,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
6857
6362
|
}
|
|
6858
6363
|
return false;
|
|
6859
6364
|
}
|
|
6860
|
-
function prefixIssues(
|
|
6365
|
+
function prefixIssues(path7, issues) {
|
|
6861
6366
|
return issues.map((iss) => {
|
|
6862
6367
|
var _a2;
|
|
6863
6368
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
6864
|
-
iss.path.unshift(
|
|
6369
|
+
iss.path.unshift(path7);
|
|
6865
6370
|
return iss;
|
|
6866
6371
|
});
|
|
6867
6372
|
}
|
|
@@ -7008,16 +6513,16 @@ function flattenError(error, mapper = (issue2) => issue2.message) {
|
|
|
7008
6513
|
}
|
|
7009
6514
|
function formatError(error, mapper = (issue2) => issue2.message) {
|
|
7010
6515
|
const fieldErrors = { _errors: [] };
|
|
7011
|
-
const processError = (error2,
|
|
6516
|
+
const processError = (error2, path7 = []) => {
|
|
7012
6517
|
for (const issue2 of error2.issues) {
|
|
7013
6518
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
7014
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
6519
|
+
issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
|
|
7015
6520
|
} else if (issue2.code === "invalid_key") {
|
|
7016
|
-
processError({ issues: issue2.issues }, [...
|
|
6521
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
7017
6522
|
} else if (issue2.code === "invalid_element") {
|
|
7018
|
-
processError({ issues: issue2.issues }, [...
|
|
6523
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
7019
6524
|
} else {
|
|
7020
|
-
const fullpath = [...
|
|
6525
|
+
const fullpath = [...path7, ...issue2.path];
|
|
7021
6526
|
if (fullpath.length === 0) {
|
|
7022
6527
|
fieldErrors._errors.push(mapper(issue2));
|
|
7023
6528
|
} else {
|
|
@@ -7044,17 +6549,17 @@ function formatError(error, mapper = (issue2) => issue2.message) {
|
|
|
7044
6549
|
}
|
|
7045
6550
|
function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
7046
6551
|
const result = { errors: [] };
|
|
7047
|
-
const processError = (error2,
|
|
6552
|
+
const processError = (error2, path7 = []) => {
|
|
7048
6553
|
var _a2, _b;
|
|
7049
6554
|
for (const issue2 of error2.issues) {
|
|
7050
6555
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
7051
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
6556
|
+
issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
|
|
7052
6557
|
} else if (issue2.code === "invalid_key") {
|
|
7053
|
-
processError({ issues: issue2.issues }, [...
|
|
6558
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
7054
6559
|
} else if (issue2.code === "invalid_element") {
|
|
7055
|
-
processError({ issues: issue2.issues }, [...
|
|
6560
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
7056
6561
|
} else {
|
|
7057
|
-
const fullpath = [...
|
|
6562
|
+
const fullpath = [...path7, ...issue2.path];
|
|
7058
6563
|
if (fullpath.length === 0) {
|
|
7059
6564
|
result.errors.push(mapper(issue2));
|
|
7060
6565
|
continue;
|
|
@@ -7086,8 +6591,8 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
|
7086
6591
|
}
|
|
7087
6592
|
function toDotPath(_path) {
|
|
7088
6593
|
const segs = [];
|
|
7089
|
-
const
|
|
7090
|
-
for (const seg of
|
|
6594
|
+
const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
6595
|
+
for (const seg of path7) {
|
|
7091
6596
|
if (typeof seg === "number")
|
|
7092
6597
|
segs.push(`[${seg}]`);
|
|
7093
6598
|
else if (typeof seg === "symbol")
|
|
@@ -19546,13 +19051,13 @@ function resolveRef(ref, ctx) {
|
|
|
19546
19051
|
if (!ref.startsWith("#")) {
|
|
19547
19052
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
19548
19053
|
}
|
|
19549
|
-
const
|
|
19550
|
-
if (
|
|
19054
|
+
const path7 = ref.slice(1).split("/").filter(Boolean);
|
|
19055
|
+
if (path7.length === 0) {
|
|
19551
19056
|
return ctx.rootSchema;
|
|
19552
19057
|
}
|
|
19553
19058
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
19554
|
-
if (
|
|
19555
|
-
const key =
|
|
19059
|
+
if (path7[0] === defsKey) {
|
|
19060
|
+
const key = path7[1];
|
|
19556
19061
|
if (!key || !ctx.defs[key]) {
|
|
19557
19062
|
throw new Error(`Reference not found: ${ref}`);
|
|
19558
19063
|
}
|
|
@@ -19959,6 +19464,16 @@ function date4(params) {
|
|
|
19959
19464
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
19960
19465
|
config(en_default());
|
|
19961
19466
|
// ../shared/src/lib/discriminator.ts
|
|
19467
|
+
function parseNameAndTag(q) {
|
|
19468
|
+
const m = /^(.+)#(\d{4,})$/.exec(q);
|
|
19469
|
+
if (!m)
|
|
19470
|
+
return null;
|
|
19471
|
+
const name = m[1].trim();
|
|
19472
|
+
const discriminator = m[2];
|
|
19473
|
+
if (!name)
|
|
19474
|
+
return null;
|
|
19475
|
+
return { name, discriminator };
|
|
19476
|
+
}
|
|
19962
19477
|
function formatHandle(name, discriminator) {
|
|
19963
19478
|
return `${name}#${discriminator}`;
|
|
19964
19479
|
}
|
|
@@ -20549,7 +20064,7 @@ function sql(strings, ...params) {
|
|
|
20549
20064
|
return new SQL([new StringChunk(str)]);
|
|
20550
20065
|
}
|
|
20551
20066
|
sql2.raw = raw;
|
|
20552
|
-
function
|
|
20067
|
+
function join8(chunks, separator) {
|
|
20553
20068
|
const result = [];
|
|
20554
20069
|
for (const [i, chunk] of chunks.entries()) {
|
|
20555
20070
|
if (i > 0 && separator !== undefined) {
|
|
@@ -20559,7 +20074,7 @@ function sql(strings, ...params) {
|
|
|
20559
20074
|
}
|
|
20560
20075
|
return new SQL(result);
|
|
20561
20076
|
}
|
|
20562
|
-
sql2.join =
|
|
20077
|
+
sql2.join = join8;
|
|
20563
20078
|
function identifier(value) {
|
|
20564
20079
|
return new Name(value);
|
|
20565
20080
|
}
|
|
@@ -21337,7 +20852,7 @@ class PrimaryKey {
|
|
|
21337
20852
|
}
|
|
21338
20853
|
|
|
21339
20854
|
// ../../node_modules/.pnpm/nanoid@5.1.16/node_modules/nanoid/index.js
|
|
21340
|
-
import { webcrypto as
|
|
20855
|
+
import { webcrypto as crypto } from "node:crypto";
|
|
21341
20856
|
|
|
21342
20857
|
// ../../node_modules/.pnpm/nanoid@5.1.16/node_modules/nanoid/url-alphabet/index.js
|
|
21343
20858
|
var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
@@ -21352,10 +20867,10 @@ function fillPool(bytes) {
|
|
|
21352
20867
|
try {
|
|
21353
20868
|
if (!pool || pool.length < bytes) {
|
|
21354
20869
|
pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
|
|
21355
|
-
|
|
20870
|
+
crypto.getRandomValues(pool);
|
|
21356
20871
|
poolOffset = 0;
|
|
21357
20872
|
} else if (poolOffset + bytes > pool.length) {
|
|
21358
|
-
|
|
20873
|
+
crypto.getRandomValues(pool);
|
|
21359
20874
|
poolOffset = 0;
|
|
21360
20875
|
}
|
|
21361
20876
|
} catch (e) {
|
|
@@ -22192,6 +21707,81 @@ var OwnerDiagnosticReportSchema = exports_external.discriminatedUnion("status",
|
|
|
22192
21707
|
]);
|
|
22193
21708
|
|
|
22194
21709
|
// ../shared/src/community-cli-contract.ts
|
|
21710
|
+
var DM_SERVER = ".dm";
|
|
21711
|
+
function parseRef(ref) {
|
|
21712
|
+
if (!ref.startsWith("/"))
|
|
21713
|
+
throw new Error(`ref must start with "/": ${ref}`);
|
|
21714
|
+
const body = ref.slice(1);
|
|
21715
|
+
const parts = body.split("/");
|
|
21716
|
+
if (parts.length < 2)
|
|
21717
|
+
throw new Error(`ref needs /<server>/<channel>: ${ref}`);
|
|
21718
|
+
const server = parts[0];
|
|
21719
|
+
if (server !== DM_SERVER && !parseNameAndTag(server)) {
|
|
21720
|
+
throw new Error(`server ref must use a name#discriminator handle: ${ref}`);
|
|
21721
|
+
}
|
|
21722
|
+
let seq;
|
|
21723
|
+
if (parts.length >= 3 && parts[parts.length - 1].startsWith("#")) {
|
|
21724
|
+
const tail = parseThreadTail(parts[parts.length - 1]);
|
|
21725
|
+
return { server, channel: parts[1], ...tail };
|
|
21726
|
+
}
|
|
21727
|
+
if (parts.length >= 3 && server !== DM_SERVER) {
|
|
21728
|
+
throw new Error(`ref has too many segments: ${ref}`);
|
|
21729
|
+
}
|
|
21730
|
+
const chSeg = parts[1];
|
|
21731
|
+
if (server === DM_SERVER) {
|
|
21732
|
+
const lastHash = chSeg.lastIndexOf("#");
|
|
21733
|
+
if (lastHash < 0)
|
|
21734
|
+
return { server, channel: chSeg };
|
|
21735
|
+
const firstHash = chSeg.indexOf("#");
|
|
21736
|
+
const tail = chSeg.slice(lastHash + 1);
|
|
21737
|
+
const isBareHandle = firstHash === lastHash && /^\d{4,}$/.test(tail);
|
|
21738
|
+
if (isBareHandle)
|
|
21739
|
+
return { server, channel: chSeg };
|
|
21740
|
+
const tailNum = Number(tail.startsWith("#") ? tail.slice(1) : tail);
|
|
21741
|
+
if (!Number.isFinite(tailNum))
|
|
21742
|
+
return { server, channel: chSeg };
|
|
21743
|
+
seq = parseSeq(tail);
|
|
21744
|
+
return { server, channel: chSeg.slice(0, lastHash), seq };
|
|
21745
|
+
}
|
|
21746
|
+
const hashIdx = chSeg.indexOf("#");
|
|
21747
|
+
if (hashIdx >= 0) {
|
|
21748
|
+
seq = parseSeq(chSeg.slice(hashIdx));
|
|
21749
|
+
return { server, channel: chSeg.slice(0, hashIdx), seq };
|
|
21750
|
+
}
|
|
21751
|
+
return { server, channel: chSeg };
|
|
21752
|
+
}
|
|
21753
|
+
function parseThreadTail(segment) {
|
|
21754
|
+
const stripped = segment.startsWith("#") ? segment.slice(1) : segment;
|
|
21755
|
+
const tokens = stripped.split("#");
|
|
21756
|
+
if (tokens.length < 1 || tokens.length > 2) {
|
|
21757
|
+
throw new Error(`bad thread ref tail: #${stripped}`);
|
|
21758
|
+
}
|
|
21759
|
+
for (const t of tokens) {
|
|
21760
|
+
if (!t)
|
|
21761
|
+
throw new Error(`bad thread ref tail: #${stripped} (empty seq)`);
|
|
21762
|
+
}
|
|
21763
|
+
const threadRootSeq = parseSeq(tokens[0]);
|
|
21764
|
+
if (tokens.length === 1)
|
|
21765
|
+
return { threadRootSeq };
|
|
21766
|
+
return { threadRootSeq, seq: parseSeq(tokens[1]) };
|
|
21767
|
+
}
|
|
21768
|
+
function formatRef(p) {
|
|
21769
|
+
if (p.seq !== undefined && p.threadRootSeq === undefined) {
|
|
21770
|
+
throw new Error("formatRef: seq without threadRootSeq is not supported");
|
|
21771
|
+
}
|
|
21772
|
+
const base = `/${p.server}/${p.channel}`;
|
|
21773
|
+
if (p.threadRootSeq === undefined)
|
|
21774
|
+
return base;
|
|
21775
|
+
if (p.seq === undefined)
|
|
21776
|
+
return `${base}/#${p.threadRootSeq}`;
|
|
21777
|
+
return `${base}/#${p.threadRootSeq}#${p.seq}`;
|
|
21778
|
+
}
|
|
21779
|
+
function parseSeq(s) {
|
|
21780
|
+
const n = Number(s.startsWith("#") ? s.slice(1) : s);
|
|
21781
|
+
if (!Number.isFinite(n))
|
|
21782
|
+
throw new Error(`bad seq: ${s}`);
|
|
21783
|
+
return n;
|
|
21784
|
+
}
|
|
22195
21785
|
var HostCommandSchema = exports_external.discriminatedUnion("type", [
|
|
22196
21786
|
exports_external.object({
|
|
22197
21787
|
type: exports_external.literal("agent:wake"),
|
|
@@ -22232,6 +21822,9 @@ var HostCommandSchema = exports_external.discriminatedUnion("type", [
|
|
|
22232
21822
|
launchId: exports_external.string().min(1)
|
|
22233
21823
|
}))
|
|
22234
21824
|
}),
|
|
21825
|
+
exports_external.strictObject({
|
|
21826
|
+
type: exports_external.literal("machine:update")
|
|
21827
|
+
}),
|
|
22235
21828
|
exports_external.object({
|
|
22236
21829
|
type: exports_external.literal("bot:added"),
|
|
22237
21830
|
botId: exports_external.string().min(1),
|
|
@@ -22256,16 +21849,641 @@ var HostCommandSchema = exports_external.discriminatedUnion("type", [
|
|
|
22256
21849
|
}),
|
|
22257
21850
|
DiagnosticCollectCommandSchema
|
|
22258
21851
|
]);
|
|
22259
|
-
// src/
|
|
22260
|
-
var
|
|
22261
|
-
var
|
|
22262
|
-
var
|
|
22263
|
-
var
|
|
22264
|
-
var
|
|
22265
|
-
|
|
22266
|
-
|
|
21852
|
+
// src/credentials/credentialProxy.ts
|
|
21853
|
+
var LOCAL_MESSAGE_REMINDER_PATH = "/__alook/local/message-reminder";
|
|
21854
|
+
var LOCAL_MESSAGE_REMINDER_BODY_MAX_BYTES = 4 * 1024;
|
|
21855
|
+
var LOCAL_MESSAGE_REMINDER_MIN_MS = 60000;
|
|
21856
|
+
var LOCAL_MESSAGE_REMINDER_MAX_MS = 24 * 60 * 60000;
|
|
21857
|
+
var DEFAULT_HEADER_NAMES = {
|
|
21858
|
+
agentId: "X-Agent-Id",
|
|
21859
|
+
client: "X-Client",
|
|
21860
|
+
capabilities: "X-Agent-Active-Capabilities"
|
|
21861
|
+
};
|
|
21862
|
+
function randomVoucher(prefix) {
|
|
21863
|
+
return prefix + crypto2.randomBytes(32).toString("base64url");
|
|
22267
21864
|
}
|
|
22268
|
-
|
|
21865
|
+
function sanitizeIdSegment(id) {
|
|
21866
|
+
return id.replace(/[^A-Za-z0-9._-]/g, "_") || "_";
|
|
21867
|
+
}
|
|
21868
|
+
|
|
21869
|
+
class CredentialBroker {
|
|
21870
|
+
registrations = new Map;
|
|
21871
|
+
voucherPrefix;
|
|
21872
|
+
voucherDir;
|
|
21873
|
+
upstreamBaseUrl;
|
|
21874
|
+
clientLabel;
|
|
21875
|
+
headerNames;
|
|
21876
|
+
constructor(config2) {
|
|
21877
|
+
if (!config2.upstreamBaseUrl)
|
|
21878
|
+
throw new Error("CredentialBroker: upstreamBaseUrl is required");
|
|
21879
|
+
this.upstreamBaseUrl = config2.upstreamBaseUrl.replace(/\/+$/, "");
|
|
21880
|
+
this.voucherPrefix = config2.voucherPrefix ?? "vch_";
|
|
21881
|
+
this.clientLabel = config2.clientLabel ?? "cli";
|
|
21882
|
+
this.headerNames = config2.headerNames ?? DEFAULT_HEADER_NAMES;
|
|
21883
|
+
this.voucherDir = config2.voucherDir ?? path7.join(os2.tmpdir(), "agent-vouchers");
|
|
21884
|
+
}
|
|
21885
|
+
mint(agentId, launchId, capabilities, runnerKey) {
|
|
21886
|
+
if (!runnerKey)
|
|
21887
|
+
throw new Error("CredentialBroker.mint: runnerKey is required (per-agent tier-2 credential)");
|
|
21888
|
+
const voucher = randomVoucher(this.voucherPrefix);
|
|
21889
|
+
const dir = path7.join(this.voucherDir, sanitizeIdSegment(agentId));
|
|
21890
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
21891
|
+
const voucherFile = path7.join(dir, `${sanitizeIdSegment(launchId)}.token`);
|
|
21892
|
+
fs5.writeFileSync(voucherFile, voucher, { mode: 384 });
|
|
21893
|
+
this.registrations.set(voucher, {
|
|
21894
|
+
agentId,
|
|
21895
|
+
launchId,
|
|
21896
|
+
capabilities: new Set(capabilities),
|
|
21897
|
+
voucherFile,
|
|
21898
|
+
runnerKey
|
|
21899
|
+
});
|
|
21900
|
+
return { voucher, agentId, launchId, capabilities: [...capabilities], voucherFile };
|
|
21901
|
+
}
|
|
21902
|
+
revoke(voucher) {
|
|
21903
|
+
const reg = this.registrations.get(voucher);
|
|
21904
|
+
if (!reg)
|
|
21905
|
+
return false;
|
|
21906
|
+
this.registrations.delete(voucher);
|
|
21907
|
+
try {
|
|
21908
|
+
fs5.rmSync(reg.voucherFile, { force: true });
|
|
21909
|
+
} catch {}
|
|
21910
|
+
return true;
|
|
21911
|
+
}
|
|
21912
|
+
revokeAgent(agentId) {
|
|
21913
|
+
let n = 0;
|
|
21914
|
+
for (const [voucher, reg] of this.registrations) {
|
|
21915
|
+
if (reg.agentId === agentId && this.revoke(voucher))
|
|
21916
|
+
n++;
|
|
21917
|
+
}
|
|
21918
|
+
return n;
|
|
21919
|
+
}
|
|
21920
|
+
get size() {
|
|
21921
|
+
return this.registrations.size;
|
|
21922
|
+
}
|
|
21923
|
+
check(authHeader, requiredCapability) {
|
|
21924
|
+
const voucher = parseBearer(authHeader);
|
|
21925
|
+
if (!voucher) {
|
|
21926
|
+
return { ok: false, status: 401, code: "missing_voucher", error: "missing bearer voucher" };
|
|
21927
|
+
}
|
|
21928
|
+
const reg = this.registrations.get(voucher);
|
|
21929
|
+
if (!reg) {
|
|
21930
|
+
return { ok: false, status: 401, code: "invalid_proxy_token", error: "invalid local agent proxy token" };
|
|
21931
|
+
}
|
|
21932
|
+
if (requiredCapability && !reg.capabilities.has(requiredCapability)) {
|
|
21933
|
+
return {
|
|
21934
|
+
ok: false,
|
|
21935
|
+
status: 403,
|
|
21936
|
+
code: "capability_denied",
|
|
21937
|
+
error: `capability '${requiredCapability}' not granted to this voucher`
|
|
21938
|
+
};
|
|
21939
|
+
}
|
|
21940
|
+
return { ok: true, reg };
|
|
21941
|
+
}
|
|
21942
|
+
}
|
|
21943
|
+
function parseBearer(authHeader) {
|
|
21944
|
+
if (!authHeader)
|
|
21945
|
+
return null;
|
|
21946
|
+
const m = /^Bearer\s+(.+)$/i.exec(authHeader.trim());
|
|
21947
|
+
return m ? m[1].trim() : null;
|
|
21948
|
+
}
|
|
21949
|
+
var DEFAULT_CAPABILITY_RESOLVER = (method, pathname) => {
|
|
21950
|
+
if (pathname.includes("/attachment"))
|
|
21951
|
+
return "attach";
|
|
21952
|
+
if (/\/friends(\/|$|\?)/.test(pathname))
|
|
21953
|
+
return "friend";
|
|
21954
|
+
const isMessagesDoor = /\/channels\/[^/]+\/messages(\/|$|\?)/.test(pathname);
|
|
21955
|
+
if (isMessagesDoor)
|
|
21956
|
+
return method === "GET" ? "read" : "send";
|
|
21957
|
+
if (/\/messages\/[^/]+\/reactions\//.test(pathname))
|
|
21958
|
+
return "send";
|
|
21959
|
+
if ((method === "PUT" || method === "DELETE") && /\/messages\/[^/]+\/marks(\/|$|\?)/.test(pathname))
|
|
21960
|
+
return "send";
|
|
21961
|
+
if (method === "GET" && /\/users\/me\/marks(\/|$|\?)/.test(pathname))
|
|
21962
|
+
return "read";
|
|
21963
|
+
if (method === "GET" && /\/messages\/[^/]+(\?|$)/.test(pathname))
|
|
21964
|
+
return "read";
|
|
21965
|
+
if (pathname.includes("/history") || pathname.includes("/search") || pathname.includes("/inbox"))
|
|
21966
|
+
return "read";
|
|
21967
|
+
if (pathname.includes("/server") || pathname.includes("/channel"))
|
|
21968
|
+
return "server";
|
|
21969
|
+
return;
|
|
21970
|
+
};
|
|
21971
|
+
var DEFAULT_UPSTREAM_TIMEOUT_MS = 20000;
|
|
21972
|
+
function writeJson(res, status, body) {
|
|
21973
|
+
res.writeHead(status, { "content-type": "application/json" });
|
|
21974
|
+
res.end(JSON.stringify(body));
|
|
21975
|
+
}
|
|
21976
|
+
function isCanonicalChannelScope(channel2) {
|
|
21977
|
+
try {
|
|
21978
|
+
const parsed = parseRef(channel2);
|
|
21979
|
+
if (!parsed.channel || parsed.seq !== undefined)
|
|
21980
|
+
return false;
|
|
21981
|
+
if (parsed.threadRootSeq !== undefined && (!Number.isSafeInteger(parsed.threadRootSeq) || parsed.threadRootSeq < 1)) {
|
|
21982
|
+
return false;
|
|
21983
|
+
}
|
|
21984
|
+
const handle = parseNameAndTag(parsed.server === ".dm" ? parsed.channel : parsed.server);
|
|
21985
|
+
if (!handle || `${handle.name}#${handle.discriminator}` !== (parsed.server === ".dm" ? parsed.channel : parsed.server)) {
|
|
21986
|
+
return false;
|
|
21987
|
+
}
|
|
21988
|
+
return formatRef(parsed) === channel2;
|
|
21989
|
+
} catch {
|
|
21990
|
+
return false;
|
|
21991
|
+
}
|
|
21992
|
+
}
|
|
21993
|
+
function parseLocalMessageReminderBody(body, agentId) {
|
|
21994
|
+
let value;
|
|
21995
|
+
try {
|
|
21996
|
+
value = JSON.parse(body.toString("utf8"));
|
|
21997
|
+
} catch {
|
|
21998
|
+
return null;
|
|
21999
|
+
}
|
|
22000
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
22001
|
+
return null;
|
|
22002
|
+
const record2 = value;
|
|
22003
|
+
if (Object.keys(record2).sort().join(",") !== "channel,remindAfterMs,sentSeq")
|
|
22004
|
+
return null;
|
|
22005
|
+
if (typeof record2.channel !== "string" || !isCanonicalChannelScope(record2.channel))
|
|
22006
|
+
return null;
|
|
22007
|
+
if (!Number.isSafeInteger(record2.sentSeq) || record2.sentSeq < 1)
|
|
22008
|
+
return null;
|
|
22009
|
+
if (!Number.isSafeInteger(record2.remindAfterMs) || record2.remindAfterMs < LOCAL_MESSAGE_REMINDER_MIN_MS || record2.remindAfterMs > LOCAL_MESSAGE_REMINDER_MAX_MS)
|
|
22010
|
+
return null;
|
|
22011
|
+
return {
|
|
22012
|
+
agentId,
|
|
22013
|
+
channel: record2.channel,
|
|
22014
|
+
sentSeq: record2.sentSeq,
|
|
22015
|
+
remindAfterMs: record2.remindAfterMs
|
|
22016
|
+
};
|
|
22017
|
+
}
|
|
22018
|
+
async function handleLocalMessageReminder(req, res, agentId, onArm) {
|
|
22019
|
+
const contentType = req.headers["content-type"] ?? "";
|
|
22020
|
+
if (!contentType.toLowerCase().startsWith("application/json")) {
|
|
22021
|
+
writeJson(res, 415, { error: "content-type must be application/json", code: "unsupported_media_type" });
|
|
22022
|
+
req.resume();
|
|
22023
|
+
return;
|
|
22024
|
+
}
|
|
22025
|
+
const declaredLength = Number(req.headers["content-length"] ?? 0);
|
|
22026
|
+
if (!Number.isFinite(declaredLength) || declaredLength > LOCAL_MESSAGE_REMINDER_BODY_MAX_BYTES) {
|
|
22027
|
+
writeJson(res, 413, { error: "request body too large", code: "body_too_large" });
|
|
22028
|
+
req.resume();
|
|
22029
|
+
return;
|
|
22030
|
+
}
|
|
22031
|
+
const chunks = [];
|
|
22032
|
+
let bytes = 0;
|
|
22033
|
+
let tooLarge = false;
|
|
22034
|
+
await new Promise((resolve3) => {
|
|
22035
|
+
req.on("data", (chunk) => {
|
|
22036
|
+
bytes += chunk.byteLength;
|
|
22037
|
+
if (bytes > LOCAL_MESSAGE_REMINDER_BODY_MAX_BYTES) {
|
|
22038
|
+
tooLarge = true;
|
|
22039
|
+
} else {
|
|
22040
|
+
chunks.push(chunk);
|
|
22041
|
+
}
|
|
22042
|
+
});
|
|
22043
|
+
req.on("end", resolve3);
|
|
22044
|
+
req.on("error", resolve3);
|
|
22045
|
+
});
|
|
22046
|
+
if (tooLarge) {
|
|
22047
|
+
writeJson(res, 413, { error: "request body too large", code: "body_too_large" });
|
|
22048
|
+
return;
|
|
22049
|
+
}
|
|
22050
|
+
const input = parseLocalMessageReminderBody(Buffer.concat(chunks), agentId);
|
|
22051
|
+
if (!input) {
|
|
22052
|
+
writeJson(res, 400, { error: "invalid local message reminder request", code: "invalid_request" });
|
|
22053
|
+
return;
|
|
22054
|
+
}
|
|
22055
|
+
if (!onArm) {
|
|
22056
|
+
writeJson(res, 503, { error: "local message reminder unavailable", code: "reminder_unavailable" });
|
|
22057
|
+
return;
|
|
22058
|
+
}
|
|
22059
|
+
try {
|
|
22060
|
+
writeJson(res, 200, await onArm(input));
|
|
22061
|
+
} catch {
|
|
22062
|
+
writeJson(res, 500, { error: "local message reminder failed", code: "reminder_failed" });
|
|
22063
|
+
}
|
|
22064
|
+
}
|
|
22065
|
+
async function startCredentialProxy(broker, options = {}) {
|
|
22066
|
+
const host = options.host ?? "127.0.0.1";
|
|
22067
|
+
const resolveCap = options.capabilityResolver ?? DEFAULT_CAPABILITY_RESOLVER;
|
|
22068
|
+
const upstream = new URL2(broker.upstreamBaseUrl);
|
|
22069
|
+
const upstreamClient = upstream.protocol === "https:" ? https : http;
|
|
22070
|
+
const onPull = options.onInboxPullResponse;
|
|
22071
|
+
const onProxyRequest = options.onProxyRequest;
|
|
22072
|
+
const server = http.createServer((req, res) => {
|
|
22073
|
+
const pathname = new URL2(req.url ?? "/", "http://placeholder").pathname;
|
|
22074
|
+
if (pathname.startsWith("/__alook/local/")) {
|
|
22075
|
+
if (req.url !== LOCAL_MESSAGE_REMINDER_PATH) {
|
|
22076
|
+
writeJson(res, 404, { error: "unknown local route", code: "not_found" });
|
|
22077
|
+
req.resume();
|
|
22078
|
+
return;
|
|
22079
|
+
}
|
|
22080
|
+
if (req.method !== "PUT") {
|
|
22081
|
+
writeJson(res, 405, { error: "method not allowed", code: "method_not_allowed" });
|
|
22082
|
+
req.resume();
|
|
22083
|
+
return;
|
|
22084
|
+
}
|
|
22085
|
+
const localVerdict = broker.check(req.headers["authorization"], "send");
|
|
22086
|
+
if (!localVerdict.ok) {
|
|
22087
|
+
writeJson(res, localVerdict.status, { error: localVerdict.error, code: localVerdict.code });
|
|
22088
|
+
req.resume();
|
|
22089
|
+
return;
|
|
22090
|
+
}
|
|
22091
|
+
handleLocalMessageReminder(req, res, localVerdict.reg.agentId, options.onMessageReminderArm);
|
|
22092
|
+
return;
|
|
22093
|
+
}
|
|
22094
|
+
const requiredCap = resolveCap(req.method ?? "GET", pathname);
|
|
22095
|
+
const verdict = broker.check(req.headers["authorization"], requiredCap);
|
|
22096
|
+
if (!verdict.ok) {
|
|
22097
|
+
res.writeHead(verdict.status, { "content-type": "application/json" });
|
|
22098
|
+
res.end(JSON.stringify({ error: verdict.error, code: verdict.code }));
|
|
22099
|
+
req.resume();
|
|
22100
|
+
return;
|
|
22101
|
+
}
|
|
22102
|
+
const canonicalCommunityDoor = /^\/api\/community\/(channels|messages|servers|invites|friends|users|bots)(\/|$)/.test(pathname);
|
|
22103
|
+
if (pathname === "/api" || pathname.startsWith("/api/") && !canonicalCommunityDoor) {
|
|
22104
|
+
res.writeHead(404, { "content-type": "application/json" });
|
|
22105
|
+
res.end(JSON.stringify({ error: "unknown API route", code: "not_found" }));
|
|
22106
|
+
req.resume();
|
|
22107
|
+
return;
|
|
22108
|
+
}
|
|
22109
|
+
const reg = verdict.reg;
|
|
22110
|
+
const isInboxPull = onPull && pathname === "/api/community/users/me/inbox/pull";
|
|
22111
|
+
if (onProxyRequest) {
|
|
22112
|
+
try {
|
|
22113
|
+
onProxyRequest(reg.agentId, req.method ?? "GET", pathname);
|
|
22114
|
+
} catch {}
|
|
22115
|
+
}
|
|
22116
|
+
const outHeaders = { ...req.headers };
|
|
22117
|
+
delete outHeaders["authorization"];
|
|
22118
|
+
delete outHeaders["host"];
|
|
22119
|
+
outHeaders["authorization"] = `Bearer ${reg.runnerKey}`;
|
|
22120
|
+
outHeaders[broker.headerNames.agentId.toLowerCase()] = reg.agentId;
|
|
22121
|
+
outHeaders[broker.headerNames.client.toLowerCase()] = broker.clientLabel;
|
|
22122
|
+
outHeaders[broker.headerNames.capabilities.toLowerCase()] = [...reg.capabilities].join(",");
|
|
22123
|
+
let responded = false;
|
|
22124
|
+
let upstreamRes;
|
|
22125
|
+
const upstreamReq = upstreamClient.request({
|
|
22126
|
+
protocol: upstream.protocol,
|
|
22127
|
+
hostname: upstream.hostname,
|
|
22128
|
+
port: upstream.port || (upstream.protocol === "https:" ? 443 : 80),
|
|
22129
|
+
method: req.method,
|
|
22130
|
+
path: joinPath(upstream.pathname, req.url ?? "/"),
|
|
22131
|
+
headers: outHeaders
|
|
22132
|
+
}, (res_) => {
|
|
22133
|
+
responded = true;
|
|
22134
|
+
upstreamRes = res_;
|
|
22135
|
+
const destroyResIfIncomplete = () => {
|
|
22136
|
+
if (!res_.complete)
|
|
22137
|
+
res.destroy();
|
|
22138
|
+
};
|
|
22139
|
+
res_.on("error", destroyResIfIncomplete);
|
|
22140
|
+
res_.on("close", destroyResIfIncomplete);
|
|
22141
|
+
if (isInboxPull && res_.statusCode && res_.statusCode < 300) {
|
|
22142
|
+
const chunks = [];
|
|
22143
|
+
res_.on("data", (chunk) => chunks.push(chunk));
|
|
22144
|
+
res_.on("end", () => {
|
|
22145
|
+
const body = Buffer.concat(chunks);
|
|
22146
|
+
res.writeHead(res_.statusCode, res_.headers);
|
|
22147
|
+
res.end(body);
|
|
22148
|
+
try {
|
|
22149
|
+
const parsed = JSON.parse(body.toString());
|
|
22150
|
+
if (parsed.messages)
|
|
22151
|
+
onPull(reg.agentId, parsed.messages);
|
|
22152
|
+
} catch {}
|
|
22153
|
+
});
|
|
22154
|
+
} else {
|
|
22155
|
+
res.writeHead(res_.statusCode ?? 502, res_.headers);
|
|
22156
|
+
res_.pipe(res);
|
|
22157
|
+
}
|
|
22158
|
+
});
|
|
22159
|
+
upstreamReq.on("error", (err) => {
|
|
22160
|
+
if (responded)
|
|
22161
|
+
return;
|
|
22162
|
+
responded = true;
|
|
22163
|
+
res.writeHead(502, { "content-type": "application/json" });
|
|
22164
|
+
res.end(JSON.stringify({ error: `upstream error: ${err.message}`, code: "upstream_error" }));
|
|
22165
|
+
});
|
|
22166
|
+
const upstreamTimeoutMs = options.upstreamTimeoutMs ?? DEFAULT_UPSTREAM_TIMEOUT_MS;
|
|
22167
|
+
upstreamReq.setTimeout(upstreamTimeoutMs, () => {
|
|
22168
|
+
upstreamReq.destroy();
|
|
22169
|
+
upstreamRes?.destroy();
|
|
22170
|
+
if (responded) {
|
|
22171
|
+
res.destroy();
|
|
22172
|
+
return;
|
|
22173
|
+
}
|
|
22174
|
+
responded = true;
|
|
22175
|
+
res.writeHead(504, { "content-type": "application/json" });
|
|
22176
|
+
res.end(JSON.stringify({ error: `upstream request timed out after ${upstreamTimeoutMs}ms`, code: "upstream_timeout" }));
|
|
22177
|
+
});
|
|
22178
|
+
res.on("close", () => {
|
|
22179
|
+
upstreamReq.destroy();
|
|
22180
|
+
upstreamRes?.destroy();
|
|
22181
|
+
});
|
|
22182
|
+
req.pipe(upstreamReq);
|
|
22183
|
+
});
|
|
22184
|
+
await new Promise((resolve3, reject) => {
|
|
22185
|
+
server.once("error", reject);
|
|
22186
|
+
server.listen(options.port ?? 0, host, () => {
|
|
22187
|
+
server.removeListener("error", reject);
|
|
22188
|
+
resolve3();
|
|
22189
|
+
});
|
|
22190
|
+
});
|
|
22191
|
+
const addr = server.address();
|
|
22192
|
+
const port = typeof addr === "object" && addr ? addr.port : options.port ?? 0;
|
|
22193
|
+
const url2 = `http://${host}:${port}`;
|
|
22194
|
+
return {
|
|
22195
|
+
url: url2,
|
|
22196
|
+
port,
|
|
22197
|
+
close: () => new Promise((resolve3) => {
|
|
22198
|
+
server.close(() => resolve3());
|
|
22199
|
+
})
|
|
22200
|
+
};
|
|
22201
|
+
}
|
|
22202
|
+
function joinPath(basePath, reqUrl) {
|
|
22203
|
+
const base = basePath.replace(/\/+$/, "");
|
|
22204
|
+
const reqPath = reqUrl.startsWith("/") ? reqUrl : `/${reqUrl}`;
|
|
22205
|
+
return base + reqPath || "/";
|
|
22206
|
+
}
|
|
22207
|
+
// src/daemon/createDaemon.ts
|
|
22208
|
+
import { homedir as homedir3 } from "os";
|
|
22209
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
22210
|
+
|
|
22211
|
+
// src/util/rotatingFileSink.ts
|
|
22212
|
+
import {
|
|
22213
|
+
appendFileSync,
|
|
22214
|
+
chmodSync,
|
|
22215
|
+
closeSync,
|
|
22216
|
+
constants,
|
|
22217
|
+
existsSync as existsSync5,
|
|
22218
|
+
fstatSync,
|
|
22219
|
+
lstatSync as lstatSync2,
|
|
22220
|
+
openSync,
|
|
22221
|
+
renameSync,
|
|
22222
|
+
statSync,
|
|
22223
|
+
unlinkSync as unlinkSync3
|
|
22224
|
+
} from "node:fs";
|
|
22225
|
+
function createRotatingFileSink(path8, maxBytes, opts = {}) {
|
|
22226
|
+
const report = (operation, error51) => {
|
|
22227
|
+
try {
|
|
22228
|
+
opts.onError?.({ operation, error: error51 });
|
|
22229
|
+
} catch {}
|
|
22230
|
+
};
|
|
22231
|
+
const secureGeneration = (filePath) => {
|
|
22232
|
+
if (!existsSync5(filePath))
|
|
22233
|
+
return true;
|
|
22234
|
+
try {
|
|
22235
|
+
const stat = lstatSync2(filePath);
|
|
22236
|
+
if (!stat.isFile()) {
|
|
22237
|
+
if (opts.mode === undefined && !opts.hardMaxBytes)
|
|
22238
|
+
return true;
|
|
22239
|
+
report("unsafe_generation", new Error("log generation is not a regular file"));
|
|
22240
|
+
return false;
|
|
22241
|
+
}
|
|
22242
|
+
if (opts.mode !== undefined)
|
|
22243
|
+
chmodSync(filePath, opts.mode);
|
|
22244
|
+
if (opts.hardMaxBytes && maxBytes > 0 && stat.size > maxBytes) {
|
|
22245
|
+
try {
|
|
22246
|
+
unlinkSync3(filePath);
|
|
22247
|
+
} catch (error51) {
|
|
22248
|
+
report("oversize_generation", error51);
|
|
22249
|
+
return false;
|
|
22250
|
+
}
|
|
22251
|
+
report("oversize_generation", new Error(`removed generation larger than ${maxBytes} bytes`));
|
|
22252
|
+
}
|
|
22253
|
+
return true;
|
|
22254
|
+
} catch (error51) {
|
|
22255
|
+
report("chmod", error51);
|
|
22256
|
+
return false;
|
|
22257
|
+
}
|
|
22258
|
+
};
|
|
22259
|
+
const rotate = () => {
|
|
22260
|
+
try {
|
|
22261
|
+
renameSync(path8, `${path8}.1`);
|
|
22262
|
+
if (opts.mode !== undefined)
|
|
22263
|
+
chmodSync(`${path8}.1`, opts.mode);
|
|
22264
|
+
return true;
|
|
22265
|
+
} catch (error51) {
|
|
22266
|
+
report("rotate", error51);
|
|
22267
|
+
return false;
|
|
22268
|
+
}
|
|
22269
|
+
};
|
|
22270
|
+
const currentSize = () => {
|
|
22271
|
+
try {
|
|
22272
|
+
return existsSync5(path8) ? statSync(path8).size : 0;
|
|
22273
|
+
} catch (error51) {
|
|
22274
|
+
report("stat", error51);
|
|
22275
|
+
return null;
|
|
22276
|
+
}
|
|
22277
|
+
};
|
|
22278
|
+
const sink = {
|
|
22279
|
+
secure() {
|
|
22280
|
+
return secureGeneration(`${path8}.1`) && secureGeneration(path8);
|
|
22281
|
+
},
|
|
22282
|
+
write(line) {
|
|
22283
|
+
try {
|
|
22284
|
+
if (!sink.secure())
|
|
22285
|
+
return;
|
|
22286
|
+
const serialized = line + `
|
|
22287
|
+
`;
|
|
22288
|
+
const serializedBytes = Buffer.byteLength(serialized, "utf8");
|
|
22289
|
+
if (opts.hardMaxBytes && maxBytes > 0 && serializedBytes > maxBytes) {
|
|
22290
|
+
report("oversize", new Error(`record exceeds ${maxBytes} bytes`));
|
|
22291
|
+
return;
|
|
22292
|
+
}
|
|
22293
|
+
const measuredBytes = currentSize();
|
|
22294
|
+
if (measuredBytes === null && opts.hardMaxBytes)
|
|
22295
|
+
return;
|
|
22296
|
+
const currentBytes = measuredBytes ?? 0;
|
|
22297
|
+
const shouldRotate = maxBytes > 0 && (opts.hardMaxBytes ? currentBytes > 0 && currentBytes + serializedBytes > maxBytes : currentBytes >= maxBytes);
|
|
22298
|
+
if (shouldRotate && !rotate() && opts.hardMaxBytes)
|
|
22299
|
+
return;
|
|
22300
|
+
appendFileSync(path8, serialized, opts.mode === undefined ? undefined : { mode: opts.mode });
|
|
22301
|
+
} catch (error51) {
|
|
22302
|
+
report("append", error51);
|
|
22303
|
+
}
|
|
22304
|
+
},
|
|
22305
|
+
openSnapshot() {
|
|
22306
|
+
const files = [];
|
|
22307
|
+
try {
|
|
22308
|
+
for (const candidate of [`${path8}.1`, path8]) {
|
|
22309
|
+
if (!existsSync5(candidate))
|
|
22310
|
+
continue;
|
|
22311
|
+
if (!secureGeneration(candidate))
|
|
22312
|
+
continue;
|
|
22313
|
+
const noFollow = "O_NOFOLLOW" in constants ? constants.O_NOFOLLOW : 0;
|
|
22314
|
+
const fd = openSync(candidate, constants.O_RDONLY | noFollow);
|
|
22315
|
+
try {
|
|
22316
|
+
const stat = fstatSync(fd);
|
|
22317
|
+
if (!stat.isFile())
|
|
22318
|
+
throw new Error("snapshot source is not a regular file");
|
|
22319
|
+
files.push({ path: candidate, fd, size: stat.size });
|
|
22320
|
+
} catch (error51) {
|
|
22321
|
+
closeSync(fd);
|
|
22322
|
+
throw error51;
|
|
22323
|
+
}
|
|
22324
|
+
}
|
|
22325
|
+
} catch (error51) {
|
|
22326
|
+
for (const file2 of files) {
|
|
22327
|
+
try {
|
|
22328
|
+
closeSync(file2.fd);
|
|
22329
|
+
} catch {}
|
|
22330
|
+
}
|
|
22331
|
+
report("snapshot", error51);
|
|
22332
|
+
return { files: [], close: () => {} };
|
|
22333
|
+
}
|
|
22334
|
+
let closed = false;
|
|
22335
|
+
return {
|
|
22336
|
+
files,
|
|
22337
|
+
close() {
|
|
22338
|
+
if (closed)
|
|
22339
|
+
return;
|
|
22340
|
+
closed = true;
|
|
22341
|
+
for (const file2 of files) {
|
|
22342
|
+
try {
|
|
22343
|
+
closeSync(file2.fd);
|
|
22344
|
+
} catch {}
|
|
22345
|
+
}
|
|
22346
|
+
}
|
|
22347
|
+
};
|
|
22348
|
+
}
|
|
22349
|
+
};
|
|
22350
|
+
return sink;
|
|
22351
|
+
}
|
|
22352
|
+
|
|
22353
|
+
// src/util/traceSampler.ts
|
|
22354
|
+
var SAMPLEABLE_EVENTS = new Set(["tick", "progress", "runtime_signal"]);
|
|
22355
|
+
var SACRED_TURN_SPAN_EVENTS = new Set(["turn_begin", "turn_end", "turn_abort"]);
|
|
22356
|
+
var DEFAULT_TRACE_SAMPLE_MS = 30000;
|
|
22357
|
+
var DEFAULT_TRACE_FILE_MAX_BYTES = 32 * 1024 * 1024;
|
|
22358
|
+
function newAgentState() {
|
|
22359
|
+
return {
|
|
22360
|
+
lastTickKey: null,
|
|
22361
|
+
lastTickEmitAt: Number.NEGATIVE_INFINITY,
|
|
22362
|
+
pendingTick: null,
|
|
22363
|
+
lastProgressEmitAt: Number.NEGATIVE_INFINITY,
|
|
22364
|
+
lastSignalPhase: null,
|
|
22365
|
+
lastSignalEmitAt: Number.NEGATIVE_INFINITY
|
|
22366
|
+
};
|
|
22367
|
+
}
|
|
22368
|
+
function tickKey(rec) {
|
|
22369
|
+
return [
|
|
22370
|
+
rec.status,
|
|
22371
|
+
rec.turnActive,
|
|
22372
|
+
rec.inbox,
|
|
22373
|
+
rec.resetting,
|
|
22374
|
+
rec.stoppingSince == null ? "s0" : "s1"
|
|
22375
|
+
].join("|");
|
|
22376
|
+
}
|
|
22377
|
+
function hasEffects(rec) {
|
|
22378
|
+
const e = rec.effects;
|
|
22379
|
+
return Array.isArray(e) && e.length > 0;
|
|
22380
|
+
}
|
|
22381
|
+
function nowOf(rec) {
|
|
22382
|
+
return typeof rec.nowMs === "number" ? rec.nowMs : 0;
|
|
22383
|
+
}
|
|
22384
|
+
function createTraceSampler(emit, throttleMs = DEFAULT_TRACE_SAMPLE_MS) {
|
|
22385
|
+
const perAgent = new Map;
|
|
22386
|
+
const stateFor = (agentId) => {
|
|
22387
|
+
let s = perAgent.get(agentId);
|
|
22388
|
+
if (!s) {
|
|
22389
|
+
s = newAgentState();
|
|
22390
|
+
perAgent.set(agentId, s);
|
|
22391
|
+
}
|
|
22392
|
+
return s;
|
|
22393
|
+
};
|
|
22394
|
+
const flushPending = (s) => {
|
|
22395
|
+
if (s.pendingTick) {
|
|
22396
|
+
const p = s.pendingTick;
|
|
22397
|
+
s.pendingTick = null;
|
|
22398
|
+
s.lastTickEmitAt = nowOf(p);
|
|
22399
|
+
emit(p);
|
|
22400
|
+
}
|
|
22401
|
+
};
|
|
22402
|
+
return {
|
|
22403
|
+
offer(rec) {
|
|
22404
|
+
const agentId = typeof rec.agentId === "string" ? rec.agentId : null;
|
|
22405
|
+
if (!agentId) {
|
|
22406
|
+
emit(rec);
|
|
22407
|
+
return;
|
|
22408
|
+
}
|
|
22409
|
+
const s = stateFor(agentId);
|
|
22410
|
+
const event = rec.event;
|
|
22411
|
+
if (rec.recordKind === "turn_span" && SACRED_TURN_SPAN_EVENTS.has(String(event))) {
|
|
22412
|
+
flushPending(s);
|
|
22413
|
+
emit(rec);
|
|
22414
|
+
return;
|
|
22415
|
+
}
|
|
22416
|
+
if (!SAMPLEABLE_EVENTS.has(event) || hasEffects(rec)) {
|
|
22417
|
+
flushPending(s);
|
|
22418
|
+
if (event === "tick") {
|
|
22419
|
+
s.lastTickKey = tickKey(rec);
|
|
22420
|
+
s.lastTickEmitAt = nowOf(rec);
|
|
22421
|
+
}
|
|
22422
|
+
emit(rec);
|
|
22423
|
+
return;
|
|
22424
|
+
}
|
|
22425
|
+
if (event === "tick") {
|
|
22426
|
+
const key = tickKey(rec);
|
|
22427
|
+
const now = nowOf(rec);
|
|
22428
|
+
if (key !== s.lastTickKey) {
|
|
22429
|
+
flushPending(s);
|
|
22430
|
+
s.lastTickKey = key;
|
|
22431
|
+
s.lastTickEmitAt = now;
|
|
22432
|
+
emit(rec);
|
|
22433
|
+
return;
|
|
22434
|
+
}
|
|
22435
|
+
if (now - s.lastTickEmitAt >= throttleMs) {
|
|
22436
|
+
s.pendingTick = null;
|
|
22437
|
+
s.lastTickEmitAt = now;
|
|
22438
|
+
emit(rec);
|
|
22439
|
+
} else {
|
|
22440
|
+
s.pendingTick = rec;
|
|
22441
|
+
}
|
|
22442
|
+
return;
|
|
22443
|
+
}
|
|
22444
|
+
if (event === "progress") {
|
|
22445
|
+
const now = nowOf(rec);
|
|
22446
|
+
if (now - s.lastProgressEmitAt >= throttleMs) {
|
|
22447
|
+
s.lastProgressEmitAt = now;
|
|
22448
|
+
emit(rec);
|
|
22449
|
+
}
|
|
22450
|
+
return;
|
|
22451
|
+
}
|
|
22452
|
+
if (event === "runtime_signal") {
|
|
22453
|
+
const phase = typeof rec.apmPhase === "string" ? rec.apmPhase : "";
|
|
22454
|
+
const now = nowOf(rec);
|
|
22455
|
+
if (phase !== s.lastSignalPhase || now - s.lastSignalEmitAt >= throttleMs) {
|
|
22456
|
+
s.lastSignalPhase = phase;
|
|
22457
|
+
s.lastSignalEmitAt = now;
|
|
22458
|
+
emit(rec);
|
|
22459
|
+
}
|
|
22460
|
+
return;
|
|
22461
|
+
}
|
|
22462
|
+
emit(rec);
|
|
22463
|
+
}
|
|
22464
|
+
};
|
|
22465
|
+
}
|
|
22466
|
+
|
|
22467
|
+
// src/util/statusFile.ts
|
|
22468
|
+
import { writeFileSync as writeFileSync4, renameSync as renameSync2 } from "node:fs";
|
|
22469
|
+
function writeStatusFile(path8, snapshot) {
|
|
22470
|
+
try {
|
|
22471
|
+
const tmp = `${path8}.tmp`;
|
|
22472
|
+
writeFileSync4(tmp, JSON.stringify(snapshot));
|
|
22473
|
+
renameSync2(tmp, path8);
|
|
22474
|
+
} catch {}
|
|
22475
|
+
}
|
|
22476
|
+
|
|
22477
|
+
// src/server/wsControlChannel.ts
|
|
22478
|
+
var WS_CONTROL_COMMAND_CONSUMED = Symbol("ws-control-command-consumed");
|
|
22479
|
+
var DEFAULT_PING_INTERVAL_MS = 15000;
|
|
22480
|
+
var DEFAULT_PONG_TIMEOUT_MS = 30000;
|
|
22481
|
+
var DEFAULT_RECONNECT_BASE_MS = 500;
|
|
22482
|
+
var DEFAULT_RECONNECT_MAX_MS = 30000;
|
|
22483
|
+
function describeErr(err) {
|
|
22484
|
+
return err instanceof Error ? err.message : String(err);
|
|
22485
|
+
}
|
|
22486
|
+
|
|
22269
22487
|
class WsControlChannel {
|
|
22270
22488
|
opts;
|
|
22271
22489
|
statusValue = "idle";
|
|
@@ -23273,6 +23491,108 @@ function createDiagnosticsCommandListener(options) {
|
|
|
23273
23491
|
};
|
|
23274
23492
|
}
|
|
23275
23493
|
|
|
23494
|
+
// src/daemon/selfUpdateCommand.ts
|
|
23495
|
+
function createSelfUpdateCommandListener(handleSelfUpdate) {
|
|
23496
|
+
return (command) => {
|
|
23497
|
+
if (command.type !== "machine:update")
|
|
23498
|
+
return;
|
|
23499
|
+
if (handleSelfUpdate) {
|
|
23500
|
+
try {
|
|
23501
|
+
Promise.resolve(handleSelfUpdate()).catch(() => {});
|
|
23502
|
+
} catch {}
|
|
23503
|
+
}
|
|
23504
|
+
return WS_CONTROL_COMMAND_CONSUMED;
|
|
23505
|
+
};
|
|
23506
|
+
}
|
|
23507
|
+
|
|
23508
|
+
// src/daemon/messageReminderScheduler.ts
|
|
23509
|
+
function reminderKey(agentId, channel2) {
|
|
23510
|
+
return `${agentId}\x00${channel2}`;
|
|
23511
|
+
}
|
|
23512
|
+
function reminderPrompt(channel2, sentRef, startedAt) {
|
|
23513
|
+
return `Reminder: At ${localISOString(new Date(startedAt))}, after sending ${sentRef} in ${channel2}, you asked to be reminded if no newer message arrived. No newer message has arrived in that conversation.`;
|
|
23514
|
+
}
|
|
23515
|
+
|
|
23516
|
+
class MessageReminderScheduler {
|
|
23517
|
+
options;
|
|
23518
|
+
reminders = new Map;
|
|
23519
|
+
latestObservedSeq = new Map;
|
|
23520
|
+
now;
|
|
23521
|
+
setTimer;
|
|
23522
|
+
clearTimer;
|
|
23523
|
+
constructor(options) {
|
|
23524
|
+
this.options = options;
|
|
23525
|
+
this.now = options.now ?? Date.now;
|
|
23526
|
+
this.setTimer = options.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
23527
|
+
this.clearTimer = options.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
23528
|
+
}
|
|
23529
|
+
arm(input) {
|
|
23530
|
+
const key = reminderKey(input.agentId, input.channel);
|
|
23531
|
+
const latest = this.latestObservedSeq.get(key);
|
|
23532
|
+
if (latest !== undefined && latest > input.sentSeq) {
|
|
23533
|
+
return { armed: false, reason: "newer_message_observed" };
|
|
23534
|
+
}
|
|
23535
|
+
this.clearReminder(key);
|
|
23536
|
+
const startedAt = this.now();
|
|
23537
|
+
const dueAt = startedAt + input.remindAfterMs;
|
|
23538
|
+
const sentRef = `${input.channel}#${input.sentSeq}`;
|
|
23539
|
+
const record2 = {
|
|
23540
|
+
...input,
|
|
23541
|
+
sentRef,
|
|
23542
|
+
startedAt,
|
|
23543
|
+
dueAt,
|
|
23544
|
+
timer: undefined
|
|
23545
|
+
};
|
|
23546
|
+
record2.timer = this.setTimer(() => {
|
|
23547
|
+
if (this.reminders.get(key) !== record2)
|
|
23548
|
+
return;
|
|
23549
|
+
this.reminders.delete(key);
|
|
23550
|
+
try {
|
|
23551
|
+
const delivery = this.options.deliver(input.agentId, {
|
|
23552
|
+
text: reminderPrompt(input.channel, sentRef, startedAt)
|
|
23553
|
+
});
|
|
23554
|
+
Promise.resolve(delivery).catch(() => {});
|
|
23555
|
+
} catch {}
|
|
23556
|
+
}, input.remindAfterMs);
|
|
23557
|
+
record2.timer.unref?.();
|
|
23558
|
+
this.reminders.set(key, record2);
|
|
23559
|
+
return { armed: true, dueAt };
|
|
23560
|
+
}
|
|
23561
|
+
observe(agentId, channel2, latestSeq) {
|
|
23562
|
+
const key = reminderKey(agentId, channel2);
|
|
23563
|
+
const previous = this.latestObservedSeq.get(key);
|
|
23564
|
+
if (previous === undefined || latestSeq > previous) {
|
|
23565
|
+
this.latestObservedSeq.set(key, latestSeq);
|
|
23566
|
+
}
|
|
23567
|
+
const reminder = this.reminders.get(key);
|
|
23568
|
+
if (reminder && latestSeq > reminder.sentSeq)
|
|
23569
|
+
this.clearReminder(key);
|
|
23570
|
+
}
|
|
23571
|
+
clearAgent(agentId) {
|
|
23572
|
+
const prefix = `${agentId}\x00`;
|
|
23573
|
+
for (const key of [...this.reminders.keys()]) {
|
|
23574
|
+
if (key.startsWith(prefix))
|
|
23575
|
+
this.clearReminder(key);
|
|
23576
|
+
}
|
|
23577
|
+
for (const key of [...this.latestObservedSeq.keys()]) {
|
|
23578
|
+
if (key.startsWith(prefix))
|
|
23579
|
+
this.latestObservedSeq.delete(key);
|
|
23580
|
+
}
|
|
23581
|
+
}
|
|
23582
|
+
clearAll() {
|
|
23583
|
+
for (const key of [...this.reminders.keys()])
|
|
23584
|
+
this.clearReminder(key);
|
|
23585
|
+
this.latestObservedSeq.clear();
|
|
23586
|
+
}
|
|
23587
|
+
clearReminder(key) {
|
|
23588
|
+
const reminder = this.reminders.get(key);
|
|
23589
|
+
if (!reminder)
|
|
23590
|
+
return;
|
|
23591
|
+
this.reminders.delete(key);
|
|
23592
|
+
this.clearTimer(reminder.timer);
|
|
23593
|
+
}
|
|
23594
|
+
}
|
|
23595
|
+
|
|
23276
23596
|
// src/daemon/createDaemon.ts
|
|
23277
23597
|
var WARMUP_BACKOFF_MS = [250, 500, 1000, 2000, 4000];
|
|
23278
23598
|
var WARMUP_CEILING_MS = 30000;
|
|
@@ -23403,6 +23723,7 @@ async function createDaemon(opts) {
|
|
|
23403
23723
|
});
|
|
23404
23724
|
let channelRef = null;
|
|
23405
23725
|
let managerRef = null;
|
|
23726
|
+
let reminderSchedulerRef = null;
|
|
23406
23727
|
const emitBotAuditEvent = (agentId, event, context) => {
|
|
23407
23728
|
channelRef?.reportBotAuditEvent?.({
|
|
23408
23729
|
type: "bot_audit_event",
|
|
@@ -23416,6 +23737,7 @@ async function createDaemon(opts) {
|
|
|
23416
23737
|
const broker = new CredentialBroker({ upstreamBaseUrl: opts.serverUrl });
|
|
23417
23738
|
const proxy = await startCredentialProxy(broker, {
|
|
23418
23739
|
onInboxPullResponse: (agentId, messages) => timeline2.appendEntryForAgent(agentId, messages),
|
|
23740
|
+
onMessageReminderArm: (input) => reminderSchedulerRef?.arm(input) ?? { armed: false, reason: "reminder_scheduler_unavailable" },
|
|
23419
23741
|
onProxyRequest: (agentId, method, pathname) => {
|
|
23420
23742
|
const subcommand = deriveAuditLogSubcommand(pathname, method);
|
|
23421
23743
|
if (!subcommand)
|
|
@@ -23688,6 +24010,10 @@ async function createDaemon(opts) {
|
|
|
23688
24010
|
});
|
|
23689
24011
|
managerRef = manager;
|
|
23690
24012
|
manager.start();
|
|
24013
|
+
reminderSchedulerRef = new MessageReminderScheduler({
|
|
24014
|
+
deliver: (agentId, message2) => manager.deliver(agentId, message2),
|
|
24015
|
+
...opts.messageReminderClock
|
|
24016
|
+
});
|
|
23691
24017
|
let statusTimer = null;
|
|
23692
24018
|
if (opts.statusFilePath) {
|
|
23693
24019
|
const statusPath = opts.statusFilePath;
|
|
@@ -23733,10 +24059,26 @@ async function createDaemon(opts) {
|
|
|
23733
24059
|
},
|
|
23734
24060
|
formatUnreadNoticeText: (notice) => `You have unread messages in channel ${notice.channel}.`
|
|
23735
24061
|
});
|
|
24062
|
+
channel2.onCommand(createSelfUpdateCommandListener(opts.handleSelfUpdate));
|
|
23736
24063
|
channel2.onCommand(createDiagnosticsCommandListener({
|
|
23737
24064
|
handleDiagnosticCommand: opts.handleDiagnosticCommand,
|
|
23738
24065
|
reportDiagnosticFailure: opts.reportDiagnosticFailure
|
|
23739
24066
|
}));
|
|
24067
|
+
channel2.onCommand((cmd) => {
|
|
24068
|
+
switch (cmd.type) {
|
|
24069
|
+
case "agent:wake":
|
|
24070
|
+
reminderSchedulerRef?.observe(cmd.agentId, cmd.unreadNotice.channel, cmd.unreadNotice.latestSeq);
|
|
24071
|
+
break;
|
|
24072
|
+
case "agent:stop":
|
|
24073
|
+
reminderSchedulerRef?.clearAgent(cmd.agentId);
|
|
24074
|
+
break;
|
|
24075
|
+
case "bot:removed":
|
|
24076
|
+
reminderSchedulerRef?.clearAgent(cmd.botId);
|
|
24077
|
+
break;
|
|
24078
|
+
default:
|
|
24079
|
+
break;
|
|
24080
|
+
}
|
|
24081
|
+
});
|
|
23740
24082
|
channel2.onCommand((cmd) => {
|
|
23741
24083
|
handleBotFrame(cmd);
|
|
23742
24084
|
});
|
|
@@ -23755,6 +24097,7 @@ async function createDaemon(opts) {
|
|
|
23755
24097
|
},
|
|
23756
24098
|
proxyUrl: proxy.url,
|
|
23757
24099
|
stop: async () => {
|
|
24100
|
+
reminderSchedulerRef?.clearAll();
|
|
23758
24101
|
for (const agentId of [...typingHeartbeats.keys()]) {
|
|
23759
24102
|
emitTypingStopsAndClear(agentId);
|
|
23760
24103
|
}
|
|
@@ -23838,6 +24181,7 @@ export {
|
|
|
23838
24181
|
PiDriver,
|
|
23839
24182
|
OpenCodeDriver,
|
|
23840
24183
|
MAX_APM_GATED_STEERING_EVENTS,
|
|
24184
|
+
LOCAL_MESSAGE_REMINDER_PATH,
|
|
23841
24185
|
KimiDriver,
|
|
23842
24186
|
GeminiDriver,
|
|
23843
24187
|
DEFAULT_STOPPING_STUCK_THRESHOLD_MS,
|