@reclaimprotocol/agent 0.1.1-dev.1 → 0.1.2

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 (232) 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/cdp/launcher.js +4 -3
  9. package/lib/config/store.d.ts +7 -6
  10. package/lib/config/store.js +2 -2
  11. package/lib/consts.d.ts +68 -0
  12. package/lib/consts.js +134 -0
  13. package/lib/index.d.ts +2 -2
  14. package/lib/index.js +1 -1
  15. package/lib/logger.js +2 -1
  16. package/lib/mcp/api/operations.gen.d.ts +100 -48
  17. package/lib/mcp/api/operations.gen.js +1129 -194
  18. package/lib/mcp/api/to-zod.d.ts +24 -0
  19. package/lib/mcp/api/to-zod.js +94 -0
  20. package/lib/mcp/api/tools.js +1 -1
  21. package/lib/mcp/credentials/authenticate.d.ts +3 -0
  22. package/lib/mcp/{tools/credentials → credentials}/authenticate.js +3 -3
  23. package/lib/mcp/credentials/build.d.ts +5 -0
  24. package/lib/mcp/credentials/build.js +18 -0
  25. package/lib/mcp/{tools/credentials → credentials}/cache.d.ts +9 -9
  26. package/lib/mcp/{tools/credentials → credentials}/cache.js +36 -19
  27. package/lib/mcp/credentials/import.d.ts +2 -0
  28. package/lib/mcp/{tools/credentials → credentials}/import.js +2 -2
  29. package/lib/mcp/credentials/issue.d.ts +2 -0
  30. package/lib/mcp/{tools/credentials → credentials}/issue.js +4 -4
  31. package/lib/mcp/credentials/keypair.d.ts +17 -0
  32. package/lib/mcp/credentials/keypair.js +48 -0
  33. package/lib/mcp/credentials/link.d.ts +3 -0
  34. package/lib/mcp/credentials/link.js +107 -0
  35. package/lib/mcp/{tools/credentials → credentials}/list-cached.d.ts +1 -1
  36. package/lib/mcp/{tools/credentials → credentials}/list-cached.js +5 -5
  37. package/lib/mcp/credentials/owner-key.d.ts +23 -0
  38. package/lib/mcp/credentials/owner-key.js +58 -0
  39. package/lib/mcp/credentials/paths.d.ts +12 -0
  40. package/lib/mcp/credentials/paths.js +35 -0
  41. package/lib/mcp/credentials/poll.d.ts +27 -0
  42. package/lib/mcp/credentials/poll.js +61 -0
  43. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.d.ts +1 -1
  44. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.js +2 -2
  45. package/lib/mcp/credentials/session-cache.d.ts +14 -0
  46. package/lib/mcp/credentials/session-cache.js +42 -0
  47. package/lib/mcp/{tools/credentials → credentials}/status.d.ts +1 -1
  48. package/lib/mcp/credentials/status.js +76 -0
  49. package/lib/mcp/instructions.d.ts +1 -0
  50. package/lib/mcp/instructions.js +11 -0
  51. package/lib/mcp/register.d.ts +38 -0
  52. package/lib/mcp/register.js +80 -0
  53. package/lib/mcp/server.d.ts +2 -8
  54. package/lib/mcp/server.js +3 -33
  55. package/lib/mcp/start.js +17 -11
  56. package/lib/mcp/tools/agent/build.d.ts +5 -9
  57. package/lib/mcp/tools/agent/build.js +7 -24
  58. package/lib/mcp/tools/agent/index.d.ts +2 -0
  59. package/lib/mcp/tools/agent/index.js +3 -0
  60. package/lib/mcp/tools/agent/proof.js +3 -7
  61. package/lib/mcp/tools/agent/propose.js +13 -2
  62. package/lib/mcp/tools/agent/publish.js +172 -75
  63. package/lib/mcp/tools/agent/replay.js +0 -5
  64. package/lib/mcp/tools/analyze.d.ts +3 -0
  65. package/lib/mcp/tools/analyze.js +26 -0
  66. package/lib/mcp/tools/attach.d.ts +11 -0
  67. package/lib/mcp/tools/attach.js +72 -0
  68. package/lib/mcp/tools/authenticate/authenticate.js +107 -0
  69. package/lib/mcp/tools/authenticate/build.d.ts +15 -0
  70. package/lib/mcp/tools/authenticate/build.js +28 -0
  71. package/lib/mcp/tools/authenticate/decrypt-key.d.ts +19 -0
  72. package/lib/mcp/tools/authenticate/decrypt-key.js +46 -0
  73. package/lib/mcp/tools/authenticate/eth-key.d.ts +31 -0
  74. package/lib/mcp/tools/authenticate/eth-key.js +116 -0
  75. package/lib/mcp/tools/authenticate/import.js +57 -0
  76. package/lib/mcp/tools/authenticate/index.d.ts +2 -0
  77. package/lib/mcp/tools/authenticate/index.js +4 -0
  78. package/lib/mcp/tools/authenticate/issue.js +42 -0
  79. package/lib/mcp/tools/authenticate/resolve-owner-key.d.ts +13 -0
  80. package/lib/mcp/tools/authenticate/resolve-owner-key.js +53 -0
  81. package/lib/mcp/tools/capture.d.ts +10 -0
  82. package/lib/mcp/tools/capture.js +142 -0
  83. package/lib/mcp/tools/eval.d.ts +5 -0
  84. package/lib/mcp/tools/eval.js +127 -0
  85. package/lib/mcp/tools/find.d.ts +3 -0
  86. package/lib/mcp/tools/find.js +60 -0
  87. package/lib/mcp/tools/index.d.ts +3 -0
  88. package/lib/mcp/tools/index.js +6 -0
  89. package/lib/mcp/tools/owner-key.d.ts +2 -0
  90. package/lib/mcp/tools/owner-key.js +63 -0
  91. package/lib/mcp/tools/proof.d.ts +3 -0
  92. package/lib/mcp/tools/proof.js +214 -0
  93. package/lib/mcp/tools/propose.d.ts +9 -0
  94. package/lib/mcp/tools/propose.js +75 -0
  95. package/lib/mcp/tools/publish.d.ts +3 -0
  96. package/lib/mcp/tools/publish.js +59 -0
  97. package/lib/mcp/tools/replay.d.ts +3 -0
  98. package/lib/mcp/tools/replay.js +46 -0
  99. package/lib/mcp/tools/session.d.ts +10 -0
  100. package/lib/mcp/tools/session.js +29 -0
  101. package/lib/mcp/tools/verification/build.d.ts +12 -0
  102. package/lib/mcp/tools/verification/build.js +14 -0
  103. package/lib/mcp/tools/verification/diagnose.d.ts +11 -0
  104. package/lib/mcp/tools/verification/diagnose.js +132 -0
  105. package/lib/mcp/tools/verification/index.d.ts +1 -0
  106. package/lib/mcp/tools/verification/index.js +2 -0
  107. package/lib/mcp/tools/verification/result.d.ts +11 -0
  108. package/lib/mcp/tools/verification/result.js +126 -0
  109. package/lib/mcp/verifications/build.d.ts +5 -0
  110. package/lib/mcp/verifications/build.js +14 -0
  111. package/lib/mcp/verifications/create.d.ts +3 -0
  112. package/lib/mcp/verifications/create.js +57 -0
  113. package/lib/mcp/verifications/find.d.ts +3 -0
  114. package/lib/mcp/verifications/find.js +37 -0
  115. package/lib/mcp/verifications/get.d.ts +3 -0
  116. package/lib/mcp/verifications/get.js +26 -0
  117. package/lib/mcp/verifications/update.d.ts +3 -0
  118. package/lib/mcp/verifications/update.js +78 -0
  119. package/lib/old/client.d.ts +3 -0
  120. package/lib/old/flag.d.ts +4 -1
  121. package/lib/old/flag.js +4 -3
  122. package/lib/old/identity.d.ts +0 -10
  123. package/lib/old/identity.js +8 -7
  124. package/lib/old/instructions.d.ts +0 -6
  125. package/lib/old/instructions.js +6 -24
  126. package/lib/old/login-cdp.d.ts +0 -3
  127. package/lib/old/login-cdp.js +2 -4
  128. package/lib/old/to-register.d.ts +3 -0
  129. package/lib/old/to-register.js +32 -7
  130. package/lib/old/tools/authenticate.js +2 -1
  131. package/lib/old/tools/publish.js +71 -10
  132. package/lib/paths.js +2 -2
  133. package/lib/proof/attestor.d.ts +33 -1
  134. package/lib/proof/attestor.js +67 -64
  135. package/lib/proof/index.d.ts +5 -0
  136. package/lib/proof/index.js +6 -0
  137. package/lib/proof/proof-core.d.ts +35 -0
  138. package/lib/proof/proof-core.js +84 -0
  139. package/lib/provider/index.d.ts +5 -0
  140. package/lib/provider/index.js +9 -0
  141. package/lib/provider/injection-doc.d.ts +1 -0
  142. package/lib/provider/injection-doc.js +17 -0
  143. package/lib/provider/match-redaction-warning.d.ts +25 -0
  144. package/lib/provider/match-redaction-warning.js +35 -0
  145. package/lib/provider/schema.d.ts +16 -0
  146. package/lib/provider/schema.js +11 -1
  147. package/lib/provider/to-version.d.ts +61 -12
  148. package/lib/provider/to-version.js +109 -24
  149. package/lib/provider/verification-checks.d.ts +12 -0
  150. package/lib/provider/verification-checks.js +30 -0
  151. package/lib/routes.gen.d.ts +271 -0
  152. package/lib/routes.gen.js +73 -0
  153. package/lib/types/openapi.gen.d.ts +3967 -0
  154. package/lib/types/openapi.gen.js +5 -0
  155. package/package.json +16 -7
  156. package/skill/SKILL.md +35 -14
  157. package/skill/builder-mode.md +13 -0
  158. package/skill/injection-api.md +42 -0
  159. package/skill/old-mode.md +15 -0
  160. package/src/cdp/capture-engine.ts +256 -0
  161. package/src/cdp/capture.ts +25 -185
  162. package/src/cdp/index.ts +6 -0
  163. package/src/cdp/launcher.ts +5 -4
  164. package/src/config/store.ts +7 -6
  165. package/src/consts.ts +164 -0
  166. package/src/index.ts +2 -4
  167. package/src/logger.ts +2 -1
  168. package/src/mcp/api/operations.gen.ts +1155 -194
  169. package/src/mcp/api/to-zod.ts +115 -0
  170. package/src/mcp/api/tools.ts +9 -3
  171. package/src/mcp/instructions.ts +15 -0
  172. package/src/mcp/server.ts +5 -50
  173. package/src/mcp/start.ts +27 -11
  174. package/src/mcp/tools/agent/build.ts +7 -27
  175. package/src/mcp/tools/agent/index.ts +4 -0
  176. package/src/mcp/tools/agent/proof.ts +3 -8
  177. package/src/mcp/tools/agent/propose.ts +13 -2
  178. package/src/mcp/tools/agent/publish.ts +218 -92
  179. package/src/mcp/tools/agent/replay.ts +0 -5
  180. package/src/mcp/tools/{credentials → authenticate}/authenticate.ts +9 -8
  181. package/src/mcp/tools/authenticate/build.ts +32 -0
  182. package/src/mcp/tools/authenticate/decrypt-key.ts +59 -0
  183. package/src/mcp/tools/authenticate/eth-key.ts +162 -0
  184. package/src/mcp/tools/authenticate/import.ts +73 -0
  185. package/src/mcp/tools/authenticate/index.ts +4 -0
  186. package/src/mcp/tools/authenticate/issue.ts +50 -0
  187. package/src/mcp/tools/authenticate/resolve-owner-key.ts +60 -0
  188. package/src/mcp/tools/index.ts +6 -0
  189. package/src/mcp/tools/verification/build.ts +16 -0
  190. package/src/mcp/tools/verification/index.ts +2 -0
  191. package/src/mcp/tools/verification/result.ts +154 -0
  192. package/src/old/client.ts +3 -0
  193. package/src/old/flag.ts +4 -3
  194. package/src/old/identity.ts +8 -7
  195. package/src/old/instructions.ts +10 -25
  196. package/src/old/login-cdp.ts +2 -5
  197. package/src/old/to-register.ts +58 -26
  198. package/src/old/tools/authenticate.ts +2 -1
  199. package/src/old/tools/publish.ts +92 -16
  200. package/src/paths.ts +2 -2
  201. package/src/proof/attestor.ts +114 -72
  202. package/src/proof/index.ts +21 -0
  203. package/src/proof/proof-core.ts +119 -0
  204. package/src/provider/index.ts +20 -0
  205. package/src/provider/injection-doc.ts +21 -0
  206. package/src/provider/match-redaction-warning.ts +53 -0
  207. package/src/provider/schema.ts +23 -0
  208. package/src/provider/to-version.ts +180 -33
  209. package/src/provider/verification-checks.ts +36 -0
  210. package/lib/mcp/tools/credentials/build.d.ts +0 -16
  211. package/lib/mcp/tools/credentials/build.js +0 -33
  212. package/lib/mcp/tools/credentials/status.js +0 -62
  213. package/src/mcp/tools/credentials/build.ts +0 -37
  214. package/src/mcp/tools/credentials/cache.ts +0 -161
  215. package/src/mcp/tools/credentials/env-file.ts +0 -101
  216. package/src/mcp/tools/credentials/import.ts +0 -72
  217. package/src/mcp/tools/credentials/issue.ts +0 -107
  218. package/src/mcp/tools/credentials/list-cached.ts +0 -66
  219. package/src/mcp/tools/credentials/register.ts +0 -49
  220. package/src/mcp/tools/credentials/resolve-owner-key.ts +0 -78
  221. package/src/mcp/tools/credentials/status.ts +0 -82
  222. package/src/types/openapi.ts +0 -7
  223. /package/lib/mcp/{tools/credentials → credentials}/env-file.d.ts +0 -0
  224. /package/lib/mcp/{tools/credentials → credentials}/env-file.js +0 -0
  225. /package/lib/mcp/{tools/credentials → credentials}/register.d.ts +0 -0
  226. /package/lib/mcp/{tools/credentials → credentials}/register.js +0 -0
  227. /package/lib/mcp/tools/{credentials → authenticate}/authenticate.d.ts +0 -0
  228. /package/lib/mcp/tools/{credentials → authenticate}/import.d.ts +0 -0
  229. /package/lib/mcp/tools/{credentials → authenticate}/issue.d.ts +0 -0
  230. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.d.ts +0 -0
  231. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.js +0 -0
  232. /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";
@@ -2,6 +2,7 @@ import { spawn } from 'node:child_process';
2
2
  import { existsSync, mkdirSync } from 'node:fs';
3
3
  import { homedir, platform } from 'node:os';
4
4
  import { join } from 'node:path';
5
+ import { chromePath, localAppData } from "../consts.js";
5
6
  import { reclaimHomeDirPath } from "../paths.js";
6
7
  /**
7
8
  * Build the platform-specific list of Chromium-family binaries to probe.
@@ -24,13 +25,13 @@ function browserCandidates() {
24
25
  '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
25
26
  ];
26
27
  case 'win32': {
27
- const localAppData = process.env['LOCALAPPDATA'] ?? join(homedir(), 'AppData', 'Local');
28
+ const appData = localAppData() ?? join(homedir(), 'AppData', 'Local');
28
29
  return [
29
30
  // System-wide Chrome
30
31
  'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
31
32
  'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
32
33
  // Per-user Chrome ("Install for me only")
33
- join(localAppData, 'Google', 'Chrome', 'Application', 'chrome.exe'),
34
+ join(appData, 'Google', 'Chrome', 'Application', 'chrome.exe'),
34
35
  // Edge — Chromium-based, preinstalled on Win 10/11
35
36
  'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
36
37
  'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
@@ -65,7 +66,7 @@ export function findChromeBinary() {
65
66
  'your chrome.exe / msedge.exe / chromium binary.');
66
67
  }
67
68
  export function launchChrome(opts) {
68
- const binary = process.env.RECLAIM_AGENT_CHROME_PATH ?? findChromeBinary();
69
+ const binary = chromePath() ?? findChromeBinary();
69
70
  if (!existsSync(opts.profileDir)) {
70
71
  mkdirSync(opts.profileDir, { recursive: true });
71
72
  }
@@ -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.
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Centralized environment access for `@reclaimprotocol/agent`.
3
+ *
4
+ * This is the ONLY non-test source file under `src/` that reads
5
+ * `process.env`. Every other module imports a getter from here instead of
6
+ * touching `process.env` directly.
7
+ *
8
+ * Almost everything is exposed as a LAZY GETTER FUNCTION rather than an eager
9
+ * `const`, on purpose: several tests mutate env at runtime (mcp-credentials
10
+ * sets/deletes RECLAIM_PRIVATE_KEY / RECLAIM_PRIVATE_KEY_FILE / RECLAIM_HOME;
11
+ * old-devtools sets/deletes USE_OLD_DEVTOOLS), and most of these vars are read
12
+ * at call time anyway. An eager const captured at import time would freeze the
13
+ * value and break those tests. The one eager const that's safe is `LOG_LEVEL`,
14
+ * which is read once at logger construction.
15
+ *
16
+ * Each var's default/fallback literal is co-located with its getter.
17
+ */
18
+ import { DEFAULT_BASE_URL } from '@reclaimprotocol/client';
19
+ import { DEFAULT_OLD_API_URL } from './old/client.ts';
20
+ /** Public attestor websocket URL — fallback for `RECLAIM_ATTESTOR_URL`. */
21
+ export declare const FALLBACK_ATTESTOR_URL = "wss://attestor.reclaimprotocol.org:444/ws";
22
+ /** The devtools dashboard login page — fallback for `RECLAIM_OLD_LOGIN_URL`. */
23
+ export declare const DEFAULT_LOGIN_URL = "https://dev.reclaimprotocol.org";
24
+ export { DEFAULT_BASE_URL, DEFAULT_OLD_API_URL };
25
+ /** Pino log level. Read once at logger construction, so an eager const is
26
+ * safe (no test mutates LOG_LEVEL at runtime). */
27
+ export declare const LOG_LEVEL: string;
28
+ /** Reclaim cache directory (`~/.reclaim`). `RECLAIM_HOME` overrides it so
29
+ * tests can sandbox writes. */
30
+ export declare function reclaimHome(): string;
31
+ /**
32
+ * `USE_OLD_DEVTOOLS` — single source of truth for which backend the MCP
33
+ * server targets. Defaults to OLD devtools while the builder backend is under
34
+ * development. Builder mode must be opted into EXPLICITLY with
35
+ * `USE_OLD_DEVTOOLS=false`; anything else keeps old mode on.
36
+ */
37
+ export declare function useOldDevtools(): boolean;
38
+ /** `%LOCALAPPDATA%` on Windows, used to probe a per-user Chrome install. */
39
+ export declare function localAppData(): string | undefined;
40
+ /** Explicit Chrome/Edge/Chromium binary path override. */
41
+ export declare function chromePath(): string | undefined;
42
+ /** Agent tools (Chrome CDP capture, synthesis, proof) are disabled in hosted
43
+ * deployments by setting `RECLAIM_AGENT_DISABLED=1`. Shared by mcp/start and
44
+ * the old-mode authenticate tool. */
45
+ export declare function agentDisabled(): boolean;
46
+ /** Old-devtools backend base URL. */
47
+ export declare function oldApiUrl(): string;
48
+ /** Builder backend base URL. Shared by mcp/start and the verification tool. */
49
+ export declare function apiUrl(): string;
50
+ /** Explicit builder API token (overrides the cached session token). */
51
+ export declare function apiToken(): string | undefined;
52
+ /** Diagnostic trace capture (`RECLAIM_AGENT_TRACE=1`). Shared by the attestor
53
+ * logger capture and the proof-run dump. */
54
+ export declare function agentTraceEnabled(): boolean;
55
+ /** Attestor websocket URL override (`RECLAIM_ATTESTOR_URL`). */
56
+ export declare function attestorUrlOverride(): string | undefined;
57
+ /** Raw 0x-hex Ethereum private key (`RECLAIM_PRIVATE_KEY`). */
58
+ export declare function privateKey(): string | undefined;
59
+ /** Path to a raw-hex key file (`RECLAIM_PRIVATE_KEY_FILE`). */
60
+ export declare function privateKeyFile(): string | undefined;
61
+ /** Per-org bearer secret (`rorg_…`) used to authenticate verification reads. */
62
+ export declare function orgSecret(): string | undefined;
63
+ /** Dashboard Firebase bearer token for the old backend. */
64
+ export declare function oldApiToken(): string | undefined;
65
+ /** An `eth:<addr>` (or bare `0x…`) uid for the old backend. */
66
+ export declare function oldEthUid(): string | undefined;
67
+ /** Override for the old-devtools dashboard login URL. */
68
+ export declare function oldLoginUrl(): string | undefined;