@agentvault/claude-bridge 0.5.10 → 0.6.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/bridge.d.ts +20 -3
- package/dist/index.js +96 -10
- package/dist/session.d.ts +12 -0
- package/dist/worker-permission.d.ts +5 -0
- package/package.json +3 -3
package/dist/bridge.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface ArmingSnapshot {
|
|
|
26
26
|
* (backend `devices.work_allowed`, Task 2). Optional so older/test payloads
|
|
27
27
|
* without it don't break — treated as `false` (fail-closed) when absent. */
|
|
28
28
|
workAllowed?: boolean;
|
|
29
|
+
/** #627: owner's "Remove all gates" grant. Absent ⇒ gates stay in place. */
|
|
30
|
+
gatesRemoved?: boolean;
|
|
29
31
|
}
|
|
30
32
|
/** Metadata SecureChannel attaches to a 1:1 `message` event. `roomId` is set only
|
|
31
33
|
* when the `message` actually originated in a room (handled by room_message), so
|
|
@@ -176,9 +178,16 @@ export declare function wireBridge(channel: RoomChannel, session: RoomSession, t
|
|
|
176
178
|
* Task 4 (C2 shell-gate): true when this worker runs Bash unconfined
|
|
177
179
|
* (AV_WORKER_OS_ISOLATED=1 — see config.ts/index.ts, threaded from the
|
|
178
180
|
* session's `osIsolated`). When true, the `arming_snapshot` handler stays
|
|
179
|
-
* disarm-only regardless of B1/worker-capability — a shell worker
|
|
180
|
-
*
|
|
181
|
-
*
|
|
181
|
+
* disarm-only regardless of B1/worker-capability — a shell worker is not
|
|
182
|
+
* armed by a web-originated flag (RCE risk).
|
|
183
|
+
*
|
|
184
|
+
* #627 EXCEPTION: unless the owner has removed all gates. That grant is an
|
|
185
|
+
* explicit, recorded decision to accept exactly this risk, so it lifts C2;
|
|
186
|
+
* revoking it disarms the rooms the web armed under it. See the
|
|
187
|
+
* `applyArmingSnapshot` comment block for the full rationale. Without the
|
|
188
|
+
* grant, C2 is unchanged.
|
|
189
|
+
*
|
|
190
|
+
* Falls back to reading the env directly only if the caller doesn't thread this.
|
|
182
191
|
*/
|
|
183
192
|
osIsolated?: boolean;
|
|
184
193
|
/**
|
|
@@ -190,5 +199,13 @@ export declare function wireBridge(channel: RoomChannel, session: RoomSession, t
|
|
|
190
199
|
* (index.ts, arming-authoritative.test.ts, bridge.test.ts) stays intact.
|
|
191
200
|
*/
|
|
192
201
|
onWorkAllowed?: (getter: () => boolean) => void;
|
|
202
|
+
/**
|
|
203
|
+
* #627: out-param that receives the live `gatesRemoved()` getter once,
|
|
204
|
+
* synchronously, during wiring — same shape as `onWorkAllowed` above, and
|
|
205
|
+
* for the same reason (keeps wireBridge's return type stable so existing
|
|
206
|
+
* callers are untouched). index.ts captures it and threads it into the
|
|
207
|
+
* worker session's gate.
|
|
208
|
+
*/
|
|
209
|
+
onGatesRemoved?: (getter: () => boolean) => void;
|
|
193
210
|
}): ArmingState;
|
|
194
211
|
//# sourceMappingURL=bridge.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -65555,7 +65555,7 @@ var init_channel = __esm2({
|
|
|
65555
65555
|
*/
|
|
65556
65556
|
sendActivitySpan(spanData) {
|
|
65557
65557
|
if (!this._ws || this._ws.readyState !== WebSocket2.OPEN) return;
|
|
65558
|
-
const pluginVersion = true ? "0.23.
|
|
65558
|
+
const pluginVersion = true ? "0.23.8" : "0.0.0-dev";
|
|
65559
65559
|
const agentName = this.config.agentName ?? "Agent";
|
|
65560
65560
|
const resource = {
|
|
65561
65561
|
"service.name": "agentvault-agent",
|
|
@@ -67172,7 +67172,7 @@ var init_channel = __esm2({
|
|
|
67172
67172
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67173
67173
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67174
67174
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67175
|
-
pluginVersion: true ? "0.23.
|
|
67175
|
+
pluginVersion: true ? "0.23.8" : "0.0.0-dev"
|
|
67176
67176
|
});
|
|
67177
67177
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67178
67178
|
}
|
|
@@ -67251,6 +67251,10 @@ var init_channel = __esm2({
|
|
|
67251
67251
|
await this._handleDeviceRevoked();
|
|
67252
67252
|
return;
|
|
67253
67253
|
}
|
|
67254
|
+
if (data.event === "connection_rejected") {
|
|
67255
|
+
await this._handleConnectionRejected(data);
|
|
67256
|
+
return;
|
|
67257
|
+
}
|
|
67254
67258
|
if (data.event === "device_linked") {
|
|
67255
67259
|
await this._handleDeviceLinked(data.data);
|
|
67256
67260
|
return;
|
|
@@ -67424,6 +67428,7 @@ var init_channel = __esm2({
|
|
|
67424
67428
|
if (data.event === "arming_snapshot") {
|
|
67425
67429
|
this.emit("arming_snapshot", {
|
|
67426
67430
|
workAllowed: data.data?.work_allowed === true,
|
|
67431
|
+
gatesRemoved: data.data?.gates_removed === true,
|
|
67427
67432
|
roomIds: data.data?.room_ids ?? []
|
|
67428
67433
|
});
|
|
67429
67434
|
}
|
|
@@ -67485,7 +67490,7 @@ var init_channel = __esm2({
|
|
|
67485
67490
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67486
67491
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67487
67492
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67488
|
-
pluginVersion: true ? "0.23.
|
|
67493
|
+
pluginVersion: true ? "0.23.8" : "0.0.0-dev"
|
|
67489
67494
|
});
|
|
67490
67495
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67491
67496
|
}
|
|
@@ -70677,6 +70682,37 @@ ${messageText}`;
|
|
|
70677
70682
|
* re-enrollment, which SHOULD wipe creds, goes through setup --force /
|
|
70678
70683
|
* _forceReEnroll in start().
|
|
70679
70684
|
*/
|
|
70685
|
+
/**
|
|
70686
|
+
* Handle a WS `connection_rejected` event.
|
|
70687
|
+
*
|
|
70688
|
+
* The server accepts the WS upgrade, then — for a device it will not serve —
|
|
70689
|
+
* emits `{event:"connection_rejected", reason, retryable}` and closes with
|
|
70690
|
+
* code 4403. `retryable:false` (e.g. `device_revoked`) is a TERMINAL
|
|
70691
|
+
* credential failure: retrying can only reconnect, get rejected, and close
|
|
70692
|
+
* again. Left unhandled, that generic close drove the reconnect classifier
|
|
70693
|
+
* into "reconnect loop detected", which the bridge treats as a restartable
|
|
70694
|
+
* exit — so launchd/systemd relaunched us every ~30s to hammer the backend
|
|
70695
|
+
* with the same rejected handshake (empirically ~1150 rejects / 2h).
|
|
70696
|
+
*
|
|
70697
|
+
* So we route a non-retryable rejection to the SAME `auth_failed` surface the
|
|
70698
|
+
* proactive/reactive reissue paths use (see channel.ts emit sites). The
|
|
70699
|
+
* bridge wires `auth_failed` to a clean exit(0) (packages/claude-room-bridge
|
|
70700
|
+
* /src/bridge.ts), so launchd stops relaunching. Mirrors those emitters:
|
|
70701
|
+
* emit only + mark the session; the consumer decides how to stop. A
|
|
70702
|
+
* `retryable` rejection is transient — fall through to a normal reconnect.
|
|
70703
|
+
*/
|
|
70704
|
+
async _handleConnectionRejected(data) {
|
|
70705
|
+
if (data?.retryable === true) {
|
|
70706
|
+
this._scheduleReconnect();
|
|
70707
|
+
return;
|
|
70708
|
+
}
|
|
70709
|
+
this._authFailedThisSession = true;
|
|
70710
|
+
const authReason = data?.reason === "device_jwt_expired" ? "device_jwt_expired" : "device_revoked";
|
|
70711
|
+
console.warn(
|
|
70712
|
+
`[SecureChannel] connection_rejected (reason=${data?.reason ?? "unknown"}, retryable=false) \u2014 terminal; surfacing auth_failed`
|
|
70713
|
+
);
|
|
70714
|
+
this.emit("auth_failed", { reason: authReason });
|
|
70715
|
+
}
|
|
70680
70716
|
async _handleDeviceRevoked() {
|
|
70681
70717
|
const now = Date.now();
|
|
70682
70718
|
this._revokeRecoveries = this._revokeRecoveries.filter(
|
|
@@ -97232,7 +97268,7 @@ var init_index = __esm2({
|
|
|
97232
97268
|
init_skill_invoker();
|
|
97233
97269
|
await init_skill_telemetry();
|
|
97234
97270
|
await init_policy_enforcer();
|
|
97235
|
-
VERSION = true ? "0.23.
|
|
97271
|
+
VERSION = true ? "0.23.8" : "0.0.0-dev";
|
|
97236
97272
|
}
|
|
97237
97273
|
});
|
|
97238
97274
|
await init_index();
|
|
@@ -118580,6 +118616,12 @@ function gateDecision(toolName, input, opts) {
|
|
|
118580
118616
|
if (!opts.isToolTurn()) {
|
|
118581
118617
|
return { deny: true, reason: "tools are disabled on this turn; reply with the say tool only" };
|
|
118582
118618
|
}
|
|
118619
|
+
if (opts.gatesRemoved?.()) {
|
|
118620
|
+
return { deny: false };
|
|
118621
|
+
}
|
|
118622
|
+
if (opts.isOwnerDmTurn?.()) {
|
|
118623
|
+
return { deny: false };
|
|
118624
|
+
}
|
|
118583
118625
|
if (toolName === "Bash") {
|
|
118584
118626
|
return opts.osIsolated ? { deny: false } : { deny: true, reason: "Bash is disabled in worker mode unless the process is OS-isolated (set AV_WORKER_OS_ISOLATED=1)" };
|
|
118585
118627
|
}
|
|
@@ -132612,6 +132654,19 @@ var PersistentClaudeSession = class {
|
|
|
132612
132654
|
// default (no getter ⇒ no tools). Read LIVE per tool decision so a Work-off
|
|
132613
132655
|
// flip / fail-closed reconnect window denies the very next call.
|
|
132614
132656
|
isToolTurn: () => (this.opts.workAllowed?.() ?? false) && (this.currentAutoReply || this.currentArmedGetter()),
|
|
132657
|
+
// Full-access lane: a 1:1 owner DM, whose ephemeral worker is seeded with
|
|
132658
|
+
// ONLY the owner's own message, so no third party can steer the turn.
|
|
132659
|
+
// Conjoined with !currentArmedGetter() deliberately: if a room turn ever
|
|
132660
|
+
// arrived with autoReplyOnText set, it must fall through to the allowlist
|
|
132661
|
+
// rather than inherit full access. Fail-safe, not merely descriptive.
|
|
132662
|
+
// Read LIVE per tool decision, like isToolTurn.
|
|
132663
|
+
isOwnerDmTurn: () => this.currentAutoReply && !this.currentArmedGetter(),
|
|
132664
|
+
// #627: the owner removed all gates for this device. Evaluated by
|
|
132665
|
+
// gateDecision AFTER isToolTurn, so an unarmed room turn stays chat-only
|
|
132666
|
+
// — this widens what a tool turn may do, never which turns get tools.
|
|
132667
|
+
// Read LIVE per decision so a revoke (or a reconnect reset) denies the
|
|
132668
|
+
// very next call, exactly like workAllowed above.
|
|
132669
|
+
gatesRemoved: () => this.opts.gatesRemoved?.() ?? false,
|
|
132615
132670
|
// Slice 2 Plan B: emit an audit self-report for each tool decision on an
|
|
132616
132671
|
// armed-room turn (both allow and deny). room_say is excluded by the hook.
|
|
132617
132672
|
isArmedTurn: () => this.currentArmedGetter(),
|
|
@@ -132627,7 +132682,7 @@ var PersistentClaudeSession = class {
|
|
|
132627
132682
|
}
|
|
132628
132683
|
};
|
|
132629
132684
|
console.error(
|
|
132630
|
-
`[worker-gate]
|
|
132685
|
+
`[worker-gate] active \u2014 1:1 owner DM: FULL access, no allowlist, no workspace fence. Armed room: FULL access whenever "Remove all gates" is on (live, owner-controlled, off by default); otherwise allowlist, workspace=${this.opts.workspaceDir ?? "(none: file tools disabled)"}, bash=${this.opts.osIsolated ? "enabled (OS-isolated)" : "disabled"}`
|
|
132631
132686
|
);
|
|
132632
132687
|
return {
|
|
132633
132688
|
...base,
|
|
@@ -132764,7 +132819,7 @@ function makeRouter(deps) {
|
|
|
132764
132819
|
push(text, reply, opts) {
|
|
132765
132820
|
const replySink = reply ?? (() => {
|
|
132766
132821
|
});
|
|
132767
|
-
const isOwnerDm = opts?.autoReplyOnText === true && deps.workAllowed();
|
|
132822
|
+
const isOwnerDm = opts?.autoReplyOnText === true && opts?.armed === void 0 && deps.workAllowed();
|
|
132768
132823
|
const isArmedRoom = opts?.armed?.() === true && deps.workAllowed();
|
|
132769
132824
|
if (isOwnerDm) {
|
|
132770
132825
|
deps.queue.enqueue({
|
|
@@ -133068,8 +133123,15 @@ function wireBridge(channel, session, target, opts = {}) {
|
|
|
133068
133123
|
let workAllowed = false;
|
|
133069
133124
|
const workAllowedGetter = () => workAllowed;
|
|
133070
133125
|
opts.onWorkAllowed?.(workAllowedGetter);
|
|
133126
|
+
let gatesRemoved = false;
|
|
133127
|
+
const gatesRemovedGetter = () => gatesRemoved;
|
|
133128
|
+
opts.onGatesRemoved?.(gatesRemovedGetter);
|
|
133129
|
+
const webArmedUnderGrant = /* @__PURE__ */ new Set();
|
|
133071
133130
|
channel.on("state", (s10) => {
|
|
133072
|
-
if (s10 !== "ready")
|
|
133131
|
+
if (s10 !== "ready") {
|
|
133132
|
+
workAllowed = false;
|
|
133133
|
+
gatesRemoved = false;
|
|
133134
|
+
}
|
|
133073
133135
|
});
|
|
133074
133136
|
const arming = new ArmingState();
|
|
133075
133137
|
if (opts.armRoom === true && opts.roomFilter) {
|
|
@@ -133119,6 +133181,7 @@ function wireBridge(channel, session, target, opts = {}) {
|
|
|
133119
133181
|
channel.on("ready", () => heartbeat());
|
|
133120
133182
|
const applyArmingSnapshot = (s10) => {
|
|
133121
133183
|
workAllowed = s10?.workAllowed === true;
|
|
133184
|
+
gatesRemoved = s10?.gatesRemoved === true;
|
|
133122
133185
|
if (!workerCapable) {
|
|
133123
133186
|
log("arming_snapshot ignored \u2014 not worker-capable");
|
|
133124
133187
|
return;
|
|
@@ -133126,7 +133189,20 @@ function wireBridge(channel, session, target, opts = {}) {
|
|
|
133126
133189
|
const roomIds = s10?.roomIds ?? [];
|
|
133127
133190
|
try {
|
|
133128
133191
|
const before = new Set(arming.armedRooms());
|
|
133129
|
-
if (osIsolated) {
|
|
133192
|
+
if (osIsolated && gatesRemoved) {
|
|
133193
|
+
arming.applyAuthoritative(roomIds);
|
|
133194
|
+
for (const r7 of roomIds) if (!before.has(r7)) webArmedUnderGrant.add(r7);
|
|
133195
|
+
} else if (osIsolated) {
|
|
133196
|
+
if (webArmedUnderGrant.size > 0) {
|
|
133197
|
+
const revoked = [...webArmedUnderGrant].filter((r7) => arming.isArmed(r7));
|
|
133198
|
+
for (const r7 of revoked) arming.disarm(r7);
|
|
133199
|
+
webArmedUnderGrant.clear();
|
|
133200
|
+
if (revoked.length > 0) {
|
|
133201
|
+
log(
|
|
133202
|
+
`arming_snapshot: "Remove all gates" was revoked \u2014 disarming the rooms it had armed on this OS-isolated worker: [${revoked.map((r7) => r7.slice(0, 8)).join(", ")}]`
|
|
133203
|
+
);
|
|
133204
|
+
}
|
|
133205
|
+
}
|
|
133130
133206
|
const dropped = arming.reconcileDisarm(roomIds);
|
|
133131
133207
|
if (dropped.length > 0) {
|
|
133132
133208
|
log(
|
|
@@ -133205,7 +133281,7 @@ async function main() {
|
|
|
133205
133281
|
"[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"
|
|
133206
133282
|
);
|
|
133207
133283
|
}
|
|
133208
|
-
console.error(`[bridge] version: ${true ? "0.
|
|
133284
|
+
console.error(`[bridge] version: ${true ? "0.6.1" : "dev"}`);
|
|
133209
133285
|
console.error(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
|
|
133210
133286
|
console.error(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
|
|
133211
133287
|
if (cfg.armRoom) {
|
|
@@ -133217,7 +133293,7 @@ async function main() {
|
|
|
133217
133293
|
);
|
|
133218
133294
|
} else {
|
|
133219
133295
|
console.error(
|
|
133220
|
-
'[bridge]
|
|
133296
|
+
'[bridge] 1:1 owner DMs run with FULL tool access when "Allow tools" is on for this agent (dashboard switch, default off): any command, any file this user account can reach, network, skills and subagents \u2014 equivalent to a terminal on this machine, with no per-action prompt. Rooms are NOT affected: they stay say-only unless armed (AV_ARM_ROOM=1 with a pinned AV_ROOM_ID), and an armed room keeps the confined allowlist. Each tool-enabled turn runs in a fresh session seeded with only that one message, so room text cannot leak into a DM turn.'
|
|
133221
133297
|
);
|
|
133222
133298
|
}
|
|
133223
133299
|
const target = new ActiveTarget();
|
|
@@ -133236,6 +133312,8 @@ async function main() {
|
|
|
133236
133312
|
};
|
|
133237
133313
|
let liveWorkAllowed = () => false;
|
|
133238
133314
|
const workAllowed = () => liveWorkAllowed();
|
|
133315
|
+
let liveGatesRemoved = () => false;
|
|
133316
|
+
const gatesRemoved = () => liveGatesRemoved();
|
|
133239
133317
|
const listener = new PersistentClaudeSession({
|
|
133240
133318
|
model: cfg.model,
|
|
133241
133319
|
systemPrompt: agentSystemPrompt,
|
|
@@ -133259,6 +133337,11 @@ async function main() {
|
|
|
133259
133337
|
// Task 4: the hard backstop — no tool runs on any worker turn (owner DM or
|
|
133260
133338
|
// armed room) unless Work is on. Read live per tool decision.
|
|
133261
133339
|
workAllowed,
|
|
133340
|
+
// #627: only the WORKER session gets this. The listener is the locked
|
|
133341
|
+
// tools:[] facet and must stay that way — a grant that widened the
|
|
133342
|
+
// always-on listener would put an unrestricted shell behind every
|
|
133343
|
+
// untrusted room message, not just tool turns.
|
|
133344
|
+
gatesRemoved,
|
|
133262
133345
|
ephemeral: true,
|
|
133263
133346
|
maxTurns: WORKER_MAX_TURNS,
|
|
133264
133347
|
workspaceDir: cfg.workspaceDir,
|
|
@@ -133291,6 +133374,9 @@ async function main() {
|
|
|
133291
133374
|
// during wiring) into the indirection the router + worker session read live.
|
|
133292
133375
|
onWorkAllowed: (getter) => {
|
|
133293
133376
|
liveWorkAllowed = getter;
|
|
133377
|
+
},
|
|
133378
|
+
onGatesRemoved: (getter) => {
|
|
133379
|
+
liveGatesRemoved = getter;
|
|
133294
133380
|
}
|
|
133295
133381
|
}
|
|
133296
133382
|
);
|
package/dist/session.d.ts
CHANGED
|
@@ -86,6 +86,18 @@ export interface SessionOpts {
|
|
|
86
86
|
* false, even if the router mis-routed. Read LIVE at each tool decision (never
|
|
87
87
|
* snapshotted). Absent getter ⇒ `?? false` ⇒ fail-closed (no tools). */
|
|
88
88
|
workAllowed?: () => boolean;
|
|
89
|
+
/**
|
|
90
|
+
* #627: live, fail-closed device-level "Remove all gates" grant. When this
|
|
91
|
+
* returns true the gate permits EVERY tool with no restrictions, in rooms
|
|
92
|
+
* and 1:1 alike — the owner's explicit decision, recorded in #627.
|
|
93
|
+
*
|
|
94
|
+
* NOT a replacement for `isToolTurn`: this is read AFTER it, so an UNARMED
|
|
95
|
+
* room turn is still chat-only. That ordering is what separates "a room you
|
|
96
|
+
* enabled" from "any room somebody added your agent to", and it costs
|
|
97
|
+
* nothing to keep. Read LIVE at each tool decision (never snapshotted);
|
|
98
|
+
* absent getter ⇒ `?? false` ⇒ gates stay in place.
|
|
99
|
+
*/
|
|
100
|
+
gatesRemoved?: () => boolean;
|
|
89
101
|
}
|
|
90
102
|
export declare class PersistentClaudeSession {
|
|
91
103
|
private opts;
|
|
@@ -13,6 +13,9 @@ export declare function makeWorkerPreToolUseHook(opts: {
|
|
|
13
13
|
/** True when the process is attested OS-isolated, gating whether Bash is allowed at all. */
|
|
14
14
|
osIsolated: boolean;
|
|
15
15
|
isToolTurn: () => boolean;
|
|
16
|
+
/** True when this turn is a 1:1 owner DM. That lane gets full access; rooms
|
|
17
|
+
* keep the allowlist. Read live per tool call, like isToolTurn. */
|
|
18
|
+
isOwnerDmTurn?: () => boolean;
|
|
16
19
|
/** Slice 2 Plan B: true when this turn comes from an armed room. When set,
|
|
17
20
|
* each tool decision (allow AND deny) is forwarded to onToolDecision for
|
|
18
21
|
* audit self-reporting. room_say is always excluded (it is the reply
|
|
@@ -36,5 +39,7 @@ export declare function makeWorkerPermission(opts: {
|
|
|
36
39
|
/** True when the process is attested OS-isolated, gating whether Bash is allowed at all. */
|
|
37
40
|
osIsolated: boolean;
|
|
38
41
|
isToolTurn: () => boolean;
|
|
42
|
+
/** True when this turn is a 1:1 owner DM (full access lane). */
|
|
43
|
+
isOwnerDmTurn?: () => boolean;
|
|
39
44
|
}): CanUseTool;
|
|
40
45
|
//# sourceMappingURL=worker-permission.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/claude-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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",
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
|
-
"build": "tsc --emitDeclarationOnly && node build.mjs",
|
|
26
|
+
"build": "npm --prefix ../plugin run build && tsc --emitDeclarationOnly && node build.mjs",
|
|
27
27
|
"start": "node dist/index.js",
|
|
28
28
|
"test": "vitest run",
|
|
29
|
-
"prepublishOnly": "
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@anthropic-ai/claude-agent-sdk": "^0.2.114",
|