@agentvault/claude-bridge 0.3.4 → 0.4.0
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 +28 -0
- package/dist/arming.d.ts +58 -0
- package/dist/audit-report.d.ts +19 -0
- package/dist/bridge.d.ts +48 -2
- package/dist/config.d.ts +9 -0
- package/dist/index.js +1694 -869
- package/dist/plan-c-crux-harness.d.ts +2 -0
- package/dist/service/backend.d.ts +25 -0
- package/dist/service/backend.d.ts.map +1 -0
- package/dist/service/launchd.d.ts +17 -0
- package/dist/service/launchd.d.ts.map +1 -0
- package/dist/service/spec.d.ts +18 -0
- package/dist/service/spec.d.ts.map +1 -0
- package/dist/service/subcommand.d.ts +11 -0
- package/dist/service/subcommand.d.ts.map +1 -0
- package/dist/service/systemd.d.ts +19 -0
- package/dist/service/systemd.d.ts.map +1 -0
- package/dist/session.d.ts +29 -7
- package/dist/worker-permission.d.ts +12 -2
- package/package.json +4 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare class ApproveArmError extends Error {
|
|
2
|
+
}
|
|
3
|
+
/** A drained local approval: the host operator approved arming `roomId` under
|
|
4
|
+
* the one-time `requestId`. Both are consumed together by ArmingState.approveLocal. */
|
|
5
|
+
export interface DrainedApproval {
|
|
6
|
+
requestId: string;
|
|
7
|
+
roomId: string;
|
|
8
|
+
}
|
|
9
|
+
/** Reject ids that could escape the approvals dir / inject into arming state
|
|
10
|
+
* (defensive — backend sends uuid4, but these values hit the filesystem). */
|
|
11
|
+
export declare function sanitizeRequestId(requestId: string): string;
|
|
12
|
+
/** Reject room-ids that aren't plain tokens (marker content is read back and
|
|
13
|
+
* compared against the pending arm request's room). */
|
|
14
|
+
export declare function sanitizeRoomId(roomId: string): string;
|
|
15
|
+
/** Write the local-approval marker (run on the host with data-dir access). The
|
|
16
|
+
* marker file is NAMED by the request-id and CONTAINS the approved room-id, so
|
|
17
|
+
* the host consent binds a specific room (the room can't be redirected by later
|
|
18
|
+
* network input — finding F1). */
|
|
19
|
+
export declare function writeApproval(dataDir: string, requestId: string, roomId: string): void;
|
|
20
|
+
/** One-shot: return the pending approvals (request-id + approved room-id) and
|
|
21
|
+
* remove their markers. A marker whose content isn't a valid room-id is drained
|
|
22
|
+
* but skipped (returned with no room ⇒ approveLocal will fail-closed). */
|
|
23
|
+
export declare function drainApprovals(dataDir: string): DrainedApproval[];
|
|
24
|
+
/** If argv is the approve-arm subcommand, perform it and return true (caller should exit).
|
|
25
|
+
* Usage: `av-bridge approve-arm <request-id> <room-id>`. Both are shown to the
|
|
26
|
+
* operator in the bridge's arm-request log line and the web arm sheet. */
|
|
27
|
+
export declare function maybeRunApproveArmSubcommand(argv: string[], dataDir: string): boolean;
|
|
28
|
+
//# sourceMappingURL=approve-cli.d.ts.map
|
package/dist/arming.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slice 2 Plan C — per-room arming map + pending arm-requests + one-time
|
|
3
|
+
* local approval.
|
|
4
|
+
*
|
|
5
|
+
* PURE in-memory data structure: no I/O, no WS, no fs. Later tasks wire this
|
|
6
|
+
* to the channel (T11) and the local-approval CLI (T9) and the gate (T10).
|
|
7
|
+
*/
|
|
8
|
+
export declare class ArmingState {
|
|
9
|
+
private armed;
|
|
10
|
+
private pending;
|
|
11
|
+
private consumed;
|
|
12
|
+
/**
|
|
13
|
+
* Replace the armed set. Use ONLY for the launch-env seed (AV_ARM_ROOM), which
|
|
14
|
+
* is itself a host-local action and is therefore allowed to arm.
|
|
15
|
+
*
|
|
16
|
+
* Do NOT use this for the backend connect `arming_snapshot`: that snapshot
|
|
17
|
+
* reflects owner *intent* (set at web-request time, before any local approval),
|
|
18
|
+
* so arming from it would let a web-session compromise arm a worker on reconnect
|
|
19
|
+
* without host access — defeating the 2-of-2 local-approval model. The channel
|
|
20
|
+
* snapshot path uses `reconcileDisarm` (disarm-only) instead.
|
|
21
|
+
*/
|
|
22
|
+
applySnapshot(roomIds: string[]): void;
|
|
23
|
+
/**
|
|
24
|
+
* Disarm-only reconciliation from the backend connect snapshot. Disarms any
|
|
25
|
+
* currently-armed room whose intent is no longer armed (i.e. NOT in
|
|
26
|
+
* `intendedArmed`) — so a disarm issued while the bridge was offline still takes
|
|
27
|
+
* effect on reconnect — but NEVER arms a room. Arming stays exclusively behind a
|
|
28
|
+
* local `approve-arm`. Returns the rooms that were disarmed. In-memory arming
|
|
29
|
+
* survives a WS reconnect, so a genuinely-approved arm is unaffected here; only a
|
|
30
|
+
* full process restart (which clears this state) requires re-approval on the host.
|
|
31
|
+
*/
|
|
32
|
+
reconcileDisarm(intendedArmed: string[]): string[];
|
|
33
|
+
isArmed(roomId: string): boolean;
|
|
34
|
+
/** Live disarm — flip a room off (a cooperating fail-safe). */
|
|
35
|
+
disarm(roomId: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Record a pending arm request; does NOT arm until approveLocal.
|
|
38
|
+
*
|
|
39
|
+
* The (request-id → room) binding is IMMUTABLE and ONE-SHOT: a request-id
|
|
40
|
+
* already consumed is never re-registered, and a second event that tries to
|
|
41
|
+
* point an existing request-id at a DIFFERENT room is rejected. Without this a
|
|
42
|
+
* compromised backend / WS-MITM could redirect an in-flight approval to another
|
|
43
|
+
* room after the operator saw the first one (confused-deputy, finding F1), and
|
|
44
|
+
* a lingering marker could be replayed against a re-registered id (F2).
|
|
45
|
+
*/
|
|
46
|
+
registerArmRequest(roomId: string, requestId: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Approve a pending request locally (one-time). The caller MUST name the room
|
|
49
|
+
* being approved (from the host-written marker); it must match the room the
|
|
50
|
+
* request originally targeted, or the approval is rejected (fail-closed). This
|
|
51
|
+
* is what makes the host consent bind a SPECIFIC room rather than "whatever room
|
|
52
|
+
* the untrusted backend currently maps to this opaque token" (F1). Returns true
|
|
53
|
+
* only if it armed the room.
|
|
54
|
+
*/
|
|
55
|
+
approveLocal(requestId: string, roomId: string): boolean;
|
|
56
|
+
armedRooms(): string[];
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=arming.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slice 2 Plan B — fire-and-forget self-report of an armed-room tool decision
|
|
3
|
+
* to the backend's hash-chained audit trail (POST /api/v1/audit/entries).
|
|
4
|
+
*
|
|
5
|
+
* UNTRUSTED self-report: the bridge runs on the owner's machine, so this is a
|
|
6
|
+
* record/signal, not a guarantee. It must NEVER block or fail a turn — the POST
|
|
7
|
+
* is fired and forgotten, all errors swallowed.
|
|
8
|
+
*/
|
|
9
|
+
export interface ToolAuditReport {
|
|
10
|
+
apiUrl: string;
|
|
11
|
+
deviceJwt: string | null;
|
|
12
|
+
agentId: string | null;
|
|
13
|
+
roomId: string;
|
|
14
|
+
tool: string;
|
|
15
|
+
decision: "allow" | "deny";
|
|
16
|
+
reason?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function reportToolAudit(opts: ToolAuditReport): void;
|
|
19
|
+
//# sourceMappingURL=audit-report.d.ts.map
|
package/dist/bridge.d.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
+
import { ArmingState } from "./arming.js";
|
|
1
2
|
export interface RoomMessage {
|
|
2
3
|
roomId: string;
|
|
3
4
|
senderName: string;
|
|
4
5
|
plaintext: string;
|
|
5
6
|
}
|
|
7
|
+
/** Slice 2 Plan C arming events (SecureChannel re-emits these from the backend). */
|
|
8
|
+
export interface ArmRequested {
|
|
9
|
+
roomId: string;
|
|
10
|
+
requestId: string;
|
|
11
|
+
workspaceLabel?: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface DisarmEvent {
|
|
14
|
+
roomId: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ArmingSnapshot {
|
|
17
|
+
roomIds: string[];
|
|
18
|
+
}
|
|
6
19
|
/** Metadata SecureChannel attaches to a 1:1 `message` event. `roomId` is set only
|
|
7
20
|
* when the `message` actually originated in a room (handled by room_message), so
|
|
8
21
|
* its ABSENCE is how we identify a true owner↔agent 1:1 DM. */
|
|
@@ -20,8 +33,17 @@ export interface RoomChannel {
|
|
|
20
33
|
on(ev: "message", cb: (text: string, metadata: MessageMeta) => void): unknown;
|
|
21
34
|
on(ev: "room_hushed", cb: (e: RoomHushed) => void): unknown;
|
|
22
35
|
on(ev: "error", cb: (err: unknown) => void): unknown;
|
|
36
|
+
on(ev: "ready", cb: () => void): unknown;
|
|
37
|
+
on(ev: "arm_requested", cb: (e: ArmRequested) => void): unknown;
|
|
38
|
+
on(ev: "disarm", cb: (e: DisarmEvent) => void): unknown;
|
|
39
|
+
on(ev: "arming_snapshot", cb: (e: ArmingSnapshot) => void): unknown;
|
|
23
40
|
sendToRoom(roomId: string, text: string): Promise<void>;
|
|
24
41
|
send(text: string): Promise<void>;
|
|
42
|
+
/** Optional so legacy/test fakes without arming support don't break wiring. */
|
|
43
|
+
sendWorkerHeartbeat?(h: {
|
|
44
|
+
workerCapable: boolean;
|
|
45
|
+
armedRooms: string[];
|
|
46
|
+
}): void;
|
|
25
47
|
}
|
|
26
48
|
/**
|
|
27
49
|
* #392 cooperative quiet: tracks per-room hush windows so the native agent holds
|
|
@@ -40,6 +62,7 @@ export interface RoomSession {
|
|
|
40
62
|
* sending plain assistant text when the model never calls the say tool (#416). */
|
|
41
63
|
push(text: string, reply?: (text: string) => Promise<void>, opts?: {
|
|
42
64
|
autoReplyOnText?: boolean;
|
|
65
|
+
armed?: () => boolean;
|
|
43
66
|
}): void;
|
|
44
67
|
}
|
|
45
68
|
/** Where a reply should go: a room (sendToRoom) or a 1:1 DM (send). */
|
|
@@ -103,10 +126,33 @@ export interface LifecycleChannel {
|
|
|
103
126
|
*/
|
|
104
127
|
export declare function attachLifecycle(channel: LifecycleChannel, opts?: {
|
|
105
128
|
log?: (msg: string) => void;
|
|
106
|
-
onTerminal?: (reason: string
|
|
129
|
+
onTerminal?: (reason: string, o: {
|
|
130
|
+
restart: boolean;
|
|
131
|
+
}) => void;
|
|
107
132
|
}): void;
|
|
133
|
+
/**
|
|
134
|
+
* Slice 2 Plan C (Task 11) — one drain of the local-approval marker dir.
|
|
135
|
+
*
|
|
136
|
+
* Local approval is the ONLY thing that actually arms a room: a marker under
|
|
137
|
+
* <dataDir>/arm-approvals/<request-id> is written by `av-bridge approve-arm`
|
|
138
|
+
* (host filesystem access required). Each drained id is matched against a
|
|
139
|
+
* pending arm request; a match arms the room and fires `onArmed`.
|
|
140
|
+
*
|
|
141
|
+
* Defensive per the T9 review advisory: a bad/locked/junk marker (or a planted
|
|
142
|
+
* subdir that makes readdir/rm throw) must NOT crash the poll loop, and a
|
|
143
|
+
* drained id that matches no pending request is logged rather than silently
|
|
144
|
+
* dropped (it usually means a mis-timed or duplicate approval).
|
|
145
|
+
*/
|
|
146
|
+
export declare function pollApprovalsOnce(arming: ArmingState, dataDir: string, onArmed: () => void, log?: (msg: string) => void): void;
|
|
108
147
|
export declare function wireBridge(channel: RoomChannel, session: RoomSession, target: ActiveTarget, opts?: {
|
|
109
148
|
roomFilter?: string;
|
|
149
|
+
armRoom?: boolean;
|
|
110
150
|
log?: (msg: string) => void;
|
|
111
|
-
|
|
151
|
+
/** Slice 2 Plan C (T11): worker capability + local-approval marker dir. */
|
|
152
|
+
worker?: boolean;
|
|
153
|
+
workspaceDir?: string;
|
|
154
|
+
dataDir?: string;
|
|
155
|
+
/** Override the approval poll interval (ms) — for tests. */
|
|
156
|
+
approvalPollMs?: number;
|
|
157
|
+
}): ArmingState;
|
|
112
158
|
//# sourceMappingURL=bridge.d.ts.map
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
export declare function hasPersistedCreds(dataDir: string): boolean;
|
|
2
|
+
export declare function slugify(name: string): string;
|
|
1
3
|
export type DataDirSource = "explicit" | "per-agent" | "legacy";
|
|
4
|
+
export declare function resolveDataDir(env: NodeJS.ProcessEnv): {
|
|
5
|
+
dataDir: string;
|
|
6
|
+
source: DataDirSource;
|
|
7
|
+
};
|
|
2
8
|
export interface BridgeConfig {
|
|
3
9
|
inviteToken: string;
|
|
4
10
|
dataDir: string;
|
|
@@ -11,6 +17,9 @@ export interface BridgeConfig {
|
|
|
11
17
|
worker: boolean;
|
|
12
18
|
workspaceDir?: string;
|
|
13
19
|
permissionMode: "auto" | "acceptEdits" | "bypassPermissions";
|
|
20
|
+
/** Slice 2: arm the pinned room (roomFilter) for worker tools on room turns.
|
|
21
|
+
* Off by default. Valid only with worker + roomFilter + workspaceDir. */
|
|
22
|
+
armRoom: boolean;
|
|
14
23
|
}
|
|
15
24
|
export declare function loadConfig(env: NodeJS.ProcessEnv, argv?: string[]): BridgeConfig;
|
|
16
25
|
//# sourceMappingURL=config.d.ts.map
|