@peekdev/mcp 0.1.0-alpha.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/NOTICE +10 -0
- package/dist/db/index.d.ts +3 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +7 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate.d.ts +37 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +86 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/migrations/0001_initial.sql +102 -0
- package/dist/db/migrations/0002_network_bodies.sql +15 -0
- package/dist/db/open.d.ts +57 -0
- package/dist/db/open.d.ts.map +1 -0
- package/dist/db/open.js +74 -0
- package/dist/db/open.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/action-schema.d.ts +223 -0
- package/dist/mcp/action-schema.d.ts.map +1 -0
- package/dist/mcp/action-schema.js +97 -0
- package/dist/mcp/action-schema.js.map +1 -0
- package/dist/mcp/event-blobs.d.ts +32 -0
- package/dist/mcp/event-blobs.d.ts.map +1 -0
- package/dist/mcp/event-blobs.js +65 -0
- package/dist/mcp/event-blobs.js.map +1 -0
- package/dist/mcp/event-walker.d.ts +86 -0
- package/dist/mcp/event-walker.d.ts.map +1 -0
- package/dist/mcp/event-walker.js +398 -0
- package/dist/mcp/event-walker.js.map +1 -0
- package/dist/mcp/host-bridge.d.ts +80 -0
- package/dist/mcp/host-bridge.d.ts.map +1 -0
- package/dist/mcp/host-bridge.js +88 -0
- package/dist/mcp/host-bridge.js.map +1 -0
- package/dist/mcp/index.d.ts +8 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +32 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/playwright-repro.d.ts +19 -0
- package/dist/mcp/playwright-repro.d.ts.map +1 -0
- package/dist/mcp/playwright-repro.js +78 -0
- package/dist/mcp/playwright-repro.js.map +1 -0
- package/dist/mcp/queries.d.ts +73 -0
- package/dist/mcp/queries.d.ts.map +1 -0
- package/dist/mcp/queries.js +139 -0
- package/dist/mcp/queries.js.map +1 -0
- package/dist/mcp/roots.d.ts +50 -0
- package/dist/mcp/roots.d.ts.map +1 -0
- package/dist/mcp/roots.js +97 -0
- package/dist/mcp/roots.js.map +1 -0
- package/dist/mcp/rrweb-types.d.ts +3 -0
- package/dist/mcp/rrweb-types.d.ts.map +1 -0
- package/dist/mcp/rrweb-types.js +7 -0
- package/dist/mcp/rrweb-types.js.map +1 -0
- package/dist/mcp/selector.d.ts +54 -0
- package/dist/mcp/selector.d.ts.map +1 -0
- package/dist/mcp/selector.js +209 -0
- package/dist/mcp/selector.js.map +1 -0
- package/dist/mcp/server.d.ts +49 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +469 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/summary.d.ts +26 -0
- package/dist/mcp/summary.d.ts.map +1 -0
- package/dist/mcp/summary.js +74 -0
- package/dist/mcp/summary.js.map +1 -0
- package/dist/native-host/action-protocol.d.ts +49 -0
- package/dist/native-host/action-protocol.d.ts.map +1 -0
- package/dist/native-host/action-protocol.js +36 -0
- package/dist/native-host/action-protocol.js.map +1 -0
- package/dist/native-host/audit.d.ts +69 -0
- package/dist/native-host/audit.d.ts.map +1 -0
- package/dist/native-host/audit.js +85 -0
- package/dist/native-host/audit.js.map +1 -0
- package/dist/native-host/config.d.ts +18 -0
- package/dist/native-host/config.d.ts.map +1 -0
- package/dist/native-host/config.js +56 -0
- package/dist/native-host/config.js.map +1 -0
- package/dist/native-host/extension-ids.json +6 -0
- package/dist/native-host/host.d.ts +30 -0
- package/dist/native-host/host.d.ts.map +1 -0
- package/dist/native-host/host.js +96 -0
- package/dist/native-host/host.js.map +1 -0
- package/dist/native-host/index.d.ts +4 -0
- package/dist/native-host/index.d.ts.map +1 -0
- package/dist/native-host/index.js +8 -0
- package/dist/native-host/index.js.map +1 -0
- package/dist/native-host/ingest.d.ts +83 -0
- package/dist/native-host/ingest.d.ts.map +1 -0
- package/dist/native-host/ingest.js +283 -0
- package/dist/native-host/ingest.js.map +1 -0
- package/dist/native-host/installer.d.ts +64 -0
- package/dist/native-host/installer.d.ts.map +1 -0
- package/dist/native-host/installer.js +110 -0
- package/dist/native-host/installer.js.map +1 -0
- package/dist/native-host/manifest.d.ts +64 -0
- package/dist/native-host/manifest.d.ts.map +1 -0
- package/dist/native-host/manifest.js +117 -0
- package/dist/native-host/manifest.js.map +1 -0
- package/dist/native-host/policy.d.ts +60 -0
- package/dist/native-host/policy.d.ts.map +1 -0
- package/dist/native-host/policy.js +116 -0
- package/dist/native-host/policy.js.map +1 -0
- package/dist/native-host/request-registry.d.ts +55 -0
- package/dist/native-host/request-registry.d.ts.map +1 -0
- package/dist/native-host/request-registry.js +111 -0
- package/dist/native-host/request-registry.js.map +1 -0
- package/dist/native-host/transport.d.ts +54 -0
- package/dist/native-host/transport.d.ts.map +1 -0
- package/dist/native-host/transport.js +113 -0
- package/dist/native-host/transport.js.map +1 -0
- package/dist/postinstall.d.ts +3 -0
- package/dist/postinstall.d.ts.map +1 -0
- package/dist/postinstall.js +72 -0
- package/dist/postinstall.js.map +1 -0
- package/package.json +59 -0
- package/src/db/migrations/0001_initial.sql +102 -0
- package/src/db/migrations/0002_network_bodies.sql +15 -0
- package/src/native-host/extension-ids.json +6 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Action } from '../mcp/action-schema.js';
|
|
2
|
+
/** SW → host: a verdict + result payload for a previously-issued request. */
|
|
3
|
+
export interface ActionResultMessage {
|
|
4
|
+
type: 'action.result';
|
|
5
|
+
requestId: string;
|
|
6
|
+
/** Mirrored from action.request so the audit log can fold it back together. */
|
|
7
|
+
tool: 'execute_action' | 'request_authorization';
|
|
8
|
+
/** What the gate decided + (for Level 3) what the user clicked. */
|
|
9
|
+
verdict: 'allow' | 'deny';
|
|
10
|
+
/** Final result of the dispatch (or 'denied' if verdict='deny'). */
|
|
11
|
+
result: 'ok' | 'denied' | 'error';
|
|
12
|
+
/** 'user' | 'allow-list-match' | 'level-4-auto'. */
|
|
13
|
+
approver: 'user' | 'allow-list-match' | 'level-4-auto';
|
|
14
|
+
/** ms-since-epoch when the user confirmed / denied (Level 3). */
|
|
15
|
+
approvalMs?: number;
|
|
16
|
+
/** The destructive term that fired, if applicable (for the audit log). */
|
|
17
|
+
destructiveTerm?: string;
|
|
18
|
+
/** Free-form detail (error message / screenshot dataURL / etc.). */
|
|
19
|
+
details?: unknown;
|
|
20
|
+
/** Error message when `result === 'error'` or 'denied'. */
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
/** host → SW: please execute / authorize this action. */
|
|
24
|
+
export interface ActionRequestMessage {
|
|
25
|
+
type: 'action.request';
|
|
26
|
+
requestId: string;
|
|
27
|
+
tool: 'execute_action' | 'request_authorization';
|
|
28
|
+
sessionId: string;
|
|
29
|
+
action: Action;
|
|
30
|
+
/** MCP client name from clientInfo (for audit-log "approver context"). */
|
|
31
|
+
client: string;
|
|
32
|
+
/** Destructive-term deltas from ~/.peek/policy.json (forwarded to the dispatcher). */
|
|
33
|
+
policy: {
|
|
34
|
+
add: readonly string[];
|
|
35
|
+
remove: readonly string[];
|
|
36
|
+
};
|
|
37
|
+
/** Optional pinning to a specific tab id (SW picks active when omitted). */
|
|
38
|
+
tabId?: number;
|
|
39
|
+
}
|
|
40
|
+
/** SW → host: the banner is now visible to the user (timing signal). */
|
|
41
|
+
export interface ActionConfirmShownMessage {
|
|
42
|
+
type: 'action.confirm.shown';
|
|
43
|
+
requestId: string;
|
|
44
|
+
/** ms-since-epoch the SW posted the banner. */
|
|
45
|
+
shownAtMs: number;
|
|
46
|
+
}
|
|
47
|
+
export type HostToSwMessage = ActionRequestMessage;
|
|
48
|
+
export type SwToHostMessage = ActionResultMessage | ActionConfirmShownMessage;
|
|
49
|
+
//# sourceMappingURL=action-protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-protocol.d.ts","sourceRoot":"","sources":["../../src/native-host/action-protocol.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,IAAI,EAAE,gBAAgB,GAAG,uBAAuB,CAAC;IACjD,mEAAmE;IACnE,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,oEAAoE;IACpE,MAAM,EAAE,IAAI,GAAG,QAAQ,GAAG,OAAO,CAAC;IAClC,oDAAoD;IACpD,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,cAAc,CAAC;IACvD,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,yDAAyD;AACzD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,gBAAgB,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,gBAAgB,GAAG,uBAAuB,CAAC;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,MAAM,EAAE;QACN,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;KAC3B,CAAC;IACF,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AACnD,MAAM,MAAM,eAAe,GAAG,mBAAmB,GAAG,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Native-host ↔ SW action-request wire protocol (Task 3.24).
|
|
2
|
+
//
|
|
3
|
+
// This is a NEW message-type catalog on top of the existing native-port
|
|
4
|
+
// channel. The capture-pipeline messages (`session.append`, `console.append`,
|
|
5
|
+
// `network.append`, `shadow.report`) are fire-and-forget INGEST traffic from
|
|
6
|
+
// the SW; the action messages here are CORRELATED REQUEST/RESPONSE traffic
|
|
7
|
+
// initiated by the host (an MCP tool call) and answered by the SW (after the
|
|
8
|
+
// MAIN-world dispatcher runs + possibly a side-panel banner).
|
|
9
|
+
//
|
|
10
|
+
// Correlation: every request carries a `requestId` (UUID). The SW echoes it on
|
|
11
|
+
// its reply; the host's `RequestRegistry` matches reply → pending tool handler.
|
|
12
|
+
//
|
|
13
|
+
// Three message types:
|
|
14
|
+
//
|
|
15
|
+
// action.request (host → SW)
|
|
16
|
+
// Asks the SW to execute (or seek confirmation for) `action`. The host
|
|
17
|
+
// attaches:
|
|
18
|
+
// - `tool` so the SW knows whether this is execute_action or
|
|
19
|
+
// request_authorization (banner copy / audit shape differ).
|
|
20
|
+
// - `policy` — the destructive add/remove deltas from ~/.peek/policy.json
|
|
21
|
+
// so the MAIN-world dispatcher merges them at decision time.
|
|
22
|
+
// - `tabId` (optional) — when the tool args identify a session whose
|
|
23
|
+
// active tab the host knows, the SW resolves to that tab; otherwise
|
|
24
|
+
// the SW picks the active tab in the focused window.
|
|
25
|
+
//
|
|
26
|
+
// action.confirm (host ↔ SW)
|
|
27
|
+
// OPTIONAL intermediate signal. The SW emits it when it surfaces the
|
|
28
|
+
// side-panel banner so the host (and the audit log) can record the time
|
|
29
|
+
// the user was prompted. NOT a correlation step — the verdict still
|
|
30
|
+
// arrives in action.result.
|
|
31
|
+
//
|
|
32
|
+
// action.result (SW → host)
|
|
33
|
+
// Terminal reply: `verdict` + `result` + `details`. The host's pending
|
|
34
|
+
// RequestRegistry entry resolves with this payload.
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=action-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-protocol.js","sourceRoot":"","sources":["../../src/native-host/action-protocol.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,wEAAwE;AACxE,8EAA8E;AAC9E,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,8DAA8D;AAC9D,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,EAAE;AACF,uBAAuB;AACvB,EAAE;AACF,gCAAgC;AAChC,2EAA2E;AAC3E,gBAAgB;AAChB,mEAAmE;AACnE,oEAAoE;AACpE,gFAAgF;AAChF,qEAAqE;AACrE,2EAA2E;AAC3E,4EAA4E;AAC5E,6DAA6D;AAC7D,EAAE;AACF,gCAAgC;AAChC,yEAAyE;AACzE,4EAA4E;AAC5E,wEAAwE;AACxE,gCAAgC;AAChC,EAAE;AACF,gCAAgC;AAChC,2EAA2E;AAC3E,wDAAwD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type Action } from '../mcp/action-schema.js';
|
|
2
|
+
/** Default location of the audit log (mirrors peek-cli/src/lib/peek-home.ts). */
|
|
3
|
+
export declare function auditLogPath(): string;
|
|
4
|
+
/** Tool names that produce audit entries — the two Level-3+ MCP write tools. */
|
|
5
|
+
export type AuditTool = 'execute_action' | 'request_authorization';
|
|
6
|
+
/** Approver values, ordered by what the dispatcher will record. */
|
|
7
|
+
export type AuditApprover = 'user' | 'allow-list-match' | 'level-4-auto';
|
|
8
|
+
/** Result values. */
|
|
9
|
+
export type AuditResult = 'ok' | 'denied' | 'error';
|
|
10
|
+
/** A single audit entry shape — matches the CLI's AuditEntry interface. */
|
|
11
|
+
export interface AuditEntry {
|
|
12
|
+
ts: string;
|
|
13
|
+
tool: AuditTool;
|
|
14
|
+
/** The action object, with sensitive fields redacted before this is built. */
|
|
15
|
+
args: unknown;
|
|
16
|
+
approvalTs?: string;
|
|
17
|
+
approver: AuditApprover;
|
|
18
|
+
client: string;
|
|
19
|
+
sessionId: string;
|
|
20
|
+
result: AuditResult;
|
|
21
|
+
/** Optional: the destructive term that fired (for diagnostics). */
|
|
22
|
+
destructiveTerm?: string;
|
|
23
|
+
/** Optional: human-readable error detail when result === 'error' / 'denied'. */
|
|
24
|
+
error?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Inputs to the writer — the caller supplies the Action; we redact it. */
|
|
27
|
+
export interface BuildAuditEntryInput {
|
|
28
|
+
readonly tool: AuditTool;
|
|
29
|
+
readonly action: Action;
|
|
30
|
+
readonly approver: AuditApprover;
|
|
31
|
+
readonly client: string;
|
|
32
|
+
readonly sessionId: string;
|
|
33
|
+
readonly result: AuditResult;
|
|
34
|
+
/** ms-since-epoch of the request — omit to use Date.now() at build time. */
|
|
35
|
+
readonly nowMs?: number;
|
|
36
|
+
/** ms-since-epoch of the user's confirmation, if applicable. */
|
|
37
|
+
readonly approvalMs?: number;
|
|
38
|
+
readonly destructiveTerm?: string;
|
|
39
|
+
readonly error?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build the JSON line for one audit entry. Pure — no filesystem — so it
|
|
43
|
+
* unit-tests cleanly. The `args` field is the Action with sensitive fields
|
|
44
|
+
* (`TypeAction.text`, `NavigateAction.url` query-string values) redacted via
|
|
45
|
+
* {@link redactActionForAudit}.
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildAuditEntry(input: BuildAuditEntryInput): AuditEntry;
|
|
48
|
+
/** Serialize an entry to its single-line JSONL representation (trailing \n). */
|
|
49
|
+
export declare function serializeAuditEntry(entry: AuditEntry): string;
|
|
50
|
+
export interface AuditWriteOptions {
|
|
51
|
+
/** Override the audit log path (tests + PEEK_HOME). */
|
|
52
|
+
readonly path?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Append an entry to the audit log, ensuring `~/.peek` exists first. Throws on
|
|
56
|
+
* I/O failure — the host's caller catches + downgrades to a console.error so a
|
|
57
|
+
* write-failure on the audit log can't tear down an action request mid-flight,
|
|
58
|
+
* but the throw is propagated so tests can assert on it.
|
|
59
|
+
*
|
|
60
|
+
* The audit log contains URL paths, DOM selectors, MCP-client identity, and
|
|
61
|
+
* session IDs. On a multi-user POSIX system we want owner-only access, so on
|
|
62
|
+
* first write we seed the file with mode `0o600`. If the file already exists
|
|
63
|
+
* we don't touch its mode — the user may have set their own. Windows ignores
|
|
64
|
+
* the `mode` argument silently; that's acceptable here.
|
|
65
|
+
*/
|
|
66
|
+
export declare function appendAuditEntry(entry: AuditEntry, options?: AuditWriteOptions): void;
|
|
67
|
+
/** Convenience: build + serialize + append in one call. */
|
|
68
|
+
export declare function recordAuditEntry(input: BuildAuditEntryInput, options?: AuditWriteOptions): AuditEntry;
|
|
69
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/native-host/audit.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,KAAK,MAAM,EAAwB,MAAM,yBAAyB,CAAC;AAE5E,iFAAiF;AACjF,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG,uBAAuB,CAAC;AAEnE,mEAAmE;AACnE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,kBAAkB,GAAG,cAAc,CAAC;AAEzE,qBAAqB;AACrB,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpD,2EAA2E;AAC3E,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,8EAA8E;IAC9E,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,UAAU,CAiBvE;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAE7D;AAED,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE,iBAAsB,GAAG,IAAI,CAOzF;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,oBAAoB,EAC3B,OAAO,GAAE,iBAAsB,GAC9B,UAAU,CAIZ"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Audit log writer (Task 3.25, ADR-0010 / P2 PRD §H.3).
|
|
2
|
+
//
|
|
3
|
+
// Every act-tool call (execute_action AND request_authorization) appends ONE
|
|
4
|
+
// JSON line to ~/.peek/audit.log. The CLI's `peek audit log [--since 1h |
|
|
5
|
+
// --tool execute_action | --client cursor]` reads + filters this file.
|
|
6
|
+
//
|
|
7
|
+
// JSONL fields (P2 PRD §H.3):
|
|
8
|
+
// ts ISO timestamp the host received the tool call
|
|
9
|
+
// tool 'execute_action' | 'request_authorization'
|
|
10
|
+
// args the Action object (passwords / token values redacted)
|
|
11
|
+
// approvalTs ISO when the user confirmed (Level 3) — omitted on YOLO auto
|
|
12
|
+
// approver 'user' | 'allow-list-match' | 'level-4-auto'
|
|
13
|
+
// client MCP client name from clientInfo (cursor, claude-code, etc.)
|
|
14
|
+
// sessionId the recording session id the action targets
|
|
15
|
+
// result 'ok' | 'denied' | 'error'
|
|
16
|
+
//
|
|
17
|
+
// Append-only: open the file with `flag: 'a'` for every write. We don't keep a
|
|
18
|
+
// long-lived file handle — a single act-tool call writes one line and closes,
|
|
19
|
+
// which is robust against host restarts / crashes mid-write. The line is
|
|
20
|
+
// flushed before the function resolves.
|
|
21
|
+
import { appendFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import { dirname, join } from 'node:path';
|
|
23
|
+
import { peekHomeDir } from '../db/open.js';
|
|
24
|
+
import { redactActionForAudit } from '../mcp/action-schema.js';
|
|
25
|
+
/** Default location of the audit log (mirrors peek-cli/src/lib/peek-home.ts). */
|
|
26
|
+
export function auditLogPath() {
|
|
27
|
+
return join(peekHomeDir(), 'audit.log');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build the JSON line for one audit entry. Pure — no filesystem — so it
|
|
31
|
+
* unit-tests cleanly. The `args` field is the Action with sensitive fields
|
|
32
|
+
* (`TypeAction.text`, `NavigateAction.url` query-string values) redacted via
|
|
33
|
+
* {@link redactActionForAudit}.
|
|
34
|
+
*/
|
|
35
|
+
export function buildAuditEntry(input) {
|
|
36
|
+
const ts = new Date(input.nowMs ?? Date.now()).toISOString();
|
|
37
|
+
const entry = {
|
|
38
|
+
ts,
|
|
39
|
+
tool: input.tool,
|
|
40
|
+
args: redactActionForAudit(input.action),
|
|
41
|
+
approver: input.approver,
|
|
42
|
+
client: input.client,
|
|
43
|
+
sessionId: input.sessionId,
|
|
44
|
+
result: input.result,
|
|
45
|
+
};
|
|
46
|
+
if (input.approvalMs !== undefined) {
|
|
47
|
+
entry.approvalTs = new Date(input.approvalMs).toISOString();
|
|
48
|
+
}
|
|
49
|
+
if (input.destructiveTerm !== undefined)
|
|
50
|
+
entry.destructiveTerm = input.destructiveTerm;
|
|
51
|
+
if (input.error !== undefined)
|
|
52
|
+
entry.error = input.error;
|
|
53
|
+
return entry;
|
|
54
|
+
}
|
|
55
|
+
/** Serialize an entry to its single-line JSONL representation (trailing \n). */
|
|
56
|
+
export function serializeAuditEntry(entry) {
|
|
57
|
+
return `${JSON.stringify(entry)}\n`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Append an entry to the audit log, ensuring `~/.peek` exists first. Throws on
|
|
61
|
+
* I/O failure — the host's caller catches + downgrades to a console.error so a
|
|
62
|
+
* write-failure on the audit log can't tear down an action request mid-flight,
|
|
63
|
+
* but the throw is propagated so tests can assert on it.
|
|
64
|
+
*
|
|
65
|
+
* The audit log contains URL paths, DOM selectors, MCP-client identity, and
|
|
66
|
+
* session IDs. On a multi-user POSIX system we want owner-only access, so on
|
|
67
|
+
* first write we seed the file with mode `0o600`. If the file already exists
|
|
68
|
+
* we don't touch its mode — the user may have set their own. Windows ignores
|
|
69
|
+
* the `mode` argument silently; that's acceptable here.
|
|
70
|
+
*/
|
|
71
|
+
export function appendAuditEntry(entry, options = {}) {
|
|
72
|
+
const path = options.path ?? auditLogPath();
|
|
73
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
74
|
+
if (!existsSync(path)) {
|
|
75
|
+
writeFileSync(path, '', { mode: 0o600 });
|
|
76
|
+
}
|
|
77
|
+
appendFileSync(path, serializeAuditEntry(entry), { encoding: 'utf8' });
|
|
78
|
+
}
|
|
79
|
+
/** Convenience: build + serialize + append in one call. */
|
|
80
|
+
export function recordAuditEntry(input, options = {}) {
|
|
81
|
+
const entry = buildAuditEntry(input);
|
|
82
|
+
appendAuditEntry(entry, options);
|
|
83
|
+
return entry;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/native-host/audit.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,6EAA6E;AAC7E,0EAA0E;AAC1E,uEAAuE;AACvE,EAAE;AACF,8BAA8B;AAC9B,gEAAgE;AAChE,6DAA6D;AAC7D,wEAAwE;AACxE,+EAA+E;AAC/E,+DAA+D;AAC/D,8EAA8E;AAC9E,8DAA8D;AAC9D,4CAA4C;AAC5C,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,yEAAyE;AACzE,wCAAwC;AAExC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAe,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE5E,iFAAiF;AACjF,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,CAAC;AAC1C,CAAC;AA4CD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7D,MAAM,KAAK,GAAe;QACxB,EAAE;QACF,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,oBAAoB,CAAC,KAAK,CAAC,MAAM,CAAC;QACxC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC;IACF,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACnC,KAAK,CAAC,UAAU,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS;QAAE,KAAK,CAAC,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC;IACvF,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACzD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,mBAAmB,CAAC,KAAiB;IACnD,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;AACtC,CAAC;AAOD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB,EAAE,UAA6B,EAAE;IACjF,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,YAAY,EAAE,CAAC;IAC5C,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,aAAa,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3C,CAAC;IACD,cAAc,CAAC,IAAI,EAAE,mBAAmB,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,gBAAgB,CAC9B,KAA2B,EAC3B,UAA6B,EAAE;IAE/B,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACrC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ExtensionIds } from './manifest.js';
|
|
2
|
+
/**
|
|
3
|
+
* Path to the published `extension-ids.json`, resolved relative to this module
|
|
4
|
+
* (the file is shipped both in `src/` and copied next to the built output).
|
|
5
|
+
*/
|
|
6
|
+
export declare function extensionIdsPath(): string;
|
|
7
|
+
/**
|
|
8
|
+
* Read, parse, and validate the configured extension IDs (Chrome Web Store /
|
|
9
|
+
* Edge / dev). Throws if the file is missing, not JSON, or has a non-string id.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadExtensionIds(path?: string): ExtensionIds;
|
|
12
|
+
/**
|
|
13
|
+
* Absolute path to the installed `peek-mcp` binary the browser spawns as the
|
|
14
|
+
* native host. The built entry is `dist/index.js`; this module lives at
|
|
15
|
+
* `dist/native-host/config.js`, so the bin is one directory up.
|
|
16
|
+
*/
|
|
17
|
+
export declare function hostBinaryPath(): string;
|
|
18
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/native-host/config.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAcD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,GAAE,MAA2B,GAAG,YAAY,CAkBhF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Load-time helpers shared by the postinstall script and the CLI's
|
|
2
|
+
// `peek status`: where the extension IDs live and which binary the browser
|
|
3
|
+
// should spawn as the native host.
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
/**
|
|
9
|
+
* Path to the published `extension-ids.json`, resolved relative to this module
|
|
10
|
+
* (the file is shipped both in `src/` and copied next to the built output).
|
|
11
|
+
*/
|
|
12
|
+
export function extensionIdsPath() {
|
|
13
|
+
return join(dirname(fileURLToPath(import.meta.url)), 'extension-ids.json');
|
|
14
|
+
}
|
|
15
|
+
// Each id, when present, must be a string. Keys may be absent (defaulted to
|
|
16
|
+
// ''), but a non-string value or unparseable file fails loudly rather than
|
|
17
|
+
// being silently masked by a `?? ''` fallback. Unknown keys (e.g. `$comment`)
|
|
18
|
+
// are ignored.
|
|
19
|
+
const ExtensionIdsSchema = z
|
|
20
|
+
.object({
|
|
21
|
+
chromeWebStore: z.string().optional(),
|
|
22
|
+
edgeAddons: z.string().optional(),
|
|
23
|
+
dev: z.string().optional(),
|
|
24
|
+
})
|
|
25
|
+
.passthrough();
|
|
26
|
+
/**
|
|
27
|
+
* Read, parse, and validate the configured extension IDs (Chrome Web Store /
|
|
28
|
+
* Edge / dev). Throws if the file is missing, not JSON, or has a non-string id.
|
|
29
|
+
*/
|
|
30
|
+
export function loadExtensionIds(path = extensionIdsPath()) {
|
|
31
|
+
let json;
|
|
32
|
+
try {
|
|
33
|
+
json = JSON.parse(readFileSync(path, 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
throw new Error(`peek: failed to read extension-ids.json at ${path} — ${err instanceof Error ? err.message : String(err)}`);
|
|
37
|
+
}
|
|
38
|
+
const parsed = ExtensionIdsSchema.safeParse(json);
|
|
39
|
+
if (!parsed.success) {
|
|
40
|
+
throw new Error(`peek: invalid extension-ids.json at ${path} — ${parsed.error.message}`);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
chromeWebStore: parsed.data.chromeWebStore ?? '',
|
|
44
|
+
edgeAddons: parsed.data.edgeAddons ?? '',
|
|
45
|
+
dev: parsed.data.dev ?? '',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Absolute path to the installed `peek-mcp` binary the browser spawns as the
|
|
50
|
+
* native host. The built entry is `dist/index.js`; this module lives at
|
|
51
|
+
* `dist/native-host/config.js`, so the bin is one directory up.
|
|
52
|
+
*/
|
|
53
|
+
export function hostBinaryPath() {
|
|
54
|
+
return join(dirname(dirname(fileURLToPath(import.meta.url))), 'index.js');
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/native-host/config.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,2EAA2E;AAC3E,mCAAmC;AAEnC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC;AAC7E,CAAC;AAED,4EAA4E;AAC5E,2EAA2E;AAC3E,8EAA8E;AAC9E,eAAe;AACf,MAAM,kBAAkB,GAAG,CAAC;KACzB,MAAM,CAAC;IACN,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC;KACD,WAAW,EAAE,CAAC;AAEjB;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe,gBAAgB,EAAE;IAChE,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,8CAA8C,IAAI,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC3G,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,uCAAuC,IAAI,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO;QACL,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,cAAc,IAAI,EAAE;QAChD,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE;QACxC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE;KAC3B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Extension IDs whose chrome-extension:// origin is allowed to message the peek native host (ADR-0007). Wildcards are forbidden by Chrome, so every supported store + the dev-mode unpacked-load ID is listed explicitly and all three go into the SAME manifest's allowed_origins (the Edge 'first registry location wins' gotcha — see P2 PRD §A7). Pre-publish, `dev` is the unpacked-load ID printed at chrome://extensions; replace `chromeWebStore` and `edgeAddons` with the assigned IDs once the products land in their stores.",
|
|
3
|
+
"chromeWebStore": "PLACEHOLDER_CHROME_WEB_STORE_ID",
|
|
4
|
+
"edgeAddons": "PLACEHOLDER_EDGE_ADDONS_ID",
|
|
5
|
+
"dev": "PLACEHOLDER_DEV_UNPACKED_ID"
|
|
6
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Readable, Writable } from 'node:stream';
|
|
2
|
+
export interface NativeHostHandle {
|
|
3
|
+
/** Resolves when stdin closes (the browser disconnected the port). */
|
|
4
|
+
readonly done: Promise<void>;
|
|
5
|
+
/** Close the SQLite connection. */
|
|
6
|
+
close(): void;
|
|
7
|
+
}
|
|
8
|
+
export interface NativeHostOptions {
|
|
9
|
+
/**
|
|
10
|
+
* The `chrome-extension://<id>/` origin Chrome/Edge passed when spawning the
|
|
11
|
+
* host — identifies which extension connected. Captured now for the
|
|
12
|
+
* audit_log in Phase 3d; surfaced in the `host.hello` reply.
|
|
13
|
+
*/
|
|
14
|
+
readonly callerOrigin?: string;
|
|
15
|
+
/** Override PEEK_HOME for tests (default: env / homedir). */
|
|
16
|
+
readonly home?: string;
|
|
17
|
+
/** Override the database path (e.g. ':memory:' for tests). */
|
|
18
|
+
readonly dbPath?: string;
|
|
19
|
+
/** Override the input stream (default: process.stdin). */
|
|
20
|
+
readonly input?: Readable;
|
|
21
|
+
/** Override the output stream (default: process.stdout). */
|
|
22
|
+
readonly output?: Writable;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Start the native messaging host: open the DB (running migrations) and begin
|
|
26
|
+
* decoding inbound messages from `process.stdin`. Unknown message types get a
|
|
27
|
+
* structured error reply rather than crashing the host.
|
|
28
|
+
*/
|
|
29
|
+
export declare function startNativeHost(options?: NativeHostOptions): NativeHostHandle;
|
|
30
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/native-host/host.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAKtD,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,mCAAmC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;CAC5B;AAUD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CA4EjF"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Native messaging host runner (ADR-0007). Spawned by the browser over stdio
|
|
2
|
+
// (via the manifest's `path`, with the calling extension's origin as an
|
|
3
|
+
// argument) or manually with `--native-host`. It owns ~/.peek/sessions.db and
|
|
4
|
+
// reads length-prefixed JSON messages from the extension.
|
|
5
|
+
//
|
|
6
|
+
// Phase 3a wired the transport + DB open and a minimal handshake so the channel
|
|
7
|
+
// is verifiable end-to-end; Phase 3d chunk 4 closes the loop with the four
|
|
8
|
+
// ingest handlers in `./ingest.ts` (session.append / console.append /
|
|
9
|
+
// network.append / shadow.report). The act-tool action-protocol dispatch
|
|
10
|
+
// (request/result correlation through the SW) lands alongside the IPC bridge.
|
|
11
|
+
import { openDb, peekHomeDir, schemaVersion } from '../db/open.js';
|
|
12
|
+
import { ingest } from './ingest.js';
|
|
13
|
+
import { readMessages, writeMessage } from './transport.js';
|
|
14
|
+
/** Message-type strings the ingest handler claims. */
|
|
15
|
+
const INGEST_TYPES = new Set([
|
|
16
|
+
'session.append',
|
|
17
|
+
'console.append',
|
|
18
|
+
'network.append',
|
|
19
|
+
'shadow.report',
|
|
20
|
+
]);
|
|
21
|
+
/**
|
|
22
|
+
* Start the native messaging host: open the DB (running migrations) and begin
|
|
23
|
+
* decoding inbound messages from `process.stdin`. Unknown message types get a
|
|
24
|
+
* structured error reply rather than crashing the host.
|
|
25
|
+
*/
|
|
26
|
+
export function startNativeHost(options = {}) {
|
|
27
|
+
const db = options.dbPath !== undefined ? openDb({ path: options.dbPath }) : openDb();
|
|
28
|
+
const home = options.home ?? peekHomeDir();
|
|
29
|
+
const ingestCtx = { db, home };
|
|
30
|
+
const output = options.output;
|
|
31
|
+
const readOptions = {
|
|
32
|
+
onError: (err) => {
|
|
33
|
+
console.error(`native host: skipped malformed frame — ${err.message}`);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
if (options.input !== undefined)
|
|
37
|
+
readOptions.input = options.input;
|
|
38
|
+
const done = readMessages((message) => {
|
|
39
|
+
// A reply-write failure (e.g. the browser closed the port mid-reply) must
|
|
40
|
+
// not become an unhandled rejection that tears down the host.
|
|
41
|
+
handleMessage(message).catch((err) => {
|
|
42
|
+
console.error(`native host: failed handling message — ${err instanceof Error ? err.message : String(err)}`);
|
|
43
|
+
});
|
|
44
|
+
}, readOptions);
|
|
45
|
+
return {
|
|
46
|
+
done,
|
|
47
|
+
close() {
|
|
48
|
+
db.close();
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
async function handleMessage(message) {
|
|
52
|
+
const type = message && typeof message === 'object' && 'type' in message
|
|
53
|
+
? String(message.type)
|
|
54
|
+
: undefined;
|
|
55
|
+
if (type === 'host.hello') {
|
|
56
|
+
await write({
|
|
57
|
+
type: 'host.hello.ok',
|
|
58
|
+
schemaVersion: schemaVersion(db),
|
|
59
|
+
...(options.callerOrigin !== undefined ? { callerOrigin: options.callerOrigin } : {}),
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (type !== undefined && INGEST_TYPES.has(type)) {
|
|
64
|
+
// ingest() catches its own throws + returns a structured reply. The
|
|
65
|
+
// try/catch here is defense in depth: if a future regression introduces
|
|
66
|
+
// a throw path, the host loop still survives.
|
|
67
|
+
let reply;
|
|
68
|
+
try {
|
|
69
|
+
reply = ingest(message, ingestCtx);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
reply = {
|
|
73
|
+
type: 'ingest.err',
|
|
74
|
+
code: 'handler_threw',
|
|
75
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
await write(reply);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Real handlers (act-tool action.request → action.result correlation)
|
|
82
|
+
// arrive with the IPC bridge in a later chunk.
|
|
83
|
+
await write({
|
|
84
|
+
type: 'error',
|
|
85
|
+
code: 'unhandled_message',
|
|
86
|
+
detail: `native host: no handler for message type '${type ?? '(none)'}' yet`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async function write(value) {
|
|
90
|
+
if (output !== undefined)
|
|
91
|
+
await writeMessage(value, output);
|
|
92
|
+
else
|
|
93
|
+
await writeMessage(value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/native-host/host.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,wEAAwE;AACxE,8EAA8E;AAC9E,0DAA0D;AAC1D,EAAE;AACF,gFAAgF;AAChF,2EAA2E;AAC3E,sEAAsE;AACtE,yEAAyE;AACzE,8EAA8E;AAG9E,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAwB,MAAM,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA0B5D,sDAAsD;AACtD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC;IAC3B,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,eAAe;CAChB,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,UAA6B,EAAE;IAC7D,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACtF,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC;IAC3C,MAAM,SAAS,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAE9B,MAAM,WAAW,GAAyD;QACxE,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACf,OAAO,CAAC,KAAK,CAAC,0CAA0C,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACzE,CAAC;KACF,CAAC;IACF,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,WAAW,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAEnE,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,OAAO,EAAE,EAAE;QACpC,0EAA0E;QAC1E,8DAA8D;QAC9D,aAAa,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACnC,OAAO,CAAC,KAAK,CACX,0CAA0C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC7F,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,WAAW,CAAC,CAAC;IAEhB,OAAO;QACL,IAAI;QACJ,KAAK;YACH,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;KACF,CAAC;IAEF,KAAK,UAAU,aAAa,CAAC,OAAgB;QAC3C,MAAM,IAAI,GACR,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,IAAI,OAAO;YACzD,CAAC,CAAC,MAAM,CAAE,OAA6B,CAAC,IAAI,CAAC;YAC7C,CAAC,CAAC,SAAS,CAAC;QAEhB,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1B,MAAM,KAAK,CAAC;gBACV,IAAI,EAAE,eAAe;gBACrB,aAAa,EAAE,aAAa,CAAC,EAAE,CAAC;gBAChC,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACtF,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,IAAI,KAAK,SAAS,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,oEAAoE;YACpE,wEAAwE;YACxE,8CAA8C;YAC9C,IAAI,KAAc,CAAC;YACnB,IAAI,CAAC;gBACH,KAAK,GAAG,MAAM,CAAC,OAA0B,EAAE,SAAS,CAAC,CAAC;YACxD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,KAAK,GAAG;oBACN,IAAI,EAAE,YAAY;oBAClB,IAAI,EAAE,eAAe;oBACrB,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBACzD,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QAED,sEAAsE;QACtE,+CAA+C;QAC/C,MAAM,KAAK,CAAC;YACV,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,mBAAmB;YACzB,MAAM,EAAE,6CAA6C,IAAI,IAAI,QAAQ,OAAO;SAC7E,CAAC,CAAC;IACL,CAAC;IAED,KAAK,UAAU,KAAK,CAAC,KAAc;QACjC,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;;YACvD,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { extensionIdsPath, hostBinaryPath, loadExtensionIds, } from './config.js';
|
|
2
|
+
export { buildRealSink, type InstallOptions, type InstallResult, type InstallSink, installManifests, realSink, type RegExecFn, } from './installer.js';
|
|
3
|
+
export { allowedOrigins, buildManifest, type ExtensionIds, type InstallTarget, MANIFEST_FILENAME, NATIVE_HOST_NAME, type NativeHostManifest, resolveInstallTargets, type SupportedPlatform, } from './manifest.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/native-host/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,gBAAgB,EAChB,QAAQ,EACR,KAAK,SAAS,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,qBAAqB,EACrB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Public native-host surface re-exported for the CLI's `peek status` and
|
|
2
|
+
// `peek init` (the consent-gated installer the postinstall defers to). Consumed
|
|
3
|
+
// as the `@peekdev/mcp/native-host` subpath export. Pure manifest/target logic
|
|
4
|
+
// plus the injectable installer sink — no postinstall side effects leak here.
|
|
5
|
+
export { extensionIdsPath, hostBinaryPath, loadExtensionIds, } from './config.js';
|
|
6
|
+
export { buildRealSink, installManifests, realSink, } from './installer.js';
|
|
7
|
+
export { allowedOrigins, buildManifest, MANIFEST_FILENAME, NATIVE_HOST_NAME, resolveInstallTargets, } from './manifest.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/native-host/index.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAE9E,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EAIb,gBAAgB,EAChB,QAAQ,GAET,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,aAAa,EAGb,iBAAiB,EACjB,gBAAgB,EAEhB,qBAAqB,GAEtB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Database } from 'better-sqlite3';
|
|
2
|
+
/** Per-host runtime context (the open DB + the home directory for blobs). */
|
|
3
|
+
export interface IngestContext {
|
|
4
|
+
readonly db: Database;
|
|
5
|
+
/** Base for `<home>/rrweb-events/<sessionId>/<seq>.json.gz`. */
|
|
6
|
+
readonly home: string;
|
|
7
|
+
}
|
|
8
|
+
export interface IncomingSessionAppend {
|
|
9
|
+
readonly type: 'session.append';
|
|
10
|
+
readonly sessionId: string;
|
|
11
|
+
readonly url?: string;
|
|
12
|
+
readonly title?: string;
|
|
13
|
+
readonly events: readonly unknown[];
|
|
14
|
+
/** Optional client-stamped seq for idempotent retries. Server assigns when omitted. */
|
|
15
|
+
readonly seq?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface IncomingConsoleEvent {
|
|
18
|
+
readonly ts: number;
|
|
19
|
+
readonly level: string;
|
|
20
|
+
readonly args: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
export interface IncomingConsoleAppend {
|
|
23
|
+
readonly type: 'console.append';
|
|
24
|
+
readonly sessionId: string;
|
|
25
|
+
readonly url?: string;
|
|
26
|
+
readonly title?: string;
|
|
27
|
+
readonly events: readonly IncomingConsoleEvent[];
|
|
28
|
+
}
|
|
29
|
+
export interface IncomingNetRecord {
|
|
30
|
+
readonly kind: 'request' | 'response' | 'error';
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly ts: number;
|
|
33
|
+
readonly url?: string;
|
|
34
|
+
readonly method?: string;
|
|
35
|
+
readonly status?: number;
|
|
36
|
+
readonly transport?: 'fetch' | 'xhr';
|
|
37
|
+
readonly error?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Deep capture (ADR-0010, PRD §A.8): the relay's `maskNetMessage` runs
|
|
40
|
+
* `redactBody` in the ISOLATED world and forwards the masked string here.
|
|
41
|
+
* Shape mirrors `NetMessage.requestBody` / `responseBody` in
|
|
42
|
+
* peek-extension/src/recorder/messages.ts. Absent for Basic capture,
|
|
43
|
+
* `request` records that carried no body, and `error` records.
|
|
44
|
+
*/
|
|
45
|
+
readonly requestBody?: string;
|
|
46
|
+
readonly responseBody?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface IncomingNetworkAppend {
|
|
49
|
+
readonly type: 'network.append';
|
|
50
|
+
readonly sessionId: string;
|
|
51
|
+
readonly url?: string;
|
|
52
|
+
readonly title?: string;
|
|
53
|
+
readonly records: readonly IncomingNetRecord[];
|
|
54
|
+
}
|
|
55
|
+
export interface IncomingShadowReport {
|
|
56
|
+
readonly type: 'shadow.report';
|
|
57
|
+
readonly sessionId: string;
|
|
58
|
+
readonly reports: readonly unknown[];
|
|
59
|
+
}
|
|
60
|
+
export type IncomingMessage = IncomingSessionAppend | IncomingConsoleAppend | IncomingNetworkAppend | IncomingShadowReport;
|
|
61
|
+
export interface OkReply<T extends string> {
|
|
62
|
+
readonly type: `${T}.ok`;
|
|
63
|
+
readonly sessionId?: string;
|
|
64
|
+
readonly seq?: number;
|
|
65
|
+
readonly count?: number;
|
|
66
|
+
}
|
|
67
|
+
export interface ErrReply<T extends string> {
|
|
68
|
+
readonly type: `${T}.err`;
|
|
69
|
+
readonly sessionId?: string;
|
|
70
|
+
readonly code: string;
|
|
71
|
+
readonly detail?: string;
|
|
72
|
+
}
|
|
73
|
+
export type IngestReply = OkReply<'session.append'> | ErrReply<'session.append'> | OkReply<'console.append'> | ErrReply<'console.append'> | OkReply<'network.append'> | ErrReply<'network.append'> | OkReply<'shadow.report'> | ErrReply<'shadow.report'> | ErrReply<'ingest'>;
|
|
74
|
+
/** Absolute path to the rrweb-events chunk store under PEEK_HOME. */
|
|
75
|
+
export declare function rrwebEventsDir(home: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Dispatch one ingest message to the appropriate handler. Pure with respect to
|
|
78
|
+
* the network: takes a context, returns a reply. Every error path returns a
|
|
79
|
+
* structured `.err` reply — never throws.
|
|
80
|
+
*/
|
|
81
|
+
export declare function ingest(message: IncomingMessage, ctx: IngestContext): IngestReply;
|
|
82
|
+
export declare function _chunkFileSize(home: string, sessionId: string, seq: number): number;
|
|
83
|
+
//# sourceMappingURL=ingest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/native-host/ingest.ts"],"names":[],"mappings":"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"}
|