@agentvault/claude-bridge 0.6.5 → 0.7.1
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/approve-cli.d.ts +9 -0
- package/dist/bridge.d.ts +19 -0
- package/dist/bsafe-arming.d.ts +30 -0
- package/dist/host-trust.d.ts +53 -0
- package/dist/index.js +255 -73
- package/package.json +1 -1
package/dist/approve-cli.d.ts
CHANGED
|
@@ -25,4 +25,13 @@ export declare function drainApprovals(dataDir: string): DrainedApproval[];
|
|
|
25
25
|
* Usage: `av-bridge approve-arm <request-id> <room-id>`. Both are shown to the
|
|
26
26
|
* operator in the bridge's arm-request log line and the web arm sheet. */
|
|
27
27
|
export declare function maybeRunApproveArmSubcommand(argv: string[], dataDir: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* #20 — `av-bridge trust-host [--yes]` / `av-bridge untrust-host [--all]`.
|
|
30
|
+
*
|
|
31
|
+
* Headless like `approve-arm` (no channel, no network) so it works over a bare
|
|
32
|
+
* SSH session with no daemon running. The device-id is ALWAYS read from the
|
|
33
|
+
* persisted identity, never taken from argv — an operator-typed id is exactly
|
|
34
|
+
* the confused-deputy input F1 closed for approve-arm.
|
|
35
|
+
*/
|
|
36
|
+
export declare function maybeRunHostTrustSubcommand(argv: string[], dataDir: string, root?: string): boolean;
|
|
28
37
|
//# sourceMappingURL=approve-cli.d.ts.map
|
package/dist/bridge.d.ts
CHANGED
|
@@ -61,7 +61,17 @@ export interface RoomChannel {
|
|
|
61
61
|
sendWorkerHeartbeat?(h: {
|
|
62
62
|
workerCapable: boolean;
|
|
63
63
|
armedRooms: string[];
|
|
64
|
+
hostTrusted: boolean;
|
|
65
|
+
gatesEffective: boolean;
|
|
64
66
|
}): void;
|
|
67
|
+
/**
|
|
68
|
+
* #20: this worker's own device-id, checked against the host-trust directory
|
|
69
|
+
* to decide whether a web-granted "Remove all gates" switch actually takes
|
|
70
|
+
* effect (see bsafe-arming.ts). Optional so legacy/test fakes without it
|
|
71
|
+
* don't break wiring — `decideArming` treats a missing id as untrusted
|
|
72
|
+
* (fail-closed), matching `channel.deviceId ?? ""`.
|
|
73
|
+
*/
|
|
74
|
+
deviceId?: string | null;
|
|
65
75
|
}
|
|
66
76
|
/**
|
|
67
77
|
* #392 cooperative quiet: tracks per-room hush windows so the native agent holds
|
|
@@ -207,5 +217,14 @@ export declare function wireBridge(channel: RoomChannel, session: RoomSession, t
|
|
|
207
217
|
* worker session's gate.
|
|
208
218
|
*/
|
|
209
219
|
onGatesRemoved?: (getter: () => boolean) => void;
|
|
220
|
+
/**
|
|
221
|
+
* #20 test-only injection point: overrides where `isTrusted` (via
|
|
222
|
+
* `decideArming`) looks for host-trust markers, mirroring `host-trust.ts`'s
|
|
223
|
+
* own `root?` parameter. Production callers never pass this, so
|
|
224
|
+
* `decideArming` receives `undefined` and `isTrusted` falls back to its
|
|
225
|
+
* default `~/.agentvault` — unchanged from today. Tests pass a `mkdtemp`
|
|
226
|
+
* dir so they can `grant(...)` into it without touching the real host.
|
|
227
|
+
*/
|
|
228
|
+
trustRoot?: string;
|
|
210
229
|
}): ArmingState;
|
|
211
230
|
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type ArmingMode = "authoritative-under-grant" | "authoritative-bsafe" | "c2-disarm-only" | "authoritative-bpure";
|
|
2
|
+
export interface ArmingDecision {
|
|
3
|
+
/** The grant actually applied — web intent AND host trust. */
|
|
4
|
+
gatesRemoved: boolean;
|
|
5
|
+
mode: ArmingMode;
|
|
6
|
+
/**
|
|
7
|
+
* True when rooms armed from THIS snapshot must be remembered as revocable:
|
|
8
|
+
* the web armed them on an OS-isolated worker under an authority that can
|
|
9
|
+
* lapse. BOTH authorities qualify — the full-access grant AND host trust on
|
|
10
|
+
* its own — because both lapse into `c2-disarm-only`, which is the only place
|
|
11
|
+
* the record is ever consumed.
|
|
12
|
+
*
|
|
13
|
+
* This was `recordUnderGrant`, true for the grant alone, and that was the
|
|
14
|
+
* defect the 2026-07-27 whole-branch review found: rooms armed under
|
|
15
|
+
* `authoritative-bsafe` went unrecorded, so `av-bridge untrust-host` left them
|
|
16
|
+
* armed — and an armed room on an OS-isolated worker means Bash is allowed.
|
|
17
|
+
*
|
|
18
|
+
* `authoritative-bpure` stays false: a non-isolated worker never reaches
|
|
19
|
+
* `c2-disarm-only`, so the record would never be read.
|
|
20
|
+
*/
|
|
21
|
+
recordWebArmed: boolean;
|
|
22
|
+
hostTrusted: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function decideArming(opts: {
|
|
25
|
+
snapshotGatesRemoved: boolean;
|
|
26
|
+
osIsolated: boolean;
|
|
27
|
+
deviceId: string;
|
|
28
|
+
trustRoot?: string;
|
|
29
|
+
}): ArmingDecision;
|
|
30
|
+
//# sourceMappingURL=bsafe-arming.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export declare class HostTrustError extends Error {
|
|
2
|
+
}
|
|
3
|
+
export declare function trustDir(root?: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Fail-closed in every direction: any doubt at all means untrusted.
|
|
6
|
+
*
|
|
7
|
+
* `lstatSync`, NOT `statSync`: a symlink must never count as a marker. This
|
|
8
|
+
* used `statSync`, which FOLLOWS symlinks, so `ln -s <any regular file>
|
|
9
|
+
* ~/.agentvault/host-trust/<device-id>` read as a grant. That was not
|
|
10
|
+
* exploitable under today's threat model — the stated adversary is web-only and
|
|
11
|
+
* cannot create files on the host, and an agent with Bash could write the real
|
|
12
|
+
* marker anyway — but it is precisely the bypass that would defeat the deferred
|
|
13
|
+
* Task 10 root-owned marker, whose whole claim is that an agent running as the
|
|
14
|
+
* user cannot forge one. The trust DIRECTORY is deliberately user-owned so that
|
|
15
|
+
* revocation needs no privilege, so an agent with a shell can plant a symlink
|
|
16
|
+
* there; pointed at any root-owned, non-group/other-writable file (`/etc/passwd`
|
|
17
|
+
* is uid 0, mode 644 on both macOS and Linux) it would satisfy every clause of
|
|
18
|
+
* that check against the TARGET. Rejecting symlinks here closes it in advance.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isTrusted(deviceId: string, root?: string): boolean;
|
|
21
|
+
export declare function grant(deviceId: string, root?: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Returns true only if a real marker was actually removed.
|
|
24
|
+
*
|
|
25
|
+
* M1. This used `existsSync` + `rmSync(p, { force: true, recursive: true })`.
|
|
26
|
+
* `existsSync` is true for a DIRECTORY, so a directory squatting at a marker
|
|
27
|
+
* path was recursively deleted — contents and all — and reported as a
|
|
28
|
+
* successful revoke. Reached in practice through `untrust-host --all`
|
|
29
|
+
* (approve-cli.ts:131), which revokes everything `listTrusted()` returns.
|
|
30
|
+
*
|
|
31
|
+
* Only a regular file is a marker, which is already `isTrusted`'s rule; `lstat`
|
|
32
|
+
* so a symlink is never followed or counted. Anything else was never a grant,
|
|
33
|
+
* so removing it is not this function's job — and refusing keeps `recursive`
|
|
34
|
+
* out of this file entirely.
|
|
35
|
+
*
|
|
36
|
+
* The invariant callers actually depend on is preserved: after `revoke()`,
|
|
37
|
+
* `isTrusted()` is false, because it rejects non-files too.
|
|
38
|
+
*/
|
|
39
|
+
export declare function revoke(deviceId: string, root?: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Filtered through `isTrusted` rather than the filename alone, so the listing
|
|
42
|
+
* cannot disagree with the decision. A name-only filter reported directories
|
|
43
|
+
* and symlinks as trusted devices, which both over-stated trust to an operator
|
|
44
|
+
* reading the list and fed non-markers to `revoke()` in the `--all` path.
|
|
45
|
+
*/
|
|
46
|
+
export declare function listTrusted(root?: string): string[];
|
|
47
|
+
/**
|
|
48
|
+
* Resolve this agent's device-id from its persisted identity. `trust-host` runs
|
|
49
|
+
* headless with no channel and possibly no running bridge, so it cannot ask the
|
|
50
|
+
* backend who it is — and it must never accept an operator-typed id (F1).
|
|
51
|
+
*/
|
|
52
|
+
export declare function readDeviceId(dataDir: string): string;
|
|
53
|
+
//# sourceMappingURL=host-trust.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -155,7 +155,7 @@ var init_launcher = __esm({
|
|
|
155
155
|
});
|
|
156
156
|
|
|
157
157
|
// src/service/spec.ts
|
|
158
|
-
import { join as
|
|
158
|
+
import { join as join8 } from "node:path";
|
|
159
159
|
function buildServiceSpec(cfg, opts) {
|
|
160
160
|
const env = {
|
|
161
161
|
AV_AGENT_NAME: cfg.agentName,
|
|
@@ -168,7 +168,7 @@ function buildServiceSpec(cfg, opts) {
|
|
|
168
168
|
if (cfg.armRoom) env.AV_ARM_ROOM = "1";
|
|
169
169
|
if (cfg.model) env.AV_CLAUDE_MODEL = cfg.model;
|
|
170
170
|
if (cfg.systemPrompt) env.AV_SYSTEM_PROMPT = cfg.systemPrompt;
|
|
171
|
-
const launcherPath =
|
|
171
|
+
const launcherPath = join8(cfg.dataDir, "launch-bridge.sh");
|
|
172
172
|
return {
|
|
173
173
|
label: `dev.agentvault.bridge-${slugify2(cfg.agentName)}`,
|
|
174
174
|
programArgs: [launcherPath],
|
|
@@ -176,8 +176,8 @@ function buildServiceSpec(cfg, opts) {
|
|
|
176
176
|
launcherScript: renderLauncher({ installPath: opts.path, entrypoint: opts.entrypoint }),
|
|
177
177
|
env,
|
|
178
178
|
workingDir: cfg.workspaceDir ? cfg.workspaceDir : opts.home,
|
|
179
|
-
logOut:
|
|
180
|
-
logErr:
|
|
179
|
+
logOut: join8(cfg.dataDir, "logs", "bridge.log"),
|
|
180
|
+
logErr: join8(cfg.dataDir, "logs", "bridge.error.log"),
|
|
181
181
|
keepAliveUnlessCleanExit: true,
|
|
182
182
|
runAtLoad: true
|
|
183
183
|
};
|
|
@@ -191,7 +191,7 @@ var init_spec = __esm({
|
|
|
191
191
|
});
|
|
192
192
|
|
|
193
193
|
// src/service/launchd.ts
|
|
194
|
-
import { dirname as dirname4, join as
|
|
194
|
+
import { dirname as dirname4, join as join9 } from "node:path";
|
|
195
195
|
function esc3(s10) {
|
|
196
196
|
return s10.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
197
197
|
}
|
|
@@ -238,7 +238,7 @@ var init_launchd = __esm({
|
|
|
238
238
|
}
|
|
239
239
|
deps;
|
|
240
240
|
plistPath(label) {
|
|
241
|
-
return
|
|
241
|
+
return join9(this.deps.home, "Library", "LaunchAgents", `${label}.plist`);
|
|
242
242
|
}
|
|
243
243
|
domain() {
|
|
244
244
|
return `gui/${this.deps.uid}`;
|
|
@@ -281,7 +281,7 @@ var init_launchd = __esm({
|
|
|
281
281
|
});
|
|
282
282
|
|
|
283
283
|
// src/service/systemd.ts
|
|
284
|
-
import { dirname as dirname5, join as
|
|
284
|
+
import { dirname as dirname5, join as join10 } from "node:path";
|
|
285
285
|
function q5(s10) {
|
|
286
286
|
const esc4 = s10.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/%/g, "%%");
|
|
287
287
|
return `"${esc4}"`;
|
|
@@ -318,7 +318,7 @@ var init_systemd = __esm({
|
|
|
318
318
|
}
|
|
319
319
|
deps;
|
|
320
320
|
unitPath(label) {
|
|
321
|
-
return
|
|
321
|
+
return join10(this.deps.home, ".config", "systemd", "user", `${label}.service`);
|
|
322
322
|
}
|
|
323
323
|
install(spec) {
|
|
324
324
|
const path2 = this.unitPath(spec.label);
|
|
@@ -349,7 +349,7 @@ var init_systemd = __esm({
|
|
|
349
349
|
|
|
350
350
|
// src/service/backend.ts
|
|
351
351
|
import { spawnSync } from "node:child_process";
|
|
352
|
-
import { mkdirSync as
|
|
352
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync3, rmSync as rmSync4, existsSync as existsSync4, chmodSync as chmodSync3 } from "node:fs";
|
|
353
353
|
import { userInfo } from "node:os";
|
|
354
354
|
function defaultDeps() {
|
|
355
355
|
return {
|
|
@@ -360,10 +360,10 @@ function defaultDeps() {
|
|
|
360
360
|
const r7 = spawnSync(cmd, args, { encoding: "utf8" });
|
|
361
361
|
return { code: r7.status ?? 1, stdout: r7.stdout ?? "" };
|
|
362
362
|
},
|
|
363
|
-
writeFile: (p2, d10) =>
|
|
364
|
-
mkdir: (p2) =>
|
|
365
|
-
chmod: (p2, m6) =>
|
|
366
|
-
rm: (p2) =>
|
|
363
|
+
writeFile: (p2, d10) => writeFileSync3(p2, d10),
|
|
364
|
+
mkdir: (p2) => mkdirSync5(p2, { recursive: true }),
|
|
365
|
+
chmod: (p2, m6) => chmodSync3(p2, m6),
|
|
366
|
+
rm: (p2) => rmSync4(p2, { force: true }),
|
|
367
367
|
exists: (p2) => existsSync4(p2)
|
|
368
368
|
};
|
|
369
369
|
}
|
|
@@ -447,8 +447,8 @@ var init_subcommand = __esm({
|
|
|
447
447
|
});
|
|
448
448
|
|
|
449
449
|
// src/index.ts
|
|
450
|
-
import { appendFileSync as appendFileSync2, mkdirSync as
|
|
451
|
-
import { join as
|
|
450
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
451
|
+
import { join as join11 } from "node:path";
|
|
452
452
|
|
|
453
453
|
// ../plugin/dist/index.js
|
|
454
454
|
import * as nc from "node:crypto";
|
|
@@ -65559,7 +65559,7 @@ var init_channel = __esm2({
|
|
|
65559
65559
|
*/
|
|
65560
65560
|
sendActivitySpan(spanData) {
|
|
65561
65561
|
if (!this._ws || this._ws.readyState !== WebSocket2.OPEN) return;
|
|
65562
|
-
const pluginVersion = true ? "0.23.
|
|
65562
|
+
const pluginVersion = true ? "0.23.11" : "0.0.0-dev";
|
|
65563
65563
|
const agentName = this.config.agentName ?? "Agent";
|
|
65564
65564
|
const resource = {
|
|
65565
65565
|
"service.name": "agentvault-agent",
|
|
@@ -65593,7 +65593,16 @@ var init_channel = __esm2({
|
|
|
65593
65593
|
this._ws.send(
|
|
65594
65594
|
JSON.stringify({
|
|
65595
65595
|
event: "worker_heartbeat",
|
|
65596
|
-
data: {
|
|
65596
|
+
data: {
|
|
65597
|
+
worker_capable: h22.workerCapable,
|
|
65598
|
+
armed_rooms: h22.armedRooms,
|
|
65599
|
+
// #20: OPTIONAL on the way in so an older bridge against a newer
|
|
65600
|
+
// plugin still emits a shape the backend can read. `=== true` means
|
|
65601
|
+
// absent/malformed becomes false — the fail-closed direction, and it
|
|
65602
|
+
// never emits `undefined` (which would drop the key entirely).
|
|
65603
|
+
host_trusted: h22.hostTrusted === true,
|
|
65604
|
+
gates_effective: h22.gatesEffective === true
|
|
65605
|
+
}
|
|
65597
65606
|
})
|
|
65598
65607
|
);
|
|
65599
65608
|
}
|
|
@@ -67289,7 +67298,7 @@ var init_channel = __esm2({
|
|
|
67289
67298
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67290
67299
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67291
67300
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67292
|
-
pluginVersion: true ? "0.23.
|
|
67301
|
+
pluginVersion: true ? "0.23.11" : "0.0.0-dev"
|
|
67293
67302
|
});
|
|
67294
67303
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67295
67304
|
}
|
|
@@ -67607,7 +67616,7 @@ var init_channel = __esm2({
|
|
|
67607
67616
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67608
67617
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67609
67618
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67610
|
-
pluginVersion: true ? "0.23.
|
|
67619
|
+
pluginVersion: true ? "0.23.11" : "0.0.0-dev"
|
|
67611
67620
|
});
|
|
67612
67621
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67613
67622
|
}
|
|
@@ -68481,13 +68490,13 @@ ${messageText}`;
|
|
|
68481
68490
|
* Looks for OpenClaw workspace config, falls back to default path.
|
|
68482
68491
|
*/
|
|
68483
68492
|
_resolveWorkspaceDir() {
|
|
68484
|
-
const
|
|
68493
|
+
const homedir2 = osHomedir();
|
|
68485
68494
|
const agentName = this.config.agentName;
|
|
68486
68495
|
if (this._persisted?.agentRole === "lead") {
|
|
68487
|
-
return join4(
|
|
68496
|
+
return join4(homedir2, ".openclaw", "workspace");
|
|
68488
68497
|
}
|
|
68489
68498
|
try {
|
|
68490
|
-
const configPath = join4(
|
|
68499
|
+
const configPath = join4(homedir2, ".openclaw", "openclaw.json");
|
|
68491
68500
|
const raw = readFileSync(configPath, "utf-8");
|
|
68492
68501
|
const config22 = JSON.parse(raw);
|
|
68493
68502
|
const agents = config22?.agents?.list;
|
|
@@ -68501,9 +68510,9 @@ ${messageText}`;
|
|
|
68501
68510
|
} catch {
|
|
68502
68511
|
}
|
|
68503
68512
|
if (agentName && agentName !== "CLI Agent" && agentName !== "OpenClaw Agent") {
|
|
68504
|
-
return join4(
|
|
68513
|
+
return join4(homedir2, ".openclaw", `workspace-${agentName}`);
|
|
68505
68514
|
}
|
|
68506
|
-
return join4(
|
|
68515
|
+
return join4(homedir2, ".openclaw", "workspace");
|
|
68507
68516
|
}
|
|
68508
68517
|
/**
|
|
68509
68518
|
* Send a structured JSON reply to a specific conversation.
|
|
@@ -68702,20 +68711,20 @@ ${messageText}`;
|
|
|
68702
68711
|
try {
|
|
68703
68712
|
const payload = JSON.parse(plaintext);
|
|
68704
68713
|
if (messageType === "credential_grant") {
|
|
68705
|
-
const
|
|
68706
|
-
if (
|
|
68714
|
+
const grant2 = payload;
|
|
68715
|
+
if (grant2.nonce && !this._credentialStore.checkNonce(roomId, grant2.nonce)) {
|
|
68707
68716
|
console.warn(`[SecureChannel] Credential grant replay detected for room ${roomId.slice(0, 8)}..., ignoring`);
|
|
68708
68717
|
return;
|
|
68709
68718
|
}
|
|
68710
68719
|
const keys = [];
|
|
68711
|
-
for (const cred of
|
|
68720
|
+
for (const cred of grant2.credentials || []) {
|
|
68712
68721
|
this._credentialStore.grant(roomId, {
|
|
68713
68722
|
key: cred.key,
|
|
68714
68723
|
value: cred.value,
|
|
68715
68724
|
type: cred.type,
|
|
68716
68725
|
scope: cred.scope,
|
|
68717
|
-
grantedAt:
|
|
68718
|
-
agreementId:
|
|
68726
|
+
grantedAt: grant2.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
68727
|
+
agreementId: grant2.agreement_id,
|
|
68719
68728
|
roomId
|
|
68720
68729
|
});
|
|
68721
68730
|
keys.push(cred.key);
|
|
@@ -68723,21 +68732,21 @@ ${messageText}`;
|
|
|
68723
68732
|
console.log(
|
|
68724
68733
|
`[SecureChannel] Credentials granted for room ${roomId.slice(0, 8)}...: ${keys.join(", ")} (${keys.length} keys)`
|
|
68725
68734
|
);
|
|
68726
|
-
this._sendCredentialAck(roomId,
|
|
68727
|
-
this.emit("credentials_granted", { roomId, keys, agreementId:
|
|
68735
|
+
this._sendCredentialAck(roomId, grant2.agreement_id, keys, "stored");
|
|
68736
|
+
this.emit("credentials_granted", { roomId, keys, agreementId: grant2.agreement_id });
|
|
68728
68737
|
} else if (messageType === "credential_revoke") {
|
|
68729
|
-
const
|
|
68738
|
+
const revoke2 = payload;
|
|
68730
68739
|
const revoked = [];
|
|
68731
|
-
for (const key of
|
|
68740
|
+
for (const key of revoke2.credential_keys || []) {
|
|
68732
68741
|
if (this._credentialStore.revoke(roomId, key)) {
|
|
68733
68742
|
revoked.push(key);
|
|
68734
68743
|
}
|
|
68735
68744
|
}
|
|
68736
68745
|
console.log(
|
|
68737
|
-
`[SecureChannel] Credentials revoked for room ${roomId.slice(0, 8)}...: ${revoked.join(", ")} (reason: ${
|
|
68746
|
+
`[SecureChannel] Credentials revoked for room ${roomId.slice(0, 8)}...: ${revoked.join(", ")} (reason: ${revoke2.reason || "none"})`
|
|
68738
68747
|
);
|
|
68739
|
-
this._sendCredentialAck(roomId,
|
|
68740
|
-
this.emit("credentials_revoked", { roomId, keys: revoked, agreementId:
|
|
68748
|
+
this._sendCredentialAck(roomId, revoke2.agreement_id, revoked, "revoked");
|
|
68749
|
+
this.emit("credentials_revoked", { roomId, keys: revoked, agreementId: revoke2.agreement_id });
|
|
68741
68750
|
}
|
|
68742
68751
|
} catch (err) {
|
|
68743
68752
|
console.error("[SecureChannel] Error handling credential message:", err);
|
|
@@ -97386,7 +97395,7 @@ var init_index = __esm2({
|
|
|
97386
97395
|
init_skill_invoker();
|
|
97387
97396
|
await init_skill_telemetry();
|
|
97388
97397
|
await init_policy_enforcer();
|
|
97389
|
-
VERSION = true ? "0.23.
|
|
97398
|
+
VERSION = true ? "0.23.11" : "0.0.0-dev";
|
|
97390
97399
|
}
|
|
97391
97400
|
});
|
|
97392
97401
|
await init_index();
|
|
@@ -118691,6 +118700,84 @@ function Z_($10, Q4) {
|
|
|
118691
118700
|
// src/worker-permission.ts
|
|
118692
118701
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
118693
118702
|
import { resolve as resolve3, dirname as dirname2, basename, sep as sep2, isAbsolute } from "node:path";
|
|
118703
|
+
|
|
118704
|
+
// src/host-trust.ts
|
|
118705
|
+
import { mkdirSync as mkdirSync3, writeFileSync, rmSync as rmSync2, readdirSync as readdirSync2, lstatSync as lstatSync2, readFileSync as readFileSync4, chmodSync as chmodSync2 } from "node:fs";
|
|
118706
|
+
import { join as join6 } from "node:path";
|
|
118707
|
+
import { homedir, hostname as hostname3 } from "node:os";
|
|
118708
|
+
var TRUST_SUBDIR = "host-trust";
|
|
118709
|
+
var DIR_MODE3 = 448;
|
|
118710
|
+
var FILE_MODE3 = 384;
|
|
118711
|
+
var ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
118712
|
+
var IDENTITY_FILES = ["agentvault.json", "secure-channel.json", "agentvault.json.bak"];
|
|
118713
|
+
var HostTrustError = class extends Error {
|
|
118714
|
+
};
|
|
118715
|
+
function trustDir(root3) {
|
|
118716
|
+
return join6(root3 ?? join6(homedir(), ".agentvault"), TRUST_SUBDIR);
|
|
118717
|
+
}
|
|
118718
|
+
function sanitize(deviceId) {
|
|
118719
|
+
if (typeof deviceId !== "string" || !ID_RE.test(deviceId)) {
|
|
118720
|
+
throw new HostTrustError("invalid device-id");
|
|
118721
|
+
}
|
|
118722
|
+
return deviceId;
|
|
118723
|
+
}
|
|
118724
|
+
function isTrusted(deviceId, root3) {
|
|
118725
|
+
try {
|
|
118726
|
+
if (typeof deviceId !== "string" || !ID_RE.test(deviceId)) return false;
|
|
118727
|
+
const p2 = join6(trustDir(root3), deviceId);
|
|
118728
|
+
return lstatSync2(p2).isFile();
|
|
118729
|
+
} catch {
|
|
118730
|
+
return false;
|
|
118731
|
+
}
|
|
118732
|
+
}
|
|
118733
|
+
function grant(deviceId, root3) {
|
|
118734
|
+
const id = sanitize(deviceId);
|
|
118735
|
+
const dir = trustDir(root3);
|
|
118736
|
+
const p2 = join6(dir, id);
|
|
118737
|
+
mkdirSync3(dir, { recursive: true, mode: DIR_MODE3 });
|
|
118738
|
+
writeFileSync(
|
|
118739
|
+
p2,
|
|
118740
|
+
`granted_at=${(/* @__PURE__ */ new Date()).toISOString()} host=${hostname3()}
|
|
118741
|
+
`,
|
|
118742
|
+
{ mode: FILE_MODE3 }
|
|
118743
|
+
);
|
|
118744
|
+
chmodSync2(dir, DIR_MODE3);
|
|
118745
|
+
chmodSync2(p2, FILE_MODE3);
|
|
118746
|
+
}
|
|
118747
|
+
function revoke(deviceId, root3) {
|
|
118748
|
+
const id = sanitize(deviceId);
|
|
118749
|
+
const p2 = join6(trustDir(root3), id);
|
|
118750
|
+
try {
|
|
118751
|
+
if (!lstatSync2(p2).isFile()) return false;
|
|
118752
|
+
} catch {
|
|
118753
|
+
return false;
|
|
118754
|
+
}
|
|
118755
|
+
rmSync2(p2, { force: true });
|
|
118756
|
+
return true;
|
|
118757
|
+
}
|
|
118758
|
+
function listTrusted(root3) {
|
|
118759
|
+
try {
|
|
118760
|
+
return readdirSync2(trustDir(root3)).filter(
|
|
118761
|
+
(n10) => ID_RE.test(n10) && isTrusted(n10, root3)
|
|
118762
|
+
);
|
|
118763
|
+
} catch {
|
|
118764
|
+
return [];
|
|
118765
|
+
}
|
|
118766
|
+
}
|
|
118767
|
+
function readDeviceId(dataDir) {
|
|
118768
|
+
for (const f7 of IDENTITY_FILES) {
|
|
118769
|
+
try {
|
|
118770
|
+
const parsed = JSON.parse(readFileSync4(join6(dataDir, f7), "utf-8"));
|
|
118771
|
+
if (parsed?.deviceId && ID_RE.test(parsed.deviceId)) return parsed.deviceId;
|
|
118772
|
+
} catch {
|
|
118773
|
+
}
|
|
118774
|
+
}
|
|
118775
|
+
throw new HostTrustError(
|
|
118776
|
+
"no device identity found in the data dir \u2014 start the bridge once to enrol, then re-run"
|
|
118777
|
+
);
|
|
118778
|
+
}
|
|
118779
|
+
|
|
118780
|
+
// src/worker-permission.ts
|
|
118694
118781
|
var PATH_FIELDS = ["file_path", "path", "notebook_path"];
|
|
118695
118782
|
function canonical(p2) {
|
|
118696
118783
|
const abs = resolve3(p2);
|
|
@@ -118734,6 +118821,12 @@ function gateDecision(toolName, input, opts) {
|
|
|
118734
118821
|
if (!opts.isToolTurn()) {
|
|
118735
118822
|
return { deny: true, reason: "tools are disabled on this turn; reply with the say tool only" };
|
|
118736
118823
|
}
|
|
118824
|
+
if (FILE_TOOLS.has(toolName)) {
|
|
118825
|
+
const trustRoot = canonical(trustDir());
|
|
118826
|
+
if (pathsOf(input).some((p2) => within(p2, trustRoot))) {
|
|
118827
|
+
return { deny: true, reason: "the host-trust directory is not writable by the agent" };
|
|
118828
|
+
}
|
|
118829
|
+
}
|
|
118737
118830
|
if (opts.gatesRemoved?.()) {
|
|
118738
118831
|
return { deny: false };
|
|
118739
118832
|
}
|
|
@@ -118973,7 +119066,7 @@ __export(external_exports, {
|
|
|
118973
119066
|
guid: () => guid4,
|
|
118974
119067
|
hash: () => hash2,
|
|
118975
119068
|
hex: () => hex4,
|
|
118976
|
-
hostname: () =>
|
|
119069
|
+
hostname: () => hostname5,
|
|
118977
119070
|
httpUrl: () => httpUrl2,
|
|
118978
119071
|
includes: () => _includes2,
|
|
118979
119072
|
instanceof: () => _instanceof2,
|
|
@@ -120360,7 +120453,7 @@ __export(regexes_exports2, {
|
|
|
120360
120453
|
extendedDuration: () => extendedDuration2,
|
|
120361
120454
|
guid: () => guid3,
|
|
120362
120455
|
hex: () => hex3,
|
|
120363
|
-
hostname: () =>
|
|
120456
|
+
hostname: () => hostname4,
|
|
120364
120457
|
html5Email: () => html5Email2,
|
|
120365
120458
|
idnEmail: () => idnEmail2,
|
|
120366
120459
|
integer: () => integer2,
|
|
@@ -120437,7 +120530,7 @@ var cidrv43 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5
|
|
|
120437
120530
|
var cidrv63 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
|
|
120438
120531
|
var base643 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
|
|
120439
120532
|
var base64url3 = /^[A-Za-z0-9_-]*$/;
|
|
120440
|
-
var
|
|
120533
|
+
var hostname4 = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
|
|
120441
120534
|
var domain2 = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
|
|
120442
120535
|
var e1643 = /^\+[1-9]\d{6,14}$/;
|
|
120443
120536
|
var dateSource2 = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`;
|
|
@@ -130807,7 +130900,7 @@ __export(schemas_exports2, {
|
|
|
130807
130900
|
guid: () => guid4,
|
|
130808
130901
|
hash: () => hash2,
|
|
130809
130902
|
hex: () => hex4,
|
|
130810
|
-
hostname: () =>
|
|
130903
|
+
hostname: () => hostname5,
|
|
130811
130904
|
httpUrl: () => httpUrl2,
|
|
130812
130905
|
instanceof: () => _instanceof2,
|
|
130813
130906
|
int: () => int2,
|
|
@@ -131311,7 +131404,7 @@ var ZodCustomStringFormat2 = /* @__PURE__ */ $constructor2("ZodCustomStringForma
|
|
|
131311
131404
|
function stringFormat2(format, fnOrRegex, _params = {}) {
|
|
131312
131405
|
return _stringFormat2(ZodCustomStringFormat2, format, fnOrRegex, _params);
|
|
131313
131406
|
}
|
|
131314
|
-
function
|
|
131407
|
+
function hostname5(_params) {
|
|
131315
131408
|
return _stringFormat2(ZodCustomStringFormat2, "hostname", regexes_exports2.hostname, _params);
|
|
131316
131409
|
}
|
|
131317
131410
|
function hex4(_params) {
|
|
@@ -133146,20 +133239,20 @@ var ArmingState = class {
|
|
|
133146
133239
|
};
|
|
133147
133240
|
|
|
133148
133241
|
// src/approve-cli.ts
|
|
133149
|
-
import { mkdirSync as
|
|
133150
|
-
import { join as
|
|
133242
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync2, readFileSync as readFileSync5, readdirSync as readdirSync3, rmSync as rmSync3, existsSync as existsSync3 } from "node:fs";
|
|
133243
|
+
import { join as join7 } from "node:path";
|
|
133151
133244
|
var APPROVALS_SUBDIR = "arm-approvals";
|
|
133152
|
-
var
|
|
133245
|
+
var ID_RE2 = /^[A-Za-z0-9_-]{1,128}$/;
|
|
133153
133246
|
var ApproveArmError = class extends Error {
|
|
133154
133247
|
};
|
|
133155
133248
|
function sanitizeRequestId(requestId) {
|
|
133156
|
-
if (!
|
|
133249
|
+
if (!ID_RE2.test(requestId)) {
|
|
133157
133250
|
throw new ApproveArmError("invalid request-id");
|
|
133158
133251
|
}
|
|
133159
133252
|
return requestId;
|
|
133160
133253
|
}
|
|
133161
133254
|
function sanitizeRoomId(roomId) {
|
|
133162
|
-
if (!
|
|
133255
|
+
if (!ID_RE2.test(roomId)) {
|
|
133163
133256
|
throw new ApproveArmError("invalid room-id");
|
|
133164
133257
|
}
|
|
133165
133258
|
return roomId;
|
|
@@ -133167,24 +133260,24 @@ function sanitizeRoomId(roomId) {
|
|
|
133167
133260
|
function writeApproval(dataDir, requestId, roomId) {
|
|
133168
133261
|
const id = sanitizeRequestId(requestId);
|
|
133169
133262
|
const room = sanitizeRoomId(roomId);
|
|
133170
|
-
const dir =
|
|
133171
|
-
|
|
133172
|
-
|
|
133263
|
+
const dir = join7(dataDir, APPROVALS_SUBDIR);
|
|
133264
|
+
mkdirSync4(dir, { recursive: true });
|
|
133265
|
+
writeFileSync2(join7(dir, id), room);
|
|
133173
133266
|
}
|
|
133174
133267
|
function drainApprovals(dataDir) {
|
|
133175
|
-
const dir =
|
|
133268
|
+
const dir = join7(dataDir, APPROVALS_SUBDIR);
|
|
133176
133269
|
if (!existsSync3(dir)) return [];
|
|
133177
133270
|
const out = [];
|
|
133178
|
-
for (const name of
|
|
133179
|
-
if (!
|
|
133271
|
+
for (const name of readdirSync3(dir)) {
|
|
133272
|
+
if (!ID_RE2.test(name)) continue;
|
|
133180
133273
|
let roomId = "";
|
|
133181
133274
|
try {
|
|
133182
|
-
roomId =
|
|
133275
|
+
roomId = readFileSync5(join7(dir, name), "utf8").trim();
|
|
133183
133276
|
} catch {
|
|
133184
133277
|
}
|
|
133185
|
-
out.push({ requestId: name, roomId:
|
|
133278
|
+
out.push({ requestId: name, roomId: ID_RE2.test(roomId) ? roomId : "" });
|
|
133186
133279
|
try {
|
|
133187
|
-
|
|
133280
|
+
rmSync3(join7(dir, name), { force: true, recursive: true });
|
|
133188
133281
|
} catch {
|
|
133189
133282
|
}
|
|
133190
133283
|
}
|
|
@@ -133208,6 +133301,60 @@ function maybeRunApproveArmSubcommand(argv, dataDir) {
|
|
|
133208
133301
|
}
|
|
133209
133302
|
return true;
|
|
133210
133303
|
}
|
|
133304
|
+
function maybeRunHostTrustSubcommand(argv, dataDir, root3) {
|
|
133305
|
+
const cmd = argv[2];
|
|
133306
|
+
if (cmd !== "trust-host" && cmd !== "untrust-host") return false;
|
|
133307
|
+
const flags = argv.slice(3);
|
|
133308
|
+
if (cmd === "untrust-host" && flags.includes("--all")) {
|
|
133309
|
+
const ids = listTrusted(root3);
|
|
133310
|
+
for (const id of ids) revoke(id, root3);
|
|
133311
|
+
console.log(`[untrust-host] revoked ${ids.length} host-trust grant(s)`);
|
|
133312
|
+
return true;
|
|
133313
|
+
}
|
|
133314
|
+
let deviceId;
|
|
133315
|
+
try {
|
|
133316
|
+
deviceId = readDeviceId(dataDir);
|
|
133317
|
+
} catch (e7) {
|
|
133318
|
+
console.error(`[${cmd}] ${e7.message}`);
|
|
133319
|
+
process.exitCode = 1;
|
|
133320
|
+
return true;
|
|
133321
|
+
}
|
|
133322
|
+
if (cmd === "untrust-host") {
|
|
133323
|
+
const removed = revoke(deviceId, root3);
|
|
133324
|
+
console.log(
|
|
133325
|
+
removed ? `[untrust-host] revoked host trust for ${deviceId}` : `[untrust-host] no host-trust grant existed for ${deviceId}`
|
|
133326
|
+
);
|
|
133327
|
+
return true;
|
|
133328
|
+
}
|
|
133329
|
+
if (!flags.includes("--yes")) {
|
|
133330
|
+
console.log(
|
|
133331
|
+
`This machine will trust web-granted full access for device ${deviceId}.
|
|
133332
|
+
Writes: ${trustDir(root3)}/${deviceId}
|
|
133333
|
+
Revoke: av-bridge untrust-host
|
|
133334
|
+
Re-run with --yes to confirm.`
|
|
133335
|
+
);
|
|
133336
|
+
return true;
|
|
133337
|
+
}
|
|
133338
|
+
grant(deviceId, root3);
|
|
133339
|
+
console.log(`[trust-host] host trust granted for ${deviceId}`);
|
|
133340
|
+
return true;
|
|
133341
|
+
}
|
|
133342
|
+
|
|
133343
|
+
// src/bsafe-arming.ts
|
|
133344
|
+
function decideArming(opts) {
|
|
133345
|
+
const hostTrusted = isTrusted(opts.deviceId, opts.trustRoot);
|
|
133346
|
+
const gatesRemoved = opts.snapshotGatesRemoved && hostTrusted;
|
|
133347
|
+
if (!opts.osIsolated) {
|
|
133348
|
+
return { gatesRemoved, mode: "authoritative-bpure", recordWebArmed: false, hostTrusted };
|
|
133349
|
+
}
|
|
133350
|
+
if (gatesRemoved) {
|
|
133351
|
+
return { gatesRemoved, mode: "authoritative-under-grant", recordWebArmed: true, hostTrusted };
|
|
133352
|
+
}
|
|
133353
|
+
if (hostTrusted) {
|
|
133354
|
+
return { gatesRemoved: false, mode: "authoritative-bsafe", recordWebArmed: true, hostTrusted };
|
|
133355
|
+
}
|
|
133356
|
+
return { gatesRemoved: false, mode: "c2-disarm-only", recordWebArmed: false, hostTrusted };
|
|
133357
|
+
}
|
|
133211
133358
|
|
|
133212
133359
|
// src/bridge.ts
|
|
133213
133360
|
var APPROVAL_POLL_MS = 1500;
|
|
@@ -133314,11 +133461,13 @@ function wireBridge(channel, session, target, opts = {}) {
|
|
|
133314
133461
|
let gatesRemoved = false;
|
|
133315
133462
|
const gatesRemovedGetter = () => gatesRemoved;
|
|
133316
133463
|
opts.onGatesRemoved?.(gatesRemovedGetter);
|
|
133317
|
-
|
|
133464
|
+
let hostTrusted = false;
|
|
133465
|
+
const webArmedRevocable = /* @__PURE__ */ new Set();
|
|
133318
133466
|
channel.on("state", (s10) => {
|
|
133319
133467
|
if (s10 !== "ready") {
|
|
133320
133468
|
workAllowed = false;
|
|
133321
133469
|
gatesRemoved = false;
|
|
133470
|
+
hostTrusted = false;
|
|
133322
133471
|
}
|
|
133323
133472
|
});
|
|
133324
133473
|
const arming = new ArmingState();
|
|
@@ -133336,7 +133485,14 @@ function wireBridge(channel, session, target, opts = {}) {
|
|
|
133336
133485
|
e7.hushedUntil ? `room ${e7.roomId.slice(0, 8)} hushed until ${e7.hushedUntil} \u2014 holding` : `room ${e7.roomId.slice(0, 8)} hush cleared`
|
|
133337
133486
|
);
|
|
133338
133487
|
});
|
|
133488
|
+
let warnedMissingSenderIsAgent = false;
|
|
133339
133489
|
channel.on("room_message", (e7) => {
|
|
133490
|
+
if (typeof e7.senderIsAgent !== "boolean" && !warnedMissingSenderIsAgent) {
|
|
133491
|
+
warnedMissingSenderIsAgent = true;
|
|
133492
|
+
log(
|
|
133493
|
+
"WARNING: inbound room_message has no `senderIsAgent` \u2014 replies to humans in rooms will be DROPPED whenever the model answers in plain text instead of calling say (#630). Most likely cause: a stale bundled plugin dist. Rebuild the bridge (npm run build in packages/claude-room-bridge) and reinstall."
|
|
133494
|
+
);
|
|
133495
|
+
}
|
|
133340
133496
|
if (opts.roomFilter && e7.roomId !== opts.roomFilter) return;
|
|
133341
133497
|
if (hush.isHushed(e7.roomId)) {
|
|
133342
133498
|
log(`inbound from ${e7.senderName} in ${e7.roomId.slice(0, 8)} \u2014 room hushed, holding (not replying)`);
|
|
@@ -133360,7 +133516,14 @@ function wireBridge(channel, session, target, opts = {}) {
|
|
|
133360
133516
|
const osIsolated = opts.osIsolated !== void 0 ? opts.osIsolated : process.env.AV_WORKER_OS_ISOLATED === "1" || process.env.AV_WORKER_OS_ISOLATED === "true";
|
|
133361
133517
|
const heartbeat = () => {
|
|
133362
133518
|
try {
|
|
133363
|
-
channel.sendWorkerHeartbeat?.({
|
|
133519
|
+
channel.sendWorkerHeartbeat?.({
|
|
133520
|
+
workerCapable,
|
|
133521
|
+
armedRooms: arming.armedRooms(),
|
|
133522
|
+
hostTrusted,
|
|
133523
|
+
// Already the conjunction: `gatesRemoved` is assigned from
|
|
133524
|
+
// decideArming, which ANDs the web grant with host trust.
|
|
133525
|
+
gatesEffective: gatesRemoved
|
|
133526
|
+
});
|
|
133364
133527
|
} catch (err) {
|
|
133365
133528
|
log(`worker heartbeat send failed (ignored): ${err instanceof Error ? err.message : String(err)}`);
|
|
133366
133529
|
}
|
|
@@ -133369,40 +133532,56 @@ function wireBridge(channel, session, target, opts = {}) {
|
|
|
133369
133532
|
channel.on("ready", () => heartbeat());
|
|
133370
133533
|
const applyArmingSnapshot = (s10) => {
|
|
133371
133534
|
workAllowed = s10?.workAllowed === true;
|
|
133372
|
-
|
|
133535
|
+
const prevHostTrusted = hostTrusted;
|
|
133536
|
+
const prevGatesEffective = gatesRemoved;
|
|
133537
|
+
const decision = decideArming({
|
|
133538
|
+
snapshotGatesRemoved: s10?.gatesRemoved === true,
|
|
133539
|
+
osIsolated,
|
|
133540
|
+
deviceId: channel.deviceId ?? "",
|
|
133541
|
+
trustRoot: opts.trustRoot
|
|
133542
|
+
});
|
|
133543
|
+
gatesRemoved = decision.gatesRemoved;
|
|
133544
|
+
hostTrusted = decision.hostTrusted;
|
|
133545
|
+
const trustChanged = hostTrusted !== prevHostTrusted || gatesRemoved !== prevGatesEffective;
|
|
133546
|
+
if (s10?.gatesRemoved === true && !decision.hostTrusted) {
|
|
133547
|
+
log(
|
|
133548
|
+
`"Remove all gates" is ON for this device but this HOST has not approved it. Full access is NOT in effect. Grant on this machine: av-bridge trust-host`
|
|
133549
|
+
);
|
|
133550
|
+
}
|
|
133373
133551
|
if (!workerCapable) {
|
|
133374
133552
|
log("arming_snapshot ignored \u2014 not worker-capable");
|
|
133553
|
+
if (trustChanged) heartbeat();
|
|
133375
133554
|
return;
|
|
133376
133555
|
}
|
|
133377
133556
|
const roomIds = s10?.roomIds ?? [];
|
|
133378
133557
|
try {
|
|
133379
133558
|
const before = new Set(arming.armedRooms());
|
|
133380
|
-
if (
|
|
133381
|
-
|
|
133382
|
-
|
|
133383
|
-
} else if (osIsolated) {
|
|
133384
|
-
if (webArmedUnderGrant.size > 0) {
|
|
133385
|
-
const revoked = [...webArmedUnderGrant].filter((r7) => arming.isArmed(r7));
|
|
133559
|
+
if (decision.mode === "c2-disarm-only") {
|
|
133560
|
+
if (webArmedRevocable.size > 0) {
|
|
133561
|
+
const revoked = [...webArmedRevocable].filter((r7) => arming.isArmed(r7));
|
|
133386
133562
|
for (const r7 of revoked) arming.disarm(r7);
|
|
133387
|
-
|
|
133563
|
+
webArmedRevocable.clear();
|
|
133388
133564
|
if (revoked.length > 0) {
|
|
133389
133565
|
log(
|
|
133390
|
-
`arming_snapshot:
|
|
133566
|
+
`arming_snapshot: full access is not in effect \u2014 disarming the rooms it had armed on this OS-isolated worker: [${revoked.map((r7) => r7.slice(0, 8)).join(", ")}]`
|
|
133391
133567
|
);
|
|
133392
133568
|
}
|
|
133393
133569
|
}
|
|
133394
133570
|
const dropped = arming.reconcileDisarm(roomIds);
|
|
133395
133571
|
if (dropped.length > 0) {
|
|
133396
133572
|
log(
|
|
133397
|
-
`arming_snapshot: OS-isolated worker \u2014 disarm-only
|
|
133573
|
+
`arming_snapshot: OS-isolated worker \u2014 disarm-only without host trust (av-bridge trust-host); disarmed: [${dropped.map((r7) => r7.slice(0, 8)).join(", ")}]`
|
|
133398
133574
|
);
|
|
133399
133575
|
}
|
|
133400
133576
|
} else {
|
|
133401
133577
|
arming.applyAuthoritative(roomIds);
|
|
133578
|
+
if (decision.recordWebArmed) {
|
|
133579
|
+
for (const r7 of roomIds) if (!before.has(r7)) webArmedRevocable.add(r7);
|
|
133580
|
+
}
|
|
133402
133581
|
}
|
|
133403
133582
|
const after = arming.armedRooms();
|
|
133404
133583
|
const changed = after.length !== before.size || after.some((r7) => !before.has(r7));
|
|
133405
|
-
if (changed) heartbeat();
|
|
133584
|
+
if (changed || trustChanged) heartbeat();
|
|
133406
133585
|
} catch (err) {
|
|
133407
133586
|
log(`arming_snapshot handling failed (ignored): ${err instanceof Error ? err.message : String(err)}`);
|
|
133408
133587
|
}
|
|
@@ -133458,6 +133637,9 @@ async function main() {
|
|
|
133458
133637
|
if (maybeRunApproveArmSubcommand(process.argv, dataDirForSubcommand)) {
|
|
133459
133638
|
process.exit(process.exitCode ?? 0);
|
|
133460
133639
|
}
|
|
133640
|
+
if (maybeRunHostTrustSubcommand(process.argv, dataDirForSubcommand)) {
|
|
133641
|
+
process.exit(process.exitCode ?? 0);
|
|
133642
|
+
}
|
|
133461
133643
|
const { maybeRunServiceSubcommand: maybeRunServiceSubcommand2 } = await Promise.resolve().then(() => (init_subcommand(), subcommand_exports));
|
|
133462
133644
|
if (maybeRunServiceSubcommand2(process.argv, process.env)) {
|
|
133463
133645
|
process.exit(process.exitCode ?? 0);
|
|
@@ -133469,7 +133651,7 @@ async function main() {
|
|
|
133469
133651
|
"[bridge] warning: passing the invite token on the command line is visible to other local users via 'ps'. Prefer: AV_INVITE_TOKEN=\u2026 npx @agentvault/claude-bridge"
|
|
133470
133652
|
);
|
|
133471
133653
|
}
|
|
133472
|
-
console.error(`[bridge] version: ${true ? "0.
|
|
133654
|
+
console.error(`[bridge] version: ${true ? "0.7.1" : "dev"}`);
|
|
133473
133655
|
console.error(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
|
|
133474
133656
|
console.error(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
|
|
133475
133657
|
if (cfg.armRoom) {
|
|
@@ -133555,9 +133737,9 @@ async function main() {
|
|
|
133555
133737
|
`[worker-trap] ${rec.at} ${rec.outcome} after ${rec.ranMs}ms (waited ${rec.waitedMs}ms behind ${rec.queueDepthAtEnqueue}, ${rec.queueDepthAtStart} still queued)` + (rec.session ? ` composed=${rec.session.composedChars} result=${rec.session.sawResult} said=${rec.session.said}` : "")
|
|
133556
133738
|
);
|
|
133557
133739
|
try {
|
|
133558
|
-
const dir =
|
|
133559
|
-
|
|
133560
|
-
appendFileSync2(
|
|
133740
|
+
const dir = join11(cfg.dataDir, "logs");
|
|
133741
|
+
mkdirSync6(dir, { recursive: true });
|
|
133742
|
+
appendFileSync2(join11(dir, "worker-incidents.jsonl"), JSON.stringify(rec) + "\n");
|
|
133561
133743
|
} catch (err) {
|
|
133562
133744
|
console.error(`[worker-trap] could not persist incident: ${err.message}`);
|
|
133563
133745
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/claude-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgentVault Claude Bridge — daemon for bridging a Claude agent into secure E2E-encrypted AgentVault 1:1 direct messages and rooms.",
|
|
6
6
|
"main": "dist/index.js",
|