@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.
- package/README.md +1 -1
- package/lib/cdp/capture-engine.d.ts +66 -0
- package/lib/cdp/capture-engine.js +153 -0
- package/lib/cdp/capture.d.ts +9 -20
- package/lib/cdp/capture.js +14 -138
- package/lib/cdp/index.d.ts +3 -0
- package/lib/cdp/index.js +6 -0
- package/lib/config/store.d.ts +7 -6
- package/lib/config/store.js +2 -2
- package/lib/index.d.ts +2 -2
- package/lib/index.js +1 -1
- package/lib/mcp/api/operations.gen.d.ts +101 -45
- package/lib/mcp/api/operations.gen.js +996 -126
- package/lib/mcp/api/to-zod.d.ts +24 -0
- package/lib/mcp/api/to-zod.js +94 -0
- package/lib/mcp/api/tools.js +1 -1
- package/lib/mcp/credentials/authenticate.d.ts +3 -0
- package/lib/mcp/{tools/credentials → credentials}/authenticate.js +3 -3
- package/lib/mcp/credentials/build.d.ts +5 -0
- package/lib/mcp/credentials/build.js +18 -0
- package/lib/mcp/{tools/credentials → credentials}/cache.d.ts +9 -9
- package/lib/mcp/{tools/credentials → credentials}/cache.js +36 -19
- package/lib/mcp/credentials/import.d.ts +2 -0
- package/lib/mcp/{tools/credentials → credentials}/import.js +2 -2
- package/lib/mcp/credentials/issue.d.ts +2 -0
- package/lib/mcp/{tools/credentials → credentials}/issue.js +4 -4
- package/lib/mcp/credentials/keypair.d.ts +17 -0
- package/lib/mcp/credentials/keypair.js +48 -0
- package/lib/mcp/credentials/link.d.ts +3 -0
- package/lib/mcp/credentials/link.js +107 -0
- package/lib/mcp/{tools/credentials → credentials}/list-cached.d.ts +1 -1
- package/lib/mcp/{tools/credentials → credentials}/list-cached.js +5 -5
- package/lib/mcp/credentials/owner-key.d.ts +23 -0
- package/lib/mcp/credentials/owner-key.js +58 -0
- package/lib/mcp/credentials/paths.d.ts +12 -0
- package/lib/mcp/credentials/paths.js +35 -0
- package/lib/mcp/credentials/poll.d.ts +27 -0
- package/lib/mcp/credentials/poll.js +61 -0
- package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.d.ts +1 -1
- package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.js +2 -2
- package/lib/mcp/credentials/session-cache.d.ts +14 -0
- package/lib/mcp/credentials/session-cache.js +42 -0
- package/lib/mcp/{tools/credentials → credentials}/status.d.ts +1 -1
- package/lib/mcp/credentials/status.js +76 -0
- package/lib/mcp/instructions.d.ts +1 -0
- package/lib/mcp/instructions.js +11 -0
- package/lib/mcp/register.d.ts +38 -0
- package/lib/mcp/register.js +80 -0
- package/lib/mcp/server.d.ts +2 -2
- package/lib/mcp/server.js +3 -3
- package/lib/mcp/start.js +10 -4
- package/lib/mcp/tools/agent/build.js +2 -15
- package/lib/mcp/tools/agent/index.d.ts +2 -0
- package/lib/mcp/tools/agent/index.js +3 -0
- package/lib/mcp/tools/agent/proof.js +1 -6
- package/lib/mcp/tools/agent/propose.js +13 -2
- package/lib/mcp/tools/agent/publish.js +10 -2
- package/lib/mcp/tools/agent/replay.js +0 -5
- package/lib/mcp/tools/analyze.d.ts +3 -0
- package/lib/mcp/tools/analyze.js +26 -0
- package/lib/mcp/tools/attach.d.ts +11 -0
- package/lib/mcp/tools/attach.js +72 -0
- package/lib/mcp/tools/authenticate/authenticate.js +107 -0
- package/lib/mcp/tools/authenticate/build.d.ts +15 -0
- package/lib/mcp/tools/authenticate/build.js +28 -0
- package/lib/mcp/tools/authenticate/decrypt-key.d.ts +32 -0
- package/lib/mcp/tools/authenticate/decrypt-key.js +49 -0
- package/lib/mcp/tools/authenticate/eth-key.d.ts +38 -0
- package/lib/mcp/tools/authenticate/eth-key.js +130 -0
- package/lib/mcp/tools/authenticate/import.js +57 -0
- package/lib/mcp/tools/authenticate/index.d.ts +2 -0
- package/lib/mcp/tools/authenticate/index.js +4 -0
- package/lib/mcp/tools/authenticate/issue.js +42 -0
- package/lib/mcp/tools/authenticate/resolve-owner-key.d.ts +13 -0
- package/lib/mcp/tools/authenticate/resolve-owner-key.js +53 -0
- package/lib/mcp/tools/capture.d.ts +10 -0
- package/lib/mcp/tools/capture.js +142 -0
- package/lib/mcp/tools/eval.d.ts +5 -0
- package/lib/mcp/tools/eval.js +127 -0
- package/lib/mcp/tools/find.d.ts +3 -0
- package/lib/mcp/tools/find.js +60 -0
- package/lib/mcp/tools/index.d.ts +3 -0
- package/lib/mcp/tools/index.js +6 -0
- package/lib/mcp/tools/owner-key.d.ts +2 -0
- package/lib/mcp/tools/owner-key.js +63 -0
- package/lib/mcp/tools/proof.d.ts +3 -0
- package/lib/mcp/tools/proof.js +214 -0
- package/lib/mcp/tools/propose.d.ts +9 -0
- package/lib/mcp/tools/propose.js +75 -0
- package/lib/mcp/tools/publish.d.ts +3 -0
- package/lib/mcp/tools/publish.js +59 -0
- package/lib/mcp/tools/replay.d.ts +3 -0
- package/lib/mcp/tools/replay.js +46 -0
- package/lib/mcp/tools/session.d.ts +10 -0
- package/lib/mcp/tools/session.js +29 -0
- package/lib/mcp/tools/verification/build.d.ts +12 -0
- package/lib/mcp/tools/verification/build.js +14 -0
- package/lib/mcp/tools/verification/diagnose.d.ts +11 -0
- package/lib/mcp/tools/verification/diagnose.js +132 -0
- package/lib/mcp/tools/verification/index.d.ts +1 -0
- package/lib/mcp/tools/verification/index.js +2 -0
- package/lib/mcp/tools/verification/result.d.ts +12 -0
- package/lib/mcp/tools/verification/result.js +126 -0
- package/lib/mcp/verifications/build.d.ts +5 -0
- package/lib/mcp/verifications/build.js +14 -0
- package/lib/mcp/verifications/create.d.ts +3 -0
- package/lib/mcp/verifications/create.js +57 -0
- package/lib/mcp/verifications/find.d.ts +3 -0
- package/lib/mcp/verifications/find.js +37 -0
- package/lib/mcp/verifications/get.d.ts +3 -0
- package/lib/mcp/verifications/get.js +26 -0
- package/lib/mcp/verifications/update.d.ts +3 -0
- package/lib/mcp/verifications/update.js +78 -0
- package/lib/old/client.d.ts +3 -0
- package/lib/old/instructions.d.ts +0 -6
- package/lib/old/instructions.js +6 -24
- package/lib/old/to-register.d.ts +3 -0
- package/lib/old/to-register.js +35 -7
- package/lib/old/tools/publish.js +13 -1
- package/lib/proof/attestor.d.ts +33 -1
- package/lib/proof/attestor.js +67 -61
- package/lib/proof/index.d.ts +5 -0
- package/lib/proof/index.js +6 -0
- package/lib/proof/proof-core.d.ts +35 -0
- package/lib/proof/proof-core.js +73 -0
- package/lib/proof/zk-files.d.ts +14 -0
- package/lib/proof/zk-files.js +73 -0
- package/lib/provider/index.d.ts +5 -0
- package/lib/provider/index.js +9 -0
- package/lib/provider/to-version.d.ts +9 -12
- package/lib/provider/to-version.js +38 -4
- package/lib/provider/verification-checks.d.ts +12 -0
- package/lib/provider/verification-checks.js +30 -0
- package/lib/routes.gen.d.ts +271 -0
- package/lib/routes.gen.js +73 -0
- package/lib/types/openapi.gen.d.ts +3967 -0
- package/lib/types/openapi.gen.js +5 -0
- package/package.json +18 -10
- package/skill/SKILL.md +35 -14
- package/skill/builder-mode.md +9 -0
- package/skill/old-mode.md +13 -0
- package/src/cdp/capture-engine.ts +256 -0
- package/src/cdp/capture.ts +25 -185
- package/src/cdp/index.ts +6 -0
- package/src/config/store.ts +7 -6
- package/src/index.ts +2 -4
- package/src/mcp/api/operations.gen.ts +1023 -121
- package/src/mcp/api/to-zod.ts +115 -0
- package/src/mcp/api/tools.ts +9 -3
- package/src/mcp/instructions.ts +15 -0
- package/src/mcp/server.ts +5 -4
- package/src/mcp/start.ts +14 -4
- package/src/mcp/tools/agent/build.ts +2 -18
- package/src/mcp/tools/agent/index.ts +4 -0
- package/src/mcp/tools/agent/proof.ts +1 -7
- package/src/mcp/tools/agent/propose.ts +13 -2
- package/src/mcp/tools/agent/publish.ts +10 -2
- package/src/mcp/tools/agent/replay.ts +0 -5
- package/src/mcp/tools/{credentials → authenticate}/authenticate.ts +9 -8
- package/src/mcp/tools/authenticate/build.ts +32 -0
- package/src/mcp/tools/authenticate/decrypt-key.ts +64 -0
- package/src/mcp/tools/authenticate/eth-key.ts +182 -0
- package/src/mcp/tools/authenticate/import.ts +73 -0
- package/src/mcp/tools/authenticate/index.ts +4 -0
- package/src/mcp/tools/authenticate/issue.ts +50 -0
- package/src/mcp/tools/authenticate/resolve-owner-key.ts +60 -0
- package/src/mcp/tools/index.ts +6 -0
- package/src/mcp/tools/verification/build.ts +16 -0
- package/src/mcp/tools/verification/index.ts +2 -0
- package/src/mcp/tools/verification/result.ts +155 -0
- package/src/old/client.ts +3 -0
- package/src/old/instructions.ts +10 -25
- package/src/old/to-register.ts +62 -27
- package/src/old/tools/publish.ts +17 -1
- package/src/proof/attestor.ts +115 -69
- package/src/proof/index.ts +21 -0
- package/src/proof/proof-core.ts +109 -0
- package/src/proof/zk-files.ts +83 -0
- package/src/provider/index.ts +20 -0
- package/src/provider/to-version.ts +52 -11
- package/src/provider/verification-checks.ts +36 -0
- package/lib/mcp/tools/credentials/build.d.ts +0 -16
- package/lib/mcp/tools/credentials/build.js +0 -33
- package/lib/mcp/tools/credentials/status.js +0 -62
- package/src/mcp/tools/credentials/build.ts +0 -37
- package/src/mcp/tools/credentials/cache.ts +0 -161
- package/src/mcp/tools/credentials/env-file.ts +0 -101
- package/src/mcp/tools/credentials/import.ts +0 -72
- package/src/mcp/tools/credentials/issue.ts +0 -107
- package/src/mcp/tools/credentials/list-cached.ts +0 -66
- package/src/mcp/tools/credentials/register.ts +0 -49
- package/src/mcp/tools/credentials/resolve-owner-key.ts +0 -78
- package/src/mcp/tools/credentials/status.ts +0 -82
- package/src/types/openapi.ts +0 -7
- /package/lib/mcp/{tools/credentials → credentials}/env-file.d.ts +0 -0
- /package/lib/mcp/{tools/credentials → credentials}/env-file.js +0 -0
- /package/lib/mcp/{tools/credentials → credentials}/register.d.ts +0 -0
- /package/lib/mcp/{tools/credentials → credentials}/register.js +0 -0
- /package/lib/mcp/tools/{credentials → authenticate}/authenticate.d.ts +0 -0
- /package/lib/mcp/tools/{credentials → authenticate}/import.d.ts +0 -0
- /package/lib/mcp/tools/{credentials → authenticate}/issue.d.ts +0 -0
- /package/lib/mcp/tools/{credentials → authenticate}/session-cache.d.ts +0 -0
- /package/lib/mcp/tools/{credentials → authenticate}/session-cache.js +0 -0
- /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
|
|
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
|
+
}
|
package/lib/cdp/capture.d.ts
CHANGED
|
@@ -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
|
|
3
|
-
|
|
4
|
-
|
|
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>;
|
package/lib/cdp/capture.js
CHANGED
|
@@ -1,157 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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(
|
|
131
|
-
|
|
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
|
},
|
package/lib/cdp/index.js
ADDED
|
@@ -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";
|
package/lib/config/store.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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
|
|
29
|
-
* (
|
|
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.
|
package/lib/config/store.js
CHANGED
|
@@ -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
|
|
6
|
-
* (
|
|
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
|
|
3
|
-
export type { AgentState } from './mcp/tools/agent/
|
|
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
|
|
7
|
+
export { buildAgentTools } from "./mcp/tools/agent/index.js";
|