switchroom 0.13.24 → 0.13.25
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 +2 -4
- package/dist/auth-broker/index.js +31 -9
- package/dist/cli/switchroom.js +366 -343
- package/dist/host-control/main.js +2 -4
- package/dist/vault/approvals/kernel-server.js +68 -65
- package/dist/vault/broker/server.js +9 -8
- package/package.json +1 -1
- package/profiles/_shared/telegram-style.md.hbs +4 -4
- package/telegram-plugin/dist/gateway/gateway.js +34 -29
- package/telegram-plugin/tests/telegram-format.test.ts +2 -2
- package/telegram-plugin/uat/scenarios/jtbd-rapid-followup-dm.test.ts +1 -1
package/dist/cli/switchroom.js
CHANGED
|
@@ -13598,7 +13598,6 @@ var init_schema = __esm(() => {
|
|
|
13598
13598
|
SessionContinuitySchema = exports_external.object({
|
|
13599
13599
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
13600
13600
|
show_handoff_line: exports_external.boolean().optional().describe("Whether the telegram plugin prepends a visible '\u21a9\ufe0f Picked up\u2026' " + "line to the first assistant reply after a restart (default true)."),
|
|
13601
|
-
summarizer_model: exports_external.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._\-/\[\]:]*$/, "Model name must be alphanumeric with ._-/[]: only").optional().describe("Anthropic model used to produce the handoff briefing."),
|
|
13602
13601
|
max_turns_in_briefing: exports_external.number().int().positive().optional().describe("Cap on recent user/assistant turn pairs fed to the summarizer."),
|
|
13603
13602
|
resume_mode: exports_external.enum(["auto", "continue", "handoff", "none"]).optional().describe("How to resume the next session. 'handoff' (default as of #362) " + "never passes --continue; a fresh Claude starts each restart and " + "reads a briefing assembled from recent Telegram messages, Hindsight " + "recall, and today's daily memory file. 'auto' uses --continue when " + "the latest JSONL is smaller than resume_max_bytes, else falls back " + "to the handoff briefing. 'continue' always passes --continue. " + "'none' starts completely fresh every time."),
|
|
13604
13603
|
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale.")
|
|
@@ -13645,10 +13644,9 @@ var init_schema = __esm(() => {
|
|
|
13645
13644
|
start: exports_external.number().int().min(0).max(23),
|
|
13646
13645
|
end: exports_external.number().int().min(0).max(23),
|
|
13647
13646
|
tz: exports_external.string().optional()
|
|
13648
|
-
}).optional()
|
|
13649
|
-
model: exports_external.string().optional()
|
|
13647
|
+
}).optional()
|
|
13650
13648
|
})).optional()
|
|
13651
|
-
}).optional().describe("Auto-dispatch rules: when a verified webhook event matches a rule, " + "
|
|
13649
|
+
}).optional().describe("Auto-dispatch rules: when a verified webhook event matches a rule, " + "inject the rendered prompt into the agent's live session (#1625). " + "Supports cooldowns, quiet hours, and label/action matchers. " + "Off by default \u2014 opt in per agent. See src/web/webhook-dispatch.ts."),
|
|
13652
13650
|
webhook_rate_limit: exports_external.object({
|
|
13653
13651
|
rpm: exports_external.number().int().positive()
|
|
13654
13652
|
}).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.")
|
|
@@ -21049,6 +21047,7 @@ __export(exports_vault, {
|
|
|
21049
21047
|
setStringSecret: () => setStringSecret,
|
|
21050
21048
|
setSecret: () => setSecret,
|
|
21051
21049
|
setFilesSecret: () => setFilesSecret,
|
|
21050
|
+
setBinarySecret: () => setBinarySecret,
|
|
21052
21051
|
saveVault: () => saveVault,
|
|
21053
21052
|
removeSecret: () => removeSecret,
|
|
21054
21053
|
openVault: () => openVault,
|
|
@@ -21312,6 +21311,13 @@ function setStringSecret(passphrase, vaultPath, key, value, format, scope) {
|
|
|
21312
21311
|
}
|
|
21313
21312
|
setSecret(passphrase, vaultPath, key, entry);
|
|
21314
21313
|
}
|
|
21314
|
+
function setBinarySecret(passphrase, vaultPath, key, value, format, scope) {
|
|
21315
|
+
const entry = format ? { kind: "binary", value, format } : { kind: "binary", value };
|
|
21316
|
+
if (scope !== undefined) {
|
|
21317
|
+
entry.scope = scope;
|
|
21318
|
+
}
|
|
21319
|
+
setSecret(passphrase, vaultPath, key, entry);
|
|
21320
|
+
}
|
|
21315
21321
|
function getStringSecret(passphrase, vaultPath, key) {
|
|
21316
21322
|
const entry = getSecret(passphrase, vaultPath, key);
|
|
21317
21323
|
if (entry === null)
|
|
@@ -21369,6 +21375,315 @@ var init_vault = __esm(() => {
|
|
|
21369
21375
|
];
|
|
21370
21376
|
});
|
|
21371
21377
|
|
|
21378
|
+
// src/vault/broker/peercred-ffi.ts
|
|
21379
|
+
function getPeerCred(fd) {
|
|
21380
|
+
if (process.platform !== "linux")
|
|
21381
|
+
return null;
|
|
21382
|
+
try {
|
|
21383
|
+
const ffi = __require("bun:ffi");
|
|
21384
|
+
const { dlopen, FFIType, ptr } = ffi;
|
|
21385
|
+
const SOL_SOCKET = 1;
|
|
21386
|
+
const SO_PEERCRED = 17;
|
|
21387
|
+
const UCRED_SIZE = 12;
|
|
21388
|
+
const cache = getPeerCred;
|
|
21389
|
+
const lib = cache._lib ?? (() => {
|
|
21390
|
+
const candidates = ["libc.so.6", "libc.so"];
|
|
21391
|
+
const symbolSpec = {
|
|
21392
|
+
getsockopt: {
|
|
21393
|
+
args: [FFIType.i32, FFIType.i32, FFIType.i32, FFIType.ptr, FFIType.ptr],
|
|
21394
|
+
returns: FFIType.i32
|
|
21395
|
+
}
|
|
21396
|
+
};
|
|
21397
|
+
const errors2 = [];
|
|
21398
|
+
for (const name of candidates) {
|
|
21399
|
+
try {
|
|
21400
|
+
const opened = dlopen(name, symbolSpec);
|
|
21401
|
+
cache._lib = opened;
|
|
21402
|
+
return opened;
|
|
21403
|
+
} catch (e) {
|
|
21404
|
+
errors2.push(`${name}: ${e instanceof Error ? e.message : String(e)}`);
|
|
21405
|
+
}
|
|
21406
|
+
}
|
|
21407
|
+
process.stderr.write(`[vault-broker] peercred-ffi: dlopen failed for all libc candidates ` + `(${errors2.join("; ")}); falling back to ss-parsing.
|
|
21408
|
+
`);
|
|
21409
|
+
throw new Error("no libc candidate could be opened");
|
|
21410
|
+
})();
|
|
21411
|
+
const credBuf = new ArrayBuffer(UCRED_SIZE);
|
|
21412
|
+
const lenBuf = new Uint32Array(1);
|
|
21413
|
+
lenBuf[0] = UCRED_SIZE;
|
|
21414
|
+
const rc = lib.symbols.getsockopt(fd, SOL_SOCKET, SO_PEERCRED, ptr(credBuf), ptr(lenBuf.buffer));
|
|
21415
|
+
if (rc !== 0)
|
|
21416
|
+
return null;
|
|
21417
|
+
if (lenBuf[0] !== UCRED_SIZE)
|
|
21418
|
+
return null;
|
|
21419
|
+
const view = new DataView(credBuf);
|
|
21420
|
+
return {
|
|
21421
|
+
pid: view.getInt32(0, true),
|
|
21422
|
+
uid: view.getInt32(4, true),
|
|
21423
|
+
gid: view.getInt32(8, true)
|
|
21424
|
+
};
|
|
21425
|
+
} catch {
|
|
21426
|
+
return null;
|
|
21427
|
+
}
|
|
21428
|
+
}
|
|
21429
|
+
|
|
21430
|
+
// src/vault/broker/peercred.ts
|
|
21431
|
+
import { execFileSync } from "node:child_process";
|
|
21432
|
+
import { readFileSync as readFileSync8, readlinkSync as readlinkSync3, fstatSync } from "node:fs";
|
|
21433
|
+
function socketPathToIdentity(socketPath) {
|
|
21434
|
+
if (typeof socketPath !== "string" || socketPath.length === 0)
|
|
21435
|
+
return null;
|
|
21436
|
+
const m = socketPath.match(SOCKET_PATH_AGENT_RE) ?? socketPath.match(SOCKET_PATH_AGENT_SUBDIR_RE);
|
|
21437
|
+
if (!m)
|
|
21438
|
+
return null;
|
|
21439
|
+
const name = m[1];
|
|
21440
|
+
if (name === "operator")
|
|
21441
|
+
return { kind: "operator" };
|
|
21442
|
+
if (RESERVED_AGENT_NAMES.has(name))
|
|
21443
|
+
return null;
|
|
21444
|
+
return { kind: "agent", name };
|
|
21445
|
+
}
|
|
21446
|
+
function socketPathToAgent(socketPath) {
|
|
21447
|
+
const identity = socketPathToIdentity(socketPath);
|
|
21448
|
+
return identity?.kind === "agent" ? identity.name : null;
|
|
21449
|
+
}
|
|
21450
|
+
function isReservedAgentName(name) {
|
|
21451
|
+
return RESERVED_AGENT_NAMES.has(name);
|
|
21452
|
+
}
|
|
21453
|
+
function unlockSocketFor(dataSocketPath) {
|
|
21454
|
+
if (dataSocketPath.endsWith("/sock")) {
|
|
21455
|
+
return dataSocketPath.slice(0, -"/sock".length) + "/unlock";
|
|
21456
|
+
}
|
|
21457
|
+
return dataSocketPath.replace(/\.sock$/, ".unlock.sock");
|
|
21458
|
+
}
|
|
21459
|
+
function parseSsRows(output) {
|
|
21460
|
+
const rows = [];
|
|
21461
|
+
const lines = output.split(`
|
|
21462
|
+
`);
|
|
21463
|
+
for (const line of lines) {
|
|
21464
|
+
if (!line.trim() || line.startsWith("Netid"))
|
|
21465
|
+
continue;
|
|
21466
|
+
const tokens = line.split(/\s+/).filter((t) => t.length > 0);
|
|
21467
|
+
if (tokens.length < 8)
|
|
21468
|
+
continue;
|
|
21469
|
+
const localAddr = tokens[4];
|
|
21470
|
+
const localInode = tokens[5];
|
|
21471
|
+
const peerAddr = tokens[6];
|
|
21472
|
+
const peerInode = tokens[7];
|
|
21473
|
+
const usersToken = tokens.slice(8).join(" ");
|
|
21474
|
+
const m = usersToken.match(/users:\(\(".*?",pid=(\d+),fd=\d+\)\)/);
|
|
21475
|
+
const pid = m ? parseInt(m[1], 10) : null;
|
|
21476
|
+
rows.push({ localAddr, localInode, peerAddr, peerInode, pid });
|
|
21477
|
+
}
|
|
21478
|
+
return rows;
|
|
21479
|
+
}
|
|
21480
|
+
function findClientPids(rows, socketPath) {
|
|
21481
|
+
const pids = [];
|
|
21482
|
+
for (const serverRow of rows) {
|
|
21483
|
+
if (serverRow.localAddr !== socketPath)
|
|
21484
|
+
continue;
|
|
21485
|
+
for (const clientRow of rows) {
|
|
21486
|
+
if (clientRow.localAddr !== "*")
|
|
21487
|
+
continue;
|
|
21488
|
+
if (clientRow.localInode !== serverRow.peerInode)
|
|
21489
|
+
continue;
|
|
21490
|
+
if (clientRow.pid === null)
|
|
21491
|
+
continue;
|
|
21492
|
+
pids.push(clientRow.pid);
|
|
21493
|
+
break;
|
|
21494
|
+
}
|
|
21495
|
+
}
|
|
21496
|
+
return pids;
|
|
21497
|
+
}
|
|
21498
|
+
function findClientPidByServerInode(rows, socketPath, serverInode) {
|
|
21499
|
+
const serverInodeStr = String(serverInode);
|
|
21500
|
+
for (const serverRow of rows) {
|
|
21501
|
+
if (serverRow.localAddr !== socketPath)
|
|
21502
|
+
continue;
|
|
21503
|
+
if (serverRow.localInode !== serverInodeStr)
|
|
21504
|
+
continue;
|
|
21505
|
+
for (const clientRow of rows) {
|
|
21506
|
+
if (clientRow.localAddr !== "*")
|
|
21507
|
+
continue;
|
|
21508
|
+
if (clientRow.localInode !== serverRow.peerInode)
|
|
21509
|
+
continue;
|
|
21510
|
+
if (clientRow.pid === null)
|
|
21511
|
+
continue;
|
|
21512
|
+
return clientRow.pid;
|
|
21513
|
+
}
|
|
21514
|
+
return null;
|
|
21515
|
+
}
|
|
21516
|
+
return null;
|
|
21517
|
+
}
|
|
21518
|
+
function readUid(pid) {
|
|
21519
|
+
try {
|
|
21520
|
+
const status = readFileSync8(`/proc/${pid}/status`, "utf8");
|
|
21521
|
+
const m = status.match(/^Uid:\s+(\d+)/m);
|
|
21522
|
+
if (!m)
|
|
21523
|
+
return null;
|
|
21524
|
+
return parseInt(m[1], 10);
|
|
21525
|
+
} catch {
|
|
21526
|
+
return null;
|
|
21527
|
+
}
|
|
21528
|
+
}
|
|
21529
|
+
function readExe(pid) {
|
|
21530
|
+
try {
|
|
21531
|
+
return readlinkSync3(`/proc/${pid}/exe`);
|
|
21532
|
+
} catch {
|
|
21533
|
+
return null;
|
|
21534
|
+
}
|
|
21535
|
+
}
|
|
21536
|
+
function readSystemdUnit(pid) {
|
|
21537
|
+
try {
|
|
21538
|
+
const content = readFileSync8(`/proc/${pid}/cgroup`, "utf8");
|
|
21539
|
+
const lines = content.split(`
|
|
21540
|
+
`);
|
|
21541
|
+
for (const line of lines) {
|
|
21542
|
+
if (!line.trim())
|
|
21543
|
+
continue;
|
|
21544
|
+
const parts = line.split(":");
|
|
21545
|
+
if (parts.length < 3)
|
|
21546
|
+
continue;
|
|
21547
|
+
const controller = parts[1];
|
|
21548
|
+
const isV2 = parts[0] === "0" && controller === "";
|
|
21549
|
+
const isV1Systemd = controller === "name=systemd";
|
|
21550
|
+
if (!isV2 && !isV1Systemd)
|
|
21551
|
+
continue;
|
|
21552
|
+
const cgroupPath = parts.slice(2).join(":");
|
|
21553
|
+
const segments = cgroupPath.split("/");
|
|
21554
|
+
const lastSegment = segments[segments.length - 1];
|
|
21555
|
+
if (!lastSegment)
|
|
21556
|
+
continue;
|
|
21557
|
+
if (/^switchroom-[a-zA-Z0-9_-]+(-cron-\d+)?\.service$/.test(lastSegment)) {
|
|
21558
|
+
return lastSegment;
|
|
21559
|
+
}
|
|
21560
|
+
}
|
|
21561
|
+
return null;
|
|
21562
|
+
} catch {
|
|
21563
|
+
return null;
|
|
21564
|
+
}
|
|
21565
|
+
}
|
|
21566
|
+
function verifySystemdUnit(unitName, runner) {
|
|
21567
|
+
let raw;
|
|
21568
|
+
try {
|
|
21569
|
+
const out = runner("systemctl", [
|
|
21570
|
+
"--user",
|
|
21571
|
+
"show",
|
|
21572
|
+
unitName,
|
|
21573
|
+
"--property=LoadState,ActiveState"
|
|
21574
|
+
], { timeout: 500, encoding: "utf8" });
|
|
21575
|
+
raw = typeof out === "string" ? out : out.toString("utf8");
|
|
21576
|
+
} catch {
|
|
21577
|
+
return false;
|
|
21578
|
+
}
|
|
21579
|
+
const props = {};
|
|
21580
|
+
for (const line of raw.split(`
|
|
21581
|
+
`)) {
|
|
21582
|
+
const m = line.match(/^([A-Za-z]+)=(.*)$/);
|
|
21583
|
+
if (m)
|
|
21584
|
+
props[m[1]] = m[2];
|
|
21585
|
+
}
|
|
21586
|
+
if (props.LoadState !== "loaded")
|
|
21587
|
+
return false;
|
|
21588
|
+
if (props.ActiveState !== "active" && props.ActiveState !== "activating") {
|
|
21589
|
+
return false;
|
|
21590
|
+
}
|
|
21591
|
+
return true;
|
|
21592
|
+
}
|
|
21593
|
+
function readFdInode(fd) {
|
|
21594
|
+
try {
|
|
21595
|
+
const stat = fstatSync(fd);
|
|
21596
|
+
return stat.ino;
|
|
21597
|
+
} catch {
|
|
21598
|
+
return null;
|
|
21599
|
+
}
|
|
21600
|
+
}
|
|
21601
|
+
function fdFromSocket(socket) {
|
|
21602
|
+
const handle = socket._handle;
|
|
21603
|
+
if (!handle || typeof handle.fd !== "number" || handle.fd < 0)
|
|
21604
|
+
return null;
|
|
21605
|
+
return handle.fd;
|
|
21606
|
+
}
|
|
21607
|
+
function identify(socketPath, socket, execFileSyncOverride) {
|
|
21608
|
+
if (process.platform !== "linux") {
|
|
21609
|
+
return null;
|
|
21610
|
+
}
|
|
21611
|
+
const runner = execFileSyncOverride ?? execFileSync;
|
|
21612
|
+
let pid = null;
|
|
21613
|
+
if (socket !== undefined) {
|
|
21614
|
+
const fd = fdFromSocket(socket);
|
|
21615
|
+
if (fd !== null) {
|
|
21616
|
+
const cred = getPeerCred(fd);
|
|
21617
|
+
if (cred !== null)
|
|
21618
|
+
pid = cred.pid;
|
|
21619
|
+
}
|
|
21620
|
+
}
|
|
21621
|
+
if (pid === null) {
|
|
21622
|
+
let ssOutput;
|
|
21623
|
+
try {
|
|
21624
|
+
const raw = runner("ss", ["-xpn"], {
|
|
21625
|
+
timeout: 200,
|
|
21626
|
+
encoding: "utf8"
|
|
21627
|
+
});
|
|
21628
|
+
ssOutput = typeof raw === "string" ? raw : raw.toString("utf8");
|
|
21629
|
+
} catch {
|
|
21630
|
+
return null;
|
|
21631
|
+
}
|
|
21632
|
+
const rows = parseSsRows(ssOutput);
|
|
21633
|
+
let serverInode = null;
|
|
21634
|
+
if (socket !== undefined) {
|
|
21635
|
+
const fd = fdFromSocket(socket);
|
|
21636
|
+
if (fd !== null)
|
|
21637
|
+
serverInode = readFdInode(fd);
|
|
21638
|
+
}
|
|
21639
|
+
if (serverInode !== null) {
|
|
21640
|
+
pid = findClientPidByServerInode(rows, socketPath, serverInode);
|
|
21641
|
+
} else {
|
|
21642
|
+
const clientPids = findClientPids(rows, socketPath);
|
|
21643
|
+
if (clientPids.length === 0)
|
|
21644
|
+
return null;
|
|
21645
|
+
if (clientPids.length > 1) {
|
|
21646
|
+
process.stderr.write(`[vault-broker] peercred: ${clientPids.length} connected peers found for ${socketPath}; ` + `using pid=${clientPids[0]}. ` + `Multiple simultaneous connections reduce identification accuracy. ` + `(This warning means identify() was called without a socket arg \u2014 likely a stale call site.)
|
|
21647
|
+
`);
|
|
21648
|
+
}
|
|
21649
|
+
pid = clientPids[0];
|
|
21650
|
+
}
|
|
21651
|
+
}
|
|
21652
|
+
if (pid === null)
|
|
21653
|
+
return null;
|
|
21654
|
+
const uid = readUid(pid);
|
|
21655
|
+
if (uid === null) {
|
|
21656
|
+
return null;
|
|
21657
|
+
}
|
|
21658
|
+
const brokerUid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
21659
|
+
if (brokerUid !== null && uid !== brokerUid) {
|
|
21660
|
+
process.stderr.write(`[vault-broker] peercred: UID mismatch \u2014 caller uid=${uid}, broker uid=${brokerUid}; denying
|
|
21661
|
+
`);
|
|
21662
|
+
return null;
|
|
21663
|
+
}
|
|
21664
|
+
const exe = readExe(pid);
|
|
21665
|
+
if (exe === null) {
|
|
21666
|
+
return null;
|
|
21667
|
+
}
|
|
21668
|
+
const cgroupClaim = readSystemdUnit(pid);
|
|
21669
|
+
let systemdUnit = null;
|
|
21670
|
+
if (cgroupClaim !== null) {
|
|
21671
|
+
if (verifySystemdUnit(cgroupClaim, runner)) {
|
|
21672
|
+
systemdUnit = cgroupClaim;
|
|
21673
|
+
} else {
|
|
21674
|
+
process.stderr.write(`[vault-broker] peercred: cgroup claims unit=${cgroupClaim} but systemd-user does not report it as loaded+running; treating caller as unidentified
|
|
21675
|
+
`);
|
|
21676
|
+
}
|
|
21677
|
+
}
|
|
21678
|
+
return { uid, pid, exe, systemdUnit };
|
|
21679
|
+
}
|
|
21680
|
+
var SOCKET_PATH_AGENT_RE, SOCKET_PATH_AGENT_SUBDIR_RE, RESERVED_AGENT_NAMES;
|
|
21681
|
+
var init_peercred = __esm(() => {
|
|
21682
|
+
SOCKET_PATH_AGENT_RE = /^\/run\/switchroom\/broker\/([a-zA-Z0-9][a-zA-Z0-9_-]*)\.sock$/;
|
|
21683
|
+
SOCKET_PATH_AGENT_SUBDIR_RE = /^\/run\/switchroom\/broker\/([a-zA-Z0-9][a-zA-Z0-9_-]*)\/sock$/;
|
|
21684
|
+
RESERVED_AGENT_NAMES = new Set(["operator", "hostd"]);
|
|
21685
|
+
});
|
|
21686
|
+
|
|
21372
21687
|
// src/vault/broker/protocol.ts
|
|
21373
21688
|
function encodeRequest(req) {
|
|
21374
21689
|
const json = JSON.stringify(req);
|
|
@@ -21420,10 +21735,14 @@ function stripWireFields(entry) {
|
|
|
21420
21735
|
files: entry.files
|
|
21421
21736
|
};
|
|
21422
21737
|
}
|
|
21423
|
-
var MAX_FRAME_BYTES, GetRequestSchema, PutRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, PreflightAccessRequestSchema, OkPreflightAccessResponseSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, ApprovalConsumeRecordRequestSchema, RequestSchema, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkPutResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, OkApprovalConsumeRecordResponseSchema, ErrorResponseSchema, ResponseSchema;
|
|
21738
|
+
var MAX_FRAME_BYTES, AgentNameSchema, GetRequestSchema, PutRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, PreflightAccessRequestSchema, OkPreflightAccessResponseSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, ApprovalConsumeRecordRequestSchema, RequestSchema, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkPutResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, OkApprovalConsumeRecordResponseSchema, ErrorResponseSchema, ResponseSchema;
|
|
21424
21739
|
var init_protocol = __esm(() => {
|
|
21425
21740
|
init_zod();
|
|
21741
|
+
init_peercred();
|
|
21426
21742
|
MAX_FRAME_BYTES = 64 * 1024;
|
|
21743
|
+
AgentNameSchema = exports_external.string().min(1).max(64, "agent name max 64 chars").regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/, "agent name must be kebab-case ASCII (alnum + _- only, first char alnum)").refine((s) => !isReservedAgentName(s), {
|
|
21744
|
+
message: "agent name is reserved"
|
|
21745
|
+
});
|
|
21427
21746
|
GetRequestSchema = exports_external.object({
|
|
21428
21747
|
v: exports_external.literal(1),
|
|
21429
21748
|
op: exports_external.literal("get"),
|
|
@@ -21451,7 +21770,7 @@ var init_protocol = __esm(() => {
|
|
|
21451
21770
|
MintGrantRequestSchema = exports_external.object({
|
|
21452
21771
|
v: exports_external.literal(1),
|
|
21453
21772
|
op: exports_external.literal("mint_grant"),
|
|
21454
|
-
agent:
|
|
21773
|
+
agent: AgentNameSchema,
|
|
21455
21774
|
keys: exports_external.array(exports_external.string().min(1)),
|
|
21456
21775
|
ttl_seconds: exports_external.number().int().positive().nullable(),
|
|
21457
21776
|
description: exports_external.string().optional(),
|
|
@@ -21462,7 +21781,7 @@ var init_protocol = __esm(() => {
|
|
|
21462
21781
|
ListGrantsRequestSchema = exports_external.object({
|
|
21463
21782
|
v: exports_external.literal(1),
|
|
21464
21783
|
op: exports_external.literal("list_grants"),
|
|
21465
|
-
agent:
|
|
21784
|
+
agent: AgentNameSchema.optional(),
|
|
21466
21785
|
passphrase: exports_external.string().optional(),
|
|
21467
21786
|
attest_via_posture: exports_external.boolean().optional()
|
|
21468
21787
|
});
|
|
@@ -21482,7 +21801,7 @@ var init_protocol = __esm(() => {
|
|
|
21482
21801
|
PreflightAccessRequestSchema = exports_external.object({
|
|
21483
21802
|
v: exports_external.literal(1),
|
|
21484
21803
|
op: exports_external.literal("preflight_access"),
|
|
21485
|
-
agent:
|
|
21804
|
+
agent: AgentNameSchema,
|
|
21486
21805
|
keys: exports_external.array(exports_external.string().min(1)).min(1).max(128)
|
|
21487
21806
|
});
|
|
21488
21807
|
OkPreflightAccessResponseSchema = exports_external.object({
|
|
@@ -21730,315 +22049,6 @@ var init_protocol = __esm(() => {
|
|
|
21730
22049
|
]);
|
|
21731
22050
|
});
|
|
21732
22051
|
|
|
21733
|
-
// src/vault/broker/peercred-ffi.ts
|
|
21734
|
-
function getPeerCred(fd) {
|
|
21735
|
-
if (process.platform !== "linux")
|
|
21736
|
-
return null;
|
|
21737
|
-
try {
|
|
21738
|
-
const ffi = __require("bun:ffi");
|
|
21739
|
-
const { dlopen, FFIType, ptr } = ffi;
|
|
21740
|
-
const SOL_SOCKET = 1;
|
|
21741
|
-
const SO_PEERCRED = 17;
|
|
21742
|
-
const UCRED_SIZE = 12;
|
|
21743
|
-
const cache = getPeerCred;
|
|
21744
|
-
const lib = cache._lib ?? (() => {
|
|
21745
|
-
const candidates = ["libc.so.6", "libc.so"];
|
|
21746
|
-
const symbolSpec = {
|
|
21747
|
-
getsockopt: {
|
|
21748
|
-
args: [FFIType.i32, FFIType.i32, FFIType.i32, FFIType.ptr, FFIType.ptr],
|
|
21749
|
-
returns: FFIType.i32
|
|
21750
|
-
}
|
|
21751
|
-
};
|
|
21752
|
-
const errors2 = [];
|
|
21753
|
-
for (const name of candidates) {
|
|
21754
|
-
try {
|
|
21755
|
-
const opened = dlopen(name, symbolSpec);
|
|
21756
|
-
cache._lib = opened;
|
|
21757
|
-
return opened;
|
|
21758
|
-
} catch (e) {
|
|
21759
|
-
errors2.push(`${name}: ${e instanceof Error ? e.message : String(e)}`);
|
|
21760
|
-
}
|
|
21761
|
-
}
|
|
21762
|
-
process.stderr.write(`[vault-broker] peercred-ffi: dlopen failed for all libc candidates ` + `(${errors2.join("; ")}); falling back to ss-parsing.
|
|
21763
|
-
`);
|
|
21764
|
-
throw new Error("no libc candidate could be opened");
|
|
21765
|
-
})();
|
|
21766
|
-
const credBuf = new ArrayBuffer(UCRED_SIZE);
|
|
21767
|
-
const lenBuf = new Uint32Array(1);
|
|
21768
|
-
lenBuf[0] = UCRED_SIZE;
|
|
21769
|
-
const rc = lib.symbols.getsockopt(fd, SOL_SOCKET, SO_PEERCRED, ptr(credBuf), ptr(lenBuf.buffer));
|
|
21770
|
-
if (rc !== 0)
|
|
21771
|
-
return null;
|
|
21772
|
-
if (lenBuf[0] !== UCRED_SIZE)
|
|
21773
|
-
return null;
|
|
21774
|
-
const view = new DataView(credBuf);
|
|
21775
|
-
return {
|
|
21776
|
-
pid: view.getInt32(0, true),
|
|
21777
|
-
uid: view.getInt32(4, true),
|
|
21778
|
-
gid: view.getInt32(8, true)
|
|
21779
|
-
};
|
|
21780
|
-
} catch {
|
|
21781
|
-
return null;
|
|
21782
|
-
}
|
|
21783
|
-
}
|
|
21784
|
-
|
|
21785
|
-
// src/vault/broker/peercred.ts
|
|
21786
|
-
import { execFileSync } from "node:child_process";
|
|
21787
|
-
import { readFileSync as readFileSync8, readlinkSync as readlinkSync3, fstatSync } from "node:fs";
|
|
21788
|
-
function socketPathToIdentity(socketPath) {
|
|
21789
|
-
if (typeof socketPath !== "string" || socketPath.length === 0)
|
|
21790
|
-
return null;
|
|
21791
|
-
const m = socketPath.match(SOCKET_PATH_AGENT_RE) ?? socketPath.match(SOCKET_PATH_AGENT_SUBDIR_RE);
|
|
21792
|
-
if (!m)
|
|
21793
|
-
return null;
|
|
21794
|
-
const name = m[1];
|
|
21795
|
-
if (name === "operator")
|
|
21796
|
-
return { kind: "operator" };
|
|
21797
|
-
if (RESERVED_AGENT_NAMES.has(name))
|
|
21798
|
-
return null;
|
|
21799
|
-
return { kind: "agent", name };
|
|
21800
|
-
}
|
|
21801
|
-
function socketPathToAgent(socketPath) {
|
|
21802
|
-
const identity = socketPathToIdentity(socketPath);
|
|
21803
|
-
return identity?.kind === "agent" ? identity.name : null;
|
|
21804
|
-
}
|
|
21805
|
-
function isReservedAgentName(name) {
|
|
21806
|
-
return RESERVED_AGENT_NAMES.has(name);
|
|
21807
|
-
}
|
|
21808
|
-
function unlockSocketFor(dataSocketPath) {
|
|
21809
|
-
if (dataSocketPath.endsWith("/sock")) {
|
|
21810
|
-
return dataSocketPath.slice(0, -"/sock".length) + "/unlock";
|
|
21811
|
-
}
|
|
21812
|
-
return dataSocketPath.replace(/\.sock$/, ".unlock.sock");
|
|
21813
|
-
}
|
|
21814
|
-
function parseSsRows(output) {
|
|
21815
|
-
const rows = [];
|
|
21816
|
-
const lines = output.split(`
|
|
21817
|
-
`);
|
|
21818
|
-
for (const line of lines) {
|
|
21819
|
-
if (!line.trim() || line.startsWith("Netid"))
|
|
21820
|
-
continue;
|
|
21821
|
-
const tokens = line.split(/\s+/).filter((t) => t.length > 0);
|
|
21822
|
-
if (tokens.length < 8)
|
|
21823
|
-
continue;
|
|
21824
|
-
const localAddr = tokens[4];
|
|
21825
|
-
const localInode = tokens[5];
|
|
21826
|
-
const peerAddr = tokens[6];
|
|
21827
|
-
const peerInode = tokens[7];
|
|
21828
|
-
const usersToken = tokens.slice(8).join(" ");
|
|
21829
|
-
const m = usersToken.match(/users:\(\(".*?",pid=(\d+),fd=\d+\)\)/);
|
|
21830
|
-
const pid = m ? parseInt(m[1], 10) : null;
|
|
21831
|
-
rows.push({ localAddr, localInode, peerAddr, peerInode, pid });
|
|
21832
|
-
}
|
|
21833
|
-
return rows;
|
|
21834
|
-
}
|
|
21835
|
-
function findClientPids(rows, socketPath) {
|
|
21836
|
-
const pids = [];
|
|
21837
|
-
for (const serverRow of rows) {
|
|
21838
|
-
if (serverRow.localAddr !== socketPath)
|
|
21839
|
-
continue;
|
|
21840
|
-
for (const clientRow of rows) {
|
|
21841
|
-
if (clientRow.localAddr !== "*")
|
|
21842
|
-
continue;
|
|
21843
|
-
if (clientRow.localInode !== serverRow.peerInode)
|
|
21844
|
-
continue;
|
|
21845
|
-
if (clientRow.pid === null)
|
|
21846
|
-
continue;
|
|
21847
|
-
pids.push(clientRow.pid);
|
|
21848
|
-
break;
|
|
21849
|
-
}
|
|
21850
|
-
}
|
|
21851
|
-
return pids;
|
|
21852
|
-
}
|
|
21853
|
-
function findClientPidByServerInode(rows, socketPath, serverInode) {
|
|
21854
|
-
const serverInodeStr = String(serverInode);
|
|
21855
|
-
for (const serverRow of rows) {
|
|
21856
|
-
if (serverRow.localAddr !== socketPath)
|
|
21857
|
-
continue;
|
|
21858
|
-
if (serverRow.localInode !== serverInodeStr)
|
|
21859
|
-
continue;
|
|
21860
|
-
for (const clientRow of rows) {
|
|
21861
|
-
if (clientRow.localAddr !== "*")
|
|
21862
|
-
continue;
|
|
21863
|
-
if (clientRow.localInode !== serverRow.peerInode)
|
|
21864
|
-
continue;
|
|
21865
|
-
if (clientRow.pid === null)
|
|
21866
|
-
continue;
|
|
21867
|
-
return clientRow.pid;
|
|
21868
|
-
}
|
|
21869
|
-
return null;
|
|
21870
|
-
}
|
|
21871
|
-
return null;
|
|
21872
|
-
}
|
|
21873
|
-
function readUid(pid) {
|
|
21874
|
-
try {
|
|
21875
|
-
const status = readFileSync8(`/proc/${pid}/status`, "utf8");
|
|
21876
|
-
const m = status.match(/^Uid:\s+(\d+)/m);
|
|
21877
|
-
if (!m)
|
|
21878
|
-
return null;
|
|
21879
|
-
return parseInt(m[1], 10);
|
|
21880
|
-
} catch {
|
|
21881
|
-
return null;
|
|
21882
|
-
}
|
|
21883
|
-
}
|
|
21884
|
-
function readExe(pid) {
|
|
21885
|
-
try {
|
|
21886
|
-
return readlinkSync3(`/proc/${pid}/exe`);
|
|
21887
|
-
} catch {
|
|
21888
|
-
return null;
|
|
21889
|
-
}
|
|
21890
|
-
}
|
|
21891
|
-
function readSystemdUnit(pid) {
|
|
21892
|
-
try {
|
|
21893
|
-
const content = readFileSync8(`/proc/${pid}/cgroup`, "utf8");
|
|
21894
|
-
const lines = content.split(`
|
|
21895
|
-
`);
|
|
21896
|
-
for (const line of lines) {
|
|
21897
|
-
if (!line.trim())
|
|
21898
|
-
continue;
|
|
21899
|
-
const parts = line.split(":");
|
|
21900
|
-
if (parts.length < 3)
|
|
21901
|
-
continue;
|
|
21902
|
-
const controller = parts[1];
|
|
21903
|
-
const isV2 = parts[0] === "0" && controller === "";
|
|
21904
|
-
const isV1Systemd = controller === "name=systemd";
|
|
21905
|
-
if (!isV2 && !isV1Systemd)
|
|
21906
|
-
continue;
|
|
21907
|
-
const cgroupPath = parts.slice(2).join(":");
|
|
21908
|
-
const segments = cgroupPath.split("/");
|
|
21909
|
-
const lastSegment = segments[segments.length - 1];
|
|
21910
|
-
if (!lastSegment)
|
|
21911
|
-
continue;
|
|
21912
|
-
if (/^switchroom-[a-zA-Z0-9_-]+(-cron-\d+)?\.service$/.test(lastSegment)) {
|
|
21913
|
-
return lastSegment;
|
|
21914
|
-
}
|
|
21915
|
-
}
|
|
21916
|
-
return null;
|
|
21917
|
-
} catch {
|
|
21918
|
-
return null;
|
|
21919
|
-
}
|
|
21920
|
-
}
|
|
21921
|
-
function verifySystemdUnit(unitName, runner) {
|
|
21922
|
-
let raw;
|
|
21923
|
-
try {
|
|
21924
|
-
const out = runner("systemctl", [
|
|
21925
|
-
"--user",
|
|
21926
|
-
"show",
|
|
21927
|
-
unitName,
|
|
21928
|
-
"--property=LoadState,ActiveState"
|
|
21929
|
-
], { timeout: 500, encoding: "utf8" });
|
|
21930
|
-
raw = typeof out === "string" ? out : out.toString("utf8");
|
|
21931
|
-
} catch {
|
|
21932
|
-
return false;
|
|
21933
|
-
}
|
|
21934
|
-
const props = {};
|
|
21935
|
-
for (const line of raw.split(`
|
|
21936
|
-
`)) {
|
|
21937
|
-
const m = line.match(/^([A-Za-z]+)=(.*)$/);
|
|
21938
|
-
if (m)
|
|
21939
|
-
props[m[1]] = m[2];
|
|
21940
|
-
}
|
|
21941
|
-
if (props.LoadState !== "loaded")
|
|
21942
|
-
return false;
|
|
21943
|
-
if (props.ActiveState !== "active" && props.ActiveState !== "activating") {
|
|
21944
|
-
return false;
|
|
21945
|
-
}
|
|
21946
|
-
return true;
|
|
21947
|
-
}
|
|
21948
|
-
function readFdInode(fd) {
|
|
21949
|
-
try {
|
|
21950
|
-
const stat = fstatSync(fd);
|
|
21951
|
-
return stat.ino;
|
|
21952
|
-
} catch {
|
|
21953
|
-
return null;
|
|
21954
|
-
}
|
|
21955
|
-
}
|
|
21956
|
-
function fdFromSocket(socket) {
|
|
21957
|
-
const handle = socket._handle;
|
|
21958
|
-
if (!handle || typeof handle.fd !== "number" || handle.fd < 0)
|
|
21959
|
-
return null;
|
|
21960
|
-
return handle.fd;
|
|
21961
|
-
}
|
|
21962
|
-
function identify(socketPath, socket, execFileSyncOverride) {
|
|
21963
|
-
if (process.platform !== "linux") {
|
|
21964
|
-
return null;
|
|
21965
|
-
}
|
|
21966
|
-
const runner = execFileSyncOverride ?? execFileSync;
|
|
21967
|
-
let pid = null;
|
|
21968
|
-
if (socket !== undefined) {
|
|
21969
|
-
const fd = fdFromSocket(socket);
|
|
21970
|
-
if (fd !== null) {
|
|
21971
|
-
const cred = getPeerCred(fd);
|
|
21972
|
-
if (cred !== null)
|
|
21973
|
-
pid = cred.pid;
|
|
21974
|
-
}
|
|
21975
|
-
}
|
|
21976
|
-
if (pid === null) {
|
|
21977
|
-
let ssOutput;
|
|
21978
|
-
try {
|
|
21979
|
-
const raw = runner("ss", ["-xpn"], {
|
|
21980
|
-
timeout: 200,
|
|
21981
|
-
encoding: "utf8"
|
|
21982
|
-
});
|
|
21983
|
-
ssOutput = typeof raw === "string" ? raw : raw.toString("utf8");
|
|
21984
|
-
} catch {
|
|
21985
|
-
return null;
|
|
21986
|
-
}
|
|
21987
|
-
const rows = parseSsRows(ssOutput);
|
|
21988
|
-
let serverInode = null;
|
|
21989
|
-
if (socket !== undefined) {
|
|
21990
|
-
const fd = fdFromSocket(socket);
|
|
21991
|
-
if (fd !== null)
|
|
21992
|
-
serverInode = readFdInode(fd);
|
|
21993
|
-
}
|
|
21994
|
-
if (serverInode !== null) {
|
|
21995
|
-
pid = findClientPidByServerInode(rows, socketPath, serverInode);
|
|
21996
|
-
} else {
|
|
21997
|
-
const clientPids = findClientPids(rows, socketPath);
|
|
21998
|
-
if (clientPids.length === 0)
|
|
21999
|
-
return null;
|
|
22000
|
-
if (clientPids.length > 1) {
|
|
22001
|
-
process.stderr.write(`[vault-broker] peercred: ${clientPids.length} connected peers found for ${socketPath}; ` + `using pid=${clientPids[0]}. ` + `Multiple simultaneous connections reduce identification accuracy. ` + `(This warning means identify() was called without a socket arg \u2014 likely a stale call site.)
|
|
22002
|
-
`);
|
|
22003
|
-
}
|
|
22004
|
-
pid = clientPids[0];
|
|
22005
|
-
}
|
|
22006
|
-
}
|
|
22007
|
-
if (pid === null)
|
|
22008
|
-
return null;
|
|
22009
|
-
const uid = readUid(pid);
|
|
22010
|
-
if (uid === null) {
|
|
22011
|
-
return null;
|
|
22012
|
-
}
|
|
22013
|
-
const brokerUid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
22014
|
-
if (brokerUid !== null && uid !== brokerUid) {
|
|
22015
|
-
process.stderr.write(`[vault-broker] peercred: UID mismatch \u2014 caller uid=${uid}, broker uid=${brokerUid}; denying
|
|
22016
|
-
`);
|
|
22017
|
-
return null;
|
|
22018
|
-
}
|
|
22019
|
-
const exe = readExe(pid);
|
|
22020
|
-
if (exe === null) {
|
|
22021
|
-
return null;
|
|
22022
|
-
}
|
|
22023
|
-
const cgroupClaim = readSystemdUnit(pid);
|
|
22024
|
-
let systemdUnit = null;
|
|
22025
|
-
if (cgroupClaim !== null) {
|
|
22026
|
-
if (verifySystemdUnit(cgroupClaim, runner)) {
|
|
22027
|
-
systemdUnit = cgroupClaim;
|
|
22028
|
-
} else {
|
|
22029
|
-
process.stderr.write(`[vault-broker] peercred: cgroup claims unit=${cgroupClaim} but systemd-user does not report it as loaded+running; treating caller as unidentified
|
|
22030
|
-
`);
|
|
22031
|
-
}
|
|
22032
|
-
}
|
|
22033
|
-
return { uid, pid, exe, systemdUnit };
|
|
22034
|
-
}
|
|
22035
|
-
var SOCKET_PATH_AGENT_RE, SOCKET_PATH_AGENT_SUBDIR_RE, RESERVED_AGENT_NAMES;
|
|
22036
|
-
var init_peercred = __esm(() => {
|
|
22037
|
-
SOCKET_PATH_AGENT_RE = /^\/run\/switchroom\/broker\/([a-zA-Z0-9][a-zA-Z0-9_-]*)\.sock$/;
|
|
22038
|
-
SOCKET_PATH_AGENT_SUBDIR_RE = /^\/run\/switchroom\/broker\/([a-zA-Z0-9][a-zA-Z0-9_-]*)\/sock$/;
|
|
22039
|
-
RESERVED_AGENT_NAMES = new Set(["operator", "hostd"]);
|
|
22040
|
-
});
|
|
22041
|
-
|
|
22042
22052
|
// src/runtime-mode.ts
|
|
22043
22053
|
function isDockerRuntime() {
|
|
22044
22054
|
return process.env.SWITCHROOM_RUNTIME === "docker";
|
|
@@ -25539,6 +25549,7 @@ var init_broker_call = __esm(() => {
|
|
|
25539
25549
|
|
|
25540
25550
|
// src/auth/account-store.ts
|
|
25541
25551
|
import {
|
|
25552
|
+
chownSync,
|
|
25542
25553
|
existsSync as existsSync26,
|
|
25543
25554
|
mkdirSync as mkdirSync15,
|
|
25544
25555
|
readFileSync as readFileSync22,
|
|
@@ -29089,7 +29100,7 @@ function decodeResponse3(line) {
|
|
|
29089
29100
|
const obj = JSON.parse(line);
|
|
29090
29101
|
return ResponseSchema3.parse(obj);
|
|
29091
29102
|
}
|
|
29092
|
-
var MAX_FRAME_BYTES3, RequestEnvelope, AgentRestartRequestSchema, UpgradeStatusRequestSchema, GetStatusRequestSchema,
|
|
29103
|
+
var MAX_FRAME_BYTES3, RequestEnvelope, AgentRestartRequestSchema, UpgradeStatusRequestSchema, GetStatusRequestSchema, AgentNameSchema2, UpdateCheckRequestSchema, UpdateApplyRequestSchema, ApplyRequestSchema, AgentStartRequestSchema, AgentStopRequestSchema, AgentLogsRequestSchema, AgentExecRequestSchema, DoctorRequestSchema, AgentSmokeRequestSchema, ConfigProposeEditRequestSchema, RequestSchema3, ResultSchema, ResponseEnvelope, ResponseSchema3;
|
|
29093
29104
|
var init_protocol3 = __esm(() => {
|
|
29094
29105
|
init_zod();
|
|
29095
29106
|
MAX_FRAME_BYTES3 = 64 * 1024;
|
|
@@ -29119,7 +29130,7 @@ var init_protocol3 = __esm(() => {
|
|
|
29119
29130
|
target_request_id: exports_external.string().min(1).max(128)
|
|
29120
29131
|
})
|
|
29121
29132
|
});
|
|
29122
|
-
|
|
29133
|
+
AgentNameSchema2 = exports_external.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/, "agent name must be kebab-case ASCII");
|
|
29123
29134
|
UpdateCheckRequestSchema = exports_external.object({
|
|
29124
29135
|
...RequestEnvelope,
|
|
29125
29136
|
op: exports_external.literal("update_check"),
|
|
@@ -29144,21 +29155,21 @@ var init_protocol3 = __esm(() => {
|
|
|
29144
29155
|
...RequestEnvelope,
|
|
29145
29156
|
op: exports_external.literal("agent_start"),
|
|
29146
29157
|
args: exports_external.object({
|
|
29147
|
-
name:
|
|
29158
|
+
name: AgentNameSchema2
|
|
29148
29159
|
})
|
|
29149
29160
|
});
|
|
29150
29161
|
AgentStopRequestSchema = exports_external.object({
|
|
29151
29162
|
...RequestEnvelope,
|
|
29152
29163
|
op: exports_external.literal("agent_stop"),
|
|
29153
29164
|
args: exports_external.object({
|
|
29154
|
-
name:
|
|
29165
|
+
name: AgentNameSchema2
|
|
29155
29166
|
})
|
|
29156
29167
|
});
|
|
29157
29168
|
AgentLogsRequestSchema = exports_external.object({
|
|
29158
29169
|
...RequestEnvelope,
|
|
29159
29170
|
op: exports_external.literal("agent_logs"),
|
|
29160
29171
|
args: exports_external.object({
|
|
29161
|
-
name:
|
|
29172
|
+
name: AgentNameSchema2,
|
|
29162
29173
|
tail: exports_external.number().int().positive().max(2000).optional()
|
|
29163
29174
|
})
|
|
29164
29175
|
});
|
|
@@ -29166,7 +29177,7 @@ var init_protocol3 = __esm(() => {
|
|
|
29166
29177
|
...RequestEnvelope,
|
|
29167
29178
|
op: exports_external.literal("agent_exec"),
|
|
29168
29179
|
args: exports_external.object({
|
|
29169
|
-
name:
|
|
29180
|
+
name: AgentNameSchema2,
|
|
29170
29181
|
argv: exports_external.array(exports_external.string().min(1)).min(1).max(32)
|
|
29171
29182
|
})
|
|
29172
29183
|
});
|
|
@@ -29179,7 +29190,7 @@ var init_protocol3 = __esm(() => {
|
|
|
29179
29190
|
...RequestEnvelope,
|
|
29180
29191
|
op: exports_external.literal("agent_smoke"),
|
|
29181
29192
|
args: exports_external.object({
|
|
29182
|
-
name:
|
|
29193
|
+
name: AgentNameSchema2,
|
|
29183
29194
|
deep: exports_external.boolean().optional()
|
|
29184
29195
|
})
|
|
29185
29196
|
});
|
|
@@ -47331,8 +47342,8 @@ var {
|
|
|
47331
47342
|
} = import__.default;
|
|
47332
47343
|
|
|
47333
47344
|
// src/build-info.ts
|
|
47334
|
-
var VERSION = "0.13.
|
|
47335
|
-
var COMMIT_SHA = "
|
|
47345
|
+
var VERSION = "0.13.25";
|
|
47346
|
+
var COMMIT_SHA = "e927d05d";
|
|
47336
47347
|
|
|
47337
47348
|
// src/cli/agent.ts
|
|
47338
47349
|
init_source();
|
|
@@ -55825,7 +55836,7 @@ import { spawn as spawn3 } from "node:child_process";
|
|
|
55825
55836
|
init_compose();
|
|
55826
55837
|
init_vault();
|
|
55827
55838
|
import * as net3 from "node:net";
|
|
55828
|
-
import { mkdirSync as mkdirSync20, chmodSync as chmodSync7, chownSync, existsSync as existsSync33, readFileSync as readFileSync29, readdirSync as readdirSync15, statSync as statSync19, unlinkSync as unlinkSync8, writeFileSync as writeFileSync18, renameSync as renameSync9 } from "node:fs";
|
|
55839
|
+
import { mkdirSync as mkdirSync20, chmodSync as chmodSync7, chownSync as chownSync2, existsSync as existsSync33, readFileSync as readFileSync29, readdirSync as readdirSync15, statSync as statSync19, unlinkSync as unlinkSync8, writeFileSync as writeFileSync18, renameSync as renameSync9 } from "node:fs";
|
|
55829
55840
|
import { dirname as dirname6, resolve as resolve25, basename as basename5 } from "node:path";
|
|
55830
55841
|
import * as os4 from "node:os";
|
|
55831
55842
|
import * as path3 from "node:path";
|
|
@@ -58622,7 +58633,7 @@ class VaultBroker {
|
|
|
58622
58633
|
const dir = abs.slice(0, -"/sock".length);
|
|
58623
58634
|
if (existsSync33(dir)) {
|
|
58624
58635
|
try {
|
|
58625
|
-
|
|
58636
|
+
chownSync2(dir, 0, 0);
|
|
58626
58637
|
} catch {}
|
|
58627
58638
|
try {
|
|
58628
58639
|
chmodSync7(dir, 448);
|
|
@@ -58649,12 +58660,12 @@ class VaultBroker {
|
|
|
58649
58660
|
try {
|
|
58650
58661
|
const uid = allocateAgentUid(agentName);
|
|
58651
58662
|
try {
|
|
58652
|
-
|
|
58663
|
+
chownSync2(abs, uid, uid);
|
|
58653
58664
|
} catch {}
|
|
58654
58665
|
if (abs.endsWith("/sock")) {
|
|
58655
58666
|
const dir = abs.slice(0, -"/sock".length);
|
|
58656
58667
|
try {
|
|
58657
|
-
|
|
58668
|
+
chownSync2(dir, uid, uid);
|
|
58658
58669
|
} catch {}
|
|
58659
58670
|
}
|
|
58660
58671
|
} catch {}
|
|
@@ -58689,7 +58700,7 @@ class VaultBroker {
|
|
|
58689
58700
|
return;
|
|
58690
58701
|
try {
|
|
58691
58702
|
if (existsSync33(this.vaultPath))
|
|
58692
|
-
|
|
58703
|
+
chownSync2(this.vaultPath, uid, uid);
|
|
58693
58704
|
} catch {}
|
|
58694
58705
|
}
|
|
58695
58706
|
bindOperatorListener(socketPath, operatorUid) {
|
|
@@ -58703,7 +58714,7 @@ class VaultBroker {
|
|
|
58703
58714
|
const dir = abs.slice(0, -"/sock".length);
|
|
58704
58715
|
if (existsSync33(dir)) {
|
|
58705
58716
|
try {
|
|
58706
|
-
|
|
58717
|
+
chownSync2(dir, 0, 0);
|
|
58707
58718
|
} catch {}
|
|
58708
58719
|
try {
|
|
58709
58720
|
chmodSync7(dir, 448);
|
|
@@ -58727,7 +58738,7 @@ class VaultBroker {
|
|
|
58727
58738
|
chmodSync7(abs, 384);
|
|
58728
58739
|
} catch {}
|
|
58729
58740
|
try {
|
|
58730
|
-
|
|
58741
|
+
chownSync2(abs, operatorUid, operatorUid);
|
|
58731
58742
|
} catch {}
|
|
58732
58743
|
const unlockServer = net3.createServer((sock) => {
|
|
58733
58744
|
this._handleUnlockConnection(sock, true);
|
|
@@ -58738,12 +58749,12 @@ class VaultBroker {
|
|
|
58738
58749
|
chmodSync7(unlockAbs, 384);
|
|
58739
58750
|
} catch {}
|
|
58740
58751
|
try {
|
|
58741
|
-
|
|
58752
|
+
chownSync2(unlockAbs, operatorUid, operatorUid);
|
|
58742
58753
|
} catch {}
|
|
58743
58754
|
if (abs.endsWith("/sock")) {
|
|
58744
58755
|
const dir = abs.slice(0, -"/sock".length);
|
|
58745
58756
|
try {
|
|
58746
|
-
|
|
58757
|
+
chownSync2(dir, operatorUid, operatorUid);
|
|
58747
58758
|
} catch {}
|
|
58748
58759
|
try {
|
|
58749
58760
|
chmodSync7(dir, 448);
|
|
@@ -59586,7 +59597,7 @@ class VaultBroker {
|
|
|
59586
59597
|
const revoked = revokeGrant(this.grantsDb, id);
|
|
59587
59598
|
try {
|
|
59588
59599
|
const row = this.grantsDb.query("SELECT agent_slug FROM vault_grants WHERE id = ?").get(id);
|
|
59589
|
-
if (row) {
|
|
59600
|
+
if (row && AgentNameSchema.safeParse(row.agent_slug).success) {
|
|
59590
59601
|
const tokenPath = path3.join(os4.homedir(), ".switchroom", "agents", row.agent_slug, ".vault-token");
|
|
59591
59602
|
if (existsSync33(tokenPath)) {
|
|
59592
59603
|
try {
|
|
@@ -61405,9 +61416,18 @@ function registerVaultCommand(program3) {
|
|
|
61405
61416
|
}
|
|
61406
61417
|
const passphrase = await getPassphrase();
|
|
61407
61418
|
let value;
|
|
61419
|
+
let isBinaryFile = false;
|
|
61408
61420
|
if (opts.file) {
|
|
61409
61421
|
try {
|
|
61410
|
-
|
|
61422
|
+
const buf = readFileSync34(resolvePath(opts.file));
|
|
61423
|
+
const asUtf8 = buf.toString("utf8");
|
|
61424
|
+
if (Buffer.compare(buf, Buffer.from(asUtf8, "utf8")) === 0) {
|
|
61425
|
+
value = asUtf8;
|
|
61426
|
+
} else {
|
|
61427
|
+
value = buf.toString("base64");
|
|
61428
|
+
isBinaryFile = true;
|
|
61429
|
+
console.error(source_default.yellow(`note: file contains non-UTF-8 bytes; storing as kind="binary" ` + `(base64-encoded). Retrieve with: switchroom vault get ${key} | base64 -d`));
|
|
61430
|
+
}
|
|
61411
61431
|
} catch (err) {
|
|
61412
61432
|
const msg = err instanceof Error ? err.message : String(err);
|
|
61413
61433
|
console.error(source_default.red(`Error reading file: ${msg}`));
|
|
@@ -61422,7 +61442,7 @@ function registerVaultCommand(program3) {
|
|
|
61422
61442
|
console.error(source_default.red("Error: Value cannot be empty"));
|
|
61423
61443
|
process.exit(1);
|
|
61424
61444
|
}
|
|
61425
|
-
if (formatHint) {
|
|
61445
|
+
if (formatHint && !isBinaryFile) {
|
|
61426
61446
|
const validationError = validateFormatHint(value, formatHint);
|
|
61427
61447
|
if (validationError) {
|
|
61428
61448
|
console.error(source_default.red(`Error: format validation failed for --format ${formatHint}: ${validationError}`));
|
|
@@ -61451,7 +61471,11 @@ function registerVaultCommand(program3) {
|
|
|
61451
61471
|
}
|
|
61452
61472
|
} catch {}
|
|
61453
61473
|
}
|
|
61454
|
-
|
|
61474
|
+
if (isBinaryFile) {
|
|
61475
|
+
setBinarySecret(passphrase, vaultPath, key, value, formatHint, scope);
|
|
61476
|
+
} else {
|
|
61477
|
+
setStringSecret(passphrase, vaultPath, key, value, formatHint, scope);
|
|
61478
|
+
}
|
|
61455
61479
|
if (formatHint && scope) {
|
|
61456
61480
|
const scopeDesc = [
|
|
61457
61481
|
scope.allow?.length ? `allow: ${scope.allow.join(", ")}` : "",
|
|
@@ -62193,13 +62217,12 @@ function registerDispatchVerb(tg, _program) {
|
|
|
62193
62217
|
`)) {
|
|
62194
62218
|
console.log(` ${line}`);
|
|
62195
62219
|
}
|
|
62196
|
-
console.log(` model: ${rule.model ?? "claude-sonnet-4-6"}`);
|
|
62197
62220
|
}
|
|
62198
62221
|
console.log();
|
|
62199
62222
|
if (matchCount === 0) {
|
|
62200
62223
|
console.log(source_default.yellow("No rules matched \u2014 no dispatch would fire."));
|
|
62201
62224
|
} else {
|
|
62202
|
-
console.log(source_default.green(`${matchCount} rule(s) matched. ` + `
|
|
62225
|
+
console.log(source_default.green(`${matchCount} rule(s) matched. ` + `The webhook turn injects into the agent's live session \u2014 ` + `agent's configured model wins.`));
|
|
62203
62226
|
}
|
|
62204
62227
|
}));
|
|
62205
62228
|
}
|
|
@@ -74040,7 +74063,7 @@ function detectInstallType() {
|
|
|
74040
74063
|
|
|
74041
74064
|
// src/cli/operator-uid.ts
|
|
74042
74065
|
import {
|
|
74043
|
-
chownSync as
|
|
74066
|
+
chownSync as chownSync3,
|
|
74044
74067
|
existsSync as existsSync66,
|
|
74045
74068
|
lstatSync as lstatSync7,
|
|
74046
74069
|
readdirSync as readdirSync24,
|
|
@@ -74074,7 +74097,7 @@ function operatorOwnedPaths(home2) {
|
|
|
74074
74097
|
];
|
|
74075
74098
|
}
|
|
74076
74099
|
function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
|
|
74077
|
-
const chown = deps.chown ?? ((p, u, g) =>
|
|
74100
|
+
const chown = deps.chown ?? ((p, u, g) => chownSync3(p, u, g));
|
|
74078
74101
|
const exists = deps.exists ?? ((p) => existsSync66(p));
|
|
74079
74102
|
const isSymlink = deps.isSymlink ?? ((p) => {
|
|
74080
74103
|
try {
|