switchroom 0.14.9 → 0.14.10
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/agent-scheduler/index.js +80 -77
- package/dist/auth-broker/index.js +1 -0
- package/dist/cli/notion-write-pretool.mjs +1 -0
- package/dist/cli/switchroom.js +100 -10
- package/dist/host-control/main.js +1 -0
- package/dist/vault/approvals/kernel-server.js +1 -0
- package/dist/vault/broker/server.js +1 -0
- package/package.json +3 -3
- package/telegram-plugin/dist/gateway/gateway.js +1027 -282
- package/telegram-plugin/gateway/gateway.ts +85 -0
- package/telegram-plugin/gateway/webhook-ingest-server.test.ts +125 -0
- package/telegram-plugin/gateway/webhook-ingest-server.ts +218 -0
|
@@ -23742,6 +23742,7 @@ var init_schema = __esm(() => {
|
|
|
23742
23742
|
webhook_rate_limit: exports_external.object({
|
|
23743
23743
|
rpm: exports_external.number().int().positive()
|
|
23744
23744
|
}).optional().describe("Per-source rate limit for the webhook ingest path (#714). " + "Off by default \u2014 when this key is absent the handler skips " + "rate-limit checks entirely. Opt in by setting `rpm` to an " + "integer requests-per-minute (token bucket per (agent, source); " + "burst equal to rpm). When enabled, exceeding the limit returns " + "429 with Retry-After header; first throttle event per " + "(agent, source) per 60s window is written to " + "<agent>/telegram/issues.jsonl. " + "Cascades from defaults.channels.telegram.webhook_rate_limit."),
|
|
23745
|
+
webhook_via_gateway: exports_external.boolean().optional().describe("Route verified webhook events to the agent's in-container gateway " + "over a peercred-gated UDS (<agent>/telegram/webhook.sock) instead " + "of having the host-side web receiver write the agent dir directly. " + "Required under the Docker runtime: the receiver runs as the host " + "operator UID and cannot write the per-agent-UID-owned agent dir " + "(EACCES 500) nor connect the gateway socket. When true the gateway " + "(running as the agent UID) becomes the sole writer of " + "webhook-events.jsonl + dedup/cooldown state and also fires " + "webhook_dispatch. Off by default for back-compat with host-runtime " + "installs. See docs/rfcs/webhook-via-gateway-socket.md."),
|
|
23745
23746
|
chat_id: exports_external.string().regex(/^-\d+$/, 'supergroup chat_id must be a negative integer as a string (e.g. "-1001234567890")').optional().describe("Per-agent supergroup ID \u2014 overrides fleet `telegram.forum_chat_id`. " + "When set, requires `default_topic_id`. Negative integer as string. " + "Forbidden when `dm_only: true`. See docs/rfcs/supergroup-mode.md."),
|
|
23746
23747
|
default_topic_id: exports_external.number().int().positive().optional().describe("Forum topic ID this agent's automated outbounds default to when " + "no more-specific alias resolves. Required when `chat_id` is set. " + "Telegram's General topic is `id=1` at MTProto but sends omit the " + "field \u2014 the outbound wrapper strips `message_thread_id === 1` " + "on send. Forbidden when `dm_only: true`."),
|
|
23747
23748
|
topic_aliases: exports_external.record(exports_external.string(), exports_external.number().int().positive()).optional().describe("Operator-friendly names for forum topic IDs (e.g. " + "`{ general: 1, planning: 17, cron: 23, admin: 31, alerts: 41 }`). " + "Referenced from per-cron `topic:` fields and the outbound router " + "for autonomous events (boot \u2192 alerts, hostd \u2192 admin, etc.). " + "Cascades per-key through defaults \u2192 profile \u2192 agent.")
|
|
@@ -25161,6 +25162,59 @@ function formatResetRelative(target, now = new Date) {
|
|
|
25161
25162
|
}
|
|
25162
25163
|
var OAUTH_BETA = "oauth-2025-04-20", DEFAULT_USER_AGENT = "claude-cli/1.0.0 (external, cli)", DEFAULT_PROBE_MODEL = "claude-haiku-4-5-20251001";
|
|
25163
25164
|
var init_quota_check = () => {};
|
|
25165
|
+
|
|
25166
|
+
// ../src/vault/broker/peercred-ffi.ts
|
|
25167
|
+
function getPeerCred(fd) {
|
|
25168
|
+
if (process.platform !== "linux")
|
|
25169
|
+
return null;
|
|
25170
|
+
try {
|
|
25171
|
+
const ffi = __require("bun:ffi");
|
|
25172
|
+
const { dlopen, FFIType, ptr } = ffi;
|
|
25173
|
+
const SOL_SOCKET = 1;
|
|
25174
|
+
const SO_PEERCRED = 17;
|
|
25175
|
+
const UCRED_SIZE = 12;
|
|
25176
|
+
const cache = getPeerCred;
|
|
25177
|
+
const lib = cache._lib ?? (() => {
|
|
25178
|
+
const candidates = ["libc.so.6", "libc.so"];
|
|
25179
|
+
const symbolSpec = {
|
|
25180
|
+
getsockopt: {
|
|
25181
|
+
args: [FFIType.i32, FFIType.i32, FFIType.i32, FFIType.ptr, FFIType.ptr],
|
|
25182
|
+
returns: FFIType.i32
|
|
25183
|
+
}
|
|
25184
|
+
};
|
|
25185
|
+
const errors3 = [];
|
|
25186
|
+
for (const name of candidates) {
|
|
25187
|
+
try {
|
|
25188
|
+
const opened = dlopen(name, symbolSpec);
|
|
25189
|
+
cache._lib = opened;
|
|
25190
|
+
return opened;
|
|
25191
|
+
} catch (e) {
|
|
25192
|
+
errors3.push(`${name}: ${e instanceof Error ? e.message : String(e)}`);
|
|
25193
|
+
}
|
|
25194
|
+
}
|
|
25195
|
+
process.stderr.write(`[vault-broker] peercred-ffi: dlopen failed for all libc candidates ` + `(${errors3.join("; ")}); falling back to ss-parsing.
|
|
25196
|
+
`);
|
|
25197
|
+
throw new Error("no libc candidate could be opened");
|
|
25198
|
+
})();
|
|
25199
|
+
const credBuf = new ArrayBuffer(UCRED_SIZE);
|
|
25200
|
+
const lenBuf = new Uint32Array(1);
|
|
25201
|
+
lenBuf[0] = UCRED_SIZE;
|
|
25202
|
+
const rc = lib.symbols.getsockopt(fd, SOL_SOCKET, SO_PEERCRED, ptr(credBuf), ptr(lenBuf.buffer));
|
|
25203
|
+
if (rc !== 0)
|
|
25204
|
+
return null;
|
|
25205
|
+
if (lenBuf[0] !== UCRED_SIZE)
|
|
25206
|
+
return null;
|
|
25207
|
+
const view = new DataView(credBuf);
|
|
25208
|
+
return {
|
|
25209
|
+
pid: view.getInt32(0, true),
|
|
25210
|
+
uid: view.getInt32(4, true),
|
|
25211
|
+
gid: view.getInt32(8, true)
|
|
25212
|
+
};
|
|
25213
|
+
} catch {
|
|
25214
|
+
return null;
|
|
25215
|
+
}
|
|
25216
|
+
}
|
|
25217
|
+
|
|
25164
25218
|
// ../src/vault/broker/peercred.ts
|
|
25165
25219
|
function isReservedAgentName(name) {
|
|
25166
25220
|
return RESERVED_AGENT_NAMES.has(name);
|
|
@@ -27869,19 +27923,19 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
27869
27923
|
}
|
|
27870
27924
|
|
|
27871
27925
|
// gateway/quota-cache.ts
|
|
27872
|
-
import { existsSync as
|
|
27873
|
-
import { join as
|
|
27926
|
+
import { existsSync as existsSync26, readFileSync as readFileSync24, writeFileSync as writeFileSync15, mkdirSync as mkdirSync14 } from "fs";
|
|
27927
|
+
import { join as join23, dirname as dirname8 } from "path";
|
|
27874
27928
|
function defaultCachePath() {
|
|
27875
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
27929
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join23(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
27876
27930
|
}
|
|
27877
27931
|
function readQuotaCache(opts = {}) {
|
|
27878
27932
|
const path = opts.path ?? defaultCachePath();
|
|
27879
27933
|
const now = opts.now ?? Date.now();
|
|
27880
|
-
if (!
|
|
27934
|
+
if (!existsSync26(path))
|
|
27881
27935
|
return null;
|
|
27882
27936
|
let entry;
|
|
27883
27937
|
try {
|
|
27884
|
-
entry = JSON.parse(
|
|
27938
|
+
entry = JSON.parse(readFileSync24(path, "utf8"));
|
|
27885
27939
|
} catch {
|
|
27886
27940
|
return null;
|
|
27887
27941
|
}
|
|
@@ -27907,8 +27961,8 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
27907
27961
|
result
|
|
27908
27962
|
};
|
|
27909
27963
|
try {
|
|
27910
|
-
|
|
27911
|
-
|
|
27964
|
+
mkdirSync14(dirname8(path), { recursive: true });
|
|
27965
|
+
writeFileSync15(path, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
27912
27966
|
} catch {}
|
|
27913
27967
|
}
|
|
27914
27968
|
var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
|
|
@@ -27918,8 +27972,8 @@ var init_quota_cache = __esm(() => {
|
|
|
27918
27972
|
});
|
|
27919
27973
|
|
|
27920
27974
|
// gateway/boot-probes.ts
|
|
27921
|
-
import { readFileSync as
|
|
27922
|
-
import { join as
|
|
27975
|
+
import { readFileSync as readFileSync25, readdirSync as readdirSync4, existsSync as existsSync27 } from "fs";
|
|
27976
|
+
import { join as join24 } from "path";
|
|
27923
27977
|
import { execFile as execFileCb } from "child_process";
|
|
27924
27978
|
import { promisify as promisify3 } from "util";
|
|
27925
27979
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -27961,11 +28015,11 @@ function mapPlan(billingType, hasExtra) {
|
|
|
27961
28015
|
}
|
|
27962
28016
|
async function probeAccount(agentDir) {
|
|
27963
28017
|
return withTimeout("Account", (async () => {
|
|
27964
|
-
const claudeDir =
|
|
27965
|
-
const claudeJsonPath =
|
|
28018
|
+
const claudeDir = join24(agentDir, ".claude");
|
|
28019
|
+
const claudeJsonPath = join24(claudeDir, ".claude.json");
|
|
27966
28020
|
let cfg = {};
|
|
27967
28021
|
try {
|
|
27968
|
-
const raw =
|
|
28022
|
+
const raw = readFileSync25(claudeJsonPath, "utf8");
|
|
27969
28023
|
cfg = JSON.parse(raw);
|
|
27970
28024
|
} catch {
|
|
27971
28025
|
return { status: "fail", label: "Account", detail: "no .claude.json" };
|
|
@@ -27983,12 +28037,12 @@ async function probeAccount(agentDir) {
|
|
|
27983
28037
|
let tokenStr = "";
|
|
27984
28038
|
let status = "ok";
|
|
27985
28039
|
for (const candidate of [
|
|
27986
|
-
|
|
27987
|
-
|
|
28040
|
+
join24(claudeDir, ".oauth-token.meta.json"),
|
|
28041
|
+
join24(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
27988
28042
|
]) {
|
|
27989
|
-
if (
|
|
28043
|
+
if (existsSync27(candidate)) {
|
|
27990
28044
|
try {
|
|
27991
|
-
const meta = JSON.parse(
|
|
28045
|
+
const meta = JSON.parse(readFileSync25(candidate, "utf8"));
|
|
27992
28046
|
if (meta.expiresAt) {
|
|
27993
28047
|
tokenStr = " \u00b7 " + formatDaysFromNow(meta.expiresAt);
|
|
27994
28048
|
const daysLeft = Math.round((meta.expiresAt - Date.now()) / 86400000);
|
|
@@ -28163,9 +28217,9 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
28163
28217
|
if (!cgroup)
|
|
28164
28218
|
return null;
|
|
28165
28219
|
const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
|
|
28166
|
-
if (!
|
|
28220
|
+
if (!existsSync27(procsPath))
|
|
28167
28221
|
return null;
|
|
28168
|
-
const pidsRaw =
|
|
28222
|
+
const pidsRaw = readFileSync25(procsPath, "utf-8");
|
|
28169
28223
|
const pids = pidsRaw.split(`
|
|
28170
28224
|
`).map((s) => s.trim()).filter(Boolean);
|
|
28171
28225
|
if (pids.length === 0)
|
|
@@ -28178,7 +28232,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
28178
28232
|
let rss = 0;
|
|
28179
28233
|
let comm = "";
|
|
28180
28234
|
try {
|
|
28181
|
-
const status =
|
|
28235
|
+
const status = readFileSync25(`/proc/${pid}/status`, "utf-8");
|
|
28182
28236
|
const rssLine = status.split(`
|
|
28183
28237
|
`).find((l) => l.startsWith("VmRSS:"));
|
|
28184
28238
|
if (rssLine) {
|
|
@@ -28190,7 +28244,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
28190
28244
|
continue;
|
|
28191
28245
|
}
|
|
28192
28246
|
try {
|
|
28193
|
-
comm =
|
|
28247
|
+
comm = readFileSync25(`/proc/${pid}/comm`, "utf-8").trim();
|
|
28194
28248
|
} catch {}
|
|
28195
28249
|
candidates.push({ pid, rss, comm });
|
|
28196
28250
|
}
|
|
@@ -28370,9 +28424,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
28370
28424
|
let claudeDirForProbe = null;
|
|
28371
28425
|
for (const candidate of [
|
|
28372
28426
|
claudeConfigDir,
|
|
28373
|
-
|
|
28427
|
+
join24(claudeConfigDir, "accounts", "default")
|
|
28374
28428
|
]) {
|
|
28375
|
-
if (
|
|
28429
|
+
if (existsSync27(join24(candidate, ".oauth-token"))) {
|
|
28376
28430
|
claudeDirForProbe = candidate;
|
|
28377
28431
|
break;
|
|
28378
28432
|
}
|
|
@@ -28437,7 +28491,7 @@ async function probeHindsight(bankName, fetchImpl = fetch) {
|
|
|
28437
28491
|
}
|
|
28438
28492
|
function readContainerBootTimeMsForProbe() {
|
|
28439
28493
|
try {
|
|
28440
|
-
const stat1 =
|
|
28494
|
+
const stat1 = readFileSync25("/proc/1/stat", "utf8");
|
|
28441
28495
|
const lastParen = stat1.lastIndexOf(")");
|
|
28442
28496
|
if (lastParen < 0)
|
|
28443
28497
|
return null;
|
|
@@ -28445,7 +28499,7 @@ function readContainerBootTimeMsForProbe() {
|
|
|
28445
28499
|
const starttimeTicks = Number(after[19]);
|
|
28446
28500
|
if (!Number.isFinite(starttimeTicks))
|
|
28447
28501
|
return null;
|
|
28448
|
-
const procStat =
|
|
28502
|
+
const procStat = readFileSync25("/proc/stat", "utf8");
|
|
28449
28503
|
const btimeLine = procStat.split(`
|
|
28450
28504
|
`).find((l) => l.startsWith("btime "));
|
|
28451
28505
|
if (!btimeLine)
|
|
@@ -28543,7 +28597,7 @@ async function probeUds(label, socketPath, opts = {}) {
|
|
|
28543
28597
|
}
|
|
28544
28598
|
return withTimeout(label, (async () => {
|
|
28545
28599
|
if (!opts.connectImpl) {
|
|
28546
|
-
if (!
|
|
28600
|
+
if (!existsSync27(socketPath)) {
|
|
28547
28601
|
return {
|
|
28548
28602
|
status: "fail",
|
|
28549
28603
|
label,
|
|
@@ -28578,9 +28632,9 @@ function udsNextStep(label, kind) {
|
|
|
28578
28632
|
return `${label} socket not reachable \u2014 bring up the daemon with \`docker compose up -d ${svc}\` (or check \`docker compose ps\`)`;
|
|
28579
28633
|
}
|
|
28580
28634
|
function defaultUdsConnect(socketPath) {
|
|
28581
|
-
const
|
|
28635
|
+
const net5 = __require("net");
|
|
28582
28636
|
return new Promise((resolve6, reject) => {
|
|
28583
|
-
const sock =
|
|
28637
|
+
const sock = net5.createConnection({ path: socketPath });
|
|
28584
28638
|
const t = setTimeout(() => {
|
|
28585
28639
|
sock.destroy();
|
|
28586
28640
|
reject(new Error("connect timeout"));
|
|
@@ -28607,7 +28661,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
28607
28661
|
return withTimeout("Skills", (async () => {
|
|
28608
28662
|
const fs2 = opts.fs ?? realSkillsFs;
|
|
28609
28663
|
const max = opts.maxNamesShown ?? 3;
|
|
28610
|
-
const skillsDir =
|
|
28664
|
+
const skillsDir = join24(agentDir, ".claude", "skills");
|
|
28611
28665
|
if (!fs2.exists(skillsDir)) {
|
|
28612
28666
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
28613
28667
|
}
|
|
@@ -28622,17 +28676,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
28622
28676
|
}
|
|
28623
28677
|
const dangling = [];
|
|
28624
28678
|
for (const name of entries) {
|
|
28625
|
-
const skillPath =
|
|
28679
|
+
const skillPath = join24(skillsDir, name);
|
|
28626
28680
|
if (!fs2.exists(skillPath)) {
|
|
28627
28681
|
dangling.push(name);
|
|
28628
28682
|
continue;
|
|
28629
28683
|
}
|
|
28630
|
-
const skillMd =
|
|
28684
|
+
const skillMd = join24(skillPath, "SKILL.md");
|
|
28631
28685
|
if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
|
|
28632
28686
|
continue;
|
|
28633
28687
|
}
|
|
28634
28688
|
}
|
|
28635
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
28689
|
+
const overlayDir = opts.overlaySkillsDir ?? join24(agentDir, "skills.d");
|
|
28636
28690
|
const overlaySlugs = new Set;
|
|
28637
28691
|
if (fs2.exists(overlayDir)) {
|
|
28638
28692
|
let overlayEntries = [];
|
|
@@ -28679,24 +28733,24 @@ var init_boot_probes = __esm(() => {
|
|
|
28679
28733
|
execFile3 = promisify3(execFileCb);
|
|
28680
28734
|
realProcFs = {
|
|
28681
28735
|
readdir: (p) => readdirSync4(p),
|
|
28682
|
-
readFile: (p) =>
|
|
28736
|
+
readFile: (p) => readFileSync25(p, "utf-8")
|
|
28683
28737
|
};
|
|
28684
28738
|
realSchedulerFs = {
|
|
28685
|
-
readFile: (p) =>
|
|
28739
|
+
readFile: (p) => readFileSync25(p, "utf-8"),
|
|
28686
28740
|
mtimeMs: (p) => {
|
|
28687
28741
|
const { statSync: statSync7 } = __require("fs");
|
|
28688
28742
|
return statSync7(p).mtimeMs;
|
|
28689
28743
|
},
|
|
28690
|
-
exists: (p) =>
|
|
28744
|
+
exists: (p) => existsSync27(p)
|
|
28691
28745
|
};
|
|
28692
28746
|
realSkillsFs = {
|
|
28693
28747
|
readdir: (p) => readdirSync4(p),
|
|
28694
|
-
exists: (p) =>
|
|
28748
|
+
exists: (p) => existsSync27(p)
|
|
28695
28749
|
};
|
|
28696
28750
|
});
|
|
28697
28751
|
|
|
28698
28752
|
// gateway/boot-issue-cache.ts
|
|
28699
|
-
import { existsSync as
|
|
28753
|
+
import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync16, mkdirSync as mkdirSync15, renameSync as renameSync9 } from "fs";
|
|
28700
28754
|
import { dirname as dirname9 } from "path";
|
|
28701
28755
|
function fingerprintProbe(key, r) {
|
|
28702
28756
|
if (r.status === "ok")
|
|
@@ -28776,11 +28830,11 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
28776
28830
|
return out;
|
|
28777
28831
|
}
|
|
28778
28832
|
function loadCache(path, now = Date.now) {
|
|
28779
|
-
if (!
|
|
28833
|
+
if (!existsSync28(path))
|
|
28780
28834
|
return { ...EMPTY_CACHE, probes: {} };
|
|
28781
28835
|
let raw;
|
|
28782
28836
|
try {
|
|
28783
|
-
raw =
|
|
28837
|
+
raw = readFileSync26(path, "utf-8");
|
|
28784
28838
|
} catch {
|
|
28785
28839
|
return { ...EMPTY_CACHE, probes: {} };
|
|
28786
28840
|
}
|
|
@@ -28823,9 +28877,9 @@ function applyAndSave(path, cache, diff) {
|
|
|
28823
28877
|
}
|
|
28824
28878
|
}
|
|
28825
28879
|
try {
|
|
28826
|
-
|
|
28880
|
+
mkdirSync15(dirname9(path), { recursive: true });
|
|
28827
28881
|
const tmp = `${path}.tmp`;
|
|
28828
|
-
|
|
28882
|
+
writeFileSync16(tmp, JSON.stringify(next), { mode: 384 });
|
|
28829
28883
|
renameSync9(tmp, path);
|
|
28830
28884
|
} catch {}
|
|
28831
28885
|
return next;
|
|
@@ -28839,7 +28893,7 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
28839
28893
|
|
|
28840
28894
|
// gateway/config-snapshot.ts
|
|
28841
28895
|
import { createHash as createHash2 } from "crypto";
|
|
28842
|
-
import { existsSync as
|
|
28896
|
+
import { existsSync as existsSync29, readFileSync as readFileSync27, writeFileSync as writeFileSync17, mkdirSync as mkdirSync16, renameSync as renameSync10 } from "fs";
|
|
28843
28897
|
import { dirname as dirname10 } from "path";
|
|
28844
28898
|
function hashStringArray(items) {
|
|
28845
28899
|
if (!items || items.length === 0)
|
|
@@ -28904,11 +28958,11 @@ function renderConfigChangeDim(dim) {
|
|
|
28904
28958
|
}
|
|
28905
28959
|
}
|
|
28906
28960
|
function loadSnapshot(path, now = Date.now) {
|
|
28907
|
-
if (!
|
|
28961
|
+
if (!existsSync29(path))
|
|
28908
28962
|
return null;
|
|
28909
28963
|
let raw;
|
|
28910
28964
|
try {
|
|
28911
|
-
raw =
|
|
28965
|
+
raw = readFileSync27(path, "utf-8");
|
|
28912
28966
|
} catch {
|
|
28913
28967
|
return null;
|
|
28914
28968
|
}
|
|
@@ -28938,9 +28992,9 @@ function loadSnapshot(path, now = Date.now) {
|
|
|
28938
28992
|
}
|
|
28939
28993
|
function persistSnapshot(path, snapshot) {
|
|
28940
28994
|
try {
|
|
28941
|
-
|
|
28995
|
+
mkdirSync16(dirname10(path), { recursive: true });
|
|
28942
28996
|
const tmp = `${path}.tmp`;
|
|
28943
|
-
|
|
28997
|
+
writeFileSync17(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
28944
28998
|
renameSync10(tmp, path);
|
|
28945
28999
|
} catch {}
|
|
28946
29000
|
}
|
|
@@ -28956,7 +29010,7 @@ __export(exports_boot_card, {
|
|
|
28956
29010
|
renderBootCard: () => renderBootCard,
|
|
28957
29011
|
renderAccountRows: () => renderAuthLine
|
|
28958
29012
|
});
|
|
28959
|
-
import { join as
|
|
29013
|
+
import { join as join25 } from "path";
|
|
28960
29014
|
function resolvePersonaName(slug, loadConfig3) {
|
|
28961
29015
|
try {
|
|
28962
29016
|
const config = loadConfig3 ? loadConfig3() : loadConfig();
|
|
@@ -29046,7 +29100,7 @@ function renderBootCard(opts) {
|
|
|
29046
29100
|
`);
|
|
29047
29101
|
}
|
|
29048
29102
|
async function runAllProbes(opts) {
|
|
29049
|
-
const claudeDir =
|
|
29103
|
+
const claudeDir = join25(opts.agentDir, ".claude");
|
|
29050
29104
|
const probes = {};
|
|
29051
29105
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
29052
29106
|
await Promise.allSettled([
|
|
@@ -29664,12 +29718,12 @@ var init_flock = () => {};
|
|
|
29664
29718
|
// ../src/vault/vault.ts
|
|
29665
29719
|
import { randomBytes as randomBytes5, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
29666
29720
|
import {
|
|
29667
|
-
readFileSync as
|
|
29668
|
-
writeFileSync as
|
|
29669
|
-
existsSync as
|
|
29721
|
+
readFileSync as readFileSync35,
|
|
29722
|
+
writeFileSync as writeFileSync23,
|
|
29723
|
+
existsSync as existsSync37,
|
|
29670
29724
|
renameSync as renameSync12,
|
|
29671
|
-
mkdirSync as
|
|
29672
|
-
unlinkSync as
|
|
29725
|
+
mkdirSync as mkdirSync23,
|
|
29726
|
+
unlinkSync as unlinkSync13,
|
|
29673
29727
|
lstatSync,
|
|
29674
29728
|
realpathSync
|
|
29675
29729
|
} from "node:fs";
|
|
@@ -29704,12 +29758,12 @@ function normalizeSecrets(raw) {
|
|
|
29704
29758
|
return out;
|
|
29705
29759
|
}
|
|
29706
29760
|
function openVault(passphrase, vaultPath) {
|
|
29707
|
-
if (!
|
|
29761
|
+
if (!existsSync37(vaultPath)) {
|
|
29708
29762
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
29709
29763
|
}
|
|
29710
29764
|
let vaultFile;
|
|
29711
29765
|
try {
|
|
29712
|
-
vaultFile = JSON.parse(
|
|
29766
|
+
vaultFile = JSON.parse(readFileSync35(vaultPath, "utf8"));
|
|
29713
29767
|
} catch {
|
|
29714
29768
|
throw new VaultError(`Failed to read vault file: ${vaultPath}`);
|
|
29715
29769
|
}
|
|
@@ -29754,16 +29808,16 @@ var init_vault = __esm(() => {
|
|
|
29754
29808
|
|
|
29755
29809
|
// ../src/vault/resolver.ts
|
|
29756
29810
|
import {
|
|
29757
|
-
chmodSync as
|
|
29811
|
+
chmodSync as chmodSync5,
|
|
29758
29812
|
closeSync as closeSync7,
|
|
29759
|
-
mkdirSync as
|
|
29813
|
+
mkdirSync as mkdirSync24,
|
|
29760
29814
|
mkdtempSync as mkdtempSync2,
|
|
29761
29815
|
openSync as openSync7,
|
|
29762
29816
|
rmSync as rmSync3,
|
|
29763
29817
|
statSync as statSync11,
|
|
29764
29818
|
writeSync as writeSync2
|
|
29765
29819
|
} from "node:fs";
|
|
29766
|
-
import { join as
|
|
29820
|
+
import { join as join35 } from "node:path";
|
|
29767
29821
|
import { tmpdir } from "node:os";
|
|
29768
29822
|
import { constants as fsConstants } from "node:fs";
|
|
29769
29823
|
function isVaultReference(value) {
|
|
@@ -29815,13 +29869,13 @@ function materializationRoot() {
|
|
|
29815
29869
|
return cachedRoot;
|
|
29816
29870
|
const xdg = process.env.XDG_RUNTIME_DIR;
|
|
29817
29871
|
if (xdg) {
|
|
29818
|
-
const base =
|
|
29819
|
-
|
|
29820
|
-
cachedRoot = mkdtempSync2(
|
|
29872
|
+
const base = join35(xdg, "switchroom", "vault");
|
|
29873
|
+
mkdirSync24(base, { recursive: true, mode: 448 });
|
|
29874
|
+
cachedRoot = mkdtempSync2(join35(base, "run-"));
|
|
29821
29875
|
} else {
|
|
29822
|
-
cachedRoot = mkdtempSync2(
|
|
29876
|
+
cachedRoot = mkdtempSync2(join35(tmpdir(), "switchroom-vault-"));
|
|
29823
29877
|
}
|
|
29824
|
-
|
|
29878
|
+
chmodSync5(cachedRoot, 448);
|
|
29825
29879
|
return cachedRoot;
|
|
29826
29880
|
}
|
|
29827
29881
|
function writeFileExclusive(filePath, content) {
|
|
@@ -29834,14 +29888,14 @@ function writeFileExclusive(filePath, content) {
|
|
|
29834
29888
|
}
|
|
29835
29889
|
}
|
|
29836
29890
|
function materializeFilesEntry(key, files) {
|
|
29837
|
-
const dir =
|
|
29891
|
+
const dir = join35(materializationRoot(), key);
|
|
29838
29892
|
if (materializedDirs.has(dir)) {
|
|
29839
29893
|
try {
|
|
29840
29894
|
rmSync3(dir, { recursive: true, force: true });
|
|
29841
29895
|
} catch {}
|
|
29842
29896
|
}
|
|
29843
|
-
|
|
29844
|
-
|
|
29897
|
+
mkdirSync24(dir, { recursive: true, mode: 448 });
|
|
29898
|
+
chmodSync5(dir, 448);
|
|
29845
29899
|
const st = statSync11(dir);
|
|
29846
29900
|
if (typeof process.getuid === "function" && st.uid !== process.getuid()) {
|
|
29847
29901
|
throw new Error(`Refusing to materialize vault entry: ${dir} not owned by caller`);
|
|
@@ -29850,7 +29904,7 @@ function materializeFilesEntry(key, files) {
|
|
|
29850
29904
|
if (filename.includes("/") || filename.includes("\\") || filename === ".." || filename === "." || filename.includes("\x00")) {
|
|
29851
29905
|
throw new Error(`Refusing to materialize vault file with unsafe name: ${filename}`);
|
|
29852
29906
|
}
|
|
29853
|
-
const filePath =
|
|
29907
|
+
const filePath = join35(dir, filename);
|
|
29854
29908
|
const content = encoding === "base64" ? Buffer.from(value, "base64") : value;
|
|
29855
29909
|
writeFileExclusive(filePath, content);
|
|
29856
29910
|
}
|
|
@@ -29983,7 +30037,7 @@ __export(exports_materialize_bot_token, {
|
|
|
29983
30037
|
materializeBotToken: () => materializeBotToken,
|
|
29984
30038
|
BotTokenMaterializeError: () => BotTokenMaterializeError
|
|
29985
30039
|
});
|
|
29986
|
-
import { existsSync as
|
|
30040
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
29987
30041
|
function pickConfiguredToken(config, agentName3) {
|
|
29988
30042
|
if (agentName3) {
|
|
29989
30043
|
const agent = config.agents?.[agentName3];
|
|
@@ -29997,7 +30051,7 @@ function tryDirectVaultRead(ref, config, passphrase) {
|
|
|
29997
30051
|
if (!passphrase)
|
|
29998
30052
|
return null;
|
|
29999
30053
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
30000
|
-
if (!
|
|
30054
|
+
if (!existsSync38(vaultPath))
|
|
30001
30055
|
return null;
|
|
30002
30056
|
try {
|
|
30003
30057
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -30307,7 +30361,7 @@ __export(exports_tmux, {
|
|
|
30307
30361
|
captureAgentPane: () => captureAgentPane
|
|
30308
30362
|
});
|
|
30309
30363
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
30310
|
-
import { mkdirSync as
|
|
30364
|
+
import { mkdirSync as mkdirSync25, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync14, writeFileSync as writeFileSync24 } from "node:fs";
|
|
30311
30365
|
import { resolve as resolve7 } from "node:path";
|
|
30312
30366
|
function captureAgentPane(opts) {
|
|
30313
30367
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -30319,7 +30373,7 @@ function captureAgentPane(opts) {
|
|
|
30319
30373
|
const reasonSlug = sanitizeReason(reason);
|
|
30320
30374
|
const outPath = resolve7(outDir, `${ts}-${reasonSlug}.txt`);
|
|
30321
30375
|
try {
|
|
30322
|
-
|
|
30376
|
+
mkdirSync25(outDir, { recursive: true, mode: 493 });
|
|
30323
30377
|
} catch (err) {
|
|
30324
30378
|
const msg = `mkdir crash-reports failed: ${err.message}`;
|
|
30325
30379
|
console.error(`[tmux-capture] ${agentName3}: ${msg}`);
|
|
@@ -30353,7 +30407,7 @@ function captureAgentPane(opts) {
|
|
|
30353
30407
|
` + `
|
|
30354
30408
|
`;
|
|
30355
30409
|
try {
|
|
30356
|
-
|
|
30410
|
+
writeFileSync24(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
30357
30411
|
mode: 420
|
|
30358
30412
|
});
|
|
30359
30413
|
} catch (err) {
|
|
@@ -30421,7 +30475,7 @@ function pruneOldReports(dir, retain) {
|
|
|
30421
30475
|
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
30422
30476
|
for (const stale of files.slice(retain)) {
|
|
30423
30477
|
try {
|
|
30424
|
-
|
|
30478
|
+
unlinkSync14(stale.full);
|
|
30425
30479
|
} catch {}
|
|
30426
30480
|
}
|
|
30427
30481
|
}
|
|
@@ -30695,23 +30749,23 @@ var import_runner2 = __toESM(require_mod3(), 1);
|
|
|
30695
30749
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
30696
30750
|
import { execFileSync as execFileSync5, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
30697
30751
|
import {
|
|
30698
|
-
readFileSync as
|
|
30699
|
-
writeFileSync as
|
|
30700
|
-
mkdirSync as
|
|
30752
|
+
readFileSync as readFileSync36,
|
|
30753
|
+
writeFileSync as writeFileSync25,
|
|
30754
|
+
mkdirSync as mkdirSync26,
|
|
30701
30755
|
readdirSync as readdirSync7,
|
|
30702
30756
|
rmSync as rmSync4,
|
|
30703
30757
|
statSync as statSync13,
|
|
30704
30758
|
renameSync as renameSync13,
|
|
30705
30759
|
realpathSync as realpathSync2,
|
|
30706
|
-
chmodSync as
|
|
30760
|
+
chmodSync as chmodSync6,
|
|
30707
30761
|
openSync as openSync8,
|
|
30708
30762
|
closeSync as closeSync8,
|
|
30709
|
-
existsSync as
|
|
30710
|
-
unlinkSync as
|
|
30711
|
-
appendFileSync as
|
|
30763
|
+
existsSync as existsSync39,
|
|
30764
|
+
unlinkSync as unlinkSync15,
|
|
30765
|
+
appendFileSync as appendFileSync5
|
|
30712
30766
|
} from "fs";
|
|
30713
|
-
import { homedir as
|
|
30714
|
-
import { join as
|
|
30767
|
+
import { homedir as homedir14 } from "os";
|
|
30768
|
+
import { join as join36, extname, sep as sep3, basename as basename7 } from "path";
|
|
30715
30769
|
|
|
30716
30770
|
// plugin-logger.ts
|
|
30717
30771
|
import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
|
|
@@ -44616,8 +44670,643 @@ function shouldSweepChatAtBoot(chatId) {
|
|
|
44616
44670
|
return n < 0;
|
|
44617
44671
|
}
|
|
44618
44672
|
|
|
44673
|
+
// gateway/webhook-ingest-server.ts
|
|
44674
|
+
import net4 from "node:net";
|
|
44675
|
+
import { chmodSync as chmodSync3, existsSync as existsSync21, unlinkSync as unlinkSync5 } from "node:fs";
|
|
44676
|
+
var MAX_REQUEST_BYTES = 1024 * 1024;
|
|
44677
|
+
function fdOf(conn) {
|
|
44678
|
+
const handle = conn._handle;
|
|
44679
|
+
if (!handle || typeof handle.fd !== "number" || handle.fd < 0)
|
|
44680
|
+
return null;
|
|
44681
|
+
return handle.fd;
|
|
44682
|
+
}
|
|
44683
|
+
function startWebhookIngestServer(opts) {
|
|
44684
|
+
const log = opts.log ?? ((s) => process.stderr.write(s));
|
|
44685
|
+
const allowed = new Set(opts.allowedUids);
|
|
44686
|
+
try {
|
|
44687
|
+
if (existsSync21(opts.socketPath))
|
|
44688
|
+
unlinkSync5(opts.socketPath);
|
|
44689
|
+
} catch (err) {
|
|
44690
|
+
log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
|
|
44691
|
+
`);
|
|
44692
|
+
}
|
|
44693
|
+
const server = net4.createServer((conn) => {
|
|
44694
|
+
const fd = fdOf(conn);
|
|
44695
|
+
const cred = fd !== null ? getPeerCred(fd) : null;
|
|
44696
|
+
if (cred === null || !allowed.has(cred.uid)) {
|
|
44697
|
+
log(`webhook-ingest-server: DENY connection uid=${cred?.uid ?? "unknown"} ` + `(allowed=${[...allowed].join(",")})
|
|
44698
|
+
`);
|
|
44699
|
+
conn.destroy();
|
|
44700
|
+
return;
|
|
44701
|
+
}
|
|
44702
|
+
let buf = "";
|
|
44703
|
+
let handled = false;
|
|
44704
|
+
conn.setEncoding("utf8");
|
|
44705
|
+
const reply = (resp) => {
|
|
44706
|
+
if (handled)
|
|
44707
|
+
return;
|
|
44708
|
+
handled = true;
|
|
44709
|
+
try {
|
|
44710
|
+
conn.write(JSON.stringify(resp) + `
|
|
44711
|
+
`);
|
|
44712
|
+
} catch {}
|
|
44713
|
+
conn.end();
|
|
44714
|
+
};
|
|
44715
|
+
conn.on("data", (chunk) => {
|
|
44716
|
+
if (handled)
|
|
44717
|
+
return;
|
|
44718
|
+
buf += chunk;
|
|
44719
|
+
if (buf.length > MAX_REQUEST_BYTES) {
|
|
44720
|
+
reply({ status: "error", error: "request too large" });
|
|
44721
|
+
return;
|
|
44722
|
+
}
|
|
44723
|
+
const nl = buf.indexOf(`
|
|
44724
|
+
`);
|
|
44725
|
+
if (nl === -1)
|
|
44726
|
+
return;
|
|
44727
|
+
const line = buf.slice(0, nl);
|
|
44728
|
+
let req;
|
|
44729
|
+
try {
|
|
44730
|
+
req = JSON.parse(line);
|
|
44731
|
+
} catch {
|
|
44732
|
+
reply({ status: "error", error: "malformed request" });
|
|
44733
|
+
return;
|
|
44734
|
+
}
|
|
44735
|
+
if (!req || typeof req.agent !== "string" || typeof req.source !== "string" || typeof req.event_type !== "string" || typeof req.rendered_text !== "string" || typeof req.payload !== "object" || req.payload === null) {
|
|
44736
|
+
reply({ status: "error", error: "invalid request shape" });
|
|
44737
|
+
return;
|
|
44738
|
+
}
|
|
44739
|
+
Promise.resolve().then(() => opts.onRecord(req)).then((resp) => reply(resp)).catch((err) => {
|
|
44740
|
+
log(`webhook-ingest-server: onRecord threw: ${String(err)}
|
|
44741
|
+
`);
|
|
44742
|
+
reply({ status: "error", error: "internal error" });
|
|
44743
|
+
});
|
|
44744
|
+
});
|
|
44745
|
+
conn.on("error", (err) => {
|
|
44746
|
+
log(`webhook-ingest-server: conn error: ${err.message}
|
|
44747
|
+
`);
|
|
44748
|
+
});
|
|
44749
|
+
conn.setTimeout(1e4, () => {
|
|
44750
|
+
if (!handled)
|
|
44751
|
+
reply({ status: "error", error: "timeout" });
|
|
44752
|
+
conn.destroy();
|
|
44753
|
+
});
|
|
44754
|
+
});
|
|
44755
|
+
server.on("error", (err) => {
|
|
44756
|
+
log(`webhook-ingest-server: server error: ${err.message}
|
|
44757
|
+
`);
|
|
44758
|
+
});
|
|
44759
|
+
try {
|
|
44760
|
+
server.listen(opts.socketPath, () => {
|
|
44761
|
+
try {
|
|
44762
|
+
chmodSync3(opts.socketPath, 438);
|
|
44763
|
+
} catch (err) {
|
|
44764
|
+
log(`webhook-ingest-server: chmod failed: ${err.message}
|
|
44765
|
+
`);
|
|
44766
|
+
}
|
|
44767
|
+
log(`webhook-ingest-server: listening at ${opts.socketPath} ` + `(allowed uids: ${[...allowed].join(",")})
|
|
44768
|
+
`);
|
|
44769
|
+
});
|
|
44770
|
+
} catch (err) {
|
|
44771
|
+
log(`webhook-ingest-server: listen failed: ${err.message}
|
|
44772
|
+
`);
|
|
44773
|
+
}
|
|
44774
|
+
return {
|
|
44775
|
+
close: () => {
|
|
44776
|
+
try {
|
|
44777
|
+
server.close();
|
|
44778
|
+
} catch {}
|
|
44779
|
+
try {
|
|
44780
|
+
if (existsSync21(opts.socketPath))
|
|
44781
|
+
unlinkSync5(opts.socketPath);
|
|
44782
|
+
} catch {}
|
|
44783
|
+
}
|
|
44784
|
+
};
|
|
44785
|
+
}
|
|
44786
|
+
|
|
44787
|
+
// ../src/web/webhook-gateway-record.ts
|
|
44788
|
+
import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync12 } from "fs";
|
|
44789
|
+
import { join as join20 } from "path";
|
|
44790
|
+
import { homedir as homedir10 } from "os";
|
|
44791
|
+
|
|
44792
|
+
// ../src/web/webhook-handler.ts
|
|
44793
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
|
|
44794
|
+
import { join as join18 } from "path";
|
|
44795
|
+
var DEDUP_MAX = 1000;
|
|
44796
|
+
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
44797
|
+
function loadDedupFile(path) {
|
|
44798
|
+
try {
|
|
44799
|
+
if (!existsSync22(path))
|
|
44800
|
+
return {};
|
|
44801
|
+
const raw = JSON.parse(readFileSync16(path, "utf-8"));
|
|
44802
|
+
return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
|
|
44803
|
+
} catch {
|
|
44804
|
+
return {};
|
|
44805
|
+
}
|
|
44806
|
+
}
|
|
44807
|
+
function saveDedupFile(path, deliveries, now) {
|
|
44808
|
+
const pruned = {};
|
|
44809
|
+
for (const [id, ts] of Object.entries(deliveries)) {
|
|
44810
|
+
if (now - ts < DEDUP_TTL_MS)
|
|
44811
|
+
pruned[id] = ts;
|
|
44812
|
+
}
|
|
44813
|
+
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
44814
|
+
const final = Object.fromEntries(sorted);
|
|
44815
|
+
writeFileSync9(path, JSON.stringify({ deliveries: final }), {
|
|
44816
|
+
mode: 384
|
|
44817
|
+
});
|
|
44818
|
+
}
|
|
44819
|
+
var agentDedupCache = new Map;
|
|
44820
|
+
function createFileDedupStore(resolveAgentDir) {
|
|
44821
|
+
return {
|
|
44822
|
+
check(agent, deliveryId, now) {
|
|
44823
|
+
const telegramDir = join18(resolveAgentDir(agent), "telegram");
|
|
44824
|
+
const filePath = join18(telegramDir, "webhook-dedup.json");
|
|
44825
|
+
if (!agentDedupCache.has(agent)) {
|
|
44826
|
+
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
44827
|
+
}
|
|
44828
|
+
const deliveries = agentDedupCache.get(agent);
|
|
44829
|
+
if (deliveries[deliveryId] !== undefined) {
|
|
44830
|
+
return deliveries[deliveryId];
|
|
44831
|
+
}
|
|
44832
|
+
deliveries[deliveryId] = now;
|
|
44833
|
+
try {
|
|
44834
|
+
mkdirSync10(telegramDir, { recursive: true });
|
|
44835
|
+
saveDedupFile(filePath, deliveries, now);
|
|
44836
|
+
} catch {}
|
|
44837
|
+
return;
|
|
44838
|
+
}
|
|
44839
|
+
};
|
|
44840
|
+
}
|
|
44841
|
+
var tokenBuckets = new Map;
|
|
44842
|
+
var throttleIssueWindow = new Map;
|
|
44843
|
+
|
|
44844
|
+
// ../src/web/webhook-dispatch.ts
|
|
44845
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync11, readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "fs";
|
|
44846
|
+
import { join as join19 } from "path";
|
|
44847
|
+
import { homedir as homedir9 } from "os";
|
|
44848
|
+
|
|
44849
|
+
// ../src/agent-scheduler/ipc-client.ts
|
|
44850
|
+
import { createConnection as createConnection2 } from "node:net";
|
|
44851
|
+
function createInjectIpcClient(options) {
|
|
44852
|
+
const {
|
|
44853
|
+
socketPath,
|
|
44854
|
+
reconnectDelayMs = 1000,
|
|
44855
|
+
maxReconnectDelayMs = 30000,
|
|
44856
|
+
connectTimeoutMs = 5000,
|
|
44857
|
+
log = () => {},
|
|
44858
|
+
_connect = (path) => createConnection2(path)
|
|
44859
|
+
} = options;
|
|
44860
|
+
let socket = null;
|
|
44861
|
+
let connected = false;
|
|
44862
|
+
let closed = false;
|
|
44863
|
+
let currentDelay = reconnectDelayMs;
|
|
44864
|
+
let reconnectTimer = null;
|
|
44865
|
+
let connectTimeoutTimer = null;
|
|
44866
|
+
function clearConnectTimeout() {
|
|
44867
|
+
if (connectTimeoutTimer !== null) {
|
|
44868
|
+
clearTimeout(connectTimeoutTimer);
|
|
44869
|
+
connectTimeoutTimer = null;
|
|
44870
|
+
}
|
|
44871
|
+
}
|
|
44872
|
+
function clearReconnectTimer() {
|
|
44873
|
+
if (reconnectTimer !== null) {
|
|
44874
|
+
clearTimeout(reconnectTimer);
|
|
44875
|
+
reconnectTimer = null;
|
|
44876
|
+
}
|
|
44877
|
+
}
|
|
44878
|
+
function scheduleReconnect() {
|
|
44879
|
+
if (closed)
|
|
44880
|
+
return;
|
|
44881
|
+
log(`scheduler ipc: reconnecting in ${currentDelay}ms`);
|
|
44882
|
+
reconnectTimer = setTimeout(() => {
|
|
44883
|
+
reconnectTimer = null;
|
|
44884
|
+
if (!closed)
|
|
44885
|
+
connect2();
|
|
44886
|
+
}, currentDelay);
|
|
44887
|
+
currentDelay = Math.min(currentDelay * 2, maxReconnectDelayMs);
|
|
44888
|
+
}
|
|
44889
|
+
function onClose() {
|
|
44890
|
+
clearConnectTimeout();
|
|
44891
|
+
connected = false;
|
|
44892
|
+
socket = null;
|
|
44893
|
+
if (!closed)
|
|
44894
|
+
scheduleReconnect();
|
|
44895
|
+
}
|
|
44896
|
+
function connect2() {
|
|
44897
|
+
if (closed)
|
|
44898
|
+
return;
|
|
44899
|
+
let s;
|
|
44900
|
+
try {
|
|
44901
|
+
s = _connect(socketPath);
|
|
44902
|
+
} catch (err) {
|
|
44903
|
+
log(`scheduler ipc: connect threw: ${err.message}`);
|
|
44904
|
+
scheduleReconnect();
|
|
44905
|
+
return;
|
|
44906
|
+
}
|
|
44907
|
+
socket = s;
|
|
44908
|
+
connectTimeoutTimer = setTimeout(() => {
|
|
44909
|
+
connectTimeoutTimer = null;
|
|
44910
|
+
if (!connected) {
|
|
44911
|
+
log(`scheduler ipc: connect timeout after ${connectTimeoutMs}ms`);
|
|
44912
|
+
try {
|
|
44913
|
+
s.destroy();
|
|
44914
|
+
} catch {}
|
|
44915
|
+
}
|
|
44916
|
+
}, connectTimeoutMs);
|
|
44917
|
+
s.on("connect", () => {
|
|
44918
|
+
clearConnectTimeout();
|
|
44919
|
+
connected = true;
|
|
44920
|
+
currentDelay = reconnectDelayMs;
|
|
44921
|
+
log(`scheduler ipc: connected to ${socketPath}`);
|
|
44922
|
+
});
|
|
44923
|
+
s.on("close", () => onClose());
|
|
44924
|
+
s.on("error", (err) => {
|
|
44925
|
+
log(`scheduler ipc: socket error: ${err.message}`);
|
|
44926
|
+
});
|
|
44927
|
+
s.on("data", () => {});
|
|
44928
|
+
}
|
|
44929
|
+
setImmediate(connect2);
|
|
44930
|
+
return {
|
|
44931
|
+
sendInjectInbound(msg) {
|
|
44932
|
+
if (!socket || !connected)
|
|
44933
|
+
return false;
|
|
44934
|
+
try {
|
|
44935
|
+
return socket.write(JSON.stringify(msg) + `
|
|
44936
|
+
`);
|
|
44937
|
+
} catch (err) {
|
|
44938
|
+
log(`scheduler ipc: write failed: ${err.message}`);
|
|
44939
|
+
return false;
|
|
44940
|
+
}
|
|
44941
|
+
},
|
|
44942
|
+
isConnected() {
|
|
44943
|
+
return connected;
|
|
44944
|
+
},
|
|
44945
|
+
waitForConnect(timeoutMs) {
|
|
44946
|
+
if (connected)
|
|
44947
|
+
return Promise.resolve(true);
|
|
44948
|
+
if (closed)
|
|
44949
|
+
return Promise.resolve(false);
|
|
44950
|
+
return new Promise((resolve6) => {
|
|
44951
|
+
const start = Date.now();
|
|
44952
|
+
const timer3 = setInterval(() => {
|
|
44953
|
+
if (connected) {
|
|
44954
|
+
clearInterval(timer3);
|
|
44955
|
+
resolve6(true);
|
|
44956
|
+
return;
|
|
44957
|
+
}
|
|
44958
|
+
if (closed || Date.now() - start >= timeoutMs) {
|
|
44959
|
+
clearInterval(timer3);
|
|
44960
|
+
resolve6(connected);
|
|
44961
|
+
}
|
|
44962
|
+
}, 50);
|
|
44963
|
+
if (typeof timer3.unref === "function") {
|
|
44964
|
+
timer3.unref();
|
|
44965
|
+
}
|
|
44966
|
+
});
|
|
44967
|
+
},
|
|
44968
|
+
close() {
|
|
44969
|
+
closed = true;
|
|
44970
|
+
clearReconnectTimer();
|
|
44971
|
+
clearConnectTimeout();
|
|
44972
|
+
if (socket) {
|
|
44973
|
+
try {
|
|
44974
|
+
socket.end();
|
|
44975
|
+
} catch {}
|
|
44976
|
+
socket = null;
|
|
44977
|
+
}
|
|
44978
|
+
connected = false;
|
|
44979
|
+
}
|
|
44980
|
+
};
|
|
44981
|
+
}
|
|
44982
|
+
|
|
44983
|
+
// ../src/web/webhook-dispatch.ts
|
|
44984
|
+
function renderTemplate(template, ctx) {
|
|
44985
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => ctx[key] ?? "");
|
|
44986
|
+
}
|
|
44987
|
+
function buildGithubContext(eventType, payload) {
|
|
44988
|
+
const repo = payload.repository?.full_name ?? "";
|
|
44989
|
+
const pr = payload.pull_request;
|
|
44990
|
+
const issue = payload.issue;
|
|
44991
|
+
const obj = pr ?? issue;
|
|
44992
|
+
const number = String(payload.number ?? obj?.number ?? "");
|
|
44993
|
+
const firstCommit = payload.commits?.[0];
|
|
44994
|
+
const title = String(obj?.title ?? firstCommit?.message ?? "");
|
|
44995
|
+
const html_url = String(obj?.html_url ?? payload.html_url ?? "");
|
|
44996
|
+
const author = String(obj?.user?.login ?? payload.sender?.login ?? "");
|
|
44997
|
+
const rawLabels = obj?.labels ?? [];
|
|
44998
|
+
const labels = rawLabels.map((l) => String(l.name ?? "")).join(", ");
|
|
44999
|
+
const action = String(payload.action ?? "");
|
|
45000
|
+
return { repo, number, title, html_url, author, labels, action, event: eventType };
|
|
45001
|
+
}
|
|
45002
|
+
function matchesRule(eventType, payload, matcher) {
|
|
45003
|
+
if (matcher.event !== eventType)
|
|
45004
|
+
return false;
|
|
45005
|
+
const ctx = buildGithubContext(eventType, payload);
|
|
45006
|
+
if (matcher.actions && matcher.actions.length > 0) {
|
|
45007
|
+
if (!matcher.actions.includes(ctx.action))
|
|
45008
|
+
return false;
|
|
45009
|
+
}
|
|
45010
|
+
if (matcher.exclude_authors && matcher.exclude_authors.length > 0) {
|
|
45011
|
+
if (matcher.exclude_authors.includes(ctx.author))
|
|
45012
|
+
return false;
|
|
45013
|
+
}
|
|
45014
|
+
if (matcher.labels_any && matcher.labels_any.length > 0) {
|
|
45015
|
+
const pr = payload.pull_request;
|
|
45016
|
+
const issue = payload.issue;
|
|
45017
|
+
const rawLabels = (pr ?? issue)?.labels ?? [];
|
|
45018
|
+
const labelNames = new Set(rawLabels.map((l) => String(l.name ?? "")));
|
|
45019
|
+
const hasAny = matcher.labels_any.some((l) => labelNames.has(l));
|
|
45020
|
+
if (!hasAny)
|
|
45021
|
+
return false;
|
|
45022
|
+
}
|
|
45023
|
+
if (matcher.labels_all && matcher.labels_all.length > 0) {
|
|
45024
|
+
const pr = payload.pull_request;
|
|
45025
|
+
const issue = payload.issue;
|
|
45026
|
+
const rawLabels = (pr ?? issue)?.labels ?? [];
|
|
45027
|
+
const labelNames = new Set(rawLabels.map((l) => String(l.name ?? "")));
|
|
45028
|
+
const hasAll = matcher.labels_all.every((l) => labelNames.has(l));
|
|
45029
|
+
if (!hasAll)
|
|
45030
|
+
return false;
|
|
45031
|
+
}
|
|
45032
|
+
return true;
|
|
45033
|
+
}
|
|
45034
|
+
function parseDurationMs(d) {
|
|
45035
|
+
const m = d.trim().match(/^(\d+)(s|m|h|d)?$/);
|
|
45036
|
+
if (!m)
|
|
45037
|
+
return 0;
|
|
45038
|
+
const n = parseInt(m[1], 10);
|
|
45039
|
+
switch (m[2]) {
|
|
45040
|
+
case "s":
|
|
45041
|
+
return n * 1000;
|
|
45042
|
+
case "m":
|
|
45043
|
+
return n * 60000;
|
|
45044
|
+
case "h":
|
|
45045
|
+
return n * 3600000;
|
|
45046
|
+
case "d":
|
|
45047
|
+
return n * 86400000;
|
|
45048
|
+
default:
|
|
45049
|
+
return n;
|
|
45050
|
+
}
|
|
45051
|
+
}
|
|
45052
|
+
function cooldownKey(eventType, repo, number, ruleIndex) {
|
|
45053
|
+
return `${eventType}:${repo}:${number}:${ruleIndex}`;
|
|
45054
|
+
}
|
|
45055
|
+
function loadCooldownFile(path) {
|
|
45056
|
+
try {
|
|
45057
|
+
if (!existsSync23(path))
|
|
45058
|
+
return {};
|
|
45059
|
+
const raw = JSON.parse(readFileSync17(path, "utf-8"));
|
|
45060
|
+
return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
|
|
45061
|
+
} catch {
|
|
45062
|
+
return {};
|
|
45063
|
+
}
|
|
45064
|
+
}
|
|
45065
|
+
function saveCooldownFile(path, dispatches) {
|
|
45066
|
+
try {
|
|
45067
|
+
writeFileSync10(path, JSON.stringify({ dispatches }), {
|
|
45068
|
+
mode: 384
|
|
45069
|
+
});
|
|
45070
|
+
} catch {}
|
|
45071
|
+
}
|
|
45072
|
+
function createFileCooldownStore(resolveAgentDir) {
|
|
45073
|
+
const cache = new Map;
|
|
45074
|
+
return {
|
|
45075
|
+
isCoolingDown(agent, key, cooldownMs, now) {
|
|
45076
|
+
if (cooldownMs <= 0)
|
|
45077
|
+
return false;
|
|
45078
|
+
const telegramDir = join19(resolveAgentDir(agent), "telegram");
|
|
45079
|
+
const filePath = join19(telegramDir, "webhook-cooldown.json");
|
|
45080
|
+
if (!cache.has(agent)) {
|
|
45081
|
+
cache.set(agent, loadCooldownFile(filePath));
|
|
45082
|
+
}
|
|
45083
|
+
const dispatches = cache.get(agent);
|
|
45084
|
+
const lastDispatch = dispatches[key];
|
|
45085
|
+
if (lastDispatch !== undefined && now - lastDispatch < cooldownMs) {
|
|
45086
|
+
return true;
|
|
45087
|
+
}
|
|
45088
|
+
dispatches[key] = now;
|
|
45089
|
+
try {
|
|
45090
|
+
mkdirSync11(telegramDir, { recursive: true });
|
|
45091
|
+
saveCooldownFile(filePath, dispatches);
|
|
45092
|
+
} catch {}
|
|
45093
|
+
return false;
|
|
45094
|
+
}
|
|
45095
|
+
};
|
|
45096
|
+
}
|
|
45097
|
+
function isQuietHour(qh, now) {
|
|
45098
|
+
const tz = qh.tz ?? "UTC";
|
|
45099
|
+
let hour;
|
|
45100
|
+
try {
|
|
45101
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
45102
|
+
timeZone: tz,
|
|
45103
|
+
hour: "numeric",
|
|
45104
|
+
hour12: false
|
|
45105
|
+
});
|
|
45106
|
+
const parts = formatter.formatToParts(now);
|
|
45107
|
+
const hourPart = parts.find((p) => p.type === "hour");
|
|
45108
|
+
hour = parseInt(hourPart?.value ?? "0", 10);
|
|
45109
|
+
} catch {
|
|
45110
|
+
hour = now.getUTCHours();
|
|
45111
|
+
}
|
|
45112
|
+
const { start, end } = qh;
|
|
45113
|
+
if (start < end) {
|
|
45114
|
+
return hour >= start && hour < end;
|
|
45115
|
+
} else {
|
|
45116
|
+
return hour >= start || hour < end;
|
|
45117
|
+
}
|
|
45118
|
+
}
|
|
45119
|
+
async function defaultInject(socketPath, agentName3, inbound) {
|
|
45120
|
+
const client3 = createInjectIpcClient({ socketPath });
|
|
45121
|
+
try {
|
|
45122
|
+
const connected = await client3.waitForConnect(5000);
|
|
45123
|
+
if (!connected)
|
|
45124
|
+
return false;
|
|
45125
|
+
return client3.sendInjectInbound({
|
|
45126
|
+
type: "inject_inbound",
|
|
45127
|
+
agentName: agentName3,
|
|
45128
|
+
inbound
|
|
45129
|
+
});
|
|
45130
|
+
} finally {
|
|
45131
|
+
client3.close();
|
|
45132
|
+
}
|
|
45133
|
+
}
|
|
45134
|
+
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
45135
|
+
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
45136
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join19(homedir9(), ".switchroom", "agents", a));
|
|
45137
|
+
const now = (deps.now ?? Date.now)();
|
|
45138
|
+
const socketPath = join19(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
45139
|
+
const inbound = {
|
|
45140
|
+
type: "inbound",
|
|
45141
|
+
chatId: ctx.chatId,
|
|
45142
|
+
...ctx.threadId !== undefined ? { threadId: ctx.threadId } : {},
|
|
45143
|
+
messageId: now,
|
|
45144
|
+
user: "webhook",
|
|
45145
|
+
userId: 0,
|
|
45146
|
+
ts: now,
|
|
45147
|
+
text: prompt,
|
|
45148
|
+
meta: {
|
|
45149
|
+
source: "webhook",
|
|
45150
|
+
event: ctx.eventType,
|
|
45151
|
+
rule_index: String(ctx.ruleIndex)
|
|
45152
|
+
}
|
|
45153
|
+
};
|
|
45154
|
+
const injectFn = deps.injectFn ?? defaultInject;
|
|
45155
|
+
injectFn(socketPath, agent, inbound).then((ok) => log(`webhook-dispatch: agent='${agent}' inject_inbound ` + `${ok ? "delivered to gateway" : "not delivered (gateway not connected)"}
|
|
45156
|
+
`)).catch((err) => log(`webhook-dispatch: agent='${agent}' inject failed: ${String(err)}
|
|
45157
|
+
`));
|
|
45158
|
+
}
|
|
45159
|
+
function evaluateDispatch(args, deps = {}) {
|
|
45160
|
+
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
45161
|
+
const now = (deps.now ?? Date.now)();
|
|
45162
|
+
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
45163
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join19(homedir9(), ".switchroom", "agents", a));
|
|
45164
|
+
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
45165
|
+
if (args.source !== "github")
|
|
45166
|
+
return 0;
|
|
45167
|
+
const rules = args.dispatchConfig.github;
|
|
45168
|
+
if (!rules || rules.length === 0)
|
|
45169
|
+
return 0;
|
|
45170
|
+
const ctx = buildGithubContext(args.eventType, args.payload);
|
|
45171
|
+
let fired = 0;
|
|
45172
|
+
for (let i = 0;i < rules.length; i++) {
|
|
45173
|
+
const rule = rules[i];
|
|
45174
|
+
if (!matchesRule(args.eventType, args.payload, rule.match))
|
|
45175
|
+
continue;
|
|
45176
|
+
if (rule.quiet_hours && isQuietHour(rule.quiet_hours, nowDate())) {
|
|
45177
|
+
log(`webhook-dispatch: agent='${args.agent}' rule=${i} skipped (quiet hours)
|
|
45178
|
+
`);
|
|
45179
|
+
continue;
|
|
45180
|
+
}
|
|
45181
|
+
const cooldownMs = rule.cooldown ? parseDurationMs(rule.cooldown) : 0;
|
|
45182
|
+
if (cooldownMs > 0) {
|
|
45183
|
+
const ck = cooldownKey(args.eventType, ctx.repo, ctx.number, i);
|
|
45184
|
+
if (cooldownStore.isCoolingDown(args.agent, ck, cooldownMs, now)) {
|
|
45185
|
+
log(`webhook-dispatch: agent='${args.agent}' rule=${i} skipped (cooldown)
|
|
45186
|
+
`);
|
|
45187
|
+
continue;
|
|
45188
|
+
}
|
|
45189
|
+
}
|
|
45190
|
+
const prompt = renderTemplate(rule.prompt, ctx);
|
|
45191
|
+
log(`webhook-dispatch: agent='${args.agent}' rule=${i} matched event='${args.eventType}' action='${ctx.action}' firing
|
|
45192
|
+
`);
|
|
45193
|
+
injectWebhookInbound(args.agent, prompt, {
|
|
45194
|
+
chatId: args.chatId,
|
|
45195
|
+
...args.threadId !== undefined ? { threadId: args.threadId } : {},
|
|
45196
|
+
eventType: args.eventType,
|
|
45197
|
+
ruleIndex: i
|
|
45198
|
+
}, { ...deps, resolveAgentDir });
|
|
45199
|
+
fired++;
|
|
45200
|
+
}
|
|
45201
|
+
return fired;
|
|
45202
|
+
}
|
|
45203
|
+
|
|
45204
|
+
// ../src/web/webhook-gateway-record.ts
|
|
45205
|
+
init_loader();
|
|
45206
|
+
init_merge();
|
|
45207
|
+
|
|
45208
|
+
// ../src/agent-scheduler/channel-target.ts
|
|
45209
|
+
init_merge();
|
|
45210
|
+
function resolveChannelTarget(config, agentName3) {
|
|
45211
|
+
const agent = config.agents?.[agentName3];
|
|
45212
|
+
const tgChannel = agent ? resolveAgentConfig(config.defaults, config.profiles, agent).channels?.telegram : undefined;
|
|
45213
|
+
const supergroupChatId = tgChannel?.chat_id;
|
|
45214
|
+
const supergroupDefaultTopic = tgChannel?.default_topic_id;
|
|
45215
|
+
if (typeof supergroupChatId === "string" && supergroupChatId.length > 0) {
|
|
45216
|
+
return {
|
|
45217
|
+
chatId: supergroupChatId,
|
|
45218
|
+
...typeof supergroupDefaultTopic === "number" ? { threadId: supergroupDefaultTopic } : {},
|
|
45219
|
+
routerConfig: {
|
|
45220
|
+
...typeof supergroupDefaultTopic === "number" ? { default_topic_id: supergroupDefaultTopic } : {},
|
|
45221
|
+
...tgChannel?.topic_aliases ? { topic_aliases: tgChannel.topic_aliases } : {}
|
|
45222
|
+
}
|
|
45223
|
+
};
|
|
45224
|
+
}
|
|
45225
|
+
const forumChatId = config.telegram?.forum_chat_id;
|
|
45226
|
+
if (typeof forumChatId !== "string" || forumChatId.length === 0)
|
|
45227
|
+
return null;
|
|
45228
|
+
const threadId = agent?.topic_id;
|
|
45229
|
+
return {
|
|
45230
|
+
chatId: forumChatId,
|
|
45231
|
+
...typeof threadId === "number" ? { threadId } : {}
|
|
45232
|
+
};
|
|
45233
|
+
}
|
|
45234
|
+
|
|
45235
|
+
// ../src/web/webhook-gateway-record.ts
|
|
45236
|
+
function recordWebhookEvent(rec, deps = {}) {
|
|
45237
|
+
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
45238
|
+
const now = rec.ts || (deps.now ?? Date.now)();
|
|
45239
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join20(homedir10(), ".switchroom", "agents", a));
|
|
45240
|
+
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
45241
|
+
const agent = rec.agent;
|
|
45242
|
+
const telegramDir = join20(resolveAgentDir(agent), "telegram");
|
|
45243
|
+
if (rec.source === "github" && rec.delivery_id) {
|
|
45244
|
+
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
45245
|
+
if (originalTs !== undefined) {
|
|
45246
|
+
log(`webhook-gateway: agent='${agent}' source='${rec.source}' deduped delivery='${rec.delivery_id}'
|
|
45247
|
+
`);
|
|
45248
|
+
return { status: "deduped", ts: originalTs };
|
|
45249
|
+
}
|
|
45250
|
+
}
|
|
45251
|
+
const logPath = join20(telegramDir, "webhook-events.jsonl");
|
|
45252
|
+
try {
|
|
45253
|
+
mkdirSync12(telegramDir, { recursive: true });
|
|
45254
|
+
const record = {
|
|
45255
|
+
ts: now,
|
|
45256
|
+
source: rec.source,
|
|
45257
|
+
event_type: rec.event_type,
|
|
45258
|
+
rendered_text: rec.rendered_text,
|
|
45259
|
+
payload: rec.payload
|
|
45260
|
+
};
|
|
45261
|
+
appendFileSync4(logPath, JSON.stringify(record) + `
|
|
45262
|
+
`, { mode: 384 });
|
|
45263
|
+
} catch (err) {
|
|
45264
|
+
log(`webhook-gateway: agent='${agent}' source='${rec.source}' write failed: ${err.message}
|
|
45265
|
+
`);
|
|
45266
|
+
return { status: "error", error: "write failed" };
|
|
45267
|
+
}
|
|
45268
|
+
log(`webhook-gateway: agent='${agent}' source='${rec.source}' event='${rec.event_type}' recorded ts=${now}
|
|
45269
|
+
`);
|
|
45270
|
+
let dispatched = 0;
|
|
45271
|
+
try {
|
|
45272
|
+
const config = (deps.loadConfig ?? loadConfig)();
|
|
45273
|
+
const rawAgent = config.agents?.[agent];
|
|
45274
|
+
const dispatchConfig = rawAgent ? resolveAgentConfig(config.defaults, config.profiles, rawAgent).channels?.telegram?.webhook_dispatch : undefined;
|
|
45275
|
+
if (dispatchConfig && rec.source === "github") {
|
|
45276
|
+
const target = resolveChannelTarget(config, agent);
|
|
45277
|
+
if (!target) {
|
|
45278
|
+
log(`webhook-gateway: agent='${agent}' dispatch skipped \u2014 no chat target (forum_chat_id / chat_id unset)
|
|
45279
|
+
`);
|
|
45280
|
+
} else {
|
|
45281
|
+
const injectFn = deps.inject;
|
|
45282
|
+
dispatched = evaluateDispatch({
|
|
45283
|
+
agent,
|
|
45284
|
+
source: rec.source,
|
|
45285
|
+
eventType: rec.event_type,
|
|
45286
|
+
payload: rec.payload,
|
|
45287
|
+
dispatchConfig,
|
|
45288
|
+
chatId: target.chatId,
|
|
45289
|
+
...target.threadId !== undefined ? { threadId: target.threadId } : {}
|
|
45290
|
+
}, {
|
|
45291
|
+
now: () => now,
|
|
45292
|
+
resolveAgentDir,
|
|
45293
|
+
log,
|
|
45294
|
+
...deps.cooldownStore ? { cooldownStore: deps.cooldownStore } : {},
|
|
45295
|
+
...injectFn ? {
|
|
45296
|
+
injectFn: async (_socketPath, agentName3, inbound) => injectFn(agentName3, inbound)
|
|
45297
|
+
} : {}
|
|
45298
|
+
});
|
|
45299
|
+
}
|
|
45300
|
+
}
|
|
45301
|
+
} catch (err) {
|
|
45302
|
+
log(`webhook-gateway: agent='${agent}' dispatch error (event recorded): ${err.message}
|
|
45303
|
+
`);
|
|
45304
|
+
}
|
|
45305
|
+
return { status: "ok", ts: now, dispatched };
|
|
45306
|
+
}
|
|
45307
|
+
|
|
44619
45308
|
// gateway/ipc-server.ts
|
|
44620
|
-
import { renameSync as renameSync5, unlinkSync as
|
|
45309
|
+
import { renameSync as renameSync5, unlinkSync as unlinkSync6 } from "fs";
|
|
44621
45310
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
44622
45311
|
var VALID_OPERATOR_KINDS = new Set([
|
|
44623
45312
|
"credentials-expired",
|
|
@@ -44735,7 +45424,7 @@ function createIpcServer(options) {
|
|
|
44735
45424
|
renameSync5(socketPath, socketPath + ".bak");
|
|
44736
45425
|
} catch {}
|
|
44737
45426
|
try {
|
|
44738
|
-
|
|
45427
|
+
unlinkSync6(socketPath + ".bak");
|
|
44739
45428
|
} catch {}
|
|
44740
45429
|
const clients = new Set;
|
|
44741
45430
|
const agentIndex = new Map;
|
|
@@ -46805,15 +47494,15 @@ function escapeBody(s) {
|
|
|
46805
47494
|
}
|
|
46806
47495
|
|
|
46807
47496
|
// gateway/pid-file.ts
|
|
46808
|
-
import { writeFileSync as
|
|
47497
|
+
import { writeFileSync as writeFileSync11, readFileSync as readFileSync18, unlinkSync as unlinkSync7, renameSync as renameSync6 } from "node:fs";
|
|
46809
47498
|
function writePidFile(path, record) {
|
|
46810
47499
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
46811
|
-
|
|
47500
|
+
writeFileSync11(tmp, JSON.stringify(record), "utf-8");
|
|
46812
47501
|
renameSync6(tmp, path);
|
|
46813
47502
|
}
|
|
46814
47503
|
function clearPidFile(path) {
|
|
46815
47504
|
try {
|
|
46816
|
-
|
|
47505
|
+
unlinkSync7(path);
|
|
46817
47506
|
} catch {}
|
|
46818
47507
|
}
|
|
46819
47508
|
|
|
@@ -46824,10 +47513,10 @@ import {
|
|
|
46824
47513
|
writeFile as writeFileAsync,
|
|
46825
47514
|
readFile as readFileAsync
|
|
46826
47515
|
} from "node:fs/promises";
|
|
46827
|
-
import { readFileSync as
|
|
47516
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
46828
47517
|
function readCurrentBootId() {
|
|
46829
47518
|
try {
|
|
46830
|
-
const stat =
|
|
47519
|
+
const stat = readFileSync19("/proc/1/stat", "utf-8");
|
|
46831
47520
|
const lastParen = stat.lastIndexOf(")");
|
|
46832
47521
|
if (lastParen < 0)
|
|
46833
47522
|
return null;
|
|
@@ -47030,15 +47719,15 @@ function safeCount(fn) {
|
|
|
47030
47719
|
}
|
|
47031
47720
|
|
|
47032
47721
|
// gateway/session-marker.ts
|
|
47033
|
-
import { writeFileSync as
|
|
47722
|
+
import { writeFileSync as writeFileSync12, readFileSync as readFileSync20, renameSync as renameSync7, unlinkSync as unlinkSync8 } from "node:fs";
|
|
47034
47723
|
function writeSessionMarker(path, marker) {
|
|
47035
47724
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
47036
|
-
|
|
47725
|
+
writeFileSync12(tmp, JSON.stringify(marker), "utf-8");
|
|
47037
47726
|
renameSync7(tmp, path);
|
|
47038
47727
|
}
|
|
47039
47728
|
function readSessionMarker(path) {
|
|
47040
47729
|
try {
|
|
47041
|
-
const raw =
|
|
47730
|
+
const raw = readFileSync20(path, "utf-8");
|
|
47042
47731
|
const parsed = JSON.parse(raw);
|
|
47043
47732
|
if (typeof parsed.pid === "number" && typeof parsed.startedAtMs === "number" && Number.isFinite(parsed.pid) && Number.isFinite(parsed.startedAtMs)) {
|
|
47044
47733
|
return { pid: parsed.pid, startedAtMs: parsed.startedAtMs };
|
|
@@ -47060,16 +47749,16 @@ function shouldFireRestartBanner(input) {
|
|
|
47060
47749
|
}
|
|
47061
47750
|
|
|
47062
47751
|
// gateway/clean-shutdown-marker.ts
|
|
47063
|
-
import { writeFileSync as
|
|
47752
|
+
import { writeFileSync as writeFileSync13, readFileSync as readFileSync21, renameSync as renameSync8, unlinkSync as unlinkSync9 } from "node:fs";
|
|
47064
47753
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
47065
47754
|
function writeCleanShutdownMarker(path, marker) {
|
|
47066
47755
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
47067
|
-
|
|
47756
|
+
writeFileSync13(tmp, JSON.stringify(marker), "utf-8");
|
|
47068
47757
|
renameSync8(tmp, path);
|
|
47069
47758
|
}
|
|
47070
47759
|
function readCleanShutdownMarker(path) {
|
|
47071
47760
|
try {
|
|
47072
|
-
const raw =
|
|
47761
|
+
const raw = readFileSync21(path, "utf-8");
|
|
47073
47762
|
const parsed = JSON.parse(raw);
|
|
47074
47763
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.signal === "string" && parsed.signal.length > 0) {
|
|
47075
47764
|
const out = { ts: parsed.ts, signal: parsed.signal };
|
|
@@ -47084,7 +47773,7 @@ function readCleanShutdownMarker(path) {
|
|
|
47084
47773
|
}
|
|
47085
47774
|
function clearCleanShutdownMarker(path) {
|
|
47086
47775
|
try {
|
|
47087
|
-
|
|
47776
|
+
unlinkSync9(path);
|
|
47088
47777
|
} catch {}
|
|
47089
47778
|
}
|
|
47090
47779
|
function shouldSuppressRecoveryBanner(marker, now, maxAgeMs = DEFAULT_MAX_AGE_MS) {
|
|
@@ -47864,16 +48553,16 @@ function classifyAdminGate(text, myAgentName) {
|
|
|
47864
48553
|
|
|
47865
48554
|
// subagent-watcher.ts
|
|
47866
48555
|
import {
|
|
47867
|
-
existsSync as
|
|
48556
|
+
existsSync as existsSync25,
|
|
47868
48557
|
openSync as openSync2,
|
|
47869
48558
|
readSync,
|
|
47870
48559
|
statSync as statSync6,
|
|
47871
48560
|
closeSync as closeSync2,
|
|
47872
48561
|
watch,
|
|
47873
48562
|
readdirSync as readdirSync3,
|
|
47874
|
-
readFileSync as
|
|
48563
|
+
readFileSync as readFileSync23
|
|
47875
48564
|
} from "fs";
|
|
47876
|
-
import { join as
|
|
48565
|
+
import { join as join22 } from "path";
|
|
47877
48566
|
|
|
47878
48567
|
// operator-events.ts
|
|
47879
48568
|
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2 = 5 * 60000;
|
|
@@ -48116,20 +48805,20 @@ function bumpSubagentActivity(db2, args) {
|
|
|
48116
48805
|
// gateway/turn-active-marker.ts
|
|
48117
48806
|
import {
|
|
48118
48807
|
closeSync,
|
|
48119
|
-
existsSync as
|
|
48120
|
-
mkdirSync as
|
|
48808
|
+
existsSync as existsSync24,
|
|
48809
|
+
mkdirSync as mkdirSync13,
|
|
48121
48810
|
openSync,
|
|
48122
|
-
readFileSync as
|
|
48811
|
+
readFileSync as readFileSync22,
|
|
48123
48812
|
statSync as statSync5,
|
|
48124
|
-
unlinkSync as
|
|
48813
|
+
unlinkSync as unlinkSync10,
|
|
48125
48814
|
utimesSync,
|
|
48126
|
-
writeFileSync as
|
|
48815
|
+
writeFileSync as writeFileSync14
|
|
48127
48816
|
} from "node:fs";
|
|
48128
|
-
import { join as
|
|
48817
|
+
import { join as join21 } from "node:path";
|
|
48129
48818
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
48130
48819
|
function touchTurnActiveMarker(stateDir) {
|
|
48131
|
-
const path =
|
|
48132
|
-
if (!
|
|
48820
|
+
const path = join21(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
48821
|
+
if (!existsSync24(path))
|
|
48133
48822
|
return;
|
|
48134
48823
|
const now = new Date;
|
|
48135
48824
|
try {
|
|
@@ -48164,7 +48853,7 @@ function backfillJsonlAgentId(db2, jsonlPath, agentId, log) {
|
|
|
48164
48853
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
48165
48854
|
let meta;
|
|
48166
48855
|
try {
|
|
48167
|
-
const raw =
|
|
48856
|
+
const raw = readFileSync23(metaPath, "utf8");
|
|
48168
48857
|
meta = JSON.parse(raw);
|
|
48169
48858
|
} catch {
|
|
48170
48859
|
log?.(`subagent-watcher: backfill skip ${agentId} \u2014 meta.json not readable at ${metaPath}`);
|
|
@@ -48351,7 +49040,7 @@ function startSubagentWatcher(config) {
|
|
|
48351
49040
|
clearTimeout(ref.ref);
|
|
48352
49041
|
});
|
|
48353
49042
|
const fs2 = config.fs ?? {
|
|
48354
|
-
existsSync:
|
|
49043
|
+
existsSync: existsSync25,
|
|
48355
49044
|
readdirSync: readdirSync3,
|
|
48356
49045
|
statSync: statSync6,
|
|
48357
49046
|
openSync: openSync2,
|
|
@@ -48585,8 +49274,8 @@ function startSubagentWatcher(config) {
|
|
|
48585
49274
|
function rescanSubagentDirs() {
|
|
48586
49275
|
if (stopped)
|
|
48587
49276
|
return;
|
|
48588
|
-
const claudeHome =
|
|
48589
|
-
const projectsRoot =
|
|
49277
|
+
const claudeHome = join22(agentDir, ".claude");
|
|
49278
|
+
const projectsRoot = join22(claudeHome, "projects");
|
|
48590
49279
|
if (!fs2.existsSync(projectsRoot))
|
|
48591
49280
|
return;
|
|
48592
49281
|
let projectDirs;
|
|
@@ -48603,7 +49292,7 @@ function startSubagentWatcher(config) {
|
|
|
48603
49292
|
}
|
|
48604
49293
|
continue;
|
|
48605
49294
|
}
|
|
48606
|
-
const projectPath =
|
|
49295
|
+
const projectPath = join22(projectsRoot, pDir);
|
|
48607
49296
|
let sessionDirs;
|
|
48608
49297
|
try {
|
|
48609
49298
|
sessionDirs = fs2.readdirSync(projectPath);
|
|
@@ -48613,7 +49302,7 @@ function startSubagentWatcher(config) {
|
|
|
48613
49302
|
for (const sDir of sessionDirs) {
|
|
48614
49303
|
if (sDir.endsWith(".jsonl"))
|
|
48615
49304
|
continue;
|
|
48616
|
-
const subagentsPath =
|
|
49305
|
+
const subagentsPath = join22(projectPath, sDir, "subagents");
|
|
48617
49306
|
if (!fs2.existsSync(subagentsPath))
|
|
48618
49307
|
continue;
|
|
48619
49308
|
if (!dirWatchers.has(subagentsPath)) {
|
|
@@ -48621,7 +49310,7 @@ function startSubagentWatcher(config) {
|
|
|
48621
49310
|
const w = fs2.watch(subagentsPath, (_event, filename) => {
|
|
48622
49311
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
48623
49312
|
return;
|
|
48624
|
-
const filePath =
|
|
49313
|
+
const filePath = join22(subagentsPath, filename.toString());
|
|
48625
49314
|
if (!knownFiles.has(filePath)) {
|
|
48626
49315
|
scanSubagentsDir(subagentsPath);
|
|
48627
49316
|
}
|
|
@@ -48646,7 +49335,7 @@ function startSubagentWatcher(config) {
|
|
|
48646
49335
|
for (const e of entries) {
|
|
48647
49336
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
48648
49337
|
continue;
|
|
48649
|
-
const filePath =
|
|
49338
|
+
const filePath = join22(subagentsPath, e);
|
|
48650
49339
|
if (knownFiles.has(filePath))
|
|
48651
49340
|
continue;
|
|
48652
49341
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -48763,15 +49452,15 @@ function determineRestartReason(opts) {
|
|
|
48763
49452
|
init_boot_card();
|
|
48764
49453
|
|
|
48765
49454
|
// gateway/update-announce.ts
|
|
48766
|
-
import { existsSync as
|
|
48767
|
-
import { join as
|
|
48768
|
-
import { homedir as
|
|
49455
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync17, openSync as openSync3, closeSync as closeSync3, readFileSync as readFileSync28 } from "node:fs";
|
|
49456
|
+
import { join as join27 } from "node:path";
|
|
49457
|
+
import { homedir as homedir12 } from "node:os";
|
|
48769
49458
|
|
|
48770
49459
|
// ../src/host-control/audit-reader.ts
|
|
48771
|
-
import { homedir as
|
|
48772
|
-
import { join as
|
|
48773
|
-
function defaultAuditLogPath(home2 =
|
|
48774
|
-
return
|
|
49460
|
+
import { homedir as homedir11 } from "node:os";
|
|
49461
|
+
import { join as join26 } from "node:path";
|
|
49462
|
+
function defaultAuditLogPath(home2 = homedir11()) {
|
|
49463
|
+
return join26(home2, ".switchroom", "host-control-audit.log");
|
|
48775
49464
|
}
|
|
48776
49465
|
function parseAuditLine(line) {
|
|
48777
49466
|
const trimmed = line.trim();
|
|
@@ -48877,8 +49566,8 @@ function readAndFilter(raw, filters, limit) {
|
|
|
48877
49566
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
48878
49567
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
48879
49568
|
const path = opts.auditLogPath ?? defaultAuditLogPath();
|
|
48880
|
-
const exists = opts.exists ??
|
|
48881
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
49569
|
+
const exists = opts.exists ?? existsSync30;
|
|
49570
|
+
const readFile = opts.readFile ?? ((p) => readFileSync28(p, "utf-8"));
|
|
48882
49571
|
if (!exists(path))
|
|
48883
49572
|
return null;
|
|
48884
49573
|
let raw;
|
|
@@ -48939,15 +49628,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
48939
49628
|
`);
|
|
48940
49629
|
}
|
|
48941
49630
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
48942
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
48943
|
-
const dir =
|
|
49631
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join27(homedir12(), ".switchroom");
|
|
49632
|
+
const dir = join27(stateDir, "update-announced");
|
|
48944
49633
|
try {
|
|
48945
|
-
|
|
49634
|
+
mkdirSync17(dir, { recursive: true });
|
|
48946
49635
|
} catch {
|
|
48947
49636
|
return false;
|
|
48948
49637
|
}
|
|
48949
49638
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
48950
|
-
const path =
|
|
49639
|
+
const path = join27(dir, safeId);
|
|
48951
49640
|
try {
|
|
48952
49641
|
const fd = openSync3(path, "wx");
|
|
48953
49642
|
closeSync3(fd);
|
|
@@ -48966,7 +49655,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
48966
49655
|
}
|
|
48967
49656
|
|
|
48968
49657
|
// issues-card.ts
|
|
48969
|
-
import { readFileSync as
|
|
49658
|
+
import { readFileSync as readFileSync29, writeFileSync as writeFileSync18 } from "node:fs";
|
|
48970
49659
|
var SEVERITY_EMOJI = {
|
|
48971
49660
|
info: "\u2139\ufe0f",
|
|
48972
49661
|
warn: "\u26a0\ufe0f",
|
|
@@ -49058,7 +49747,7 @@ function extractRetryAfterSecs(err) {
|
|
|
49058
49747
|
var COOLDOWN_JITTER_MS = 500;
|
|
49059
49748
|
function readPersistedMessageId(path, log) {
|
|
49060
49749
|
try {
|
|
49061
|
-
const raw =
|
|
49750
|
+
const raw = readFileSync29(path, "utf8");
|
|
49062
49751
|
const parsed = JSON.parse(raw);
|
|
49063
49752
|
const v = parsed.messageId;
|
|
49064
49753
|
if (typeof v === "number" && Number.isInteger(v) && v > 0)
|
|
@@ -49074,7 +49763,7 @@ function readPersistedMessageId(path, log) {
|
|
|
49074
49763
|
}
|
|
49075
49764
|
function writePersistedMessageId(path, messageId, log) {
|
|
49076
49765
|
try {
|
|
49077
|
-
|
|
49766
|
+
writeFileSync18(path, JSON.stringify({ messageId }) + `
|
|
49078
49767
|
`, { mode: 384 });
|
|
49079
49768
|
} catch (err) {
|
|
49080
49769
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -49167,24 +49856,24 @@ function createIssuesCardHandle(opts) {
|
|
|
49167
49856
|
}
|
|
49168
49857
|
|
|
49169
49858
|
// issues-watcher.ts
|
|
49170
|
-
import { existsSync as
|
|
49171
|
-
import { join as
|
|
49859
|
+
import { existsSync as existsSync32, statSync as statSync8 } from "node:fs";
|
|
49860
|
+
import { join as join29 } from "node:path";
|
|
49172
49861
|
|
|
49173
49862
|
// ../src/issues/store.ts
|
|
49174
49863
|
import {
|
|
49175
49864
|
closeSync as closeSync4,
|
|
49176
|
-
existsSync as
|
|
49177
|
-
mkdirSync as
|
|
49865
|
+
existsSync as existsSync31,
|
|
49866
|
+
mkdirSync as mkdirSync18,
|
|
49178
49867
|
openSync as openSync4,
|
|
49179
49868
|
readdirSync as readdirSync5,
|
|
49180
|
-
readFileSync as
|
|
49869
|
+
readFileSync as readFileSync30,
|
|
49181
49870
|
renameSync as renameSync11,
|
|
49182
49871
|
statSync as statSync7,
|
|
49183
|
-
unlinkSync as
|
|
49184
|
-
writeFileSync as
|
|
49872
|
+
unlinkSync as unlinkSync11,
|
|
49873
|
+
writeFileSync as writeFileSync19,
|
|
49185
49874
|
writeSync
|
|
49186
49875
|
} from "node:fs";
|
|
49187
|
-
import { join as
|
|
49876
|
+
import { join as join28 } from "node:path";
|
|
49188
49877
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
49189
49878
|
import { execSync } from "node:child_process";
|
|
49190
49879
|
|
|
@@ -49200,12 +49889,12 @@ var SEVERITY_RANK2 = {
|
|
|
49200
49889
|
var ISSUES_FILE = "issues.jsonl";
|
|
49201
49890
|
var ISSUES_LOCK = "issues.lock";
|
|
49202
49891
|
function readAll(stateDir) {
|
|
49203
|
-
const path =
|
|
49204
|
-
if (!
|
|
49892
|
+
const path = join28(stateDir, ISSUES_FILE);
|
|
49893
|
+
if (!existsSync31(path))
|
|
49205
49894
|
return [];
|
|
49206
49895
|
let raw;
|
|
49207
49896
|
try {
|
|
49208
|
-
raw =
|
|
49897
|
+
raw = readFileSync30(path, "utf-8");
|
|
49209
49898
|
} catch {
|
|
49210
49899
|
return [];
|
|
49211
49900
|
}
|
|
@@ -49237,7 +49926,7 @@ function list(stateDir, opts = {}) {
|
|
|
49237
49926
|
});
|
|
49238
49927
|
}
|
|
49239
49928
|
function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
49240
|
-
if (!
|
|
49929
|
+
if (!existsSync31(join28(stateDir, ISSUES_FILE)))
|
|
49241
49930
|
return 0;
|
|
49242
49931
|
return withLock(stateDir, () => {
|
|
49243
49932
|
const all = readAll(stateDir);
|
|
@@ -49255,13 +49944,13 @@ function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
49255
49944
|
});
|
|
49256
49945
|
}
|
|
49257
49946
|
function writeAll(stateDir, events) {
|
|
49258
|
-
const path =
|
|
49947
|
+
const path = join28(stateDir, ISSUES_FILE);
|
|
49259
49948
|
sweepOrphanTmpFiles(stateDir);
|
|
49260
49949
|
const tmp = `${path}.tmp-${process.pid}-${randomBytes4(4).toString("hex")}`;
|
|
49261
49950
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
49262
49951
|
`) + `
|
|
49263
49952
|
`;
|
|
49264
|
-
|
|
49953
|
+
writeFileSync19(tmp, body, "utf-8");
|
|
49265
49954
|
renameSync11(tmp, path);
|
|
49266
49955
|
}
|
|
49267
49956
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
@@ -49277,11 +49966,11 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
49277
49966
|
for (const entry of entries) {
|
|
49278
49967
|
if (!entry.startsWith(TMP_PREFIX))
|
|
49279
49968
|
continue;
|
|
49280
|
-
const tmpPath2 =
|
|
49969
|
+
const tmpPath2 = join28(stateDir, entry);
|
|
49281
49970
|
try {
|
|
49282
49971
|
const stat = statSync7(tmpPath2);
|
|
49283
49972
|
if (stat.mtimeMs < cutoff) {
|
|
49284
|
-
|
|
49973
|
+
unlinkSync11(tmpPath2);
|
|
49285
49974
|
}
|
|
49286
49975
|
} catch {}
|
|
49287
49976
|
}
|
|
@@ -49289,7 +49978,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
49289
49978
|
var LOCK_RETRY_MS = 25;
|
|
49290
49979
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
49291
49980
|
function withLock(stateDir, fn) {
|
|
49292
|
-
const lockPath =
|
|
49981
|
+
const lockPath = join28(stateDir, ISSUES_LOCK);
|
|
49293
49982
|
const startedAt = Date.now();
|
|
49294
49983
|
let fd = null;
|
|
49295
49984
|
while (fd === null) {
|
|
@@ -49317,27 +50006,27 @@ function withLock(stateDir, fn) {
|
|
|
49317
50006
|
closeSync4(fd);
|
|
49318
50007
|
} catch {}
|
|
49319
50008
|
try {
|
|
49320
|
-
|
|
50009
|
+
unlinkSync11(lockPath);
|
|
49321
50010
|
} catch {}
|
|
49322
50011
|
}
|
|
49323
50012
|
}
|
|
49324
50013
|
function tryStealStaleLock(lockPath) {
|
|
49325
50014
|
let pidStr;
|
|
49326
50015
|
try {
|
|
49327
|
-
pidStr =
|
|
50016
|
+
pidStr = readFileSync30(lockPath, "utf-8").trim();
|
|
49328
50017
|
} catch {
|
|
49329
50018
|
return true;
|
|
49330
50019
|
}
|
|
49331
50020
|
const pid = Number(pidStr);
|
|
49332
50021
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
49333
50022
|
try {
|
|
49334
|
-
|
|
50023
|
+
unlinkSync11(lockPath);
|
|
49335
50024
|
} catch {}
|
|
49336
50025
|
return true;
|
|
49337
50026
|
}
|
|
49338
50027
|
if (pid === process.pid) {
|
|
49339
50028
|
try {
|
|
49340
|
-
|
|
50029
|
+
unlinkSync11(lockPath);
|
|
49341
50030
|
} catch {}
|
|
49342
50031
|
return true;
|
|
49343
50032
|
}
|
|
@@ -49352,7 +50041,7 @@ function tryStealStaleLock(lockPath) {
|
|
|
49352
50041
|
return false;
|
|
49353
50042
|
}
|
|
49354
50043
|
try {
|
|
49355
|
-
|
|
50044
|
+
unlinkSync11(lockPath);
|
|
49356
50045
|
} catch {}
|
|
49357
50046
|
return true;
|
|
49358
50047
|
}
|
|
@@ -49374,7 +50063,7 @@ function isIssueEvent(v) {
|
|
|
49374
50063
|
// issues-watcher.ts
|
|
49375
50064
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
49376
50065
|
function startIssuesWatcher(opts) {
|
|
49377
|
-
const path =
|
|
50066
|
+
const path = join29(opts.stateDir, ISSUES_FILE);
|
|
49378
50067
|
const log = opts.log ?? (() => {});
|
|
49379
50068
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
49380
50069
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -49422,7 +50111,7 @@ function startIssuesWatcher(opts) {
|
|
|
49422
50111
|
};
|
|
49423
50112
|
}
|
|
49424
50113
|
function defaultSignatureProvider(path) {
|
|
49425
|
-
if (!
|
|
50114
|
+
if (!existsSync32(path))
|
|
49426
50115
|
return null;
|
|
49427
50116
|
try {
|
|
49428
50117
|
const stat = statSync8(path);
|
|
@@ -49906,8 +50595,8 @@ function extractFlowItems(line) {
|
|
|
49906
50595
|
}
|
|
49907
50596
|
|
|
49908
50597
|
// credits-watch.ts
|
|
49909
|
-
import { readFileSync as
|
|
49910
|
-
import { join as
|
|
50598
|
+
import { readFileSync as readFileSync31, writeFileSync as writeFileSync20, existsSync as existsSync33, mkdirSync as mkdirSync19 } from "fs";
|
|
50599
|
+
import { join as join30 } from "path";
|
|
49911
50600
|
var STATE_FILE = "credits-watch.json";
|
|
49912
50601
|
var FATAL_REASONS = new Set([
|
|
49913
50602
|
"out_of_credits",
|
|
@@ -49919,12 +50608,12 @@ function emptyCreditState() {
|
|
|
49919
50608
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
49920
50609
|
}
|
|
49921
50610
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
49922
|
-
const path =
|
|
49923
|
-
if (!
|
|
50611
|
+
const path = join30(claudeConfigDir, ".claude.json");
|
|
50612
|
+
if (!existsSync33(path))
|
|
49924
50613
|
return null;
|
|
49925
50614
|
let raw;
|
|
49926
50615
|
try {
|
|
49927
|
-
raw =
|
|
50616
|
+
raw = readFileSync31(path, "utf-8");
|
|
49928
50617
|
} catch {
|
|
49929
50618
|
return null;
|
|
49930
50619
|
}
|
|
@@ -50004,11 +50693,11 @@ function escapeHtml10(s) {
|
|
|
50004
50693
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
50005
50694
|
}
|
|
50006
50695
|
function loadCreditState(stateDir) {
|
|
50007
|
-
const path =
|
|
50008
|
-
if (!
|
|
50696
|
+
const path = join30(stateDir, STATE_FILE);
|
|
50697
|
+
if (!existsSync33(path))
|
|
50009
50698
|
return emptyCreditState();
|
|
50010
50699
|
try {
|
|
50011
|
-
const raw =
|
|
50700
|
+
const raw = readFileSync31(path, "utf-8");
|
|
50012
50701
|
const parsed = JSON.parse(raw);
|
|
50013
50702
|
if (parsed && typeof parsed === "object" && (parsed.lastNotifiedReason === null || typeof parsed.lastNotifiedReason === "string") && typeof parsed.lastNotifiedAt === "number" && Number.isFinite(parsed.lastNotifiedAt)) {
|
|
50014
50703
|
return {
|
|
@@ -50020,15 +50709,15 @@ function loadCreditState(stateDir) {
|
|
|
50020
50709
|
return emptyCreditState();
|
|
50021
50710
|
}
|
|
50022
50711
|
function saveCreditState(stateDir, state4) {
|
|
50023
|
-
|
|
50024
|
-
const path =
|
|
50025
|
-
|
|
50712
|
+
mkdirSync19(stateDir, { recursive: true });
|
|
50713
|
+
const path = join30(stateDir, STATE_FILE);
|
|
50714
|
+
writeFileSync20(path, JSON.stringify(state4, null, 2) + `
|
|
50026
50715
|
`, { mode: 384 });
|
|
50027
50716
|
}
|
|
50028
50717
|
|
|
50029
50718
|
// quota-watch.ts
|
|
50030
|
-
import { readFileSync as
|
|
50031
|
-
import { join as
|
|
50719
|
+
import { readFileSync as readFileSync32, writeFileSync as writeFileSync21, existsSync as existsSync34, mkdirSync as mkdirSync20 } from "fs";
|
|
50720
|
+
import { join as join31 } from "path";
|
|
50032
50721
|
var STATE_FILE2 = "quota-watch.json";
|
|
50033
50722
|
function emptyQuotaWatchState() {
|
|
50034
50723
|
return {};
|
|
@@ -50118,11 +50807,11 @@ function escapeHtml11(s) {
|
|
|
50118
50807
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
50119
50808
|
}
|
|
50120
50809
|
function loadQuotaWatchState(stateDir) {
|
|
50121
|
-
const path =
|
|
50122
|
-
if (!
|
|
50810
|
+
const path = join31(stateDir, STATE_FILE2);
|
|
50811
|
+
if (!existsSync34(path))
|
|
50123
50812
|
return emptyQuotaWatchState();
|
|
50124
50813
|
try {
|
|
50125
|
-
const raw =
|
|
50814
|
+
const raw = readFileSync32(path, "utf-8");
|
|
50126
50815
|
const parsed = JSON.parse(raw);
|
|
50127
50816
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
50128
50817
|
return emptyQuotaWatchState();
|
|
@@ -50139,9 +50828,9 @@ function loadQuotaWatchState(stateDir) {
|
|
|
50139
50828
|
}
|
|
50140
50829
|
}
|
|
50141
50830
|
function saveQuotaWatchState(stateDir, state4) {
|
|
50142
|
-
|
|
50143
|
-
const path =
|
|
50144
|
-
|
|
50831
|
+
mkdirSync20(stateDir, { recursive: true });
|
|
50832
|
+
const path = join31(stateDir, STATE_FILE2);
|
|
50833
|
+
writeFileSync21(path, JSON.stringify(state4, null, 2) + `
|
|
50145
50834
|
`, { mode: 384 });
|
|
50146
50835
|
}
|
|
50147
50836
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -50154,27 +50843,27 @@ init_auth_snapshot_format();
|
|
|
50154
50843
|
// gateway/turn-active-marker.ts
|
|
50155
50844
|
import {
|
|
50156
50845
|
closeSync as closeSync5,
|
|
50157
|
-
existsSync as
|
|
50158
|
-
mkdirSync as
|
|
50846
|
+
existsSync as existsSync35,
|
|
50847
|
+
mkdirSync as mkdirSync21,
|
|
50159
50848
|
openSync as openSync5,
|
|
50160
|
-
readFileSync as
|
|
50849
|
+
readFileSync as readFileSync33,
|
|
50161
50850
|
statSync as statSync9,
|
|
50162
|
-
unlinkSync as
|
|
50851
|
+
unlinkSync as unlinkSync12,
|
|
50163
50852
|
utimesSync as utimesSync2,
|
|
50164
|
-
writeFileSync as
|
|
50853
|
+
writeFileSync as writeFileSync22
|
|
50165
50854
|
} from "node:fs";
|
|
50166
|
-
import { join as
|
|
50855
|
+
import { join as join32 } from "node:path";
|
|
50167
50856
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
50168
50857
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
50169
50858
|
try {
|
|
50170
|
-
|
|
50171
|
-
|
|
50859
|
+
mkdirSync21(stateDir, { recursive: true });
|
|
50860
|
+
writeFileSync22(join32(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
50172
50861
|
`, { mode: 384 });
|
|
50173
50862
|
} catch {}
|
|
50174
50863
|
}
|
|
50175
50864
|
function touchTurnActiveMarker2(stateDir) {
|
|
50176
|
-
const path =
|
|
50177
|
-
if (!
|
|
50865
|
+
const path = join32(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
50866
|
+
if (!existsSync35(path))
|
|
50178
50867
|
return;
|
|
50179
50868
|
const now = new Date;
|
|
50180
50869
|
try {
|
|
@@ -50188,12 +50877,12 @@ function touchTurnActiveMarker2(stateDir) {
|
|
|
50188
50877
|
}
|
|
50189
50878
|
function removeTurnActiveMarker(stateDir) {
|
|
50190
50879
|
try {
|
|
50191
|
-
|
|
50880
|
+
unlinkSync12(join32(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
50192
50881
|
} catch {}
|
|
50193
50882
|
}
|
|
50194
50883
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
50195
|
-
const path =
|
|
50196
|
-
if (!
|
|
50884
|
+
const path = join32(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
50885
|
+
if (!existsSync35(path))
|
|
50197
50886
|
return false;
|
|
50198
50887
|
const now = opts.now ?? Date.now();
|
|
50199
50888
|
try {
|
|
@@ -50205,9 +50894,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
50205
50894
|
return false;
|
|
50206
50895
|
let payload = null;
|
|
50207
50896
|
try {
|
|
50208
|
-
payload =
|
|
50897
|
+
payload = readFileSync33(path, "utf8");
|
|
50209
50898
|
} catch {}
|
|
50210
|
-
|
|
50899
|
+
unlinkSync12(path);
|
|
50211
50900
|
if (opts.onRemove) {
|
|
50212
50901
|
try {
|
|
50213
50902
|
opts.onRemove({
|
|
@@ -50224,10 +50913,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
50224
50913
|
}
|
|
50225
50914
|
|
|
50226
50915
|
// ../src/build-info.ts
|
|
50227
|
-
var VERSION = "0.14.
|
|
50228
|
-
var COMMIT_SHA = "
|
|
50229
|
-
var COMMIT_DATE = "2026-05-
|
|
50230
|
-
var LATEST_PR =
|
|
50916
|
+
var VERSION = "0.14.10";
|
|
50917
|
+
var COMMIT_SHA = "0cd391b5";
|
|
50918
|
+
var COMMIT_DATE = "2026-05-29T05:14:25Z";
|
|
50919
|
+
var LATEST_PR = 1987;
|
|
50231
50920
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
50232
50921
|
|
|
50233
50922
|
// gateway/boot-version.ts
|
|
@@ -50298,14 +50987,14 @@ function classifyRejection(err, opts = {}) {
|
|
|
50298
50987
|
// ../src/vault/broker/client.ts
|
|
50299
50988
|
init_protocol2();
|
|
50300
50989
|
init_peercred();
|
|
50301
|
-
import * as
|
|
50990
|
+
import * as net5 from "node:net";
|
|
50302
50991
|
import * as fs2 from "node:fs";
|
|
50303
|
-
import { homedir as
|
|
50304
|
-
import { join as
|
|
50992
|
+
import { homedir as homedir13 } from "node:os";
|
|
50993
|
+
import { join as join33 } from "node:path";
|
|
50305
50994
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
50306
50995
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
50307
|
-
var LEGACY_SOCKET_PATH2 =
|
|
50308
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
50996
|
+
var LEGACY_SOCKET_PATH2 = join33(homedir13(), ".switchroom", "vault-broker.sock");
|
|
50997
|
+
var OPERATOR_SOCKET_PATH2 = join33(homedir13(), ".switchroom", "broker-operator", "sock");
|
|
50309
50998
|
function defaultBrokerSocketPath2() {
|
|
50310
50999
|
if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
|
|
50311
51000
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -50334,7 +51023,7 @@ async function rpc2(req, opts) {
|
|
|
50334
51023
|
settled = true;
|
|
50335
51024
|
resolve7(val);
|
|
50336
51025
|
};
|
|
50337
|
-
const client3 = new
|
|
51026
|
+
const client3 = new net5.Socket;
|
|
50338
51027
|
const timer3 = setTimeout(() => {
|
|
50339
51028
|
client3.destroy();
|
|
50340
51029
|
settle({ kind: "unreachable", msg: `broker did not respond within ${timeoutMs}ms` });
|
|
@@ -50428,7 +51117,7 @@ async function unlockViaBroker(passphrase, opts) {
|
|
|
50428
51117
|
client3.destroy();
|
|
50429
51118
|
settle({ ok: false, msg: "Timeout waiting for broker" });
|
|
50430
51119
|
}, timeoutMs);
|
|
50431
|
-
const client3 =
|
|
51120
|
+
const client3 = net5.createConnection({ path: unlockSocketPath });
|
|
50432
51121
|
client3.on("error", (err) => {
|
|
50433
51122
|
clearTimeout(timer3);
|
|
50434
51123
|
settle({ ok: false, msg: `Broker unreachable: ${err.message}` });
|
|
@@ -50527,8 +51216,8 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
50527
51216
|
}
|
|
50528
51217
|
|
|
50529
51218
|
// registry/turns-schema.ts
|
|
50530
|
-
import { chmodSync as
|
|
50531
|
-
import { join as
|
|
51219
|
+
import { chmodSync as chmodSync4, mkdirSync as mkdirSync22 } from "fs";
|
|
51220
|
+
import { join as join34 } from "path";
|
|
50532
51221
|
var DatabaseClass2 = null;
|
|
50533
51222
|
function loadDatabaseClass2() {
|
|
50534
51223
|
if (DatabaseClass2 != null)
|
|
@@ -50587,13 +51276,13 @@ function applySchema(db2) {
|
|
|
50587
51276
|
}
|
|
50588
51277
|
function openTurnsDb(agentDir) {
|
|
50589
51278
|
const Database = loadDatabaseClass2();
|
|
50590
|
-
const dir =
|
|
50591
|
-
|
|
50592
|
-
const path =
|
|
51279
|
+
const dir = join34(agentDir, "telegram");
|
|
51280
|
+
mkdirSync22(dir, { recursive: true, mode: 448 });
|
|
51281
|
+
const path = join34(dir, "registry.db");
|
|
50593
51282
|
const db2 = new Database(path, { create: true });
|
|
50594
51283
|
applySchema(db2);
|
|
50595
51284
|
try {
|
|
50596
|
-
|
|
51285
|
+
chmodSync4(path, 384);
|
|
50597
51286
|
} catch {}
|
|
50598
51287
|
return db2;
|
|
50599
51288
|
}
|
|
@@ -50730,11 +51419,11 @@ installGlobalErrorHandlers();
|
|
|
50730
51419
|
process.on("beforeExit", () => {
|
|
50731
51420
|
shutdownAnalytics();
|
|
50732
51421
|
});
|
|
50733
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
50734
|
-
var ACCESS_FILE =
|
|
50735
|
-
var APPROVED_DIR =
|
|
50736
|
-
var ENV_FILE =
|
|
50737
|
-
var INBOX_DIR =
|
|
51422
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join36(homedir14(), ".claude", "channels", "telegram");
|
|
51423
|
+
var ACCESS_FILE = join36(STATE_DIR, "access.json");
|
|
51424
|
+
var APPROVED_DIR = join36(STATE_DIR, "approved");
|
|
51425
|
+
var ENV_FILE = join36(STATE_DIR, ".env");
|
|
51426
|
+
var INBOX_DIR = join36(STATE_DIR, "inbox");
|
|
50738
51427
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
50739
51428
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
50740
51429
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -50798,8 +51487,8 @@ function formatBootVersion() {
|
|
|
50798
51487
|
});
|
|
50799
51488
|
}
|
|
50800
51489
|
try {
|
|
50801
|
-
|
|
50802
|
-
for (const line of
|
|
51490
|
+
chmodSync6(ENV_FILE, 384);
|
|
51491
|
+
for (const line of readFileSync36(ENV_FILE, "utf8").split(`
|
|
50803
51492
|
`)) {
|
|
50804
51493
|
const m = line.match(/^(\w+)=(.*)$/);
|
|
50805
51494
|
if (m && process.env[m[1]] === undefined)
|
|
@@ -50852,7 +51541,7 @@ installTgPostLogger(bot);
|
|
|
50852
51541
|
var _rawSendMessageDraft = bot.api.raw.sendMessageDraft;
|
|
50853
51542
|
var GRAMMY_VERSION = (() => {
|
|
50854
51543
|
try {
|
|
50855
|
-
const raw =
|
|
51544
|
+
const raw = readFileSync36(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
|
|
50856
51545
|
return JSON.parse(raw).version ?? "unknown";
|
|
50857
51546
|
} catch {
|
|
50858
51547
|
return "unknown";
|
|
@@ -50925,7 +51614,7 @@ function assertSendable(f) {
|
|
|
50925
51614
|
} catch {
|
|
50926
51615
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
50927
51616
|
}
|
|
50928
|
-
const inbox =
|
|
51617
|
+
const inbox = join36(stateReal, "inbox");
|
|
50929
51618
|
if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
|
|
50930
51619
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
50931
51620
|
}
|
|
@@ -50944,7 +51633,7 @@ function assertSendable(f) {
|
|
|
50944
51633
|
}
|
|
50945
51634
|
function readAccessFile() {
|
|
50946
51635
|
try {
|
|
50947
|
-
const raw =
|
|
51636
|
+
const raw = readFileSync36(ACCESS_FILE, "utf8");
|
|
50948
51637
|
const parsed = JSON.parse(raw);
|
|
50949
51638
|
const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
|
|
50950
51639
|
const groups = {};
|
|
@@ -51009,9 +51698,9 @@ function assertAllowedChat(chat_id) {
|
|
|
51009
51698
|
function saveAccess(a) {
|
|
51010
51699
|
if (STATIC)
|
|
51011
51700
|
return;
|
|
51012
|
-
|
|
51701
|
+
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
51013
51702
|
const tmp = ACCESS_FILE + ".tmp";
|
|
51014
|
-
|
|
51703
|
+
writeFileSync25(tmp, JSON.stringify(a, null, 2) + `
|
|
51015
51704
|
`, { mode: 384 });
|
|
51016
51705
|
renameSync13(tmp, ACCESS_FILE);
|
|
51017
51706
|
}
|
|
@@ -51031,7 +51720,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
51031
51720
|
if (HISTORY_ENABLED) {
|
|
51032
51721
|
try {
|
|
51033
51722
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
51034
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
51723
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join36(STATE_DIR, "history.db")}
|
|
51035
51724
|
`);
|
|
51036
51725
|
} catch (err) {
|
|
51037
51726
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -51048,10 +51737,10 @@ try {
|
|
|
51048
51737
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s) as ended_via='restart'
|
|
51049
51738
|
`);
|
|
51050
51739
|
} else {
|
|
51051
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
51740
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join36(agentDir, "telegram", "registry.db")}
|
|
51052
51741
|
`);
|
|
51053
51742
|
}
|
|
51054
|
-
const pendingEnvPath =
|
|
51743
|
+
const pendingEnvPath = join36(agentDir, ".pending-turn.env");
|
|
51055
51744
|
try {
|
|
51056
51745
|
const pending2 = findMostRecentInterruptedTurn(turnsDb);
|
|
51057
51746
|
if (pending2 != null) {
|
|
@@ -51065,13 +51754,13 @@ try {
|
|
|
51065
51754
|
`SWITCHROOM_PENDING_STARTED_AT=${pending2.started_at}`
|
|
51066
51755
|
];
|
|
51067
51756
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
51068
|
-
|
|
51757
|
+
writeFileSync25(pendingEnvTmp, lines.join(`
|
|
51069
51758
|
`) + `
|
|
51070
51759
|
`, { mode: 384 });
|
|
51071
51760
|
renameSync13(pendingEnvTmp, pendingEnvPath);
|
|
51072
51761
|
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
51073
51762
|
`);
|
|
51074
|
-
} else if (
|
|
51763
|
+
} else if (existsSync39(pendingEnvPath)) {
|
|
51075
51764
|
rmSync4(pendingEnvPath, { force: true });
|
|
51076
51765
|
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
51077
51766
|
`);
|
|
@@ -51125,7 +51814,7 @@ function checkApprovals() {
|
|
|
51125
51814
|
return;
|
|
51126
51815
|
}
|
|
51127
51816
|
for (const senderId of files) {
|
|
51128
|
-
const file =
|
|
51817
|
+
const file = join36(APPROVED_DIR, senderId);
|
|
51129
51818
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync4(file, { force: true }), (err) => {
|
|
51130
51819
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
51131
51820
|
`);
|
|
@@ -52014,11 +52703,11 @@ var unpinProgressCardForChat = null;
|
|
|
52014
52703
|
var getPinnedProgressCardMessageId = null;
|
|
52015
52704
|
var completeProgressCardTurn = null;
|
|
52016
52705
|
var subagentWatcher = null;
|
|
52017
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
52018
|
-
|
|
52019
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
52020
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
52021
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
52706
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join36(STATE_DIR, "gateway.sock");
|
|
52707
|
+
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
52708
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join36(STATE_DIR, "gateway.pid.json");
|
|
52709
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join36(STATE_DIR, "gateway-session.json");
|
|
52710
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join36(STATE_DIR, "clean-shutdown.json");
|
|
52022
52711
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
52023
52712
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
52024
52713
|
var activeBootCard = null;
|
|
@@ -52047,7 +52736,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
52047
52736
|
bot: botApi,
|
|
52048
52737
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
52049
52738
|
`),
|
|
52050
|
-
persistPath:
|
|
52739
|
+
persistPath: join36(stateDir, "issues-card.json")
|
|
52051
52740
|
});
|
|
52052
52741
|
activeIssuesWatcher = startIssuesWatcher({
|
|
52053
52742
|
stateDir,
|
|
@@ -52221,13 +52910,13 @@ startTimer2({
|
|
|
52221
52910
|
}
|
|
52222
52911
|
});
|
|
52223
52912
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
52224
|
-
path:
|
|
52913
|
+
path: join36(STATE_DIR, "inbound-spool.jsonl"),
|
|
52225
52914
|
fs: {
|
|
52226
|
-
appendFileSync: (p, d) =>
|
|
52227
|
-
readFileSync: (p) =>
|
|
52228
|
-
writeFileSync: (p, d) =>
|
|
52915
|
+
appendFileSync: (p, d) => appendFileSync5(p, d),
|
|
52916
|
+
readFileSync: (p) => readFileSync36(p, "utf8"),
|
|
52917
|
+
writeFileSync: (p, d) => writeFileSync25(p, d),
|
|
52229
52918
|
renameSync: (a, b) => renameSync13(a, b),
|
|
52230
|
-
existsSync: (p) =>
|
|
52919
|
+
existsSync: (p) => existsSync39(p),
|
|
52231
52920
|
statSizeSync: (p) => statSync13(p).size
|
|
52232
52921
|
}
|
|
52233
52922
|
});
|
|
@@ -52354,7 +53043,7 @@ var ipcServer = createIpcServer({
|
|
|
52354
53043
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
52355
53044
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
52356
53045
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
52357
|
-
configSnapshotPath:
|
|
53046
|
+
configSnapshotPath: join36(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
52358
53047
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
52359
53048
|
}, ackMsgId).then((handle) => {
|
|
52360
53049
|
activeBootCard = handle;
|
|
@@ -52769,6 +53458,62 @@ ${reminder}
|
|
|
52769
53458
|
log: (msg) => process.stderr.write(`telegram gateway: ipc \u2014 ${msg}
|
|
52770
53459
|
`)
|
|
52771
53460
|
});
|
|
53461
|
+
(() => {
|
|
53462
|
+
try {
|
|
53463
|
+
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
53464
|
+
if (!selfAgent)
|
|
53465
|
+
return;
|
|
53466
|
+
let viaGateway = false;
|
|
53467
|
+
try {
|
|
53468
|
+
const cfg = loadConfig2();
|
|
53469
|
+
const raw = cfg.agents?.[selfAgent];
|
|
53470
|
+
viaGateway = raw ? resolveAgentConfig2(cfg.defaults, cfg.profiles, raw).channels?.telegram?.webhook_via_gateway === true : false;
|
|
53471
|
+
} catch (err) {
|
|
53472
|
+
process.stderr.write(`telegram gateway: webhook-ingest config probe failed: ${err.message}
|
|
53473
|
+
`);
|
|
53474
|
+
}
|
|
53475
|
+
if (!viaGateway)
|
|
53476
|
+
return;
|
|
53477
|
+
const allowedUids = [];
|
|
53478
|
+
const ownUid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
53479
|
+
if (ownUid !== null)
|
|
53480
|
+
allowedUids.push(ownUid);
|
|
53481
|
+
const receiverUidRaw = process.env.SWITCHROOM_WEBHOOK_RECEIVER_UID;
|
|
53482
|
+
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
53483
|
+
if (Number.isInteger(receiverUid))
|
|
53484
|
+
allowedUids.push(receiverUid);
|
|
53485
|
+
const socketPath = join36(STATE_DIR, "webhook.sock");
|
|
53486
|
+
const webhookInject = (agentName3, inbound) => {
|
|
53487
|
+
const msg = inbound;
|
|
53488
|
+
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
53489
|
+
if (delivered)
|
|
53490
|
+
markClaudeBusyForInbound(msg);
|
|
53491
|
+
else
|
|
53492
|
+
pendingInboundBuffer.push(agentName3, msg);
|
|
53493
|
+
return delivered;
|
|
53494
|
+
};
|
|
53495
|
+
startWebhookIngestServer({
|
|
53496
|
+
socketPath,
|
|
53497
|
+
allowedUids,
|
|
53498
|
+
log: (s) => process.stderr.write(`telegram gateway: ${s}`),
|
|
53499
|
+
onRecord: (req) => recordWebhookEvent({
|
|
53500
|
+
agent: selfAgent,
|
|
53501
|
+
source: req.source,
|
|
53502
|
+
event_type: req.event_type,
|
|
53503
|
+
ts: req.ts,
|
|
53504
|
+
rendered_text: req.rendered_text,
|
|
53505
|
+
payload: req.payload,
|
|
53506
|
+
...req.delivery_id ? { delivery_id: req.delivery_id } : {}
|
|
53507
|
+
}, {
|
|
53508
|
+
inject: webhookInject,
|
|
53509
|
+
log: (s) => process.stderr.write(`telegram gateway: ${s}`)
|
|
53510
|
+
})
|
|
53511
|
+
});
|
|
53512
|
+
} catch (err) {
|
|
53513
|
+
process.stderr.write(`telegram gateway: webhook-ingest server start failed (non-fatal): ${err.message}
|
|
53514
|
+
`);
|
|
53515
|
+
}
|
|
53516
|
+
})();
|
|
52772
53517
|
var IDLE_DRAIN_INTERVAL_MS = 5000;
|
|
52773
53518
|
if (!STATIC) {
|
|
52774
53519
|
setInterval(() => {
|
|
@@ -53734,11 +54479,11 @@ async function executeSendGif(rawArgs) {
|
|
|
53734
54479
|
};
|
|
53735
54480
|
}
|
|
53736
54481
|
async function publishToTelegraph(text, shortName, authorName) {
|
|
53737
|
-
const accountPath =
|
|
54482
|
+
const accountPath = join36(STATE_DIR, "telegraph-account.json");
|
|
53738
54483
|
let account = null;
|
|
53739
54484
|
try {
|
|
53740
|
-
if (
|
|
53741
|
-
const raw =
|
|
54485
|
+
if (existsSync39(accountPath)) {
|
|
54486
|
+
const raw = readFileSync36(accountPath, "utf-8");
|
|
53742
54487
|
const parsed = JSON.parse(raw);
|
|
53743
54488
|
if (parsed.shortName && parsed.accessToken) {
|
|
53744
54489
|
account = parsed;
|
|
@@ -53757,8 +54502,8 @@ async function publishToTelegraph(text, shortName, authorName) {
|
|
|
53757
54502
|
}
|
|
53758
54503
|
account = created.value;
|
|
53759
54504
|
try {
|
|
53760
|
-
|
|
53761
|
-
|
|
54505
|
+
mkdirSync26(STATE_DIR, { recursive: true, mode: 448 });
|
|
54506
|
+
writeFileSync25(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
53762
54507
|
} catch (err) {
|
|
53763
54508
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
53764
54509
|
`);
|
|
@@ -54000,9 +54745,9 @@ async function executeDownloadAttachment(args) {
|
|
|
54000
54745
|
fileUniqueId: file.file_unique_id,
|
|
54001
54746
|
now: Date.now()
|
|
54002
54747
|
});
|
|
54003
|
-
|
|
54748
|
+
mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
|
|
54004
54749
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
54005
|
-
|
|
54750
|
+
writeFileSync25(dlPath, buf, { mode: 384 });
|
|
54006
54751
|
return { content: [{ type: "text", text: dlPath }] };
|
|
54007
54752
|
}
|
|
54008
54753
|
async function executeEditMessage(args) {
|
|
@@ -55777,14 +56522,14 @@ function restartMarkerPath() {
|
|
|
55777
56522
|
const agentDir = resolveAgentDirFromEnv();
|
|
55778
56523
|
if (!agentDir)
|
|
55779
56524
|
return null;
|
|
55780
|
-
return
|
|
56525
|
+
return join36(agentDir, "restart-pending.json");
|
|
55781
56526
|
}
|
|
55782
56527
|
function writeRestartMarker(marker) {
|
|
55783
56528
|
const p = restartMarkerPath();
|
|
55784
56529
|
if (!p)
|
|
55785
56530
|
return;
|
|
55786
56531
|
try {
|
|
55787
|
-
|
|
56532
|
+
writeFileSync25(p, JSON.stringify(marker));
|
|
55788
56533
|
lastPlannedRestartAt = Date.now();
|
|
55789
56534
|
process.stderr.write(`telegram gateway: restart-marker: write chat_id=${marker.chat_id} thread_id=${marker.thread_id ?? "-"} ack=${marker.ack_message_id ?? "-"} path=${p}
|
|
55790
56535
|
`);
|
|
@@ -55803,7 +56548,7 @@ function readRestartMarker() {
|
|
|
55803
56548
|
if (!p)
|
|
55804
56549
|
return null;
|
|
55805
56550
|
try {
|
|
55806
|
-
return JSON.parse(
|
|
56551
|
+
return JSON.parse(readFileSync36(p, "utf8"));
|
|
55807
56552
|
} catch {
|
|
55808
56553
|
return null;
|
|
55809
56554
|
}
|
|
@@ -55901,7 +56646,7 @@ var _dockerReachable;
|
|
|
55901
56646
|
function isDockerReachable() {
|
|
55902
56647
|
if (_dockerReachable !== undefined)
|
|
55903
56648
|
return _dockerReachable;
|
|
55904
|
-
if (!
|
|
56649
|
+
if (!existsSync39("/var/run/docker.sock")) {
|
|
55905
56650
|
_dockerReachable = false;
|
|
55906
56651
|
return _dockerReachable;
|
|
55907
56652
|
}
|
|
@@ -55918,12 +56663,12 @@ function _resetDockerReachableCache() {
|
|
|
55918
56663
|
}
|
|
55919
56664
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
55920
56665
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
55921
|
-
const logPath =
|
|
56666
|
+
const logPath = join36(STATE_DIR, "detached-spawn.log");
|
|
55922
56667
|
let outFd = null;
|
|
55923
56668
|
try {
|
|
55924
|
-
|
|
56669
|
+
mkdirSync26(STATE_DIR, { recursive: true });
|
|
55925
56670
|
outFd = openSync8(logPath, "a");
|
|
55926
|
-
|
|
56671
|
+
writeFileSync25(logPath, `
|
|
55927
56672
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
55928
56673
|
`, { flag: "a" });
|
|
55929
56674
|
} catch {}
|
|
@@ -55949,7 +56694,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
55949
56694
|
return;
|
|
55950
56695
|
let tail = "";
|
|
55951
56696
|
try {
|
|
55952
|
-
const full =
|
|
56697
|
+
const full = readFileSync36(logPath, "utf8");
|
|
55953
56698
|
tail = full.split(`
|
|
55954
56699
|
`).slice(-30).join(`
|
|
55955
56700
|
`).trim();
|
|
@@ -56291,10 +57036,10 @@ bot.use(async (ctx, next) => {
|
|
|
56291
57036
|
});
|
|
56292
57037
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
56293
57038
|
try {
|
|
56294
|
-
const auditPath =
|
|
56295
|
-
if (!
|
|
57039
|
+
const auditPath = join36(homedir14(), ".switchroom", "vault-audit.log");
|
|
57040
|
+
if (!existsSync39(auditPath))
|
|
56296
57041
|
return [];
|
|
56297
|
-
const raw =
|
|
57042
|
+
const raw = readFileSync36(auditPath, "utf8");
|
|
56298
57043
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
56299
57044
|
} catch {
|
|
56300
57045
|
return [];
|
|
@@ -56345,7 +57090,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
56345
57090
|
try {
|
|
56346
57091
|
const agentDir = resolveAgentDirFromEnv();
|
|
56347
57092
|
if (agentDir) {
|
|
56348
|
-
const raw =
|
|
57093
|
+
const raw = readFileSync36(join36(agentDir, ".claude", ".claude.json"), "utf8");
|
|
56349
57094
|
claudeJson = JSON.parse(raw);
|
|
56350
57095
|
}
|
|
56351
57096
|
} catch {}
|
|
@@ -56559,10 +57304,10 @@ bot.command("restart", async (ctx) => {
|
|
|
56559
57304
|
function flushAgentHandoff(agentDir) {
|
|
56560
57305
|
let removed = 0;
|
|
56561
57306
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
56562
|
-
const p =
|
|
57307
|
+
const p = join36(agentDir, fname);
|
|
56563
57308
|
try {
|
|
56564
|
-
if (
|
|
56565
|
-
|
|
57309
|
+
if (existsSync39(p)) {
|
|
57310
|
+
unlinkSync15(p);
|
|
56566
57311
|
removed++;
|
|
56567
57312
|
}
|
|
56568
57313
|
} catch (err) {
|
|
@@ -56617,7 +57362,7 @@ async function handleNewOrResetCommand(ctx, kind) {
|
|
|
56617
57362
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
56618
57363
|
if (agentDir != null) {
|
|
56619
57364
|
try {
|
|
56620
|
-
|
|
57365
|
+
writeFileSync25(join36(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
56621
57366
|
`, "utf8");
|
|
56622
57367
|
} catch (err) {
|
|
56623
57368
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -56976,16 +57721,16 @@ bot.command("interrupt", async (ctx) => {
|
|
|
56976
57721
|
await runSwitchroomCommand(ctx, ["agent", "interrupt", name], `interrupt ${name}`);
|
|
56977
57722
|
});
|
|
56978
57723
|
var lockoutOps = {
|
|
56979
|
-
readFileSync: (p, enc) =>
|
|
56980
|
-
writeFileSync: (p, data, opts) =>
|
|
56981
|
-
existsSync: (p) =>
|
|
56982
|
-
mkdirSync: (p, opts) =>
|
|
56983
|
-
joinPath: (...parts) =>
|
|
57724
|
+
readFileSync: (p, enc) => readFileSync36(p, enc),
|
|
57725
|
+
writeFileSync: (p, data, opts) => writeFileSync25(p, data, opts),
|
|
57726
|
+
existsSync: (p) => existsSync39(p),
|
|
57727
|
+
mkdirSync: (p, opts) => mkdirSync26(p, opts),
|
|
57728
|
+
joinPath: (...parts) => join36(...parts)
|
|
56984
57729
|
};
|
|
56985
57730
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
56986
57731
|
function isAuthBrokerSocketReachable() {
|
|
56987
57732
|
try {
|
|
56988
|
-
return
|
|
57733
|
+
return existsSync39(resolveAuthBrokerSocketPath2());
|
|
56989
57734
|
} catch {
|
|
56990
57735
|
return false;
|
|
56991
57736
|
}
|
|
@@ -57046,7 +57791,7 @@ async function runCreditWatch() {
|
|
|
57046
57791
|
if (!agentDir)
|
|
57047
57792
|
return;
|
|
57048
57793
|
const agentName3 = getMyAgentName();
|
|
57049
|
-
const claudeConfigDir =
|
|
57794
|
+
const claudeConfigDir = join36(agentDir, ".claude");
|
|
57050
57795
|
const stateDir = STATE_DIR;
|
|
57051
57796
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
57052
57797
|
const prev = loadCreditState(stateDir);
|
|
@@ -57349,10 +58094,10 @@ async function handleVaultRecentDenialCallback(ctx, data) {
|
|
|
57349
58094
|
return;
|
|
57350
58095
|
}
|
|
57351
58096
|
const { token, id } = result;
|
|
57352
|
-
const tokenPath =
|
|
58097
|
+
const tokenPath = join36(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
|
|
57353
58098
|
try {
|
|
57354
|
-
|
|
57355
|
-
|
|
58099
|
+
mkdirSync26(join36(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
|
|
58100
|
+
writeFileSync25(tokenPath, token, { mode: 384 });
|
|
57356
58101
|
} catch (err) {
|
|
57357
58102
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
57358
58103
|
<i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(agentName3)} --keys ${escapeHtmlForTg(keyName)} --duration 30d</code> on the host.</i>`, { html: true });
|
|
@@ -57428,10 +58173,10 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
|
|
|
57428
58173
|
return;
|
|
57429
58174
|
}
|
|
57430
58175
|
const { token, id } = result;
|
|
57431
|
-
const tokenPath =
|
|
58176
|
+
const tokenPath = join36(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
|
|
57432
58177
|
try {
|
|
57433
|
-
|
|
57434
|
-
|
|
58178
|
+
mkdirSync26(join36(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
|
|
58179
|
+
writeFileSync25(tokenPath, token, { mode: 384 });
|
|
57435
58180
|
} catch (err) {
|
|
57436
58181
|
await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
|
|
57437
58182
|
<i>Recover with: <code>switchroom vault grant ${escapeHtmlForTg(pending2.agent)} --keys ${escapeHtmlForTg(pending2.key)} --duration ${Math.round(pending2.ttl_seconds / 86400)}d</code> on the host.</i>`, { html: true });
|
|
@@ -57906,10 +58651,10 @@ async function executeGrantWizard(ctx, chatId, state4) {
|
|
|
57906
58651
|
return;
|
|
57907
58652
|
}
|
|
57908
58653
|
const { token, id } = result;
|
|
57909
|
-
const tokenPath =
|
|
58654
|
+
const tokenPath = join36(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
|
|
57910
58655
|
try {
|
|
57911
|
-
|
|
57912
|
-
|
|
58656
|
+
mkdirSync26(join36(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
|
|
58657
|
+
writeFileSync25(tokenPath, token, { mode: 384 });
|
|
57913
58658
|
} catch (err) {
|
|
57914
58659
|
await switchroomReply(ctx, `<b>Grant created but token write failed:</b> ${escapeHtmlForTg(String(err))}`, { html: true });
|
|
57915
58660
|
return;
|
|
@@ -58757,7 +59502,7 @@ bot.command("usage", async (ctx) => {
|
|
|
58757
59502
|
await switchroomReply(ctx, "<b>/usage:</b> cannot resolve agent dir.", { html: true });
|
|
58758
59503
|
return;
|
|
58759
59504
|
}
|
|
58760
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
59505
|
+
const result = await fetchQuota2({ claudeConfigDir: join36(agentDir, ".claude") });
|
|
58761
59506
|
if (!result.ok) {
|
|
58762
59507
|
await switchroomReply(ctx, `<b>/usage:</b> ${escapeHtmlForTg(result.reason)}`, { html: true });
|
|
58763
59508
|
return;
|
|
@@ -59198,7 +59943,7 @@ ${prettyInput}`;
|
|
|
59198
59943
|
const unifiedDiff = (() => {
|
|
59199
59944
|
try {
|
|
59200
59945
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
59201
|
-
const raw =
|
|
59946
|
+
const raw = readFileSync36(cfgPath, "utf8");
|
|
59202
59947
|
return synthesizeAllowRuleDiff({ agentName: agentName3, rule: rule.rule, configText: raw });
|
|
59203
59948
|
} catch (err) {
|
|
59204
59949
|
process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
|
|
@@ -59335,9 +60080,9 @@ bot.on("message:photo", async (ctx) => {
|
|
|
59335
60080
|
fileUniqueId: best.file_unique_id,
|
|
59336
60081
|
now: Date.now()
|
|
59337
60082
|
});
|
|
59338
|
-
|
|
60083
|
+
mkdirSync26(INBOX_DIR, { recursive: true, mode: 448 });
|
|
59339
60084
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
59340
|
-
|
|
60085
|
+
writeFileSync25(dlPath, buf, { mode: 384 });
|
|
59341
60086
|
return dlPath;
|
|
59342
60087
|
} catch (err) {
|
|
59343
60088
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -59377,8 +60122,8 @@ async function maybeTranscribeVoice(fileId, mimeType, language) {
|
|
|
59377
60122
|
let apiKey = null;
|
|
59378
60123
|
try {
|
|
59379
60124
|
const path = __require("path").join(__require("os").homedir(), ".switchroom", "openai-api-key");
|
|
59380
|
-
if (
|
|
59381
|
-
apiKey =
|
|
60125
|
+
if (existsSync39(path)) {
|
|
60126
|
+
apiKey = readFileSync36(path, "utf-8").trim();
|
|
59382
60127
|
}
|
|
59383
60128
|
} catch (err) {
|
|
59384
60129
|
process.stderr.write(`telegram gateway: voice-in: failed to read api key: ${err.message}
|
|
@@ -60234,7 +60979,7 @@ var didOneTimeSetup = false;
|
|
|
60234
60979
|
return;
|
|
60235
60980
|
}
|
|
60236
60981
|
})();
|
|
60237
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
60982
|
+
const resolvedAgentDirForBootCard = agentDir ?? join36(homedir14(), ".switchroom", "agents", agentSlug);
|
|
60238
60983
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
60239
60984
|
agentName: agentDisplayName,
|
|
60240
60985
|
agentSlug,
|
|
@@ -60248,7 +60993,7 @@ var didOneTimeSetup = false;
|
|
|
60248
60993
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
60249
60994
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
60250
60995
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
60251
|
-
configSnapshotPath:
|
|
60996
|
+
configSnapshotPath: join36(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
60252
60997
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
60253
60998
|
}, ackMsgId);
|
|
60254
60999
|
activeBootCard = handle;
|