@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.
- 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/cdp/launcher.js +4 -3
- package/lib/config/store.d.ts +7 -6
- package/lib/config/store.js +2 -2
- package/lib/consts.d.ts +68 -0
- package/lib/consts.js +134 -0
- package/lib/index.d.ts +2 -2
- package/lib/index.js +1 -1
- package/lib/logger.js +2 -1
- package/lib/mcp/api/operations.gen.d.ts +100 -48
- package/lib/mcp/api/operations.gen.js +1129 -194
- 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 -8
- package/lib/mcp/server.js +3 -33
- package/lib/mcp/start.js +17 -11
- package/lib/mcp/tools/agent/build.d.ts +5 -9
- package/lib/mcp/tools/agent/build.js +7 -24
- 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 +3 -7
- package/lib/mcp/tools/agent/propose.js +13 -2
- package/lib/mcp/tools/agent/publish.js +172 -75
- 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 +19 -0
- package/lib/mcp/tools/authenticate/decrypt-key.js +46 -0
- package/lib/mcp/tools/authenticate/eth-key.d.ts +31 -0
- package/lib/mcp/tools/authenticate/eth-key.js +116 -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 +11 -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/flag.d.ts +4 -1
- package/lib/old/flag.js +4 -3
- package/lib/old/identity.d.ts +0 -10
- package/lib/old/identity.js +8 -7
- package/lib/old/instructions.d.ts +0 -6
- package/lib/old/instructions.js +6 -24
- package/lib/old/login-cdp.d.ts +0 -3
- package/lib/old/login-cdp.js +2 -4
- package/lib/old/to-register.d.ts +3 -0
- package/lib/old/to-register.js +32 -7
- package/lib/old/tools/authenticate.js +2 -1
- package/lib/old/tools/publish.js +71 -10
- package/lib/paths.js +2 -2
- package/lib/proof/attestor.d.ts +33 -1
- package/lib/proof/attestor.js +67 -64
- 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 +84 -0
- package/lib/provider/index.d.ts +5 -0
- package/lib/provider/index.js +9 -0
- package/lib/provider/injection-doc.d.ts +1 -0
- package/lib/provider/injection-doc.js +17 -0
- package/lib/provider/match-redaction-warning.d.ts +25 -0
- package/lib/provider/match-redaction-warning.js +35 -0
- package/lib/provider/schema.d.ts +16 -0
- package/lib/provider/schema.js +11 -1
- package/lib/provider/to-version.d.ts +61 -12
- package/lib/provider/to-version.js +109 -24
- 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 +16 -7
- package/skill/SKILL.md +35 -14
- package/skill/builder-mode.md +13 -0
- package/skill/injection-api.md +42 -0
- package/skill/old-mode.md +15 -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/cdp/launcher.ts +5 -4
- package/src/config/store.ts +7 -6
- package/src/consts.ts +164 -0
- package/src/index.ts +2 -4
- package/src/logger.ts +2 -1
- package/src/mcp/api/operations.gen.ts +1155 -194
- 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 -50
- package/src/mcp/start.ts +27 -11
- package/src/mcp/tools/agent/build.ts +7 -27
- package/src/mcp/tools/agent/index.ts +4 -0
- package/src/mcp/tools/agent/proof.ts +3 -8
- package/src/mcp/tools/agent/propose.ts +13 -2
- package/src/mcp/tools/agent/publish.ts +218 -92
- 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 +59 -0
- package/src/mcp/tools/authenticate/eth-key.ts +162 -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 +154 -0
- package/src/old/client.ts +3 -0
- package/src/old/flag.ts +4 -3
- package/src/old/identity.ts +8 -7
- package/src/old/instructions.ts +10 -25
- package/src/old/login-cdp.ts +2 -5
- package/src/old/to-register.ts +58 -26
- package/src/old/tools/authenticate.ts +2 -1
- package/src/old/tools/publish.ts +92 -16
- package/src/paths.ts +2 -2
- package/src/proof/attestor.ts +114 -72
- package/src/proof/index.ts +21 -0
- package/src/proof/proof-core.ts +119 -0
- package/src/provider/index.ts +20 -0
- package/src/provider/injection-doc.ts +21 -0
- package/src/provider/match-redaction-warning.ts +53 -0
- package/src/provider/schema.ts +23 -0
- package/src/provider/to-version.ts +180 -33
- 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/src/old/tools/publish.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { Boom, isBoom } from '@hapi/boom'
|
|
1
2
|
import { defineTool, type RegisteredTool } from '../../mcp/server.ts'
|
|
3
|
+
import {
|
|
4
|
+
countsFor,
|
|
5
|
+
matchRedactionWarnings,
|
|
6
|
+
} from '../../provider/match-redaction-warning.ts'
|
|
2
7
|
import type { ReclaimProvider } from '../../provider/schema.ts'
|
|
3
8
|
import type { ReclaimOldClient } from '../client.ts'
|
|
4
9
|
import {
|
|
@@ -11,6 +16,35 @@ import {
|
|
|
11
16
|
|
|
12
17
|
const DEFAULT_VERSION_INFO = 'Updated from MCP browser capture'
|
|
13
18
|
|
|
19
|
+
/**
|
|
20
|
+
* The old backend lets you add a version only to a provider YOU own. A
|
|
21
|
+
* `providerId` for a public provider (or anyone else's) comes back as
|
|
22
|
+
* 401/403/404 — the dev's likely intent is to publish their OWN version of that
|
|
23
|
+
* site, which on this backend means registering a separate copy. Rethrow those
|
|
24
|
+
* failures with that actionable suggestion; leave other errors (e.g. a 400
|
|
25
|
+
* version collision) untouched so their real detail surfaces.
|
|
26
|
+
*/
|
|
27
|
+
function rethrowOwnershipError(err: unknown, providerId: string): never {
|
|
28
|
+
const status = isBoom(err) ? err.output.statusCode : undefined
|
|
29
|
+
if(status !== 401 && status !== 403 && status !== 404) {
|
|
30
|
+
throw err
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const base = isBoom(err) ? err.message : String(err)
|
|
34
|
+
throw new Boom(
|
|
35
|
+
`${base} — could not add a version to provider ${providerId}. You can ` +
|
|
36
|
+
'only publish new versions on providers you own. If this is a public ' +
|
|
37
|
+
"provider (or one you don't own), re-run " +
|
|
38
|
+
'create_provider_version_from_capture WITHOUT `providerId` to register ' +
|
|
39
|
+
"your own copy. If it IS yours, confirm you're authenticated " +
|
|
40
|
+
'(reclaim_authenticate) as the account that created it.',
|
|
41
|
+
{
|
|
42
|
+
statusCode: status,
|
|
43
|
+
data: isBoom(err) ? err.data : { status, detail: base },
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
14
48
|
/**
|
|
15
49
|
* Publish a drafted provider to the OLD devtools backend. Keeps the builder
|
|
16
50
|
* tool NAME (`create_provider_version_from_capture`) so the skill flow is
|
|
@@ -33,6 +67,7 @@ export function createVersionFromCaptureTool(
|
|
|
33
67
|
providerId?: string
|
|
34
68
|
description?: string
|
|
35
69
|
geoLocation?: string
|
|
70
|
+
useProxy?: boolean
|
|
36
71
|
providerType?: 'PRIVATE' | 'PUBLIC'
|
|
37
72
|
notes?: string
|
|
38
73
|
}>(
|
|
@@ -47,6 +82,10 @@ export function createVersionFromCaptureTool(
|
|
|
47
82
|
'1.0.0; re-running creates a duplicate). Pass `providerId` (from ' +
|
|
48
83
|
'get_me_providers) to add a NEW VERSION to that existing provider ' +
|
|
49
84
|
'(the patch is auto-bumped). \n\n' +
|
|
85
|
+
'You can only add versions to a provider YOU own — the `providerId` ' +
|
|
86
|
+
'must come from get_me_providers. To publish your own version of a ' +
|
|
87
|
+
"PUBLIC provider you don't own, omit `providerId` to register your " +
|
|
88
|
+
'own copy (you cannot edit someone else\'s provider here). \n\n' +
|
|
50
89
|
'Captured requests must be GET/POST. OPRF redactions are supported.',
|
|
51
90
|
inputSchema: {
|
|
52
91
|
type: 'object',
|
|
@@ -83,7 +122,18 @@ export function createVersionFromCaptureTool(
|
|
|
83
122
|
type: 'string',
|
|
84
123
|
description: 'Provider description (new-provider register only).',
|
|
85
124
|
},
|
|
86
|
-
geoLocation: {
|
|
125
|
+
geoLocation: {
|
|
126
|
+
type: 'string',
|
|
127
|
+
description:
|
|
128
|
+
'Geo the verification appears to originate from. Defaults to ' +
|
|
129
|
+
"'{{DYNAMIC_GEO}}' (the user's geo) when omitted.",
|
|
130
|
+
},
|
|
131
|
+
useProxy: {
|
|
132
|
+
type: 'boolean',
|
|
133
|
+
description:
|
|
134
|
+
'Route portal/attestation traffic through a proxy. Defaults to ' +
|
|
135
|
+
'true when omitted. Applied on register only.',
|
|
136
|
+
},
|
|
87
137
|
providerType: { type: 'string', enum: ['PRIVATE', 'PUBLIC'] },
|
|
88
138
|
notes: {
|
|
89
139
|
type: 'string',
|
|
@@ -105,31 +155,57 @@ export function createVersionFromCaptureTool(
|
|
|
105
155
|
opts.geoLocation = args.geoLocation
|
|
106
156
|
}
|
|
107
157
|
|
|
158
|
+
if(args.useProxy !== undefined) {
|
|
159
|
+
opts.useProxy = args.useProxy
|
|
160
|
+
}
|
|
161
|
+
|
|
108
162
|
if(args.providerType !== undefined) {
|
|
109
163
|
opts.providerType = args.providerType
|
|
110
164
|
}
|
|
111
165
|
|
|
166
|
+
// Non-blocking advisory — never alters or blocks the publish; the
|
|
167
|
+
// author keeps full control. Attaches under `_notes` when relevant.
|
|
168
|
+
const notes = matchRedactionWarnings([
|
|
169
|
+
countsFor('Request', args.provider),
|
|
170
|
+
])
|
|
171
|
+
const withNotes = (result: unknown) => {
|
|
172
|
+
if(notes.length && result && typeof result === 'object') {
|
|
173
|
+
return { ...result, _notes: notes }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return result
|
|
177
|
+
}
|
|
178
|
+
|
|
112
179
|
// New provider.
|
|
113
180
|
if(!args.providerId) {
|
|
114
181
|
const body = providerToRegisterBody(args.provider, opts)
|
|
115
|
-
return client.registerProvider(body)
|
|
182
|
+
return withNotes(await client.registerProvider(body))
|
|
116
183
|
}
|
|
117
184
|
|
|
118
185
|
// New version of an existing provider — pick the next patch above
|
|
119
|
-
// the highest existing version.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
186
|
+
// the highest existing version. A provider you don't own (e.g. a
|
|
187
|
+
// public one) fails here with 401/403/404; surface the register-a-copy
|
|
188
|
+
// workaround instead of a bare permission error.
|
|
189
|
+
const providerId = args.providerId
|
|
190
|
+
try {
|
|
191
|
+
const versionsResp = await client.listVersions(providerId)
|
|
192
|
+
const version = bumpPatchString(extractVersions(versionsResp))
|
|
193
|
+
const versionInfo =
|
|
194
|
+
args.notes && args.notes.trim().length >= 10
|
|
195
|
+
? args.notes.trim()
|
|
196
|
+
: DEFAULT_VERSION_INFO
|
|
197
|
+
const body = providerToConfigBody(
|
|
198
|
+
args.provider,
|
|
199
|
+
opts,
|
|
200
|
+
version,
|
|
201
|
+
versionInfo,
|
|
202
|
+
)
|
|
203
|
+
return withNotes(
|
|
204
|
+
await client.updateProviderConfig(providerId, body),
|
|
205
|
+
)
|
|
206
|
+
} catch(err) {
|
|
207
|
+
rethrowOwnershipError(err, providerId)
|
|
208
|
+
}
|
|
133
209
|
},
|
|
134
210
|
)
|
|
135
211
|
}
|
package/src/paths.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
|
-
import { homedir } from 'node:os'
|
|
3
2
|
import { dirname, join, resolve } from 'node:path'
|
|
3
|
+
import { reclaimHome } from './consts.ts'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Resolve the Reclaim cache directory (`~/.reclaim`). Honours the
|
|
7
7
|
* `RECLAIM_HOME` env var as an override so tests can sandbox writes.
|
|
8
8
|
*/
|
|
9
9
|
export function reclaimHomeDirPath() {
|
|
10
|
-
return
|
|
10
|
+
return reclaimHome()
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
package/src/proof/attestor.ts
CHANGED
|
@@ -6,11 +6,16 @@
|
|
|
6
6
|
// revealed slice; jsonPath/xPath live on responseRedactions (what the prover
|
|
7
7
|
// reveals), never on responseMatches.
|
|
8
8
|
|
|
9
|
+
import { bytesToHex } from '@noble/hashes/utils.js'
|
|
9
10
|
import {
|
|
10
|
-
createClaimOnAttestor, type Logger,
|
|
11
|
+
createClaimOnAttestor, type Logger,
|
|
11
12
|
} from '@reclaimprotocol/attestor-core'
|
|
12
|
-
import {
|
|
13
|
+
import { agentTraceEnabled } from '../consts.ts'
|
|
14
|
+
import { resolveEthKey } from '../mcp/tools/authenticate/eth-key.ts'
|
|
13
15
|
import type { ReclaimProvider } from '../provider/schema.ts'
|
|
16
|
+
import {
|
|
17
|
+
buildHttpParams, ensureAttestorCrypto, parseClaim, resolveAttestorUrl,
|
|
18
|
+
} from './proof-core.ts'
|
|
14
19
|
import { ensureZkFiles } from './zk-files.ts'
|
|
15
20
|
|
|
16
21
|
export interface AttestorLogEntry {
|
|
@@ -19,6 +24,37 @@ export interface AttestorLogEntry {
|
|
|
19
24
|
ctx?: Record<string, unknown>
|
|
20
25
|
}
|
|
21
26
|
|
|
27
|
+
/** The attestor's signed claim data + signature, for building the nested proof
|
|
28
|
+
* JWS a Consumer verifies against the attestor's key. Populated when
|
|
29
|
+
* `includeAttestorClaim` is set (the app's verification runner needs it; the
|
|
30
|
+
* MCP `run_proof` tool ignores it). */
|
|
31
|
+
export interface AttestorClaim {
|
|
32
|
+
/** ProviderClaimData: provider, parameters, context (JSON string), owner,
|
|
33
|
+
* timestampS, epoch, identifier. */
|
|
34
|
+
claim: Record<string, unknown>
|
|
35
|
+
/** Hex (0x) attestor ETH address that signed the claim (the nested `kid`). */
|
|
36
|
+
attestorAddress: string
|
|
37
|
+
/** Hex (0x) attestor claim signature (EIP-191 over the claim sign-data). */
|
|
38
|
+
claimSignature: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Options for {@link runAttestorProof}. */
|
|
42
|
+
export interface RunAttestorProofOptions {
|
|
43
|
+
attestorUrl?: string
|
|
44
|
+
/** When resolving the key from env/.env/file, the address the caller expects
|
|
45
|
+
* to sign with (sanity check). Ignored when `ownerPrivateKey` is given. */
|
|
46
|
+
ownerAddress?: string
|
|
47
|
+
/** Sign with this key directly (e.g. the app/VC's ephemeral per-session key)
|
|
48
|
+
* instead of resolving from env/.env/file. */
|
|
49
|
+
ownerPrivateKey?: string
|
|
50
|
+
/** ZK engine for `createClaimOnAttestor`. Defaults to `'gnark'`; the app's
|
|
51
|
+
* server-side verification runner uses `'snarkjs'`. */
|
|
52
|
+
zkEngine?: 'gnark' | 'snarkjs'
|
|
53
|
+
/** Extract the structured {@link AttestorClaim} (claim + attestor signature)
|
|
54
|
+
* onto the result for nested-proof construction. */
|
|
55
|
+
includeAttestorClaim?: boolean
|
|
56
|
+
}
|
|
57
|
+
|
|
22
58
|
export interface ProofResult {
|
|
23
59
|
proof: unknown
|
|
24
60
|
extractedValue: string
|
|
@@ -27,6 +63,9 @@ export interface ProofResult {
|
|
|
27
63
|
owner?: string
|
|
28
64
|
verified: boolean
|
|
29
65
|
error?: string
|
|
66
|
+
/** Structured claim + signature for nested-proof verification. Set only when
|
|
67
|
+
* `includeAttestorClaim` was requested and the claim carries signatures. */
|
|
68
|
+
attestorClaim?: AttestorClaim
|
|
30
69
|
/** Set to `'missing-owner-key'` when no credential was available to
|
|
31
70
|
* sign with — distinguishes this recoverable case from other proof
|
|
32
71
|
* errors so the skill can point the dev at `issue_credentials` /
|
|
@@ -139,16 +178,6 @@ function makeCaptureLogger(): {
|
|
|
139
178
|
return { logger: make({}), entries }
|
|
140
179
|
}
|
|
141
180
|
|
|
142
|
-
/** Default public attestor websocket URL. Resolved per-call so an env-var
|
|
143
|
-
* change or per-request override takes effect without restarting the
|
|
144
|
-
* MCP server. */
|
|
145
|
-
const FALLBACK_ATTESTOR_URL = 'wss://attestor.reclaimprotocol.org:444/ws'
|
|
146
|
-
function resolveAttestorUrl(override?: string): string {
|
|
147
|
-
return (
|
|
148
|
-
override ?? process.env['RECLAIM_ATTESTOR_URL'] ?? FALLBACK_ATTESTOR_URL
|
|
149
|
-
)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
181
|
// ---------------------------------------------------------------------------
|
|
153
182
|
// Main entry point
|
|
154
183
|
// ---------------------------------------------------------------------------
|
|
@@ -156,52 +185,67 @@ function resolveAttestorUrl(override?: string): string {
|
|
|
156
185
|
export async function runAttestorProof(
|
|
157
186
|
provider: ReclaimProvider,
|
|
158
187
|
secrets: Record<string, string>,
|
|
159
|
-
|
|
160
|
-
ownerAddress?: string,
|
|
188
|
+
opts: RunAttestorProofOptions = {},
|
|
161
189
|
): Promise<ProofResult> {
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
190
|
+
// Sign with the caller-supplied key (the app/VC's ephemeral per-session
|
|
191
|
+
// key) when given; otherwise resolve env/.env/file. `ownerAddress` is a
|
|
192
|
+
// sanity check on the resolved key (ignored when a key is supplied).
|
|
193
|
+
let ownerPrivateKey: string
|
|
194
|
+
if(opts.ownerPrivateKey) {
|
|
195
|
+
ownerPrivateKey = opts.ownerPrivateKey
|
|
196
|
+
} else {
|
|
197
|
+
const resolved = resolveEthKey()
|
|
198
|
+
if(!resolved) {
|
|
199
|
+
return {
|
|
200
|
+
proof: undefined,
|
|
201
|
+
extractedValue: '',
|
|
202
|
+
extractedParameters: {},
|
|
203
|
+
verified: false,
|
|
204
|
+
error:
|
|
205
|
+
'No eth proof-owner key available (no RECLAIM_PRIVATE_KEY in env, ' +
|
|
206
|
+
'.env, or RECLAIM_PRIVATE_KEY_FILE). Call resolve_owner_key, then ' +
|
|
207
|
+
'issue_credentials (generate) or import_credentials (provide one), ' +
|
|
208
|
+
'and retry run_proof.',
|
|
209
|
+
errorKind: 'missing-owner-key',
|
|
210
|
+
}
|
|
179
211
|
}
|
|
212
|
+
|
|
213
|
+
if(
|
|
214
|
+
opts.ownerAddress
|
|
215
|
+
&& opts.ownerAddress.toLowerCase() !== resolved.address
|
|
216
|
+
) {
|
|
217
|
+
return {
|
|
218
|
+
proof: undefined,
|
|
219
|
+
extractedValue: '',
|
|
220
|
+
extractedParameters: {},
|
|
221
|
+
verified: false,
|
|
222
|
+
error:
|
|
223
|
+
`Requested ownerAddress "${opts.ownerAddress}" does not match ` +
|
|
224
|
+
`the resolved eth key (${resolved.address}, source: ` +
|
|
225
|
+
`${resolved.source}). ` +
|
|
226
|
+
'Call resolve_owner_key to get the current address, or update the ' +
|
|
227
|
+
'configured key.',
|
|
228
|
+
errorKind: 'missing-owner-key',
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
ownerPrivateKey = resolved.privateKey
|
|
180
233
|
}
|
|
181
234
|
|
|
182
235
|
try {
|
|
183
|
-
//
|
|
184
|
-
//
|
|
236
|
+
// Init the TLS crypto backend (idempotent), then fetch ZK circuits
|
|
237
|
+
// lazily on the first proof (not at install time).
|
|
238
|
+
ensureAttestorCrypto()
|
|
185
239
|
await ensureZkFiles()
|
|
186
240
|
|
|
187
|
-
const params
|
|
188
|
-
url: provider.url,
|
|
189
|
-
method: provider.method,
|
|
190
|
-
...(provider.headers !== undefined && { headers: provider.headers }),
|
|
191
|
-
...(provider.body !== undefined && { body: provider.body }),
|
|
192
|
-
responseMatches: provider.responseMatches,
|
|
193
|
-
responseRedactions: provider.responseRedactions ?? [],
|
|
194
|
-
...(provider.paramValues !== undefined && {
|
|
195
|
-
paramValues: provider.paramValues,
|
|
196
|
-
}),
|
|
197
|
-
}
|
|
241
|
+
const params = buildHttpParams(provider)
|
|
198
242
|
|
|
199
243
|
// Diagnostic logger capture is gated behind RECLAIM_AGENT_TRACE so the
|
|
200
244
|
// happy path doesn't pay the cost (the attestor SDK emits a *lot* of
|
|
201
245
|
// trace/debug entries during ZK setup). Flip RECLAIM_AGENT_TRACE=1 to
|
|
202
246
|
// turn it back on when something's broken; logs flow into the proof
|
|
203
247
|
// response and into the on-disk proof-runs dump.
|
|
204
|
-
const traceEnabled =
|
|
248
|
+
const traceEnabled = agentTraceEnabled()
|
|
205
249
|
const capture = traceEnabled ? makeCaptureLogger() : undefined
|
|
206
250
|
const attestorLogs = capture?.entries
|
|
207
251
|
|
|
@@ -214,8 +258,8 @@ export async function runAttestorProof(
|
|
|
214
258
|
headers: secrets,
|
|
215
259
|
},
|
|
216
260
|
ownerPrivateKey,
|
|
217
|
-
client: { url: resolveAttestorUrl(attestorUrl) },
|
|
218
|
-
zkEngine: '
|
|
261
|
+
client: { url: resolveAttestorUrl(opts.attestorUrl) },
|
|
262
|
+
zkEngine: opts.zkEngine ?? 'gnark',
|
|
219
263
|
...(capture !== undefined && { logger: capture.logger as Logger }),
|
|
220
264
|
})
|
|
221
265
|
} catch(err) {
|
|
@@ -234,27 +278,8 @@ export async function runAttestorProof(
|
|
|
234
278
|
return errorResult
|
|
235
279
|
}
|
|
236
280
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const claimData =
|
|
240
|
-
claimRecord['claim'] as Record<string, unknown> | undefined
|
|
241
|
-
|
|
242
|
-
// claimData.context is a JSON-encoded string carrying
|
|
243
|
-
// { extractedParameters: {...}, providerHash, ... }.
|
|
244
|
-
let context: Record<string, unknown> = {}
|
|
245
|
-
const rawContext = claimData?.['context']
|
|
246
|
-
if(typeof rawContext === 'string') {
|
|
247
|
-
try {
|
|
248
|
-
context = JSON.parse(rawContext) as Record<string, unknown>
|
|
249
|
-
} catch{
|
|
250
|
-
// Leave context empty if it isn't valid JSON.
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const extractedParameters =
|
|
255
|
-
(context['extractedParameters'] as Record<string, string> | undefined) ??
|
|
256
|
-
{}
|
|
257
|
-
const extractedValue = Object.values(extractedParameters)[0] ?? ''
|
|
281
|
+
const { extractedValue, extractedParameters, identifier, owner, claimData }
|
|
282
|
+
= parseClaim(claim)
|
|
258
283
|
|
|
259
284
|
const result: ProofResult = {
|
|
260
285
|
proof: claim,
|
|
@@ -262,16 +287,33 @@ export async function runAttestorProof(
|
|
|
262
287
|
extractedParameters,
|
|
263
288
|
verified: true,
|
|
264
289
|
}
|
|
265
|
-
|
|
266
|
-
if(typeof identifier === 'string') {
|
|
290
|
+
if(identifier !== undefined) {
|
|
267
291
|
result.identifier = identifier
|
|
268
292
|
}
|
|
269
293
|
|
|
270
|
-
|
|
271
|
-
if(typeof owner === 'string') {
|
|
294
|
+
if(owner !== undefined) {
|
|
272
295
|
result.owner = owner
|
|
273
296
|
}
|
|
274
297
|
|
|
298
|
+
// Structured claim + signature so the result JWS can carry a nested,
|
|
299
|
+
// attestor-signed proof the Consumer verifies independently.
|
|
300
|
+
if(opts.includeAttestorClaim) {
|
|
301
|
+
const sigs = (claim as {
|
|
302
|
+
signatures?: { attestorAddress?: unknown, claimSignature?: unknown }
|
|
303
|
+
}).signatures
|
|
304
|
+
if(
|
|
305
|
+
claimData
|
|
306
|
+
&& typeof sigs?.attestorAddress === 'string'
|
|
307
|
+
&& sigs.claimSignature instanceof Uint8Array
|
|
308
|
+
) {
|
|
309
|
+
result.attestorClaim = {
|
|
310
|
+
claim: claimData,
|
|
311
|
+
attestorAddress: sigs.attestorAddress,
|
|
312
|
+
claimSignature: '0x' + bytesToHex(sigs.claimSignature),
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
275
317
|
// Only set when RECLAIM_AGENT_TRACE=1. Full stream — the MCP layer
|
|
276
318
|
// trims for the LLM-visible response; the on-disk dump gets it all.
|
|
277
319
|
if(attestorLogs !== undefined && attestorLogs.length > 0) {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Proof primitives shared with the app's verification runner: the attestor
|
|
2
|
+
// param/claim plumbing, the ZK-file bootstrap, and the agent's own
|
|
3
|
+
// `runAttestorProof`. Exposed as `@reclaimprotocol/agent/proof`.
|
|
4
|
+
export type {
|
|
5
|
+
AttestorClaim,
|
|
6
|
+
AttestorLogEntry,
|
|
7
|
+
ProofResult,
|
|
8
|
+
RunAttestorProofOptions,
|
|
9
|
+
} from './attestor.ts'
|
|
10
|
+
export { runAttestorProof, tailLogs, warnsAndErrors } from './attestor.ts'
|
|
11
|
+
export {
|
|
12
|
+
buildHttpParams,
|
|
13
|
+
ensureAttestorCrypto,
|
|
14
|
+
type ParsedClaim,
|
|
15
|
+
parseClaim,
|
|
16
|
+
resolveAttestorUrl,
|
|
17
|
+
} from './proof-core.ts'
|
|
18
|
+
export { ensureZkFiles } from './zk-files.ts'
|
|
19
|
+
// Re-exported attestor-core claim primitive so the agent is the single
|
|
20
|
+
// integration point with @reclaimprotocol/attestor-core across the builder.
|
|
21
|
+
export { createClaimOnAttestor } from '@reclaimprotocol/attestor-core'
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared attestor-claim plumbing used by both proof callers: the agent's MCP
|
|
3
|
+
* `run_proof` (which resolves the owner key from the credential cache and
|
|
4
|
+
* captures trace logs) and the app's verification runner (which signs with an
|
|
5
|
+
* ephemeral per-session key and builds a nested-proof claim). Only those
|
|
6
|
+
* concerns differ — building the HTTP params, resolving the attestor URL, and
|
|
7
|
+
* parsing the claim response are identical, so they live here.
|
|
8
|
+
*/
|
|
9
|
+
import type { ProviderParams } from '@reclaimprotocol/attestor-core'
|
|
10
|
+
import { setCryptoImplementation } from '@reclaimprotocol/tls'
|
|
11
|
+
import { webcryptoCrypto } from '@reclaimprotocol/tls/webcrypto'
|
|
12
|
+
import { attestorUrlOverride, FALLBACK_ATTESTOR_URL } from '../consts.ts'
|
|
13
|
+
import type { ReclaimProvider } from '../provider/schema.ts'
|
|
14
|
+
import { normalizeGeoLocation } from '../provider/schema.ts'
|
|
15
|
+
|
|
16
|
+
let cryptoInitialized = false
|
|
17
|
+
|
|
18
|
+
/** One-time crypto backend init for `@reclaimprotocol/tls` (idempotent). The
|
|
19
|
+
* package ships with an empty `crypto = {}` and defers the backend to the
|
|
20
|
+
* caller; without this the first proof dies inside the TLS client with
|
|
21
|
+
* `crypto.randomBytes is not a function`. Shared by every attestor caller —
|
|
22
|
+
* the agent MCP `run_proof` tool and the app's verification runner. */
|
|
23
|
+
export function ensureAttestorCrypto(): void {
|
|
24
|
+
if(cryptoInitialized) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setCryptoImplementation(webcryptoCrypto)
|
|
29
|
+
cryptoInitialized = true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Resolve the attestor websocket URL: explicit override → env → public
|
|
33
|
+
* default. Resolved per-call so an env change or per-request override takes
|
|
34
|
+
* effect without restarting. */
|
|
35
|
+
export function resolveAttestorUrl(override?: string): string {
|
|
36
|
+
return (
|
|
37
|
+
override ?? attestorUrlOverride() ?? FALLBACK_ATTESTOR_URL
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Map a verification's provider recipe straight to the attestor's HTTP
|
|
42
|
+
* params — never synthesized from captured traffic. */
|
|
43
|
+
export function buildHttpParams(
|
|
44
|
+
provider: ReclaimProvider,
|
|
45
|
+
): ProviderParams<'http'> {
|
|
46
|
+
// A blank egress is treated as unset: omit it so the local proof uses the
|
|
47
|
+
// tester's own egress rather than shipping a blank or an unresolved template.
|
|
48
|
+
const geoLocation = normalizeGeoLocation(provider.geoLocation)
|
|
49
|
+
return {
|
|
50
|
+
url: provider.url,
|
|
51
|
+
method: provider.method,
|
|
52
|
+
...(provider.headers !== undefined && { headers: provider.headers }),
|
|
53
|
+
...(provider.body !== undefined && { body: provider.body }),
|
|
54
|
+
responseMatches: provider.responseMatches,
|
|
55
|
+
responseRedactions: provider.responseRedactions ?? [],
|
|
56
|
+
...(provider.paramValues !== undefined && {
|
|
57
|
+
paramValues: provider.paramValues,
|
|
58
|
+
}),
|
|
59
|
+
...(provider.writeRedactionMode !== undefined && {
|
|
60
|
+
writeRedactionMode: provider.writeRedactionMode,
|
|
61
|
+
}),
|
|
62
|
+
...(provider.additionalClientOptions !== undefined && {
|
|
63
|
+
additionalClientOptions: provider.additionalClientOptions,
|
|
64
|
+
}),
|
|
65
|
+
...(geoLocation !== undefined && { geoLocation }),
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ParsedClaim {
|
|
70
|
+
/** ProviderClaimData: provider, parameters, context, owner, identifier, … */
|
|
71
|
+
claimData?: Record<string, unknown>
|
|
72
|
+
extractedParameters: Record<string, string>
|
|
73
|
+
extractedValue: string
|
|
74
|
+
identifier?: string
|
|
75
|
+
owner?: string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Pull claimData + decoded `context.extractedParameters` (plus identifier /
|
|
79
|
+
* owner) out of a `createClaimOnAttestor` result. The full result still
|
|
80
|
+
* carries signatures the caller may need for nested-proof building. */
|
|
81
|
+
export function parseClaim(claim: unknown): ParsedClaim {
|
|
82
|
+
const claimRecord = claim as Record<string, unknown>
|
|
83
|
+
const claimData = claimRecord['claim'] as Record<string, unknown> | undefined
|
|
84
|
+
|
|
85
|
+
// claimData.context is a JSON-encoded string carrying
|
|
86
|
+
// { extractedParameters: {...}, providerHash, ... }.
|
|
87
|
+
let context: Record<string, unknown> = {}
|
|
88
|
+
const rawContext = claimData?.['context']
|
|
89
|
+
if(typeof rawContext === 'string') {
|
|
90
|
+
try {
|
|
91
|
+
context = JSON.parse(rawContext) as Record<string, unknown>
|
|
92
|
+
} catch{
|
|
93
|
+
// Leave context empty if it isn't valid JSON.
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const extractedParameters =
|
|
98
|
+
(context['extractedParameters'] as Record<string, string> | undefined) ?? {}
|
|
99
|
+
|
|
100
|
+
const parsed: ParsedClaim = {
|
|
101
|
+
extractedParameters,
|
|
102
|
+
extractedValue: Object.values(extractedParameters)[0] ?? '',
|
|
103
|
+
}
|
|
104
|
+
if(claimData) {
|
|
105
|
+
parsed.claimData = claimData
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const identifier = claimData?.['identifier']
|
|
109
|
+
if(typeof identifier === 'string') {
|
|
110
|
+
parsed.identifier = identifier
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const owner = claimData?.['owner']
|
|
114
|
+
if(typeof owner === 'string') {
|
|
115
|
+
parsed.owner = owner
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return parsed
|
|
119
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Provider primitives shared with the app's capture pipeline:
|
|
2
|
+
// the attestor-derived recipe types, secret classification, and the GraphQL
|
|
3
|
+
// op parser. Exposed as `@reclaimprotocol/agent/provider`.
|
|
4
|
+
export { extractGraphqlOp, looksLikeGraphqlEndpoint } from './graphql.ts'
|
|
5
|
+
export type {
|
|
6
|
+
CapturedRequest,
|
|
7
|
+
HttpMethod,
|
|
8
|
+
OprfMode,
|
|
9
|
+
ReclaimProvider,
|
|
10
|
+
RequestConcern,
|
|
11
|
+
ResponseMatch,
|
|
12
|
+
ResponseRedaction,
|
|
13
|
+
SecretParamRef,
|
|
14
|
+
} from './schema.ts'
|
|
15
|
+
export { classifyHeader, splitSecrets } from './secrets.ts'
|
|
16
|
+
// Re-exported attestor-core provider primitives (the registry, the param hash,
|
|
17
|
+
// and the param type) so the builder imports them via the agent rather than
|
|
18
|
+
// depending on @reclaimprotocol/attestor-core directly.
|
|
19
|
+
export { hashProviderParams, providers } from '@reclaimprotocol/attestor-core'
|
|
20
|
+
export type { ProviderParams } from '@reclaimprotocol/attestor-core'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guidance for authoring page-injection scripts (`webSettings.jsUserScripts` on
|
|
3
|
+
* the builder, `customInjection` on old-devtools), surfaced to the authoring
|
|
4
|
+
* model from the publish tool description. The prose lives in
|
|
5
|
+
* `skill/injection-api.md` so it can be edited as markdown without touching
|
|
6
|
+
* code — the same pattern as `mcp/instructions.ts`. The path resolves the same
|
|
7
|
+
* from `src/provider` (dev) and `lib/provider` (published): both are two levels
|
|
8
|
+
* below the package root.
|
|
9
|
+
*
|
|
10
|
+
* The guide leads with plain browser JavaScript (navigation/interaction via
|
|
11
|
+
* standard APIs; the client already intercepts page requests) and treats
|
|
12
|
+
* `window.Reclaim` as a rarely-needed edge-case bridge, pointing at the
|
|
13
|
+
* authoritative `src/support/env.d.ts` rather than reproducing its surface.
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync } from 'node:fs'
|
|
16
|
+
import { fileURLToPath } from 'node:url'
|
|
17
|
+
|
|
18
|
+
export const INJECTION_GUIDE = readFileSync(
|
|
19
|
+
fileURLToPath(new URL('../../skill/injection-api.md', import.meta.url)),
|
|
20
|
+
'utf8',
|
|
21
|
+
).trim()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-blocking advisory for a request whose `responseMatches` count is less
|
|
3
|
+
* than its `responseRedactions` count.
|
|
4
|
+
*
|
|
5
|
+
* Some verification clients (notably the InApp SDK) pair the two arrays by
|
|
6
|
+
* index and may not behave correctly when there are fewer matches than
|
|
7
|
+
* redactions. This is NOT enforced — the author keeps full control over what
|
|
8
|
+
* they publish in either old-devtools or builder mode. The tools surface these
|
|
9
|
+
* strings so the dev can make an informed decision (e.g. add a functional match
|
|
10
|
+
* per redaction) rather than being blocked.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** One request's match/redaction counts, with a human label for the message. */
|
|
14
|
+
export interface RequestCounts {
|
|
15
|
+
label: string
|
|
16
|
+
matchCount: number
|
|
17
|
+
redactionCount: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Build advisory warnings for any request with matchCount < redactionCount.
|
|
21
|
+
* Returns an empty array when everything is fine. */
|
|
22
|
+
export function matchRedactionWarnings(requests: RequestCounts[]): string[] {
|
|
23
|
+
const warnings: string[] = []
|
|
24
|
+
for(const req of requests) {
|
|
25
|
+
if(req.matchCount < req.redactionCount) {
|
|
26
|
+
warnings.push(
|
|
27
|
+
`${req.label}: ${req.matchCount} response match(es) but ` +
|
|
28
|
+
`${req.redactionCount} response redaction(s). Some verification ` +
|
|
29
|
+
'clients (e.g. the InApp SDK) may not work correctly when ' +
|
|
30
|
+
'responseMatches is shorter than responseRedactions — they pair the ' +
|
|
31
|
+
'two by index. This is allowed; add a functional match per redaction ' +
|
|
32
|
+
'if you hit verification issues.',
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return warnings
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Counts for a single drafted request shape (provider or injected request). */
|
|
41
|
+
export function countsFor(
|
|
42
|
+
label: string,
|
|
43
|
+
req: {
|
|
44
|
+
responseMatches?: unknown[]
|
|
45
|
+
responseRedactions?: unknown[]
|
|
46
|
+
},
|
|
47
|
+
): RequestCounts {
|
|
48
|
+
return {
|
|
49
|
+
label,
|
|
50
|
+
matchCount: req.responseMatches?.length ?? 0,
|
|
51
|
+
redactionCount: req.responseRedactions?.length ?? 0,
|
|
52
|
+
}
|
|
53
|
+
}
|