@reclaimprotocol/agent 0.1.0 → 0.1.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.
Files changed (204) hide show
  1. package/README.md +1 -1
  2. package/lib/cdp/capture-engine.d.ts +66 -0
  3. package/lib/cdp/capture-engine.js +153 -0
  4. package/lib/cdp/capture.d.ts +9 -20
  5. package/lib/cdp/capture.js +14 -138
  6. package/lib/cdp/index.d.ts +3 -0
  7. package/lib/cdp/index.js +6 -0
  8. package/lib/config/store.d.ts +7 -6
  9. package/lib/config/store.js +2 -2
  10. package/lib/index.d.ts +2 -2
  11. package/lib/index.js +1 -1
  12. package/lib/mcp/api/operations.gen.d.ts +101 -45
  13. package/lib/mcp/api/operations.gen.js +996 -126
  14. package/lib/mcp/api/to-zod.d.ts +24 -0
  15. package/lib/mcp/api/to-zod.js +94 -0
  16. package/lib/mcp/api/tools.js +1 -1
  17. package/lib/mcp/credentials/authenticate.d.ts +3 -0
  18. package/lib/mcp/{tools/credentials → credentials}/authenticate.js +3 -3
  19. package/lib/mcp/credentials/build.d.ts +5 -0
  20. package/lib/mcp/credentials/build.js +18 -0
  21. package/lib/mcp/{tools/credentials → credentials}/cache.d.ts +9 -9
  22. package/lib/mcp/{tools/credentials → credentials}/cache.js +36 -19
  23. package/lib/mcp/credentials/import.d.ts +2 -0
  24. package/lib/mcp/{tools/credentials → credentials}/import.js +2 -2
  25. package/lib/mcp/credentials/issue.d.ts +2 -0
  26. package/lib/mcp/{tools/credentials → credentials}/issue.js +4 -4
  27. package/lib/mcp/credentials/keypair.d.ts +17 -0
  28. package/lib/mcp/credentials/keypair.js +48 -0
  29. package/lib/mcp/credentials/link.d.ts +3 -0
  30. package/lib/mcp/credentials/link.js +107 -0
  31. package/lib/mcp/{tools/credentials → credentials}/list-cached.d.ts +1 -1
  32. package/lib/mcp/{tools/credentials → credentials}/list-cached.js +5 -5
  33. package/lib/mcp/credentials/owner-key.d.ts +23 -0
  34. package/lib/mcp/credentials/owner-key.js +58 -0
  35. package/lib/mcp/credentials/paths.d.ts +12 -0
  36. package/lib/mcp/credentials/paths.js +35 -0
  37. package/lib/mcp/credentials/poll.d.ts +27 -0
  38. package/lib/mcp/credentials/poll.js +61 -0
  39. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.d.ts +1 -1
  40. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.js +2 -2
  41. package/lib/mcp/credentials/session-cache.d.ts +14 -0
  42. package/lib/mcp/credentials/session-cache.js +42 -0
  43. package/lib/mcp/{tools/credentials → credentials}/status.d.ts +1 -1
  44. package/lib/mcp/credentials/status.js +76 -0
  45. package/lib/mcp/instructions.d.ts +1 -0
  46. package/lib/mcp/instructions.js +11 -0
  47. package/lib/mcp/register.d.ts +38 -0
  48. package/lib/mcp/register.js +80 -0
  49. package/lib/mcp/server.d.ts +2 -2
  50. package/lib/mcp/server.js +3 -3
  51. package/lib/mcp/start.js +10 -4
  52. package/lib/mcp/tools/agent/build.js +2 -15
  53. package/lib/mcp/tools/agent/index.d.ts +2 -0
  54. package/lib/mcp/tools/agent/index.js +3 -0
  55. package/lib/mcp/tools/agent/proof.js +1 -6
  56. package/lib/mcp/tools/agent/propose.js +13 -2
  57. package/lib/mcp/tools/agent/publish.js +10 -2
  58. package/lib/mcp/tools/agent/replay.js +0 -5
  59. package/lib/mcp/tools/analyze.d.ts +3 -0
  60. package/lib/mcp/tools/analyze.js +26 -0
  61. package/lib/mcp/tools/attach.d.ts +11 -0
  62. package/lib/mcp/tools/attach.js +72 -0
  63. package/lib/mcp/tools/authenticate/authenticate.js +107 -0
  64. package/lib/mcp/tools/authenticate/build.d.ts +15 -0
  65. package/lib/mcp/tools/authenticate/build.js +28 -0
  66. package/lib/mcp/tools/authenticate/decrypt-key.d.ts +32 -0
  67. package/lib/mcp/tools/authenticate/decrypt-key.js +49 -0
  68. package/lib/mcp/tools/authenticate/eth-key.d.ts +38 -0
  69. package/lib/mcp/tools/authenticate/eth-key.js +130 -0
  70. package/lib/mcp/tools/authenticate/import.js +57 -0
  71. package/lib/mcp/tools/authenticate/index.d.ts +2 -0
  72. package/lib/mcp/tools/authenticate/index.js +4 -0
  73. package/lib/mcp/tools/authenticate/issue.js +42 -0
  74. package/lib/mcp/tools/authenticate/resolve-owner-key.d.ts +13 -0
  75. package/lib/mcp/tools/authenticate/resolve-owner-key.js +53 -0
  76. package/lib/mcp/tools/capture.d.ts +10 -0
  77. package/lib/mcp/tools/capture.js +142 -0
  78. package/lib/mcp/tools/eval.d.ts +5 -0
  79. package/lib/mcp/tools/eval.js +127 -0
  80. package/lib/mcp/tools/find.d.ts +3 -0
  81. package/lib/mcp/tools/find.js +60 -0
  82. package/lib/mcp/tools/index.d.ts +3 -0
  83. package/lib/mcp/tools/index.js +6 -0
  84. package/lib/mcp/tools/owner-key.d.ts +2 -0
  85. package/lib/mcp/tools/owner-key.js +63 -0
  86. package/lib/mcp/tools/proof.d.ts +3 -0
  87. package/lib/mcp/tools/proof.js +214 -0
  88. package/lib/mcp/tools/propose.d.ts +9 -0
  89. package/lib/mcp/tools/propose.js +75 -0
  90. package/lib/mcp/tools/publish.d.ts +3 -0
  91. package/lib/mcp/tools/publish.js +59 -0
  92. package/lib/mcp/tools/replay.d.ts +3 -0
  93. package/lib/mcp/tools/replay.js +46 -0
  94. package/lib/mcp/tools/session.d.ts +10 -0
  95. package/lib/mcp/tools/session.js +29 -0
  96. package/lib/mcp/tools/verification/build.d.ts +12 -0
  97. package/lib/mcp/tools/verification/build.js +14 -0
  98. package/lib/mcp/tools/verification/diagnose.d.ts +11 -0
  99. package/lib/mcp/tools/verification/diagnose.js +132 -0
  100. package/lib/mcp/tools/verification/index.d.ts +1 -0
  101. package/lib/mcp/tools/verification/index.js +2 -0
  102. package/lib/mcp/tools/verification/result.d.ts +12 -0
  103. package/lib/mcp/tools/verification/result.js +126 -0
  104. package/lib/mcp/verifications/build.d.ts +5 -0
  105. package/lib/mcp/verifications/build.js +14 -0
  106. package/lib/mcp/verifications/create.d.ts +3 -0
  107. package/lib/mcp/verifications/create.js +57 -0
  108. package/lib/mcp/verifications/find.d.ts +3 -0
  109. package/lib/mcp/verifications/find.js +37 -0
  110. package/lib/mcp/verifications/get.d.ts +3 -0
  111. package/lib/mcp/verifications/get.js +26 -0
  112. package/lib/mcp/verifications/update.d.ts +3 -0
  113. package/lib/mcp/verifications/update.js +78 -0
  114. package/lib/old/client.d.ts +3 -0
  115. package/lib/old/instructions.d.ts +0 -6
  116. package/lib/old/instructions.js +6 -24
  117. package/lib/old/to-register.d.ts +3 -0
  118. package/lib/old/to-register.js +35 -7
  119. package/lib/old/tools/publish.js +13 -1
  120. package/lib/proof/attestor.d.ts +33 -1
  121. package/lib/proof/attestor.js +67 -61
  122. package/lib/proof/index.d.ts +5 -0
  123. package/lib/proof/index.js +6 -0
  124. package/lib/proof/proof-core.d.ts +35 -0
  125. package/lib/proof/proof-core.js +73 -0
  126. package/lib/proof/zk-files.d.ts +14 -0
  127. package/lib/proof/zk-files.js +73 -0
  128. package/lib/provider/index.d.ts +5 -0
  129. package/lib/provider/index.js +9 -0
  130. package/lib/provider/to-version.d.ts +9 -12
  131. package/lib/provider/to-version.js +38 -4
  132. package/lib/provider/verification-checks.d.ts +12 -0
  133. package/lib/provider/verification-checks.js +30 -0
  134. package/lib/routes.gen.d.ts +271 -0
  135. package/lib/routes.gen.js +73 -0
  136. package/lib/types/openapi.gen.d.ts +3967 -0
  137. package/lib/types/openapi.gen.js +5 -0
  138. package/package.json +18 -10
  139. package/skill/SKILL.md +35 -14
  140. package/skill/builder-mode.md +9 -0
  141. package/skill/old-mode.md +13 -0
  142. package/src/cdp/capture-engine.ts +256 -0
  143. package/src/cdp/capture.ts +25 -185
  144. package/src/cdp/index.ts +6 -0
  145. package/src/config/store.ts +7 -6
  146. package/src/index.ts +2 -4
  147. package/src/mcp/api/operations.gen.ts +1023 -121
  148. package/src/mcp/api/to-zod.ts +115 -0
  149. package/src/mcp/api/tools.ts +9 -3
  150. package/src/mcp/instructions.ts +15 -0
  151. package/src/mcp/server.ts +5 -4
  152. package/src/mcp/start.ts +14 -4
  153. package/src/mcp/tools/agent/build.ts +2 -18
  154. package/src/mcp/tools/agent/index.ts +4 -0
  155. package/src/mcp/tools/agent/proof.ts +1 -7
  156. package/src/mcp/tools/agent/propose.ts +13 -2
  157. package/src/mcp/tools/agent/publish.ts +10 -2
  158. package/src/mcp/tools/agent/replay.ts +0 -5
  159. package/src/mcp/tools/{credentials → authenticate}/authenticate.ts +9 -8
  160. package/src/mcp/tools/authenticate/build.ts +32 -0
  161. package/src/mcp/tools/authenticate/decrypt-key.ts +64 -0
  162. package/src/mcp/tools/authenticate/eth-key.ts +182 -0
  163. package/src/mcp/tools/authenticate/import.ts +73 -0
  164. package/src/mcp/tools/authenticate/index.ts +4 -0
  165. package/src/mcp/tools/authenticate/issue.ts +50 -0
  166. package/src/mcp/tools/authenticate/resolve-owner-key.ts +60 -0
  167. package/src/mcp/tools/index.ts +6 -0
  168. package/src/mcp/tools/verification/build.ts +16 -0
  169. package/src/mcp/tools/verification/index.ts +2 -0
  170. package/src/mcp/tools/verification/result.ts +155 -0
  171. package/src/old/client.ts +3 -0
  172. package/src/old/instructions.ts +10 -25
  173. package/src/old/to-register.ts +62 -27
  174. package/src/old/tools/publish.ts +17 -1
  175. package/src/proof/attestor.ts +115 -69
  176. package/src/proof/index.ts +21 -0
  177. package/src/proof/proof-core.ts +109 -0
  178. package/src/proof/zk-files.ts +83 -0
  179. package/src/provider/index.ts +20 -0
  180. package/src/provider/to-version.ts +52 -11
  181. package/src/provider/verification-checks.ts +36 -0
  182. package/lib/mcp/tools/credentials/build.d.ts +0 -16
  183. package/lib/mcp/tools/credentials/build.js +0 -33
  184. package/lib/mcp/tools/credentials/status.js +0 -62
  185. package/src/mcp/tools/credentials/build.ts +0 -37
  186. package/src/mcp/tools/credentials/cache.ts +0 -161
  187. package/src/mcp/tools/credentials/env-file.ts +0 -101
  188. package/src/mcp/tools/credentials/import.ts +0 -72
  189. package/src/mcp/tools/credentials/issue.ts +0 -107
  190. package/src/mcp/tools/credentials/list-cached.ts +0 -66
  191. package/src/mcp/tools/credentials/register.ts +0 -49
  192. package/src/mcp/tools/credentials/resolve-owner-key.ts +0 -78
  193. package/src/mcp/tools/credentials/status.ts +0 -82
  194. package/src/types/openapi.ts +0 -7
  195. /package/lib/mcp/{tools/credentials → credentials}/env-file.d.ts +0 -0
  196. /package/lib/mcp/{tools/credentials → credentials}/env-file.js +0 -0
  197. /package/lib/mcp/{tools/credentials → credentials}/register.d.ts +0 -0
  198. /package/lib/mcp/{tools/credentials → credentials}/register.js +0 -0
  199. /package/lib/mcp/tools/{credentials → authenticate}/authenticate.d.ts +0 -0
  200. /package/lib/mcp/tools/{credentials → authenticate}/import.d.ts +0 -0
  201. /package/lib/mcp/tools/{credentials → authenticate}/issue.d.ts +0 -0
  202. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.d.ts +0 -0
  203. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.js +0 -0
  204. /package/src/mcp/tools/{credentials → authenticate}/session-cache.ts +0 -0
package/README.md CHANGED
@@ -188,7 +188,7 @@ The MCP server targets one of two backends, selected by `USE_OLD_DEVTOOLS`:
188
188
  - `reclaim_authenticate` — by default opens the dashboard in Chrome via CDP, waits for sign-in, reads the Firebase token from page storage, and closes the tab (or pass a `token` / `ethAddress` to skip the browser).
189
189
  - `create_provider_version_from_capture` — registers a new provider, or adds a version to an existing one when given a `providerId`.
190
190
  - `get_me_providers` — list the providers you own (the only provider-listing tool in this mode).
191
- - **Builder (`USE_OLD_DEVTOOLS=false`).** Registers the auto-generated HTTP-API tools plus the builder credential tools (`reclaim_authenticate` device-code flow, `reclaim_status`, and the local credential tools).
191
+ - **Builder (`USE_OLD_DEVTOOLS=false`).** Registers the auto-generated HTTP-API tools plus the authentication/credential tools (`authenticate` device-pairing login, `issue_credentials` / `import_credentials` to set up the local eth proof-owner key, `resolve_owner_key`) and a verification-result helper (`get_verification_result`).
192
192
 
193
193
  Relevant env vars: `RECLAIM_OLD_API_URL` (default `https://devapi.reclaimprotocol.org`), `RECLAIM_OLD_API_TOKEN` / `RECLAIM_OLD_ETH_UID` (headless identity), `RECLAIM_OLD_LOGIN_URL` (default `https://dev.reclaimprotocol.org`); builder mode uses `RECLAIM_API_URL` / `RECLAIM_API_TOKEN`.
194
194
 
@@ -0,0 +1,66 @@
1
+ import type { CapturedRequest } from '../provider/schema.ts';
2
+ import { BodyStore } from './eviction.ts';
3
+ /** The slice of CDP `Network.requestWillBeSent` the engine reads. Kept
4
+ * structural — not devtools-protocol's full event type — so a transport
5
+ * pinned to a different devtools-protocol version still satisfies it. */
6
+ export interface RequestWillBeSentLike {
7
+ requestId: string;
8
+ request: {
9
+ method: string;
10
+ url: string;
11
+ headers: Record<string, string>;
12
+ postData?: string;
13
+ };
14
+ }
15
+ /** The slice of CDP `Network.responseReceived` the engine reads. */
16
+ export interface ResponseReceivedLike {
17
+ requestId: string;
18
+ response: {
19
+ status: number;
20
+ mimeType: string;
21
+ encodedDataLength: number;
22
+ headers: Record<string, string>;
23
+ };
24
+ }
25
+ /** Drop HTTP/2 pseudo-headers (`:authority`, `:method`, `:path`, `:scheme`,
26
+ * `:status`) from a CDP header map. Chrome emits them on HTTP/2 requests,
27
+ * but they're a transport-layer abstraction with no place in our domain:
28
+ * the URL already carries authority/path/scheme, the request object carries
29
+ * method, and Node's `fetch` (undici) throws on any header starting with
30
+ * `:`. Stripping at capture time keeps every downstream consumer
31
+ * (`get_request`, draft, replay, proof) clean by construction. */
32
+ export declare function stripPseudoHeaders(headers: Record<string, string>): Record<string, string>;
33
+ export interface CaptureSession {
34
+ captureId: string;
35
+ requests: Map<string, CapturedRequest>;
36
+ bodies: BodyStore;
37
+ stop: () => Promise<void>;
38
+ }
39
+ /** Public knobs a transport accepts; mapped into `CaptureEngineOptions`. */
40
+ export interface StartCaptureOptions {
41
+ maxBodyBytes?: number;
42
+ }
43
+ export interface CaptureEngineOptions {
44
+ maxBodyBytes?: number;
45
+ /** Fetch a finished response body. Transport-specific: the agent calls
46
+ * `Network.getResponseBody` on the page binding, the app routes the
47
+ * command through its Popcorn session. */
48
+ fetchBody: (requestId: string) => Promise<{
49
+ body: string;
50
+ base64Encoded: boolean;
51
+ }>;
52
+ }
53
+ /** The engine state plus the handlers a transport drives. A transport feeds
54
+ * raw CDP events into these and exposes `captureId`/`requests`/`bodies` to
55
+ * its caller. */
56
+ export interface CaptureEngine {
57
+ captureId: string;
58
+ requests: Map<string, CapturedRequest>;
59
+ bodies: BodyStore;
60
+ onRequestWillBeSent: (p: RequestWillBeSentLike) => void;
61
+ mergeRequestHeaders: (requestId: string, extra: Record<string, string>) => void;
62
+ onResponseReceived: (p: ResponseReceivedLike) => void;
63
+ mergeResponseHeaders: (requestId: string, extra: Record<string, string>) => void;
64
+ onLoadingFinished: (requestId: string) => Promise<void>;
65
+ }
66
+ export declare function createCaptureEngine(opts: CaptureEngineOptions): CaptureEngine;
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Transport-agnostic CDP Network → CapturedRequest state machine. The agent
3
+ * (local CDP, page-level `Network.*` bindings) and the app (remote Popcorn,
4
+ * events read off a session-filtered `event` stream) share this entire engine;
5
+ * only how events are delivered and how a response body is fetched differ, so
6
+ * those two are injected. See `cdp/capture.ts` and the app's
7
+ * `capture/capture.ts` for the two thin transports that wire into it.
8
+ */
9
+ import { randomUUID } from 'node:crypto';
10
+ import { extractGraphqlOp } from "../provider/graphql.js";
11
+ import { BodyStore } from "./eviction.js";
12
+ /** Drop HTTP/2 pseudo-headers (`:authority`, `:method`, `:path`, `:scheme`,
13
+ * `:status`) from a CDP header map. Chrome emits them on HTTP/2 requests,
14
+ * but they're a transport-layer abstraction with no place in our domain:
15
+ * the URL already carries authority/path/scheme, the request object carries
16
+ * method, and Node's `fetch` (undici) throws on any header starting with
17
+ * `:`. Stripping at capture time keeps every downstream consumer
18
+ * (`get_request`, draft, replay, proof) clean by construction. */
19
+ export function stripPseudoHeaders(headers) {
20
+ const out = {};
21
+ for (const [k, v] of Object.entries(headers)) {
22
+ if (!k.startsWith(':')) {
23
+ out[k] = v;
24
+ }
25
+ }
26
+ return out;
27
+ }
28
+ export function createCaptureEngine(opts) {
29
+ const captureId = randomUUID();
30
+ const requests = new Map();
31
+ const inFlight = new Map();
32
+ const bodies = new BodyStore({
33
+ maxBytes: opts.maxBodyBytes ?? 200 * 1024 * 1024,
34
+ });
35
+ // CDP doesn't guarantee `requestWillBeSent` arrives before
36
+ // `requestWillBeSentExtraInfo` (same for the response pair). When extra
37
+ // info lands first, buffer it here and drain on the matching primary
38
+ // event. Without this, headers (Cookie, Authorization, …) get silently
39
+ // dropped for any request whose events race in the unlucky order.
40
+ const pendingRequestExtra = new Map();
41
+ const pendingResponseExtra = new Map();
42
+ return {
43
+ captureId,
44
+ requests,
45
+ bodies,
46
+ onRequestWillBeSent,
47
+ mergeRequestHeaders,
48
+ onResponseReceived,
49
+ mergeResponseHeaders,
50
+ onLoadingFinished,
51
+ };
52
+ /** Merge extra headers from the ...ExtraInfo events into either the
53
+ * in-flight entry or the finalized captured request, whichever has
54
+ * arrived. If neither exists yet (extra-info-first race), buffer the
55
+ * headers and drain on the matching primary event. */
56
+ function mergeRequestHeaders(requestId, extra) {
57
+ const clean = stripPseudoHeaders(extra);
58
+ const f = inFlight.get(requestId);
59
+ if (f) {
60
+ f.requestHeaders = { ...f.requestHeaders, ...clean };
61
+ return;
62
+ }
63
+ const r = requests.get(requestId);
64
+ if (r) {
65
+ r.requestHeaders = { ...r.requestHeaders, ...clean };
66
+ return;
67
+ }
68
+ const buffered = pendingRequestExtra.get(requestId) ?? {};
69
+ pendingRequestExtra.set(requestId, { ...buffered, ...clean });
70
+ }
71
+ function mergeResponseHeaders(requestId, extra) {
72
+ const clean = stripPseudoHeaders(extra);
73
+ const r = requests.get(requestId);
74
+ if (r) {
75
+ r.responseHeaders = { ...r.responseHeaders, ...clean };
76
+ return;
77
+ }
78
+ const buffered = pendingResponseExtra.get(requestId) ?? {};
79
+ pendingResponseExtra.set(requestId, { ...buffered, ...clean });
80
+ }
81
+ function onRequestWillBeSent(p) {
82
+ const f = {
83
+ requestId: p.requestId,
84
+ method: p.request.method,
85
+ url: p.request.url,
86
+ requestHeaders: stripPseudoHeaders(p.request.headers),
87
+ startedAt: Date.now(),
88
+ };
89
+ if (p.request.postData !== undefined) {
90
+ f.requestBody = p.request.postData;
91
+ }
92
+ // Drain any extra-info that arrived before us.
93
+ const buffered = pendingRequestExtra.get(p.requestId);
94
+ if (buffered) {
95
+ f.requestHeaders = { ...f.requestHeaders, ...buffered };
96
+ pendingRequestExtra.delete(p.requestId);
97
+ }
98
+ inFlight.set(p.requestId, f);
99
+ }
100
+ function onResponseReceived(p) {
101
+ const f = inFlight.get(p.requestId);
102
+ if (!f) {
103
+ return;
104
+ }
105
+ const captured = {
106
+ requestId: p.requestId,
107
+ method: f.method,
108
+ url: f.url,
109
+ status: p.response.status,
110
+ contentType: p.response.mimeType,
111
+ size: p.response.encodedDataLength,
112
+ requestHeaders: f.requestHeaders,
113
+ responseHeaders: stripPseudoHeaders(p.response.headers),
114
+ finishedAt: 0,
115
+ };
116
+ // Drain any response extra-info that arrived before us.
117
+ const bufferedResp = pendingResponseExtra.get(p.requestId);
118
+ if (bufferedResp) {
119
+ captured.responseHeaders = {
120
+ ...captured.responseHeaders,
121
+ ...bufferedResp,
122
+ };
123
+ pendingResponseExtra.delete(p.requestId);
124
+ }
125
+ if (f.requestBody !== undefined) {
126
+ captured.requestBody = f.requestBody;
127
+ }
128
+ const op = extractGraphqlOp(f.requestBody);
129
+ if (op !== undefined) {
130
+ captured.graphqlOp = op;
131
+ }
132
+ requests.set(p.requestId, captured);
133
+ }
134
+ async function onLoadingFinished(requestId) {
135
+ const r = requests.get(requestId);
136
+ if (!r) {
137
+ return;
138
+ }
139
+ try {
140
+ const { body, base64Encoded } = await opts.fetchBody(requestId);
141
+ const text = base64Encoded
142
+ ? Buffer.from(body, 'base64').toString('utf8')
143
+ : body;
144
+ bodies.set(requestId, text);
145
+ r.responseBody = text;
146
+ r.finishedAt = Date.now();
147
+ }
148
+ catch {
149
+ // Body might be unavailable for
150
+ // some content-types (e.g. websocket). Skip.
151
+ }
152
+ }
153
+ }
@@ -1,22 +1,11 @@
1
+ /**
2
+ * Local-CDP transport for the capture engine. Binds the page-level `Network.*`
3
+ * events directly and fetches bodies via the page's `getResponseBody`. All the
4
+ * request/response bookkeeping lives in `capture-engine.ts`, shared with the
5
+ * app's remote-Popcorn transport.
6
+ */
1
7
  import type CDP from 'chrome-remote-interface';
2
- import type { Protocol } from 'devtools-protocol';
3
- import type { CapturedRequest } from '../provider/schema.ts';
4
- import { BodyStore } from './eviction.ts';
5
- /** Drop HTTP/2 pseudo-headers (`:authority`, `:method`, `:path`, `:scheme`,
6
- * `:status`) from a CDP header map. Chrome emits them on HTTP/2 requests,
7
- * but they're a transport-layer abstraction with no place in our domain:
8
- * the URL already carries authority/path/scheme, the request object carries
9
- * method, and Node's `fetch` (undici) throws on any header starting with
10
- * `:`. Stripping at capture time keeps every downstream consumer
11
- * (`get_request`, draft, replay, proof) clean by construction. */
12
- export declare function stripPseudoHeaders(headers: Protocol.Network.Headers): Record<string, string>;
13
- export interface CaptureSession {
14
- captureId: string;
15
- requests: Map<string, CapturedRequest>;
16
- bodies: BodyStore;
17
- stop: () => Promise<void>;
18
- }
19
- export interface StartCaptureOptions {
20
- maxBodyBytes?: number;
21
- }
8
+ import { type CaptureSession, type StartCaptureOptions } from './capture-engine.ts';
9
+ export { stripPseudoHeaders } from './capture-engine.ts';
10
+ export type { CaptureSession, StartCaptureOptions } from './capture-engine.ts';
22
11
  export declare function startCapture(tab: CDP.Client, opts?: StartCaptureOptions): Promise<CaptureSession>;
@@ -1,157 +1,33 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { extractGraphqlOp } from "../provider/graphql.js";
3
- import { BodyStore } from "./eviction.js";
4
- /** Drop HTTP/2 pseudo-headers (`:authority`, `:method`, `:path`, `:scheme`,
5
- * `:status`) from a CDP header map. Chrome emits them on HTTP/2 requests,
6
- * but they're a transport-layer abstraction with no place in our domain:
7
- * the URL already carries authority/path/scheme, the request object carries
8
- * method, and Node's `fetch` (undici) throws on any header starting with
9
- * `:`. Stripping at capture time keeps every downstream consumer
10
- * (`get_request`, draft, replay, proof) clean by construction. */
11
- export function stripPseudoHeaders(headers) {
12
- const out = {};
13
- for (const [k, v] of Object.entries(headers)) {
14
- if (!k.startsWith(':')) {
15
- out[k] = v;
16
- }
17
- }
18
- return out;
19
- }
1
+ import { createCaptureEngine, } from "./capture-engine.js";
2
+ export { stripPseudoHeaders } from "./capture-engine.js";
20
3
  export async function startCapture(tab, opts = {}) {
21
- const captureId = randomUUID();
22
- const requests = new Map();
23
- const inFlight = new Map();
24
- const bodies = new BodyStore({
25
- maxBytes: opts.maxBodyBytes ?? 200 * 1024 * 1024,
4
+ const engine = createCaptureEngine({
5
+ ...(opts.maxBodyBytes !== undefined && { maxBodyBytes: opts.maxBodyBytes }),
6
+ fetchBody: (requestId) => tab.Network.getResponseBody({ requestId }),
26
7
  });
27
- // CDP doesn't guarantee `requestWillBeSent` arrives before
28
- // `requestWillBeSentExtraInfo` (same for the response pair). When extra
29
- // info lands first, buffer it here and drain on the matching primary
30
- // event. Without this, headers (Cookie, Authorization, …) get silently
31
- // dropped for any request whose events race in the unlucky order.
32
- const pendingRequestExtra = new Map();
33
- const pendingResponseExtra = new Map();
34
8
  const { Network } = tab;
35
9
  await Network.enable();
36
- /** Merge extra headers from the ...ExtraInfo events into either the
37
- * in-flight entry or the finalized captured request, whichever has
38
- * arrived. If neither exists yet (extra-info-first race), buffer the
39
- * headers and drain on the matching primary event. */
40
- function mergeRequestHeaders(requestId, extra) {
41
- const clean = stripPseudoHeaders(extra);
42
- const f = inFlight.get(requestId);
43
- if (f) {
44
- f.requestHeaders = { ...f.requestHeaders, ...clean };
45
- return;
46
- }
47
- const r = requests.get(requestId);
48
- if (r) {
49
- r.requestHeaders = { ...r.requestHeaders, ...clean };
50
- return;
51
- }
52
- const buffered = pendingRequestExtra.get(requestId) ?? {};
53
- pendingRequestExtra.set(requestId, { ...buffered, ...clean });
54
- }
55
- function mergeResponseHeaders(requestId, extra) {
56
- const clean = stripPseudoHeaders(extra);
57
- const r = requests.get(requestId);
58
- if (r) {
59
- r.responseHeaders = { ...r.responseHeaders, ...clean };
60
- return;
61
- }
62
- const buffered = pendingResponseExtra.get(requestId) ?? {};
63
- pendingResponseExtra.set(requestId, { ...buffered, ...clean });
64
- }
65
- Network.requestWillBeSent(({ requestId, request }) => {
66
- const f = {
67
- requestId,
68
- method: request.method,
69
- url: request.url,
70
- requestHeaders: stripPseudoHeaders(request.headers),
71
- startedAt: Date.now(),
72
- };
73
- if (request.postData !== undefined) {
74
- f.requestBody = request.postData;
75
- }
76
- // Drain any extra-info that arrived before us.
77
- const buffered = pendingRequestExtra.get(requestId);
78
- if (buffered) {
79
- f.requestHeaders = { ...f.requestHeaders, ...buffered };
80
- pendingRequestExtra.delete(requestId);
81
- }
82
- inFlight.set(requestId, f);
83
- });
10
+ Network.requestWillBeSent(engine.onRequestWillBeSent);
84
11
  // CDP intentionally omits `Cookie` (and other browser-injected headers)
85
12
  // from `requestWillBeSent.request.headers` for privacy. The real
86
13
  // outgoing headers — including Cookie — arrive on the matching
87
14
  // `requestWillBeSentExtraInfo` event.
88
15
  Network.requestWillBeSentExtraInfo(({ requestId, headers }) => {
89
- mergeRequestHeaders(requestId, headers);
90
- });
91
- Network.responseReceived(({ requestId, response }) => {
92
- const f = inFlight.get(requestId);
93
- if (!f) {
94
- return;
95
- }
96
- const captured = {
97
- requestId,
98
- method: f.method,
99
- url: f.url,
100
- status: response.status,
101
- contentType: response.mimeType,
102
- size: response.encodedDataLength,
103
- requestHeaders: f.requestHeaders,
104
- responseHeaders: stripPseudoHeaders(response.headers),
105
- finishedAt: 0,
106
- };
107
- // Drain any response extra-info that arrived before us.
108
- const bufferedResp = pendingResponseExtra.get(requestId);
109
- if (bufferedResp) {
110
- captured.responseHeaders = {
111
- ...captured.responseHeaders,
112
- ...bufferedResp,
113
- };
114
- pendingResponseExtra.delete(requestId);
115
- }
116
- if (f.requestBody !== undefined) {
117
- captured.requestBody = f.requestBody;
118
- }
119
- const op = extractGraphqlOp(f.requestBody);
120
- if (op !== undefined) {
121
- captured.graphqlOp = op;
122
- }
123
- requests.set(requestId, captured);
16
+ engine.mergeRequestHeaders(requestId, headers);
124
17
  });
18
+ Network.responseReceived(engine.onResponseReceived);
125
19
  // Same privacy carve-out applies to `Set-Cookie` and other browser-
126
20
  // visible response headers; they arrive on responseReceivedExtraInfo.
127
21
  Network.responseReceivedExtraInfo(({ requestId, headers }) => {
128
- mergeResponseHeaders(requestId, headers);
22
+ engine.mergeResponseHeaders(requestId, headers);
129
23
  });
130
- Network.loadingFinished(async ({ requestId }) => {
131
- const r = requests.get(requestId);
132
- if (!r) {
133
- return;
134
- }
135
- try {
136
- const { body, base64Encoded } = await Network.getResponseBody({
137
- requestId,
138
- });
139
- const text = base64Encoded
140
- ? Buffer.from(body, 'base64').toString('utf8')
141
- : body;
142
- bodies.set(requestId, text);
143
- r.responseBody = text;
144
- r.finishedAt = Date.now();
145
- }
146
- catch {
147
- // Body might be unavailable for
148
- // some content-types (e.g. websocket). Skip.
149
- }
24
+ Network.loadingFinished(({ requestId }) => {
25
+ void engine.onLoadingFinished(requestId);
150
26
  });
151
27
  return {
152
- captureId,
153
- requests,
154
- bodies,
28
+ captureId: engine.captureId,
29
+ requests: engine.requests,
30
+ bodies: engine.bodies,
155
31
  stop: async () => {
156
32
  await Network.disable();
157
33
  },
@@ -0,0 +1,3 @@
1
+ export * from './capture-engine.ts';
2
+ export { startCapture } from './capture.ts';
3
+ export { BodyStore } from './eviction.ts';
@@ -0,0 +1,6 @@
1
+ // Capture pipeline shared with the app's remote-Popcorn transport:
2
+ // the transport-agnostic engine, the body store, and the local-CDP
3
+ // `startCapture`. Exposed as `@reclaimprotocol/agent/capture`.
4
+ export * from "./capture-engine.js";
5
+ export { startCapture } from "./capture.js";
6
+ export { BodyStore } from "./eviction.js";
@@ -1,5 +1,4 @@
1
- import type { CachedCredential } from '../mcp/tools/credentials/cache.ts';
2
- import type { CachedSession } from '../mcp/tools/credentials/session-cache.ts';
1
+ import type { CachedSession } from '../mcp/tools/authenticate/session-cache.ts';
3
2
  import type { OldIdentity } from '../old/client.ts';
4
3
  export interface ReclaimAgentConfig {
5
4
  attachMode: 'dedicated' | 'attach';
@@ -9,24 +8,26 @@ export interface ReclaimAgentConfig {
9
8
  * The single on-disk document at `~/.reclaim/config.json`. Each top-level
10
9
  * key is an independent section owned by a different subsystem:
11
10
  * - `browser_agent` — Chrome attach mode/port (the agent tools)
12
- * - `credentials` — cached app key-pairs (the credential tools)
13
11
  * - `session` — the auth bearer token (authenticate)
14
12
  * - `old_devtools` — externally-supplied identity for OLD-devtools mode
15
13
  * (USE_OLD_DEVTOOLS); independent of `session`'s
16
14
  * builder token. Deletable with the rest of `src/old/`.
17
15
  * Sections are persisted independently via `ConfigStore.write`, which
18
16
  * merges rather than replaces, so one subsystem never clobbers another's.
17
+ *
18
+ * NOTE: eth proof-owner keys are NOT cached here — they live as PEMs in the
19
+ * project (see authenticate/eth-key.ts). This config holds no private keys
20
+ * other than the (short-lived) session bearer token.
19
21
  */
20
22
  export interface ReclaimConfig {
21
23
  version: 1;
22
24
  browser_agent?: ReclaimAgentConfig;
23
- credentials?: CachedCredential[];
24
25
  session?: CachedSession;
25
26
  old_devtools?: OldIdentity;
26
27
  }
27
28
  /**
28
- * Read/write the unified Reclaim config document. The file holds secrets
29
- * (a bearer token and private keys), so it is always locked to `0600` and
29
+ * Read/write the unified Reclaim config document. The file holds the
30
+ * (short-lived) session bearer token, so it is always locked to `0600` and
30
31
  * its directory to `0700` — the `~/.ssh` posture. `read()` returns
31
32
  * `undefined` when the file is missing or unparseable; callers layer their
32
33
  * own validation (token expiry, credential shape) on top.
@@ -2,8 +2,8 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from '
2
2
  import { dirname, join } from 'node:path';
3
3
  import { reclaimHomeDirPath } from "../paths.js";
4
4
  /**
5
- * Read/write the unified Reclaim config document. The file holds secrets
6
- * (a bearer token and private keys), so it is always locked to `0600` and
5
+ * Read/write the unified Reclaim config document. The file holds the
6
+ * (short-lived) session bearer token, so it is always locked to `0600` and
7
7
  * its directory to `0700` — the `~/.ssh` posture. `read()` returns
8
8
  * `undefined` when the file is missing or unparseable; callers layer their
9
9
  * own validation (token expiry, credential shape) on top.
package/lib/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from '@reclaimprotocol/client';
2
- export { buildAgentTools, } from './mcp/tools/agent/build.ts';
3
- export type { AgentState } from './mcp/tools/agent/build.ts';
2
+ export { buildAgentTools } from './mcp/tools/agent/index.ts';
3
+ export type { AgentState } from './mcp/tools/agent/index.ts';
package/lib/index.js CHANGED
@@ -4,4 +4,4 @@
4
4
  // only need one package for both the typed HTTP client and the agent tools.
5
5
  export * from '@reclaimprotocol/client';
6
6
  // Agent — Chrome CDP capture, provider synthesis, replay, proof.
7
- export { buildAgentTools, } from "./mcp/tools/agent/build.js";
7
+ export { buildAgentTools } from "./mcp/tools/agent/index.js";