@reclaimprotocol/agent 0.1.27 → 0.1.29
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 +5 -5
- package/lib/authoring/tools.js +2 -1
- package/lib/consts.d.ts +1 -1
- package/lib/consts.js +1 -1
- package/lib/live/tunnel.d.ts +4 -0
- package/lib/live/tunnel.js +21 -12
- package/lib/mcp/api/operations.gen.d.ts +8 -8
- package/lib/mcp/api/operations.gen.js +339 -581
- package/lib/mcp/api/tools.d.ts +1 -1
- package/lib/mcp/instructions.js +7 -3
- package/lib/mcp/remap_errors.js +9 -5
- package/lib/mcp/start.js +1 -1
- package/lib/mcp/tools/agent/attach.d.ts +2 -2
- package/lib/mcp/tools/agent/attach.js +4 -0
- package/lib/mcp/tools/agent/build.d.ts +1 -1
- package/lib/mcp/tools/agent/live-view-url.d.ts +2 -0
- package/lib/mcp/tools/agent/live-view-url.js +11 -0
- package/lib/mcp/tools/agent/live-view.d.ts +4 -2
- package/lib/mcp/tools/agent/live-view.js +42 -39
- package/lib/mcp/tools/agent/propose.js +10 -4
- package/lib/mcp/tools/agent/publish.d.ts +1 -1
- package/lib/mcp/tools/agent/publish.js +49 -26
- package/lib/mcp/tools/agent/shared.js +0 -9
- package/lib/mcp/tools/authenticate/authenticate.d.ts +1 -1
- package/lib/mcp/tools/authenticate/build.d.ts +1 -1
- package/lib/mcp/tools/authenticate/builder-access.d.ts +1 -1
- package/lib/mcp/tools/verification/result.js +12 -13
- package/lib/old/login-cdp.d.ts +17 -6
- package/lib/old/login-cdp.js +28 -11
- package/lib/old/to-register.d.ts +6 -0
- package/lib/old/to-register.js +35 -5
- package/lib/old/tools/authenticate.d.ts +5 -4
- package/lib/old/tools/authenticate.js +9 -5
- package/lib/old/tools/publish.d.ts +2 -2
- package/lib/old/tools/publish.js +47 -21
- package/lib/proof/attestor-auth.d.ts +13 -0
- package/lib/proof/attestor-auth.js +16 -0
- package/lib/proof/attestor.d.ts +7 -4
- package/lib/proof/attestor.js +7 -4
- package/lib/proof/index.d.ts +1 -0
- package/lib/proof/index.js +1 -0
- package/lib/proof/pod-prover.d.ts +2 -0
- package/lib/proof/pod-prover.js +5 -5
- package/lib/provider/injection-doc.d.ts +2 -0
- package/lib/provider/injection-doc.js +7 -1
- package/lib/provider/post-publish-browser.d.ts +12 -0
- package/lib/provider/post-publish-browser.js +40 -0
- package/lib/provider/to-version.d.ts +19 -7
- package/lib/provider/to-version.js +67 -25
- package/lib/tools/registry.d.ts +1 -1
- package/lib/tools/registry.js +7 -4
- package/package.json +2 -2
- package/skill/SKILL.builder.md +118 -13
- package/skill/SKILL.common.md +30 -19
- package/skill/SKILL.old.md +22 -12
- package/skill/builder-mode.md +19 -9
- package/skill/community-contributions.md +108 -0
- package/skill/hash-validation.md +8 -5
- package/skill/interception.md +35 -22
- package/skill/old-mode.md +11 -5
- package/skill/user-script.md +41 -10
- package/src/authoring/tools.ts +2 -1
- package/src/consts.ts +1 -2
- package/src/live/tunnel.ts +28 -14
- package/src/mcp/api/operations.gen.ts +341 -585
- package/src/mcp/api/tools.ts +1 -1
- package/src/mcp/instructions.ts +7 -3
- package/src/mcp/remap_errors.ts +9 -7
- package/src/mcp/start.ts +1 -1
- package/src/mcp/tools/agent/attach.ts +6 -2
- package/src/mcp/tools/agent/build.ts +1 -1
- package/src/mcp/tools/agent/live-view-url.ts +13 -0
- package/src/mcp/tools/agent/live-view.ts +44 -39
- package/src/mcp/tools/agent/propose.ts +10 -4
- package/src/mcp/tools/agent/publish.ts +63 -28
- package/src/mcp/tools/agent/shared.ts +5 -0
- package/src/mcp/tools/authenticate/authenticate.ts +1 -1
- package/src/mcp/tools/authenticate/build.ts +1 -1
- package/src/mcp/tools/authenticate/builder-access.ts +1 -1
- package/src/mcp/tools/verification/result.ts +11 -13
- package/src/old/login-cdp.ts +38 -11
- package/src/old/to-register.ts +56 -7
- package/src/old/tools/authenticate.ts +9 -5
- package/src/old/tools/publish.ts +64 -22
- package/src/proof/attestor-auth.ts +33 -0
- package/src/proof/attestor.ts +14 -8
- package/src/proof/index.ts +4 -0
- package/src/proof/pod-prover.ts +7 -5
- package/src/provider/injection-doc.ts +9 -1
- package/src/provider/post-publish-browser.ts +56 -0
- package/src/provider/to-version.ts +104 -27
- package/src/tools/registry.ts +12 -9
package/lib/old/login-cdp.js
CHANGED
|
@@ -79,12 +79,30 @@ export const EXTRACT_TOKEN_JS = `(async () => {
|
|
|
79
79
|
} catch (e) { return null }
|
|
80
80
|
})()`;
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
* Accept only the two loopback Chrome modes used for legacy dashboard login.
|
|
83
|
+
* Config files are user-editable, so validate at runtime rather than trusting
|
|
84
|
+
* the TypeScript shape. Remote Builder, container, custom-CDP, and URL inputs
|
|
85
|
+
* are deliberately unsupported here.
|
|
86
|
+
*/
|
|
87
|
+
export function localDashboardBrowser(value) {
|
|
88
|
+
if (!value || typeof value !== 'object') {
|
|
89
|
+
return { mode: 'dedicated', port: 9222 };
|
|
90
|
+
}
|
|
91
|
+
const mode = Reflect.get(value, 'attachMode');
|
|
92
|
+
const port = Reflect.get(value, 'port');
|
|
93
|
+
if ((mode === 'dedicated' || mode === 'attach')
|
|
94
|
+
&& Number.isInteger(port) && Number(port) > 0 && Number(port) <= 65535) {
|
|
95
|
+
return { mode, port: Number(port) };
|
|
96
|
+
}
|
|
97
|
+
return { mode: 'dedicated', port: 9222 };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Open the dashboard login page in a local Chrome via loopback CDP, wait for
|
|
101
|
+
* the developer to finish signing in, read the resulting Firebase ID token
|
|
102
|
+
* from page storage, then clean up. This never uses a Builder, container,
|
|
103
|
+
* custom-CDP, or shared browser. If we launch a dedicated Chrome, we close the
|
|
104
|
+
* whole browser afterward; if we attach to an existing local Chrome, we close
|
|
105
|
+
* only the tab we opened.
|
|
88
106
|
*/
|
|
89
107
|
export async function captureFirebaseTokenViaCdp(opts) {
|
|
90
108
|
const loginUrl = opts.loginUrl || oldLoginUrl() || DEFAULT_LOGIN_URL;
|
|
@@ -96,9 +114,7 @@ export async function captureFirebaseTokenViaCdp(opts) {
|
|
|
96
114
|
// followed looked like a 6-minute hang with no output.
|
|
97
115
|
const timeoutMs = opts.timeoutMs ?? 360000;
|
|
98
116
|
const pollMs = opts.pollMs ?? 1000;
|
|
99
|
-
const
|
|
100
|
-
const mode = cfg?.attachMode ?? 'dedicated';
|
|
101
|
-
const port = cfg?.port ?? 9222;
|
|
117
|
+
const { mode, port } = localDashboardBrowser(new ConfigStore().read()?.browser_agent);
|
|
102
118
|
// Ensure something is answering CDP. In dedicated mode we may launch it.
|
|
103
119
|
let launchedByUs = false;
|
|
104
120
|
try {
|
|
@@ -106,9 +122,10 @@ export async function captureFirebaseTokenViaCdp(opts) {
|
|
|
106
122
|
}
|
|
107
123
|
catch {
|
|
108
124
|
if (mode !== 'dedicated') {
|
|
109
|
-
throw new Error(`attach mode: nothing answering CDP on port ${port}.
|
|
125
|
+
throw new Error(`local attach mode: nothing answering CDP on port ${port}. ` +
|
|
126
|
+
'Launch Chrome ' +
|
|
110
127
|
`with --remote-debugging-port=${port} AND a non-default ` +
|
|
111
|
-
'--user-data-dir, or
|
|
128
|
+
'--user-data-dir, or use the dedicated local login mode.');
|
|
112
129
|
}
|
|
113
130
|
launchChrome({ port, profileDir: defaultProfileDir() });
|
|
114
131
|
await waitForCdpReady(port, 10000);
|
package/lib/old/to-register.d.ts
CHANGED
|
@@ -127,6 +127,7 @@ export interface SemverTriplet {
|
|
|
127
127
|
minor: number;
|
|
128
128
|
patch: number;
|
|
129
129
|
}
|
|
130
|
+
export type SemverBump = 'major' | 'minor' | 'patch';
|
|
130
131
|
/**
|
|
131
132
|
* Pull `{major,minor,patch}` triplets out of a `/versions` response, tolerant
|
|
132
133
|
* of the exact envelope (array, `{ providerVersions: [...] }`, `{ items }`,
|
|
@@ -140,4 +141,9 @@ export declare function extractVersions(resp: unknown): SemverTriplet[];
|
|
|
140
141
|
* rather than collide with the guaranteed 1.0.0).
|
|
141
142
|
*/
|
|
142
143
|
export declare function bumpPatchString(versions: SemverTriplet[]): string;
|
|
144
|
+
/** Resolve a strictly newer immutable version for old-devtools publishing. */
|
|
145
|
+
export declare function resolveNewVersionString(versions: SemverTriplet[], input?: {
|
|
146
|
+
version?: SemverTriplet;
|
|
147
|
+
bump?: SemverBump;
|
|
148
|
+
}): string;
|
|
143
149
|
export {};
|
package/lib/old/to-register.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { badRequest, conflict } from '@hapi/boom';
|
|
1
2
|
import assert from 'node:assert';
|
|
2
3
|
/** Default web settings, mirroring the builder openapi defaults
|
|
3
4
|
* (`ProviderWebSettings.geoLocation`, `PortalClientOptions.useProxy`). The
|
|
@@ -208,18 +209,47 @@ export function extractVersions(resp) {
|
|
|
208
209
|
* rather than collide with the guaranteed 1.0.0).
|
|
209
210
|
*/
|
|
210
211
|
export function bumpPatchString(versions) {
|
|
211
|
-
|
|
212
|
+
return resolveNewVersionString(versions);
|
|
213
|
+
}
|
|
214
|
+
/** Resolve a strictly newer immutable version for old-devtools publishing. */
|
|
215
|
+
export function resolveNewVersionString(versions, input = {}) {
|
|
216
|
+
assert(!(input.version && input.bump), badRequest('Pass either `version` or `bump`, not both.'));
|
|
217
|
+
const highest = highestVersion(versions);
|
|
218
|
+
if (input.version) {
|
|
219
|
+
assert(!highest || compareVersions(input.version, highest) > 0, conflict(`Version ${formatVersion(input.version)} must be higher than the ` +
|
|
220
|
+
`current highest version ${highest ? formatVersion(highest) : 'none'}.`));
|
|
221
|
+
return formatVersion(input.version);
|
|
222
|
+
}
|
|
223
|
+
if (!highest) {
|
|
212
224
|
return '1.0.1';
|
|
213
225
|
}
|
|
226
|
+
const bump = input.bump ?? 'patch';
|
|
227
|
+
switch (bump) {
|
|
228
|
+
case 'major':
|
|
229
|
+
return `${highest.major + 1}.0.0`;
|
|
230
|
+
case 'minor':
|
|
231
|
+
return `${highest.major}.${highest.minor + 1}.0`;
|
|
232
|
+
case 'patch':
|
|
233
|
+
return `${highest.major}.${highest.minor}.${highest.patch + 1}`;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function highestVersion(versions) {
|
|
237
|
+
if (versions.length === 0) {
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
214
240
|
let hi = versions[0];
|
|
215
241
|
for (const v of versions) {
|
|
216
|
-
if (v
|
|
217
|
-
(v.major === hi.major && v.minor > hi.minor) ||
|
|
218
|
-
(v.major === hi.major && v.minor === hi.minor && v.patch > hi.patch)) {
|
|
242
|
+
if (compareVersions(v, hi) > 0) {
|
|
219
243
|
hi = v;
|
|
220
244
|
}
|
|
221
245
|
}
|
|
222
|
-
return
|
|
246
|
+
return hi;
|
|
247
|
+
}
|
|
248
|
+
function compareVersions(a, b) {
|
|
249
|
+
return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
|
|
250
|
+
}
|
|
251
|
+
function formatVersion(version) {
|
|
252
|
+
return `${version.major}.${version.minor}.${version.patch}`;
|
|
223
253
|
}
|
|
224
254
|
function pickArray(resp) {
|
|
225
255
|
if (Array.isArray(resp)) {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type RegisteredTool } from '../../mcp/server.ts';
|
|
2
2
|
import type { ReclaimOldClient } from '../client.ts';
|
|
3
3
|
/**
|
|
4
|
-
* OLD-devtools authenticate. The default, zero-arg flow drives
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* OLD-devtools authenticate. The default, zero-arg flow drives only a local
|
|
5
|
+
* Chrome through loopback CDP. It never uses a Builder, container, custom-CDP,
|
|
6
|
+
* or shared browser. It opens the devtools dashboard, waits for the developer
|
|
7
|
+
* to sign in, reads the Firebase ID token from page storage, then closes the
|
|
8
|
+
* tab or the dedicated browser it launched. Two manual overrides remain:
|
|
8
9
|
*
|
|
9
10
|
* - `token`: paste a Firebase ID token yourself (skips the browser).
|
|
10
11
|
* - `ethAddress`: an `eth:<addr>`/`0x…` uid for an already-provisioned eth
|
|
@@ -3,10 +3,11 @@ import { defineTool } from "../../mcp/server.js";
|
|
|
3
3
|
import { persistIdentity, toEthUid } from "../identity.js";
|
|
4
4
|
import { captureFirebaseTokenViaCdp } from "../login-cdp.js";
|
|
5
5
|
/**
|
|
6
|
-
* OLD-devtools authenticate. The default, zero-arg flow drives
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* OLD-devtools authenticate. The default, zero-arg flow drives only a local
|
|
7
|
+
* Chrome through loopback CDP. It never uses a Builder, container, custom-CDP,
|
|
8
|
+
* or shared browser. It opens the devtools dashboard, waits for the developer
|
|
9
|
+
* to sign in, reads the Firebase ID token from page storage, then closes the
|
|
10
|
+
* tab or the dedicated browser it launched. Two manual overrides remain:
|
|
10
11
|
*
|
|
11
12
|
* - `token`: paste a Firebase ID token yourself (skips the browser).
|
|
12
13
|
* - `ethAddress`: an `eth:<addr>`/`0x…` uid for an already-provisioned eth
|
|
@@ -31,7 +32,9 @@ export function authenticateTool(client) {
|
|
|
31
32
|
return defineTool({
|
|
32
33
|
name: 'reclaim_authenticate',
|
|
33
34
|
description: 'Authenticate against the OLD Reclaim devtools backend. \n\n' +
|
|
34
|
-
'Default (no args): opens the devtools dashboard in
|
|
35
|
+
'Default (no args): opens the devtools dashboard in a LOCAL Chrome ' +
|
|
36
|
+
'over loopback CDP. It never uses a Builder, container, custom-CDP, ' +
|
|
37
|
+
'or shared browser. It ' +
|
|
35
38
|
'waits for you to sign in, grabs the Firebase token automatically, ' +
|
|
36
39
|
'and closes the tab. A previously stored identity that is still ' +
|
|
37
40
|
'valid is reused first (pass `useBrowser: true` to force a fresh ' +
|
|
@@ -128,6 +131,7 @@ export function authenticateTool(client) {
|
|
|
128
131
|
return {
|
|
129
132
|
authenticated: true,
|
|
130
133
|
method: 'browser',
|
|
134
|
+
browser: 'local-chrome',
|
|
131
135
|
closedBrowser: result.closedBrowser,
|
|
132
136
|
note: 'Signed in via the dashboard; Firebase token captured and cached. ' +
|
|
133
137
|
'Tokens expire (~1h) — re-run reclaim_authenticate on 401.',
|
|
@@ -9,8 +9,8 @@ import type { ReclaimOldClient } from '../client.ts';
|
|
|
9
9
|
* - WITHOUT `providerId` → `POST /api/providers/register`: creates a
|
|
10
10
|
* brand-new provider + version 1.0.0. Re-running creates a DUPLICATE.
|
|
11
11
|
* - WITH `providerId` → `POST /api/providers/:providerId/config`: adds a
|
|
12
|
-
* new version to an existing provider (
|
|
13
|
-
*
|
|
12
|
+
* new immutable version to an existing provider (patch by default;
|
|
13
|
+
* major/minor/patch or an exact higher version when requested).
|
|
14
14
|
*
|
|
15
15
|
* Get a `providerId` from `get_me_providers`.
|
|
16
16
|
*
|
package/lib/old/tools/publish.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { Boom, isBoom } from '@hapi/boom';
|
|
1
|
+
import { badRequest, Boom, isBoom } from '@hapi/boom';
|
|
2
|
+
import assert from 'node:assert';
|
|
2
3
|
import { defineTool } from "../../mcp/server.js";
|
|
3
|
-
import { ALLOWED_REQUESTS_HINT, USER_SCRIPT_HINT, } from "../../provider/injection-doc.js";
|
|
4
|
+
import { ALLOWED_REQUESTS_HINT, COOKIE_ATTACHMENT_HINT, USER_SCRIPT_HINT, } from "../../provider/injection-doc.js";
|
|
4
5
|
import { countsFor, matchRedactionWarnings, } from "../../provider/match-redaction-warning.js";
|
|
5
|
-
import {
|
|
6
|
+
import { postPublishBrowserNotes } from "../../provider/post-publish-browser.js";
|
|
7
|
+
import { extractVersions, injectedRequestDataFrom, providerToConfigBody, providerToRegisterBody, resolveNewVersionString, } from "../to-register.js";
|
|
6
8
|
const DEFAULT_VERSION_INFO = 'Updated from MCP browser capture';
|
|
7
9
|
/** Pull the new provider's id out of a register response, tolerant of the
|
|
8
10
|
* envelope (`{providerId}`, `{provider:{providerId}}`, `{id}`). */
|
|
@@ -42,8 +44,8 @@ function rethrowOwnershipError(err, providerId) {
|
|
|
42
44
|
* - WITHOUT `providerId` → `POST /api/providers/register`: creates a
|
|
43
45
|
* brand-new provider + version 1.0.0. Re-running creates a DUPLICATE.
|
|
44
46
|
* - WITH `providerId` → `POST /api/providers/:providerId/config`: adds a
|
|
45
|
-
* new version to an existing provider (
|
|
46
|
-
*
|
|
47
|
+
* new immutable version to an existing provider (patch by default;
|
|
48
|
+
* major/minor/patch or an exact higher version when requested).
|
|
47
49
|
*
|
|
48
50
|
* Get a `providerId` from `get_me_providers`.
|
|
49
51
|
*
|
|
@@ -67,8 +69,11 @@ export function createVersionFromCaptureTool(client, attachRef) {
|
|
|
67
69
|
+ 'redactions are supported.\n\n'
|
|
68
70
|
+ 'Omit `providerId` to REGISTER A NEW provider (creates it + '
|
|
69
71
|
+ 'version 1.0.0 — re-running creates a DUPLICATE). Pass '
|
|
70
|
-
+ '`providerId` (from get_me_providers) to add a NEW VERSION
|
|
71
|
-
+ '
|
|
72
|
+
+ '`providerId` (from get_me_providers) to add a NEW IMMUTABLE VERSION '
|
|
73
|
+
+ 'to it. Omit `version` and `bump` for a patch bump, pass `bump` for '
|
|
74
|
+
+ 'a major/minor/patch bump, or pass an exact higher `version` when '
|
|
75
|
+
+ 'the user directs it. Never edit an existing version. You can only '
|
|
76
|
+
+ 'version a provider YOU own — '
|
|
72
77
|
+ 'to publish your own take on one you don\'t own, omit '
|
|
73
78
|
+ '`providerId` to register your own copy.\n\n'
|
|
74
79
|
+ 'THIS BACKEND ONLY EVER CREATES A NEW VERSION — there is no '
|
|
@@ -167,6 +172,25 @@ export function createVersionFromCaptureTool(client, attachRef) {
|
|
|
167
172
|
description: 'Existing provider to add a new version to. Omit to register ' +
|
|
168
173
|
'a brand-new provider.',
|
|
169
174
|
},
|
|
175
|
+
version: {
|
|
176
|
+
type: 'object',
|
|
177
|
+
description: 'Exact new semver requested by the user. Existing-provider ' +
|
|
178
|
+
'publishing only; it must be higher than the current highest ' +
|
|
179
|
+
'version. Do not pass with `bump`.',
|
|
180
|
+
properties: {
|
|
181
|
+
major: { type: 'integer', minimum: 0 },
|
|
182
|
+
minor: { type: 'integer', minimum: 0 },
|
|
183
|
+
patch: { type: 'integer', minimum: 0 },
|
|
184
|
+
},
|
|
185
|
+
required: ['major', 'minor', 'patch'],
|
|
186
|
+
},
|
|
187
|
+
bump: {
|
|
188
|
+
type: 'string',
|
|
189
|
+
enum: ['major', 'minor', 'patch'],
|
|
190
|
+
description: 'Semantic increment from the highest version. Defaults to ' +
|
|
191
|
+
'`patch`. Existing-provider publishing only; do not pass with ' +
|
|
192
|
+
'an exact `version`.',
|
|
193
|
+
},
|
|
170
194
|
description: {
|
|
171
195
|
type: 'string',
|
|
172
196
|
description: 'Provider description (new-provider register only).',
|
|
@@ -360,7 +384,8 @@ export function createVersionFromCaptureTool(client, attachRef) {
|
|
|
360
384
|
'certain none is configured. Builder calls the equivalent ' +
|
|
361
385
|
'concept `interceptorType` (no `NONE` there — omitting the ' +
|
|
362
386
|
'whole `interceptorOptions` object is its no-interception ' +
|
|
363
|
-
'signal instead).
|
|
387
|
+
'signal instead). ' + COOKIE_ATTACHMENT_HINT +
|
|
388
|
+
'What each value actually does: ' +
|
|
364
389
|
'how_it_works({ topic: "interception" }).',
|
|
365
390
|
},
|
|
366
391
|
disableRequestReplay: {
|
|
@@ -379,6 +404,9 @@ export function createVersionFromCaptureTool(client, attachRef) {
|
|
|
379
404
|
required: ['initialUrl'],
|
|
380
405
|
},
|
|
381
406
|
}, async (args) => {
|
|
407
|
+
assert(!(args.version && args.bump), badRequest('Pass either `version` or `bump`, not both.'));
|
|
408
|
+
assert(args.providerId || (!args.version && !args.bump), badRequest('`version` and `bump` require `providerId`; new-provider ' +
|
|
409
|
+
'registration starts at the backend-defined version.'));
|
|
382
410
|
// `provider` / `providers` are just two spellings of the same input —
|
|
383
411
|
// fold them into one array. Zero drafts is allowed ONLY when adding a
|
|
384
412
|
// version to an EXISTING provider (`providerId` set): that publishes
|
|
@@ -427,15 +455,12 @@ export function createVersionFromCaptureTool(client, attachRef) {
|
|
|
427
455
|
// developer before closing it (a builder-mode session is
|
|
428
456
|
// quota-accounted and otherwise just sits until its TTL expires).
|
|
429
457
|
const attached = attachRef?.current;
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
'further edits — only call dispose_browser once they confirm ' +
|
|
437
|
-
'they\'re done with it.');
|
|
438
|
-
}
|
|
458
|
+
notes.push(...postPublishBrowserNotes(attached, {
|
|
459
|
+
initialUrl: args.initialUrl,
|
|
460
|
+
hasUserScript: !!args.customInjection?.trim(),
|
|
461
|
+
hasInterceptor: args.injectionType !== 'NONE'
|
|
462
|
+
&& !!args.customInjection?.trim(),
|
|
463
|
+
}));
|
|
439
464
|
const withNotes = (result) => {
|
|
440
465
|
if (notes.length && result && typeof result === 'object') {
|
|
441
466
|
return { ...result, _notes: notes };
|
|
@@ -486,7 +511,7 @@ export function createVersionFromCaptureTool(client, attachRef) {
|
|
|
486
511
|
// providerId.
|
|
487
512
|
try {
|
|
488
513
|
const versionsResp = await client.listVersions(newProviderId);
|
|
489
|
-
const followVersion =
|
|
514
|
+
const followVersion = resolveNewVersionString(extractVersions(versionsResp));
|
|
490
515
|
const configBody = providerToConfigBody(providers, opts, followVersion, DEFAULT_VERSION_INFO);
|
|
491
516
|
await client.updateProviderConfig(newProviderId, configBody);
|
|
492
517
|
notes.push(`Created an automatic follow-up version (${followVersion}) to ` +
|
|
@@ -517,8 +542,9 @@ export function createVersionFromCaptureTool(client, attachRef) {
|
|
|
517
542
|
}
|
|
518
543
|
return withNotes(registered);
|
|
519
544
|
}
|
|
520
|
-
// New version of an existing provider —
|
|
521
|
-
// the highest existing version. A provider you don't own
|
|
545
|
+
// New version of an existing provider — resolve the requested semantic
|
|
546
|
+
// increment above the highest existing version. A provider you don't own
|
|
547
|
+
// (for example, a
|
|
522
548
|
// public one) fails here with 401/403/404; surface the register-a-copy
|
|
523
549
|
// workaround instead of a bare permission error.
|
|
524
550
|
const providerId = args.providerId;
|
|
@@ -541,7 +567,7 @@ export function createVersionFromCaptureTool(client, attachRef) {
|
|
|
541
567
|
'version with get_provider_info and re-publish with them ' +
|
|
542
568
|
'included.');
|
|
543
569
|
const versionsResp = await client.listVersions(providerId);
|
|
544
|
-
const version =
|
|
570
|
+
const version = resolveNewVersionString(extractVersions(versionsResp), { version: args.version, bump: args.bump });
|
|
545
571
|
const versionInfo = args.notes && args.notes.trim().length >= 10
|
|
546
572
|
? args.notes.trim()
|
|
547
573
|
: DEFAULT_VERSION_INFO;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type proto } from '@reclaimprotocol/attestor-core';
|
|
2
|
+
/** Session-bound authorization in both forms used by Reclaim proof clients. */
|
|
3
|
+
export interface AttestorAuthentication {
|
|
4
|
+
/** Native object consumed by the JavaScript attestor client. */
|
|
5
|
+
request: proto.AuthenticationRequest;
|
|
6
|
+
/** Legacy base64 JSON token consumed by native TEE clients. */
|
|
7
|
+
token: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Create legacy-compatible attestor authorization when the deployment has an
|
|
11
|
+
* auth private key. An absent key intentionally means authentication is off.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createAttestorAuthentication(sessionId: string, privateKey: string | undefined): Promise<AttestorAuthentication | undefined>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createAuthRequest } from '@reclaimprotocol/attestor-core';
|
|
2
|
+
/**
|
|
3
|
+
* Create legacy-compatible attestor authorization when the deployment has an
|
|
4
|
+
* auth private key. An absent key intentionally means authentication is off.
|
|
5
|
+
*/
|
|
6
|
+
export async function createAttestorAuthentication(sessionId, privateKey) {
|
|
7
|
+
if (!privateKey?.trim()) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const request = await createAuthRequest({ id: sessionId }, privateKey);
|
|
11
|
+
const token = Buffer.from(JSON.stringify({
|
|
12
|
+
...request,
|
|
13
|
+
signature: Buffer.from(request.signature).toString('base64'),
|
|
14
|
+
}), 'utf8').toString('base64');
|
|
15
|
+
return { request, token };
|
|
16
|
+
}
|
package/lib/proof/attestor.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type proto } from '@reclaimprotocol/attestor-core';
|
|
1
2
|
import './patch-domhandler.ts';
|
|
2
3
|
import type { ReclaimProvider } from '../provider/schema.ts';
|
|
3
4
|
export interface AttestorLogEntry {
|
|
@@ -5,8 +6,8 @@ export interface AttestorLogEntry {
|
|
|
5
6
|
msg: string;
|
|
6
7
|
ctx?: Record<string, unknown>;
|
|
7
8
|
}
|
|
8
|
-
/** The attestor's signed claim data + signature, for building the
|
|
9
|
-
*
|
|
9
|
+
/** The attestor's signed claim data + signature, for building the exact legacy
|
|
10
|
+
* Proof a Consumer verifies against the attestor's key. Populated when
|
|
10
11
|
* `includeAttestorClaim` is set (the app's verification runner needs it; the
|
|
11
12
|
* MCP `run_proof` tool ignores it). */
|
|
12
13
|
export interface AttestorClaim {
|
|
@@ -21,6 +22,8 @@ export interface AttestorClaim {
|
|
|
21
22
|
/** Options for {@link runAttestorProof}. */
|
|
22
23
|
export interface RunAttestorProofOptions {
|
|
23
24
|
attestorUrl?: string;
|
|
25
|
+
/** Optional session-bound authorization generated by the trusted backend. */
|
|
26
|
+
authRequest?: proto.AuthenticationRequest;
|
|
24
27
|
/** When resolving the key from env/.env/file, the address the caller expects
|
|
25
28
|
* to sign with (sanity check). Ignored when `ownerPrivateKey` is given. */
|
|
26
29
|
ownerAddress?: string;
|
|
@@ -33,7 +36,7 @@ export interface RunAttestorProofOptions {
|
|
|
33
36
|
* want gnark's speed pin it explicitly (for example, the verify runner). */
|
|
34
37
|
zkEngine?: 'gnark' | 'snarkjs' | 'stwo';
|
|
35
38
|
/** Extract the structured {@link AttestorClaim} (claim + attestor signature)
|
|
36
|
-
* onto the result for
|
|
39
|
+
* onto the result for legacy Proof construction. */
|
|
37
40
|
includeAttestorClaim?: boolean;
|
|
38
41
|
}
|
|
39
42
|
export interface ProofResult {
|
|
@@ -44,7 +47,7 @@ export interface ProofResult {
|
|
|
44
47
|
owner?: string;
|
|
45
48
|
verified: boolean;
|
|
46
49
|
error?: string;
|
|
47
|
-
/** Structured claim + signature for
|
|
50
|
+
/** Structured claim + signature for legacy Proof verification. Set only when
|
|
48
51
|
* `includeAttestorClaim` was requested and the claim carries signatures. */
|
|
49
52
|
attestorClaim?: AttestorClaim;
|
|
50
53
|
/** Set to `'missing-owner-key'` when no credential was available to
|
package/lib/proof/attestor.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// revealed slice; jsonPath/xPath live on responseRedactions (what the prover
|
|
7
7
|
// reveals), never on responseMatches.
|
|
8
8
|
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
9
|
-
import { createClaimOnAttestor } from '@reclaimprotocol/attestor-core';
|
|
9
|
+
import { createClaimOnAttestor, } from '@reclaimprotocol/attestor-core';
|
|
10
10
|
// Side-effect: make external domhandler nodes DOM-like so attestor-core's
|
|
11
11
|
// bundled `xpath` accepts them (see patch-domhandler.ts). Must load before any
|
|
12
12
|
// proof runs its response-redaction extraction.
|
|
@@ -158,7 +158,10 @@ export async function runAttestorProof(provider, secrets, opts = {}) {
|
|
|
158
158
|
headers: secrets,
|
|
159
159
|
},
|
|
160
160
|
ownerPrivateKey,
|
|
161
|
-
client: {
|
|
161
|
+
client: {
|
|
162
|
+
url: resolveAttestorUrl(opts.attestorUrl),
|
|
163
|
+
...(opts.authRequest ? { authRequest: opts.authRequest } : {}),
|
|
164
|
+
},
|
|
162
165
|
zkEngine: opts.zkEngine ?? 'stwo',
|
|
163
166
|
...(capture !== undefined && { logger: capture.logger }),
|
|
164
167
|
});
|
|
@@ -190,8 +193,8 @@ export async function runAttestorProof(provider, secrets, opts = {}) {
|
|
|
190
193
|
if (owner !== undefined) {
|
|
191
194
|
result.owner = owner;
|
|
192
195
|
}
|
|
193
|
-
// Structured claim + signature so the result JWS can carry
|
|
194
|
-
//
|
|
196
|
+
// Structured claim + signature so the result JWS can carry the exact
|
|
197
|
+
// legacy Proof the Consumer verifies independently.
|
|
195
198
|
if (opts.includeAttestorClaim) {
|
|
196
199
|
const sigs = claim.signatures;
|
|
197
200
|
if (claimData
|
package/lib/proof/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type { AttestorClaim, AttestorLogEntry, ProofResult, RunAttestorProofOptions, } from './attestor.ts';
|
|
2
2
|
export { runAttestorProof, tailLogs, warnsAndErrors } from './attestor.ts';
|
|
3
|
+
export { createAttestorAuthentication, type AttestorAuthentication, } from './attestor-auth.ts';
|
|
3
4
|
export { buildHttpParams, ensureAttestorCrypto, type ParsedClaim, parseClaim, resolveAttestorUrl, } from './proof-core.ts';
|
|
4
5
|
export { type HttpParams, type HttpSecretParams, type PopcornProveOptions, popcornProve, popcornValidateExtraction, type ValidateExtractionResult, } from './pod-prover.ts';
|
|
5
6
|
export { ensureZkFiles } from './zk-files.ts';
|
package/lib/proof/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { runAttestorProof, tailLogs, warnsAndErrors } from "./attestor.js";
|
|
2
|
+
export { createAttestorAuthentication, } from "./attestor-auth.js";
|
|
2
3
|
export { buildHttpParams, ensureAttestorCrypto, parseClaim, resolveAttestorUrl, } from "./proof-core.js";
|
|
3
4
|
export { popcornProve, popcornValidateExtraction, } from "./pod-prover.js";
|
|
4
5
|
export { ensureZkFiles } from "./zk-files.js";
|
|
@@ -16,6 +16,8 @@ export interface PopcornProveOptions {
|
|
|
16
16
|
secretParams: HttpSecretParams;
|
|
17
17
|
/** Session context, stringified into the claim context if given. */
|
|
18
18
|
context?: Record<string, unknown>;
|
|
19
|
+
/** Base64 JSON authorization forwarded by reclaim-tee to the attestor. */
|
|
20
|
+
authRequest?: string;
|
|
19
21
|
fetchImpl?: typeof fetch;
|
|
20
22
|
}
|
|
21
23
|
export interface ValidateExtractionResult {
|
package/lib/proof/pod-prover.js
CHANGED
|
@@ -26,10 +26,9 @@
|
|
|
26
26
|
*
|
|
27
27
|
* The pod API is unauthenticated (gateway/session is the trust boundary); call
|
|
28
28
|
* it over the same internal host used for CDP. Consumers verify
|
|
29
|
-
* `claim_signature`
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* `result_signature` is unused by any consumer.
|
|
29
|
+
* `claim_signature` with the legacy SDK's `verifyProof`, so this hands back an
|
|
30
|
+
* {@link AttestorClaim} that the app places in the exact legacy `Proof` shape.
|
|
31
|
+
* `result_signature` is unused by consumers.
|
|
33
32
|
*/
|
|
34
33
|
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
35
34
|
/**
|
|
@@ -75,7 +74,7 @@ function b64ToBytes(b64) {
|
|
|
75
74
|
* the reason (never throws for an expected proof failure).
|
|
76
75
|
*/
|
|
77
76
|
export async function popcornProve(opts) {
|
|
78
|
-
const { apiUrl, params, secretParams, context } = opts;
|
|
77
|
+
const { apiUrl, params, secretParams, context, authRequest } = opts;
|
|
79
78
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
80
79
|
const base = apiUrl.replace(/\/$/, '');
|
|
81
80
|
const providerParamsJson = JSON.stringify({
|
|
@@ -83,6 +82,7 @@ export async function popcornProve(opts) {
|
|
|
83
82
|
params,
|
|
84
83
|
secretParams,
|
|
85
84
|
...(context ? { context: JSON.stringify(context) } : {}),
|
|
85
|
+
...(authRequest ? { authRequest } : {}),
|
|
86
86
|
});
|
|
87
87
|
const fail = (error) => ({
|
|
88
88
|
proof: undefined,
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Shared by both publish tools so the two can't drift apart.
|
|
16
16
|
*/
|
|
17
|
+
/** Auth behavior shared by intercepted and script-requested claims. */
|
|
18
|
+
export declare const COOKIE_ATTACHMENT_HINT: string;
|
|
17
19
|
/** What the script is and where the real guidance lives. */
|
|
18
20
|
export declare const USER_SCRIPT_HINT: string;
|
|
19
21
|
/** Prefixes the `allowedJsRequests` / `allowedInjectedRequestData` field. The
|
|
@@ -14,12 +14,18 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Shared by both publish tools so the two can't drift apart.
|
|
16
16
|
*/
|
|
17
|
+
/** Auth behavior shared by intercepted and script-requested claims. */
|
|
18
|
+
export const COOKIE_ATTACHMENT_HINT = 'For an intercepted request or window.Reclaim.requestClaim, the verification '
|
|
19
|
+
+ 'client automatically attaches cookies applicable to the request URL to '
|
|
20
|
+
+ 'the claim sent to the attestor. Do not read, hardcode, or copy Cookie '
|
|
21
|
+
+ 'values into the script or public params. ';
|
|
17
22
|
/** What the script is and where the real guidance lives. */
|
|
18
23
|
export const USER_SCRIPT_HINT = 'Plain JavaScript injected into the provider page before every page load. '
|
|
19
24
|
+ 'Use standard browser APIs (window.location, element.click(), forms) to '
|
|
20
25
|
+ 'drive login/navigation so the target request fires — the client already '
|
|
21
26
|
+ 'intercepts page requests, and window.Reclaim is only for rare edge cases, '
|
|
22
|
-
+ 'not navigation.
|
|
27
|
+
+ 'not navigation. ' + COOKIE_ATTACHMENT_HINT
|
|
28
|
+
+ 'Full guide: how_it_works({ topic: "user-script" }).';
|
|
23
29
|
/** Prefixes the `allowedJsRequests` / `allowedInjectedRequestData` field. The
|
|
24
30
|
* trap it describes is silent and total — a mis-declared entry makes
|
|
25
31
|
* `verifyProof({ providerId })` reject EVERY real proof — so the warning
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface AttachedBrowser {
|
|
2
|
+
mode: string;
|
|
3
|
+
browserSessionId?: string;
|
|
4
|
+
}
|
|
5
|
+
interface BrowserRetestOptions {
|
|
6
|
+
initialUrl: string;
|
|
7
|
+
hasUserScript: boolean;
|
|
8
|
+
hasInterceptor: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Advisory follow-up when a browser remains after provider creation. */
|
|
11
|
+
export declare function postPublishBrowserNotes(attached: AttachedBrowser | undefined, options: BrowserRetestOptions): string[];
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Advisory follow-up when a browser remains after provider creation. */
|
|
2
|
+
export function postPublishBrowserNotes(attached, options) {
|
|
3
|
+
if (!attached) {
|
|
4
|
+
return [];
|
|
5
|
+
}
|
|
6
|
+
const identity = `A "${attached.mode}" browser session is still attached`
|
|
7
|
+
+ (attached.browserSessionId
|
|
8
|
+
? ` (browserSessionId: ${attached.browserSessionId})`
|
|
9
|
+
: '');
|
|
10
|
+
const retest = 'Before disposing it, best-effort test the '
|
|
11
|
+
+ 'already-authenticated path only when this profile is still signed in: '
|
|
12
|
+
+ `navigate to initialUrl (${options.initialUrl}) without clearing cookies `
|
|
13
|
+
+ 'or forcing another login. '
|
|
14
|
+
+ (options.hasUserScript
|
|
15
|
+
? 'Use test_user_script with this version\'s script and initialUrl; '
|
|
16
|
+
: 'Navigate to initialUrl normally; ')
|
|
17
|
+
+ 'confirm every required target request still fires, then replay and '
|
|
18
|
+
+ 'prove each one when the existing capture has enough data. Check that '
|
|
19
|
+
+ 'the flow handles '
|
|
20
|
+
+ 'both a fresh sign-in and an already-signed-in landing from current page '
|
|
21
|
+
+ 'state, not only a login transition. This is advisory: skip it when no '
|
|
22
|
+
+ 'authenticated state is available. An inconclusive result must not block '
|
|
23
|
+
+ 'disposal or trigger a republish.';
|
|
24
|
+
const interceptor = options.hasInterceptor
|
|
25
|
+
? 'The attached authoring browser validates page behavior and the user '
|
|
26
|
+
+ 'script, but it does not prove compatibility with an in-page '
|
|
27
|
+
+ 'production interceptor. '
|
|
28
|
+
+ 'For the InApp SDK, verifier app, or browser extension, keep any '
|
|
29
|
+
+ 'client-specific interceptor verification as a separate follow-up.'
|
|
30
|
+
: undefined;
|
|
31
|
+
const dispose = 'Ask the developer whether they want to keep the browser '
|
|
32
|
+
+ 'open '
|
|
33
|
+
+ 'for this retest or further edits. Only call dispose_browser after they '
|
|
34
|
+
+ 'confirm they are done.';
|
|
35
|
+
return [
|
|
36
|
+
identity + '. ' + retest,
|
|
37
|
+
...(interceptor ? [interceptor] : []),
|
|
38
|
+
dispose,
|
|
39
|
+
];
|
|
40
|
+
}
|