@reclaimprotocol/agent 0.1.36 → 0.1.38
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 +8 -8
- package/lib/authoring/backend.d.ts +12 -1
- package/lib/authoring/backend.js +3 -0
- package/lib/authoring/tools.js +33 -5
- package/lib/authoring/util.d.ts +43 -0
- package/lib/authoring/util.js +54 -0
- package/lib/cdp/chrome-for-testing.d.ts +14 -0
- package/lib/cdp/chrome-for-testing.js +43 -0
- package/lib/cdp/geo.d.ts +3 -0
- package/lib/cdp/geo.js +27 -15
- package/lib/cdp/launcher.d.ts +79 -1
- package/lib/cdp/launcher.js +244 -24
- package/lib/logger.js +3 -1
- package/lib/mcp/instructions.js +3 -3
- package/lib/mcp/start.js +17 -2
- package/lib/mcp/tools/agent/attach.d.ts +6 -0
- package/lib/mcp/tools/agent/attach.js +58 -118
- package/lib/mcp/tools/agent/build.js +8 -0
- package/lib/mcp/tools/agent/capture.js +33 -10
- package/lib/mcp/tools/agent/check-attestor.js +15 -7
- package/lib/mcp/tools/agent/draft-tools.d.ts +3 -0
- package/lib/mcp/tools/agent/draft-tools.js +170 -0
- package/lib/mcp/tools/agent/example-app.d.ts +2 -0
- package/lib/mcp/tools/agent/example-app.js +167 -0
- package/lib/mcp/tools/agent/pod-proof.js +5 -1
- package/lib/mcp/tools/agent/proof.d.ts +28 -0
- package/lib/mcp/tools/agent/proof.js +72 -0
- package/lib/mcp/tools/agent/shared.d.ts +8 -1
- package/lib/mcp/tools/agent/shared.js +98 -19
- package/lib/mcp/tools/agent/start-integration.d.ts +2 -1
- package/lib/mcp/tools/agent/start-integration.js +10 -6
- package/lib/mcp/tools/authenticate/authenticate.d.ts +48 -10
- package/lib/mcp/tools/authenticate/authenticate.js +212 -116
- package/lib/mcp/tools/authenticate/build.d.ts +2 -2
- package/lib/mcp/tools/authenticate/build.js +6 -3
- package/lib/mcp/tools/authenticate/builder-access.js +5 -2
- package/lib/mcp/tools/authenticate/eth-key.d.ts +13 -2
- package/lib/mcp/tools/authenticate/eth-key.js +29 -5
- package/lib/mcp/tools/authenticate/index.d.ts +1 -1
- package/lib/mcp/tools/authenticate/index.js +1 -1
- package/lib/mcp/tools/authenticate/issue-org-keypair.d.ts +10 -0
- package/lib/mcp/tools/authenticate/issue-org-keypair.js +82 -0
- package/lib/mcp/tools/authenticate/oauth-config.d.ts +31 -0
- package/lib/mcp/tools/authenticate/oauth-config.js +67 -0
- package/lib/mcp/tools/authenticate/resolve-owner-key.d.ts +4 -5
- package/lib/mcp/tools/authenticate/resolve-owner-key.js +40 -36
- package/lib/mcp/tools/authenticate/session-cache.d.ts +25 -1
- package/lib/mcp/tools/authenticate/session-cache.js +95 -4
- package/lib/mcp/tools/mode/mode.js +6 -6
- package/lib/old/login-cdp.js +2 -3
- package/lib/proof/attestor.js +2 -3
- package/lib/proof/check-attestor.d.ts +12 -2
- package/lib/proof/check-attestor.js +40 -10
- package/lib/proof/check-matchers.d.ts +36 -0
- package/lib/proof/check-matchers.js +161 -0
- package/lib/proof/zk-files.d.ts +50 -11
- package/lib/proof/zk-files.js +259 -63
- package/lib/provider/draft.d.ts +24 -1
- package/lib/provider/draft.js +135 -0
- package/lib/provider/matcher.js +171 -4
- package/package.json +6 -4
- package/skill/SKILL.builder.md +7 -2
- package/skill/SKILL.common.md +90 -70
- package/skill/SKILL.old.md +3 -3
- package/skill/builder-mode.md +35 -5
- package/skill/old-mode.md +18 -13
- package/skill/start-integration.md +50 -33
- package/skill/verification-client-integration.md +21 -6
- package/src/authoring/backend.ts +12 -1
- package/src/authoring/tools.ts +48 -7
- package/src/authoring/util.ts +64 -0
- package/src/cdp/chrome-for-testing.ts +66 -0
- package/src/cdp/geo.ts +36 -21
- package/src/cdp/launcher.ts +328 -24
- package/src/logger.ts +3 -1
- package/src/mcp/instructions.ts +3 -3
- package/src/mcp/start.ts +20 -1
- package/src/mcp/tools/agent/attach.ts +65 -128
- package/src/mcp/tools/agent/build.ts +9 -0
- package/src/mcp/tools/agent/capture.ts +54 -11
- package/src/mcp/tools/agent/check-attestor.ts +15 -7
- package/src/mcp/tools/agent/draft-tools.ts +234 -0
- package/src/mcp/tools/agent/example-app.ts +227 -0
- package/src/mcp/tools/agent/pod-proof.ts +5 -1
- package/src/mcp/tools/agent/proof.ts +110 -1
- package/src/mcp/tools/agent/shared.ts +130 -19
- package/src/mcp/tools/agent/start-integration.ts +10 -6
- package/src/mcp/tools/authenticate/authenticate.ts +276 -131
- package/src/mcp/tools/authenticate/build.ts +6 -3
- package/src/mcp/tools/authenticate/builder-access.ts +5 -2
- package/src/mcp/tools/authenticate/eth-key.ts +41 -7
- package/src/mcp/tools/authenticate/index.ts +1 -1
- package/src/mcp/tools/authenticate/issue-org-keypair.ts +106 -0
- package/src/mcp/tools/authenticate/oauth-config.ts +85 -0
- package/src/mcp/tools/authenticate/resolve-owner-key.ts +42 -39
- package/src/mcp/tools/authenticate/session-cache.ts +125 -5
- package/src/mcp/tools/mode/mode.ts +6 -6
- package/src/old/login-cdp.ts +2 -3
- package/src/proof/attestor.ts +2 -3
- package/src/proof/check-attestor.ts +55 -12
- package/src/proof/check-matchers.ts +227 -0
- package/src/proof/zk-files.ts +357 -65
- package/src/provider/draft.ts +215 -7
- package/src/provider/matcher.ts +237 -4
|
@@ -14,11 +14,21 @@ import {
|
|
|
14
14
|
extendTool,
|
|
15
15
|
type KeptScriptRef,
|
|
16
16
|
} from '../../../authoring/index.ts'
|
|
17
|
+
import { lookupIpCountry } from '../../../cdp/geo.ts'
|
|
17
18
|
import { runAttestorProof } from '../../../proof/attestor.ts'
|
|
18
19
|
import { checkAttestor } from '../../../proof/check-attestor.ts'
|
|
19
20
|
import { replayProviderInPage } from '../../../proof/page-replay.ts'
|
|
20
|
-
import
|
|
21
|
+
import {
|
|
22
|
+
DYNAMIC_GEO,
|
|
23
|
+
normalizeGeoLocation,
|
|
24
|
+
type OprfMode,
|
|
25
|
+
type ReclaimProvider,
|
|
26
|
+
} from '../../../provider/schema.ts'
|
|
21
27
|
import type { RegisteredTool } from '../../server.ts'
|
|
28
|
+
import {
|
|
29
|
+
GENERATED_KEY_NOTE,
|
|
30
|
+
resolveOrCreateEthKey,
|
|
31
|
+
} from '../authenticate/eth-key.ts'
|
|
22
32
|
import type { CaptureEntry, CaptureStore } from './capture.ts'
|
|
23
33
|
import { diagnoseNetworkFailure, shapeRichProofResult } from './proof.ts'
|
|
24
34
|
|
|
@@ -53,6 +63,42 @@ function withLocalOprfMode(provider: ReclaimProvider): ReclaimProvider {
|
|
|
53
63
|
}
|
|
54
64
|
}
|
|
55
65
|
|
|
66
|
+
/** Templates a verification client replaces with the end user's country. */
|
|
67
|
+
const DYNAMIC_GEO_TEMPLATES = new Set([DYNAMIC_GEO, '{{DYNAMIC_GEO_SECRET}}'])
|
|
68
|
+
|
|
69
|
+
export interface ProofEgress {
|
|
70
|
+
/** ISO country (or param template) the attestor's request goes out from;
|
|
71
|
+
* absent when it goes out from the attestor's own network. */
|
|
72
|
+
geoLocation?: string
|
|
73
|
+
source: 'argument' | 'draft' | 'developer-ip' | 'none'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Where a local proof's request goes out from, resolved the way a
|
|
78
|
+
* verification client does it: the `geoLocation` argument, else the draft's
|
|
79
|
+
* fixed country, else (unset or `{{DYNAMIC_GEO}}`) the developer's own IP
|
|
80
|
+
* country, which is what `{{DYNAMIC_GEO}}` becomes when they verify.
|
|
81
|
+
*/
|
|
82
|
+
async function resolveProofEgress(
|
|
83
|
+
provider: ReclaimProvider,
|
|
84
|
+
argument: string | undefined,
|
|
85
|
+
lookupCountry: () => Promise<string | undefined>,
|
|
86
|
+
): Promise<ProofEgress> {
|
|
87
|
+
if(argument) {
|
|
88
|
+
return { geoLocation: argument.toUpperCase(), source: 'argument' }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const fixed = normalizeGeoLocation(provider.geoLocation)
|
|
92
|
+
if(fixed && !DYNAMIC_GEO_TEMPLATES.has(fixed)) {
|
|
93
|
+
return { geoLocation: fixed, source: 'draft' }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const country = await lookupCountry()
|
|
97
|
+
return country
|
|
98
|
+
? { geoLocation: country, source: 'developer-ip' }
|
|
99
|
+
: { source: 'none' }
|
|
100
|
+
}
|
|
101
|
+
|
|
56
102
|
/** The local backend for one capture: drive its tab over CDP, read its
|
|
57
103
|
* captured requests, prove via the in-process attestor, replay by re-fetching
|
|
58
104
|
* FROM INSIDE the tab (real cookies/session — see proof/page-replay.ts). */
|
|
@@ -60,6 +106,8 @@ function localBackend(
|
|
|
60
106
|
entry: CaptureEntry,
|
|
61
107
|
kept: KeptScriptRef,
|
|
62
108
|
check: typeof checkAttestor,
|
|
109
|
+
runProof: typeof runAttestorProof,
|
|
110
|
+
lookupCountry: () => Promise<string | undefined>,
|
|
63
111
|
): AuthoringBackend {
|
|
64
112
|
const { tabClient, session, drafts } = entry
|
|
65
113
|
const sendCdp = (method: string, params?: Record<string, unknown>) => {
|
|
@@ -96,6 +144,16 @@ function localBackend(
|
|
|
96
144
|
const result = await replayProviderInPage(
|
|
97
145
|
{ sendCdp }, draft.provider, draft.secrets, opts,
|
|
98
146
|
)
|
|
147
|
+
// Remember the authenticated replay so a failed proof can tell "the
|
|
148
|
+
// site blocks non-browser clients" from a broken recipe.
|
|
149
|
+
if(!opts?.withoutSecrets) {
|
|
150
|
+
draft.lastReplay = {
|
|
151
|
+
status: result.status,
|
|
152
|
+
ok: result.status >= 200 && result.status < 300
|
|
153
|
+
&& result.matchesOriginal,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
99
157
|
return {
|
|
100
158
|
status: result.status,
|
|
101
159
|
matchesOriginal: result.matchesOriginal,
|
|
@@ -104,13 +162,29 @@ function localBackend(
|
|
|
104
162
|
}
|
|
105
163
|
},
|
|
106
164
|
prove: async(draft, opts) => {
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
const
|
|
110
|
-
|
|
165
|
+
// No ownerAddress: resolve the configured key, or create a local test
|
|
166
|
+
// key, so a proof never stops to ask the developer for one.
|
|
167
|
+
const ownerKey = opts?.ownerAddress
|
|
168
|
+
? undefined
|
|
169
|
+
: resolveOrCreateEthKey()
|
|
170
|
+
const ownerAddress = opts?.ownerAddress ?? ownerKey?.address
|
|
171
|
+
const egress = await resolveProofEgress(
|
|
172
|
+
draft.provider, opts?.geoLocation, lookupCountry,
|
|
173
|
+
)
|
|
174
|
+
// Prove with the locally-runnable OPRF mode and the resolved egress;
|
|
175
|
+
// the draft (→ published recipe) keeps oprf-mpc and its template.
|
|
176
|
+
const { geoLocation: _template, ...provider } = withLocalOprfMode(
|
|
177
|
+
draft.provider,
|
|
178
|
+
)
|
|
179
|
+
void _template
|
|
180
|
+
const result = await runProof(
|
|
181
|
+
{
|
|
182
|
+
...provider,
|
|
183
|
+
...(egress.geoLocation ? { geoLocation: egress.geoLocation } : {}),
|
|
184
|
+
},
|
|
111
185
|
draft.secrets,
|
|
112
186
|
{
|
|
113
|
-
...(
|
|
187
|
+
...(ownerAddress ? { ownerAddress } : {}),
|
|
114
188
|
...(opts?.attestorUrl ? { attestorUrl: opts.attestorUrl } : {}),
|
|
115
189
|
},
|
|
116
190
|
)
|
|
@@ -118,11 +192,27 @@ function localBackend(
|
|
|
118
192
|
provider: draft.provider,
|
|
119
193
|
hasSecrets: Object.keys(draft.secrets).length > 0,
|
|
120
194
|
...(opts?.attestorUrl ? { attestorUrl: opts.attestorUrl } : {}),
|
|
195
|
+
...(draft.lastReplay ? { lastReplay: draft.lastReplay } : {}),
|
|
196
|
+
egress,
|
|
121
197
|
})
|
|
122
198
|
const diagnosis = await diagnoseNetworkFailure(
|
|
123
199
|
result, check, opts?.attestorUrl,
|
|
124
200
|
)
|
|
125
|
-
return
|
|
201
|
+
return {
|
|
202
|
+
...shaped,
|
|
203
|
+
egress,
|
|
204
|
+
...(diagnosis ? { diagnosis } : {}),
|
|
205
|
+
...(ownerKey?.source === 'generated'
|
|
206
|
+
? {
|
|
207
|
+
ownerKey: {
|
|
208
|
+
address: ownerKey.address,
|
|
209
|
+
source: ownerKey.source,
|
|
210
|
+
path: ownerKey.path,
|
|
211
|
+
note: GENERATED_KEY_NOTE,
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
: {}),
|
|
215
|
+
}
|
|
126
216
|
},
|
|
127
217
|
}
|
|
128
218
|
}
|
|
@@ -134,36 +224,55 @@ const RUN_PROOF_EXTRAS = {
|
|
|
134
224
|
description:
|
|
135
225
|
'Proves the drafted recipe with a full zkTLS proof through the attestor '
|
|
136
226
|
+ 'SDK. Call it after `replay_request` looks right; attestor calls are '
|
|
137
|
-
+ 'expensive.
|
|
138
|
-
+ '
|
|
227
|
+
+ 'expensive. It signs with the configured proof-owner key and creates '
|
|
228
|
+
+ 'a local test key in the project `.env` when none exists; then the '
|
|
229
|
+
+ 'result has `ownerKey` with a `note`: tell the developer once, after '
|
|
230
|
+
+ 'the fact. Do not ask for a key first. Writes the full claim to '
|
|
139
231
|
+ '`~/.reclaim/claims/<timestamp>.json`. Returns `identifier`, '
|
|
140
232
|
+ '`extractedParameters`, `claimPath`, and a readable `claim` (request '
|
|
141
233
|
+ 'removed, byte arrays as base64). Set `RECLAIM_AGENT_TRACE=1` to also '
|
|
142
234
|
+ 'get `runLogPath`, a diagnostic dump with the attestor log stream. '
|
|
143
235
|
+ 'Set `attestorUrl` only if the developer supplied one; never invent '
|
|
144
|
-
+ 'backup URLs. If `errorKind` is `missing-owner-key`, the
|
|
145
|
-
+ 'no known private key:
|
|
146
|
-
+ '
|
|
236
|
+
+ 'backup URLs. If `errorKind` is `missing-owner-key`, the '
|
|
237
|
+
+ '`ownerAddress` you passed has no known private key: retry without '
|
|
238
|
+
+ '`ownerAddress`. Unlike `replay_request`, which re-sends from '
|
|
147
239
|
+ 'inside the attached browser tab, the attestor calls the target from '
|
|
148
240
|
+ 'its own network and IP. A request that replayed can still fail here '
|
|
149
241
|
+ 'for geo- or IP-bound content, such as country-locked pages or '
|
|
150
242
|
+ 'IP-allowlisted APIs. That is a property of the site, not a draft '
|
|
151
|
-
+ 'bug.
|
|
243
|
+
+ 'bug. The request goes out from `egress.geoLocation` in the result: '
|
|
244
|
+
+ 'the `geoLocation` argument, else the draft\'s fixed country, else '
|
|
245
|
+
+ 'your own IP country. After a network-like failure ("connection closed", '
|
|
152
246
|
+ '`ERROR_NETWORK_ERROR`, timeout), the result includes `diagnosis` '
|
|
153
247
|
+ 'with `attestor` (a `check_attestor` result), `likelyCause`, and '
|
|
154
|
-
+ '`next`. Follow `next`; do not retry the same proof.'
|
|
248
|
+
+ '`next`. Follow `next`; do not retry the same proof. When the '
|
|
249
|
+
+ 'attestor gets HTTP 403/406/429/503 or a challenge page, `diagnosis` '
|
|
250
|
+
+ 'has `kind: "blocked-non-browser-client"`: the site blocks '
|
|
251
|
+
+ 'non-browser clients or traffic from that country, and copying '
|
|
252
|
+
+ 'headers or cookies will not help. Follow its `next`. Run '
|
|
253
|
+
+ '`check_matchers` first to rule out the recipe offline.',
|
|
155
254
|
properties: {
|
|
156
255
|
ownerAddress: {
|
|
157
256
|
type: 'string',
|
|
158
257
|
pattern: '^0x[0-9a-fA-F]{40}$',
|
|
159
258
|
description:
|
|
160
|
-
'ETH address
|
|
161
|
-
+ '
|
|
162
|
-
+ 'locally (
|
|
259
|
+
'Optional ETH address the resolved signing key must match. Omit '
|
|
260
|
+
+ 'it: run_proof resolves (or creates) the key itself. The '
|
|
261
|
+
+ 'private key is resolved locally (env / .env / key file), '
|
|
262
|
+
+ 'never passed here.',
|
|
163
263
|
},
|
|
164
264
|
attestorUrl: { type: 'string' },
|
|
265
|
+
geoLocation: {
|
|
266
|
+
type: 'string',
|
|
267
|
+
pattern: '^[A-Za-z]{2}$',
|
|
268
|
+
description:
|
|
269
|
+
'ISO country the attestor\'s request goes out from, for example '
|
|
270
|
+
+ '`US`. Omit it to use the draft\'s fixed country, or else your '
|
|
271
|
+
+ 'own IP country, as `{{DYNAMIC_GEO}}` resolves for you when you '
|
|
272
|
+
+ 'verify.',
|
|
273
|
+
},
|
|
165
274
|
},
|
|
166
|
-
required: [
|
|
275
|
+
required: [],
|
|
167
276
|
} satisfies {
|
|
168
277
|
description: string
|
|
169
278
|
properties: JSONSchema.ObjectSchema['properties']
|
|
@@ -178,6 +287,8 @@ const RUN_PROOF_EXTRAS = {
|
|
|
178
287
|
export function sharedAuthoringTools(
|
|
179
288
|
captures: CaptureStore,
|
|
180
289
|
check: typeof checkAttestor = checkAttestor,
|
|
290
|
+
runProof: typeof runAttestorProof = runAttestorProof,
|
|
291
|
+
lookupCountry: () => Promise<string | undefined> = lookupIpCountry,
|
|
181
292
|
): RegisteredTool[] {
|
|
182
293
|
const resolveBackend = (args: Record<string, unknown>): AuthoringBackend => {
|
|
183
294
|
const captureId = String(args.captureId)
|
|
@@ -189,7 +300,7 @@ export function sharedAuthoringTools(
|
|
|
189
300
|
)
|
|
190
301
|
}
|
|
191
302
|
|
|
192
|
-
return localBackend(entry, captures, check)
|
|
303
|
+
return localBackend(entry, captures, check, runProof, lookupCountry)
|
|
193
304
|
}
|
|
194
305
|
|
|
195
306
|
return authoringTools(resolveBackend).map((tool) => {
|
|
@@ -10,7 +10,8 @@ interface StartIntegrationArgs {
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Returns the plan a coding agent follows to add Reclaim verification to the
|
|
13
|
-
* developer's app, from sign-in
|
|
13
|
+
* developer's app, from sign-in and a working proof to a tested Verify
|
|
14
|
+
* button. The plan lives in
|
|
14
15
|
* `skill/start-integration.md`; this tool fills in the inputs.
|
|
15
16
|
*/
|
|
16
17
|
export function startIntegrationTool(): RegisteredTool {
|
|
@@ -19,11 +20,14 @@ export function startIntegrationTool(): RegisteredTool {
|
|
|
19
20
|
name: 'start_integration',
|
|
20
21
|
description:
|
|
21
22
|
'Returns a step-by-step plan for adding Reclaim verification to the '
|
|
22
|
-
+ 'current codebase: sign in,
|
|
23
|
-
+ '
|
|
24
|
-
+ '
|
|
25
|
-
+ '
|
|
26
|
-
+ '
|
|
23
|
+
+ 'current codebase: sign in to Builder, attach the browser, capture, '
|
|
24
|
+
+ 'and prove the provider first, then set up the app (session route, '
|
|
25
|
+
+ 'callback, Verify button, local event proxy) after the proof or '
|
|
26
|
+
+ 'while waiting on the developer, bootstrapping from the '
|
|
27
|
+
+ 'builder-example repository when there is no app yet, then wire '
|
|
28
|
+
+ 'the provider and test. '
|
|
29
|
+
+ 'Call it first when a developer wants their users to prove '
|
|
30
|
+
+ 'something, and follow the plan to the end. Changes nothing itself.',
|
|
27
31
|
inputSchema: {
|
|
28
32
|
type: 'object',
|
|
29
33
|
required: ['goal'],
|