@reclaimprotocol/agent 0.1.5 → 0.1.6
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/lib/authoring/backend.d.ts +0 -24
- package/lib/authoring/find.d.ts +3 -0
- package/lib/authoring/find.js +54 -0
- package/lib/authoring/index.d.ts +3 -3
- package/lib/authoring/index.js +4 -3
- package/lib/authoring/propose.d.ts +3 -0
- package/lib/authoring/propose.js +109 -0
- package/lib/authoring/replay.d.ts +10 -0
- package/lib/authoring/replay.js +34 -0
- package/lib/authoring/tools.d.ts +13 -6
- package/lib/authoring/tools.js +108 -293
- package/lib/authoring/util.d.ts +27 -0
- package/lib/authoring/util.js +21 -0
- package/lib/consts.d.ts +6 -1
- package/lib/consts.js +9 -2
- package/lib/mcp/server.d.ts +2 -41
- package/lib/mcp/server.js +6 -34
- package/lib/mcp/start.js +4 -2
- package/lib/mcp/tools/agent/analyze.js +3 -3
- package/lib/mcp/tools/agent/build.d.ts +4 -4
- package/lib/mcp/tools/agent/build.js +13 -15
- package/lib/mcp/tools/agent/capture.d.ts +13 -1
- package/lib/mcp/tools/agent/capture.js +18 -85
- package/lib/mcp/tools/agent/eval.d.ts +5 -2
- package/lib/mcp/tools/agent/eval.js +11 -121
- package/lib/mcp/tools/agent/find.d.ts +10 -3
- package/lib/mcp/tools/agent/find.js +25 -31
- package/lib/mcp/tools/agent/proof.d.ts +13 -3
- package/lib/mcp/tools/agent/proof.js +81 -132
- package/lib/mcp/tools/agent/propose.d.ts +10 -9
- package/lib/mcp/tools/agent/propose.js +87 -64
- package/lib/mcp/tools/agent/replay.d.ts +10 -3
- package/lib/mcp/tools/agent/replay.js +29 -36
- package/lib/mcp/tools/agent/session.d.ts +1 -3
- package/lib/mcp/tools/agent/session.js +5 -5
- package/lib/mcp/tools/agent/shared.d.ts +5 -3
- package/lib/mcp/tools/agent/shared.js +29 -19
- package/lib/old/client.d.ts +35 -0
- package/lib/old/client.js +18 -0
- package/lib/old/login-cdp.d.ts +24 -5
- package/lib/old/login-cdp.js +29 -15
- package/lib/old/sdk-client.d.ts +39 -0
- package/lib/old/sdk-client.js +85 -0
- package/lib/old/to-register.d.ts +69 -0
- package/lib/old/to-register.js +52 -2
- package/lib/old/tools/authenticate.js +3 -7
- package/lib/old/tools/build.d.ts +6 -1
- package/lib/old/tools/build.js +11 -1
- package/lib/old/tools/get-provider-info.d.ts +16 -0
- package/lib/old/tools/get-provider-info.js +73 -0
- package/lib/old/tools/get-version-config.d.ts +17 -0
- package/lib/old/tools/get-version-config.js +47 -0
- package/lib/old/tools/publish.js +189 -2
- package/lib/old/tools/update-provider-metadata.d.ts +16 -0
- package/lib/old/tools/update-provider-metadata.js +61 -0
- package/lib/proof/attestor.d.ts +6 -3
- package/lib/proof/attestor.js +10 -1
- package/lib/proof/debug-response-dump.d.ts +4 -0
- package/lib/proof/debug-response-dump.js +55 -0
- package/lib/proof/patch-domhandler.d.ts +1 -0
- package/lib/proof/patch-domhandler.js +59 -0
- package/lib/proof/zk-files.js +7 -6
- package/lib/provider/dom-xpath.d.ts +17 -0
- package/lib/provider/dom-xpath.js +187 -0
- package/lib/provider/draft.d.ts +30 -2
- package/lib/provider/draft.js +117 -28
- package/lib/provider/index.d.ts +4 -0
- package/lib/provider/index.js +7 -0
- package/lib/provider/injection-doc.js +6 -0
- package/lib/provider/matcher.d.ts +17 -20
- package/lib/provider/matcher.js +394 -280
- package/lib/provider/schema.d.ts +7 -5
- package/lib/tools/registry.d.ts +14 -0
- package/lib/tools/registry.js +32 -0
- package/package.json +9 -1
- package/skill/SKILL.builder.md +42 -23
- package/skill/SKILL.old.md +6 -2
- package/skill/builder-mode.md +1 -1
- package/skill/injection-api.md +117 -0
- package/src/authoring/backend.ts +98 -0
- package/src/authoring/index.ts +20 -0
- package/src/authoring/tools.ts +273 -0
- package/src/authoring/util.ts +42 -0
- package/src/consts.ts +15 -2
- package/src/mcp/server.ts +11 -78
- package/src/mcp/start.ts +4 -1
- package/src/mcp/tools/agent/analyze.ts +3 -3
- package/src/mcp/tools/agent/build.ts +13 -21
- package/src/mcp/tools/agent/capture.ts +31 -115
- package/src/mcp/tools/agent/eval.ts +19 -155
- package/src/mcp/tools/agent/find.ts +33 -36
- package/src/mcp/tools/agent/proof.ts +87 -160
- package/src/mcp/tools/agent/propose.ts +93 -85
- package/src/mcp/tools/agent/replay.ts +32 -42
- package/src/mcp/tools/agent/session.ts +4 -5
- package/src/mcp/tools/agent/shared.ts +152 -0
- package/src/old/client.ts +56 -0
- package/src/old/login-cdp.ts +29 -23
- package/src/old/sdk-client.ts +105 -0
- package/src/old/to-register.ts +98 -6
- package/src/old/tools/authenticate.ts +3 -11
- package/src/old/tools/build.ts +12 -0
- package/src/old/tools/get-provider-info.ts +91 -0
- package/src/old/tools/get-version-config.ts +56 -0
- package/src/old/tools/publish.ts +244 -1
- package/src/old/tools/update-provider-metadata.ts +72 -0
- package/src/proof/attestor.ts +16 -4
- package/src/proof/debug-response-dump.ts +70 -0
- package/src/proof/patch-domhandler.ts +68 -0
- package/src/proof/zk-files.ts +7 -6
- package/src/provider/dom-xpath.ts +225 -0
- package/src/provider/draft.ts +168 -35
- package/src/provider/index.ts +8 -0
- package/src/provider/injection-doc.ts +6 -0
- package/src/provider/matcher.ts +477 -357
- package/src/provider/schema.ts +7 -5
- package/src/tools/registry.ts +92 -0
package/lib/old/client.d.ts
CHANGED
|
@@ -32,12 +32,36 @@ export interface RegisterProviderBody {
|
|
|
32
32
|
useProxy?: boolean;
|
|
33
33
|
providerType?: 'PRIVATE' | 'PUBLIC';
|
|
34
34
|
requestData: unknown[];
|
|
35
|
+
/** Page-injection script (old-devtools name for the builder's
|
|
36
|
+
* `webSettings.jsUserScripts`). Top-level on register; nested under
|
|
37
|
+
* `providerConfig` on the config (add-version) body instead. */
|
|
38
|
+
customInjection?: string;
|
|
39
|
+
/** Accepted at register too (unlike most `providerConfig` extras, which
|
|
40
|
+
* the register controller only reads on config/add-version). */
|
|
41
|
+
userAgent?: {
|
|
42
|
+
ios?: string;
|
|
43
|
+
android?: string;
|
|
44
|
+
};
|
|
45
|
+
/** Accepted at register too. */
|
|
46
|
+
pageTitle?: string;
|
|
35
47
|
}
|
|
36
48
|
export interface UpdateProviderConfigBody {
|
|
37
49
|
providerConfig: Record<string, unknown>;
|
|
38
50
|
version: string;
|
|
39
51
|
versionInfo: string;
|
|
40
52
|
}
|
|
53
|
+
/** Body for `POST /api/providers/:providerId` (`updateProviderMetadata`) —
|
|
54
|
+
* a genuine PARTIAL update on the PROVIDER document itself (name/
|
|
55
|
+
* description/providerType/tags/isActive), distinct from both `register`
|
|
56
|
+
* (creates the provider) and `config` (adds an immutable new version). Only
|
|
57
|
+
* the fields present get changed; everything else is left untouched. */
|
|
58
|
+
export interface UpdateProviderMetadataBody {
|
|
59
|
+
name?: string;
|
|
60
|
+
description?: string;
|
|
61
|
+
providerType?: 'PRIVATE' | 'PUBLIC';
|
|
62
|
+
tags?: string[];
|
|
63
|
+
isActive?: boolean;
|
|
64
|
+
}
|
|
41
65
|
export interface MyProvidersQuery {
|
|
42
66
|
pageKey?: number;
|
|
43
67
|
pageSize?: number;
|
|
@@ -71,6 +95,17 @@ export declare class ReclaimOldClient {
|
|
|
71
95
|
registerProvider(body: RegisterProviderBody): Promise<unknown>;
|
|
72
96
|
updateProviderConfig(providerId: string, body: UpdateProviderConfigBody): Promise<unknown>;
|
|
73
97
|
listVersions(providerId: string): Promise<unknown>;
|
|
98
|
+
/** Full `providerConfig` (loginUrl, customInjection, requestData, ...) for
|
|
99
|
+
* one version — the latest when `version` is omitted. Readable for ANY
|
|
100
|
+
* non-archived provider (not owner-restricted), same as the dashboard's
|
|
101
|
+
* edit-form prefill fetch. */
|
|
102
|
+
getProviderConfig(providerId: string, version?: string): Promise<unknown>;
|
|
103
|
+
/** Partial update on the PROVIDER document itself — name/description/
|
|
104
|
+
* providerType/tags/isActive, after creation. The backend 403s if you
|
|
105
|
+
* don't own the provider (and aren't admin); other providerConfig-style
|
|
106
|
+
* fields sent here are simply ignored (this endpoint only reads the
|
|
107
|
+
* ones above from the body). */
|
|
108
|
+
updateProviderMetadata(providerId: string, body: UpdateProviderMetadataBody): Promise<unknown>;
|
|
74
109
|
/** Analytics logs for a verification session, from the logs service (a
|
|
75
110
|
* DIFFERENT host than the devtools API — hence its own fetch, not
|
|
76
111
|
* `#request`). Attaches the caller identity when one is set; queried
|
package/lib/old/client.js
CHANGED
|
@@ -49,6 +49,24 @@ export class ReclaimOldClient {
|
|
|
49
49
|
listVersions(providerId) {
|
|
50
50
|
return this.#request('GET', `/providers/${encodeURIComponent(providerId)}/versions`, { auth: true });
|
|
51
51
|
}
|
|
52
|
+
/** Full `providerConfig` (loginUrl, customInjection, requestData, ...) for
|
|
53
|
+
* one version — the latest when `version` is omitted. Readable for ANY
|
|
54
|
+
* non-archived provider (not owner-restricted), same as the dashboard's
|
|
55
|
+
* edit-form prefill fetch. */
|
|
56
|
+
getProviderConfig(providerId, version) {
|
|
57
|
+
return this.#request('GET', `/providers/${encodeURIComponent(providerId)}/config`, {
|
|
58
|
+
auth: true,
|
|
59
|
+
query: version ? { versionNumber: version } : undefined,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Partial update on the PROVIDER document itself — name/description/
|
|
63
|
+
* providerType/tags/isActive, after creation. The backend 403s if you
|
|
64
|
+
* don't own the provider (and aren't admin); other providerConfig-style
|
|
65
|
+
* fields sent here are simply ignored (this endpoint only reads the
|
|
66
|
+
* ones above from the body). */
|
|
67
|
+
updateProviderMetadata(providerId, body) {
|
|
68
|
+
return this.#request('POST', `/providers/${encodeURIComponent(providerId)}`, { body, auth: true });
|
|
69
|
+
}
|
|
52
70
|
/** Analytics logs for a verification session, from the logs service (a
|
|
53
71
|
* DIFFERENT host than the devtools API — hence its own fetch, not
|
|
54
72
|
* `#request`). Attaches the caller identity when one is set; queried
|
package/lib/old/login-cdp.d.ts
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the Firebase ID token the dashboard persisted after login.
|
|
3
|
+
*
|
|
4
|
+
* The frontend uses `browserLocalPersistence`, which the Firebase JS SDK
|
|
5
|
+
* implements on `localStorage` under a `firebase:authUser:<apiKey>:[DEFAULT]`
|
|
6
|
+
* key whose JSON value carries `stsTokenManager.accessToken`. We scan for any
|
|
7
|
+
* such key (so we never hard-code the project apiKey) and fall back to the
|
|
8
|
+
* IndexedDB persistence layout (`firebaseLocalStorageDb` /
|
|
9
|
+
* `firebaseLocalStorage`) in case a build switches persistence. Returns the
|
|
10
|
+
* token string, or null when the user has not finished logging in yet.
|
|
11
|
+
*
|
|
12
|
+
* The CDP-launched Chrome reuses a PERSISTENT profile dir across runs, so a
|
|
13
|
+
* stale `firebase:authUser:*` entry from an earlier (now-expired, ~1h TTL)
|
|
14
|
+
* session is often still sitting in storage the instant the page loads —
|
|
15
|
+
* before the developer has clicked anything. Returning that immediately
|
|
16
|
+
* makes the poll "succeed" on tick one, closing the tab before the developer
|
|
17
|
+
* can sign in, and the captured token then fails backend verification. So
|
|
18
|
+
* every candidate's JWT `exp` claim is checked here (in-page, where `atob` is
|
|
19
|
+
* available) and stale ones are skipped — the loop keeps polling until a
|
|
20
|
+
* genuinely fresh token shows up from an interactive sign-in.
|
|
21
|
+
*
|
|
22
|
+
* Evaluated in-page with `awaitPromise` so the async IndexedDB branch resolves.
|
|
23
|
+
*/
|
|
24
|
+
export declare const EXTRACT_TOKEN_JS = "(async () => {\n\tconst isFresh = (token) => {\n\t\ttry {\n\t\t\tconst payload = JSON.parse(\n\t\t\t\tatob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/'))\n\t\t\t)\n\t\t\t// No exp claim to check \u2014 don't block a token we can't evaluate.\n\t\t\tif (typeof payload.exp !== 'number') return true\n\t\t\t// 60s buffer so a token that's about to expire isn't treated as fresh.\n\t\t\treturn payload.exp * 1000 > Date.now() + 60000\n\t\t} catch (e) {\n\t\t\treturn true\n\t\t}\n\t}\n\ttry {\n\t\tfor (let i = 0; i < localStorage.length; i++) {\n\t\t\tconst k = localStorage.key(i)\n\t\t\tif (k && k.indexOf('firebase:authUser:') === 0) {\n\t\t\t\tconst v = JSON.parse(localStorage.getItem(k) || 'null')\n\t\t\t\tconst t = v && v.stsTokenManager && v.stsTokenManager.accessToken\n\t\t\t\tif (t && isFresh(t)) return t\n\t\t\t}\n\t\t}\n\t} catch (e) {}\n\ttry {\n\t\treturn await new Promise((resolve) => {\n\t\t\tconst req = indexedDB.open('firebaseLocalStorageDb')\n\t\t\treq.onerror = () => resolve(null)\n\t\t\treq.onsuccess = () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst db = req.result\n\t\t\t\t\tif (!db.objectStoreNames.contains('firebaseLocalStorage')) {\n\t\t\t\t\t\tresolve(null); return\n\t\t\t\t\t}\n\t\t\t\t\tconst store = db\n\t\t\t\t\t\t.transaction('firebaseLocalStorage', 'readonly')\n\t\t\t\t\t\t.objectStore('firebaseLocalStorage')\n\t\t\t\t\tconst all = store.getAll()\n\t\t\t\t\tall.onerror = () => resolve(null)\n\t\t\t\t\tall.onsuccess = () => {\n\t\t\t\t\t\tfor (const row of (all.result || [])) {\n\t\t\t\t\t\t\tconst m = row && row.value && row.value.stsTokenManager\n\t\t\t\t\t\t\tif (m && m.accessToken && isFresh(m.accessToken)) {\n\t\t\t\t\t\t\t\tresolve(m.accessToken); return\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolve(null)\n\t\t\t\t\t}\n\t\t\t\t} catch (e) { resolve(null) }\n\t\t\t}\n\t\t})\n\t} catch (e) { return null }\n})()";
|
|
1
25
|
export interface CdpLoginResult {
|
|
2
26
|
token: string;
|
|
3
27
|
/** True when we launched (and then closed) a dedicated Chrome just for
|
|
@@ -17,9 +41,4 @@ export declare function captureFirebaseTokenViaCdp(opts: {
|
|
|
17
41
|
loginUrl?: string;
|
|
18
42
|
timeoutMs?: number;
|
|
19
43
|
pollMs?: number;
|
|
20
|
-
/** Called every ~10s while waiting for the human to finish signing in, so
|
|
21
|
-
* MCP clients that track progress on this call can reset their own
|
|
22
|
-
* request timeout instead of giving up on a login that's still in
|
|
23
|
-
* flight (see `ToolContext` in `mcp/server.ts`). */
|
|
24
|
-
onProgress?: (message: string) => Promise<void>;
|
|
25
44
|
}): Promise<CdpLoginResult>;
|
package/lib/old/login-cdp.js
CHANGED
|
@@ -13,16 +13,39 @@ import { DEFAULT_LOGIN_URL, oldLoginUrl } from "../consts.js";
|
|
|
13
13
|
* `firebaseLocalStorage`) in case a build switches persistence. Returns the
|
|
14
14
|
* token string, or null when the user has not finished logging in yet.
|
|
15
15
|
*
|
|
16
|
+
* The CDP-launched Chrome reuses a PERSISTENT profile dir across runs, so a
|
|
17
|
+
* stale `firebase:authUser:*` entry from an earlier (now-expired, ~1h TTL)
|
|
18
|
+
* session is often still sitting in storage the instant the page loads —
|
|
19
|
+
* before the developer has clicked anything. Returning that immediately
|
|
20
|
+
* makes the poll "succeed" on tick one, closing the tab before the developer
|
|
21
|
+
* can sign in, and the captured token then fails backend verification. So
|
|
22
|
+
* every candidate's JWT `exp` claim is checked here (in-page, where `atob` is
|
|
23
|
+
* available) and stale ones are skipped — the loop keeps polling until a
|
|
24
|
+
* genuinely fresh token shows up from an interactive sign-in.
|
|
25
|
+
*
|
|
16
26
|
* Evaluated in-page with `awaitPromise` so the async IndexedDB branch resolves.
|
|
17
27
|
*/
|
|
18
|
-
const EXTRACT_TOKEN_JS = `(async () => {
|
|
28
|
+
export const EXTRACT_TOKEN_JS = `(async () => {
|
|
29
|
+
const isFresh = (token) => {
|
|
30
|
+
try {
|
|
31
|
+
const payload = JSON.parse(
|
|
32
|
+
atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/'))
|
|
33
|
+
)
|
|
34
|
+
// No exp claim to check — don't block a token we can't evaluate.
|
|
35
|
+
if (typeof payload.exp !== 'number') return true
|
|
36
|
+
// 60s buffer so a token that's about to expire isn't treated as fresh.
|
|
37
|
+
return payload.exp * 1000 > Date.now() + 60000
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
}
|
|
19
42
|
try {
|
|
20
43
|
for (let i = 0; i < localStorage.length; i++) {
|
|
21
44
|
const k = localStorage.key(i)
|
|
22
45
|
if (k && k.indexOf('firebase:authUser:') === 0) {
|
|
23
46
|
const v = JSON.parse(localStorage.getItem(k) || 'null')
|
|
24
47
|
const t = v && v.stsTokenManager && v.stsTokenManager.accessToken
|
|
25
|
-
if (t) return t
|
|
48
|
+
if (t && isFresh(t)) return t
|
|
26
49
|
}
|
|
27
50
|
}
|
|
28
51
|
} catch (e) {}
|
|
@@ -44,7 +67,9 @@ const EXTRACT_TOKEN_JS = `(async () => {
|
|
|
44
67
|
all.onsuccess = () => {
|
|
45
68
|
for (const row of (all.result || [])) {
|
|
46
69
|
const m = row && row.value && row.value.stsTokenManager
|
|
47
|
-
if (m && m.accessToken
|
|
70
|
+
if (m && m.accessToken && isFresh(m.accessToken)) {
|
|
71
|
+
resolve(m.accessToken); return
|
|
72
|
+
}
|
|
48
73
|
}
|
|
49
74
|
resolve(null)
|
|
50
75
|
}
|
|
@@ -65,8 +90,6 @@ export async function captureFirebaseTokenViaCdp(opts) {
|
|
|
65
90
|
const loginUrl = opts.loginUrl || oldLoginUrl() || DEFAULT_LOGIN_URL;
|
|
66
91
|
const timeoutMs = opts.timeoutMs ?? 180_000;
|
|
67
92
|
const pollMs = opts.pollMs ?? 1000;
|
|
68
|
-
const onProgress = opts.onProgress ?? (async () => { });
|
|
69
|
-
const progressIntervalMs = 10_000;
|
|
70
93
|
const cfg = new ConfigStore().read()?.browser_agent;
|
|
71
94
|
const mode = cfg?.attachMode ?? 'dedicated';
|
|
72
95
|
const port = cfg?.port ?? 9222;
|
|
@@ -91,9 +114,7 @@ export async function captureFirebaseTokenViaCdp(opts) {
|
|
|
91
114
|
try {
|
|
92
115
|
const session = await handles.attachToTab(targetId);
|
|
93
116
|
const { Runtime } = session;
|
|
94
|
-
const
|
|
95
|
-
const deadline = startedAt + timeoutMs;
|
|
96
|
-
let lastProgressAt = startedAt;
|
|
117
|
+
const deadline = Date.now() + timeoutMs;
|
|
97
118
|
while (Date.now() < deadline) {
|
|
98
119
|
const r = await Runtime.evaluate({
|
|
99
120
|
expression: EXTRACT_TOKEN_JS,
|
|
@@ -104,13 +125,6 @@ export async function captureFirebaseTokenViaCdp(opts) {
|
|
|
104
125
|
if (typeof value === 'string' && value.length > 0) {
|
|
105
126
|
return { token: value, closedBrowser: launchedByUs };
|
|
106
127
|
}
|
|
107
|
-
const now = Date.now();
|
|
108
|
-
if (now - lastProgressAt >= progressIntervalMs) {
|
|
109
|
-
lastProgressAt = now;
|
|
110
|
-
const remainingSec = Math.round((deadline - now) / 1000);
|
|
111
|
-
await onProgress(`Still waiting for sign-in at ${loginUrl} ` +
|
|
112
|
-
`(${remainingSec}s left before this call times out).`);
|
|
113
|
-
}
|
|
114
128
|
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
115
129
|
}
|
|
116
130
|
throw new Error('Timed out waiting for login. Finish signing in at the opened ' +
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default origin of the PUBLIC production read API that the Reclaim
|
|
3
|
+
* verification SDK/attestor itself calls at runtime to fetch a provider's
|
|
4
|
+
* recipe — a DIFFERENT service from the old-devtools dashboard backend
|
|
5
|
+
* (`ReclaimOldClient`, `devapi.reclaimprotocol.org`). Both endpoints below are
|
|
6
|
+
* unauthenticated reads (no `authenticateToken` middleware on this backend's
|
|
7
|
+
* routes), so this client needs no identity. Override with
|
|
8
|
+
* `RECLAIM_SDK_API_URL`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_SDK_API_URL = "https://api.reclaimprotocol.org";
|
|
11
|
+
/**
|
|
12
|
+
* Minimal read-only client for the public provider-info endpoints on the SDK
|
|
13
|
+
* backend (`reclaim-sdk-backend`). Two distinct reads, on purpose:
|
|
14
|
+
*
|
|
15
|
+
* - `getProvider` (`GET /providers/:id`) — the full provider row merged
|
|
16
|
+
* with its version's WHOLE providerConfig, `customInjection` included.
|
|
17
|
+
* - `getProviderConfigs` (`GET /providers/:id/configs`) — the trimmed
|
|
18
|
+
* recipe the attestor actually consumes at verification time: a fixed
|
|
19
|
+
* field projection that explicitly OMITS `customInjection` and adds a
|
|
20
|
+
* computed `isScriptRequestingClaim` flag instead.
|
|
21
|
+
*
|
|
22
|
+
* Errors are thrown as `@hapi/boom` so `remapErrorAsResponse` renders them
|
|
23
|
+
* the same way as every other tool in this package.
|
|
24
|
+
*/
|
|
25
|
+
export declare class ReclaimSdkClient {
|
|
26
|
+
#private;
|
|
27
|
+
readonly baseUrl: string;
|
|
28
|
+
constructor(opts?: {
|
|
29
|
+
baseUrl?: string;
|
|
30
|
+
fetch?: typeof fetch;
|
|
31
|
+
});
|
|
32
|
+
/** Full provider + providerConfig (includes `customInjection`). Latest
|
|
33
|
+
* version when `versionNumber` is omitted. */
|
|
34
|
+
getProvider(providerId: string, versionNumber?: string): Promise<unknown>;
|
|
35
|
+
/** Trimmed runtime recipe (no `customInjection`; adds
|
|
36
|
+
* `isScriptRequestingClaim`). Latest version when `versionNumber` is
|
|
37
|
+
* omitted. */
|
|
38
|
+
getProviderConfigs(providerId: string, versionNumber?: string): Promise<unknown>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { NAME, VERSION } from "../pkg.js";
|
|
3
|
+
/**
|
|
4
|
+
* Default origin of the PUBLIC production read API that the Reclaim
|
|
5
|
+
* verification SDK/attestor itself calls at runtime to fetch a provider's
|
|
6
|
+
* recipe — a DIFFERENT service from the old-devtools dashboard backend
|
|
7
|
+
* (`ReclaimOldClient`, `devapi.reclaimprotocol.org`). Both endpoints below are
|
|
8
|
+
* unauthenticated reads (no `authenticateToken` middleware on this backend's
|
|
9
|
+
* routes), so this client needs no identity. Override with
|
|
10
|
+
* `RECLAIM_SDK_API_URL`.
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_SDK_API_URL = 'https://api.reclaimprotocol.org';
|
|
13
|
+
/**
|
|
14
|
+
* Minimal read-only client for the public provider-info endpoints on the SDK
|
|
15
|
+
* backend (`reclaim-sdk-backend`). Two distinct reads, on purpose:
|
|
16
|
+
*
|
|
17
|
+
* - `getProvider` (`GET /providers/:id`) — the full provider row merged
|
|
18
|
+
* with its version's WHOLE providerConfig, `customInjection` included.
|
|
19
|
+
* - `getProviderConfigs` (`GET /providers/:id/configs`) — the trimmed
|
|
20
|
+
* recipe the attestor actually consumes at verification time: a fixed
|
|
21
|
+
* field projection that explicitly OMITS `customInjection` and adds a
|
|
22
|
+
* computed `isScriptRequestingClaim` flag instead.
|
|
23
|
+
*
|
|
24
|
+
* Errors are thrown as `@hapi/boom` so `remapErrorAsResponse` renders them
|
|
25
|
+
* the same way as every other tool in this package.
|
|
26
|
+
*/
|
|
27
|
+
export class ReclaimSdkClient {
|
|
28
|
+
baseUrl;
|
|
29
|
+
#fetch;
|
|
30
|
+
constructor(opts = {}) {
|
|
31
|
+
this.baseUrl = (opts.baseUrl || DEFAULT_SDK_API_URL).replace(/\/+$/, '');
|
|
32
|
+
this.#fetch = opts.fetch ?? fetch;
|
|
33
|
+
}
|
|
34
|
+
/** Full provider + providerConfig (includes `customInjection`). Latest
|
|
35
|
+
* version when `versionNumber` is omitted. */
|
|
36
|
+
getProvider(providerId, versionNumber) {
|
|
37
|
+
return this.#get(`/providers/${encodeURIComponent(providerId)}`, versionNumber);
|
|
38
|
+
}
|
|
39
|
+
/** Trimmed runtime recipe (no `customInjection`; adds
|
|
40
|
+
* `isScriptRequestingClaim`). Latest version when `versionNumber` is
|
|
41
|
+
* omitted. */
|
|
42
|
+
getProviderConfigs(providerId, versionNumber) {
|
|
43
|
+
return this.#get(`/providers/${encodeURIComponent(providerId)}/configs`, versionNumber);
|
|
44
|
+
}
|
|
45
|
+
async #get(path, versionNumber) {
|
|
46
|
+
let url = `${this.baseUrl}/api${path}`;
|
|
47
|
+
if (versionNumber) {
|
|
48
|
+
url += `?versionNumber=${encodeURIComponent(versionNumber)}`;
|
|
49
|
+
}
|
|
50
|
+
const res = await this.#fetch(url, {
|
|
51
|
+
method: 'GET',
|
|
52
|
+
headers: {
|
|
53
|
+
Accept: 'application/json',
|
|
54
|
+
'User-Agent': `${NAME}/${VERSION}`,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
let json;
|
|
58
|
+
try {
|
|
59
|
+
json = await res.json();
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
json = undefined;
|
|
63
|
+
}
|
|
64
|
+
const failed = !res.ok ||
|
|
65
|
+
(json !== undefined &&
|
|
66
|
+
typeof json === 'object' &&
|
|
67
|
+
json.isSuccess === false);
|
|
68
|
+
if (failed) {
|
|
69
|
+
const message = (json && typeof json === 'object' &&
|
|
70
|
+
typeof json.message === 'string'
|
|
71
|
+
? json.message
|
|
72
|
+
: undefined) || res.statusText || 'SDK backend request failed';
|
|
73
|
+
throw new Boom(message, {
|
|
74
|
+
statusCode: res.ok ? 502 : res.status,
|
|
75
|
+
data: {
|
|
76
|
+
status: res.status,
|
|
77
|
+
title: message,
|
|
78
|
+
detail: message,
|
|
79
|
+
body: json,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return json;
|
|
84
|
+
}
|
|
85
|
+
}
|
package/lib/old/to-register.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReclaimProvider } from '../provider/schema.ts';
|
|
2
|
+
import type { AllowedJsRequest } from '../provider/to-version.ts';
|
|
2
3
|
import type { RegisterProviderBody, UpdateProviderConfigBody } from './client.ts';
|
|
3
4
|
/**
|
|
4
5
|
* Translate a drafted `ReclaimProvider` (from `propose_provider`) into the
|
|
@@ -22,7 +23,74 @@ export interface OldProviderOpts {
|
|
|
22
23
|
/** Defaults to `true` when omitted. Register only (Provider-level flag). */
|
|
23
24
|
useProxy?: boolean;
|
|
24
25
|
providerType?: 'PRIVATE' | 'PUBLIC';
|
|
26
|
+
/** Page-injection script — old-devtools' name for the builder's
|
|
27
|
+
* `webSettings.jsUserScripts`. Top-level on register, nested under
|
|
28
|
+
* `providerConfig` on config (add-version). The backend rejects a few
|
|
29
|
+
* deprecated injection APIs (e.g. `window.payloadData`) with a 400. */
|
|
30
|
+
customInjection?: string;
|
|
31
|
+
/** Accepted at register too (unlike the four fields below, which the
|
|
32
|
+
* backend only accepts on config/add-version). */
|
|
33
|
+
userAgent?: {
|
|
34
|
+
ios?: string;
|
|
35
|
+
android?: string;
|
|
36
|
+
};
|
|
37
|
+
/** Accepted at register too. */
|
|
38
|
+
pageTitle?: string;
|
|
39
|
+
/** Config (add-version) ONLY — the register endpoint hardcodes this to
|
|
40
|
+
* `[]` and ignores whatever is sent. Already old-backend-shaped: build
|
|
41
|
+
* fresh entries with {@link injectedRequestDataFrom}; a carried-forward
|
|
42
|
+
* value (read back from an existing version) passes through unchanged. */
|
|
43
|
+
allowedInjectedRequestData?: OldInjectedRequestData[];
|
|
44
|
+
/** Config (add-version) ONLY — the register endpoint never reads this
|
|
45
|
+
* field from the request body. */
|
|
46
|
+
stepsToFollow?: string;
|
|
47
|
+
/** Config (add-version) ONLY — the register endpoint never reads this
|
|
48
|
+
* field from the request body. */
|
|
49
|
+
useIncognitoWebview?: boolean;
|
|
50
|
+
/** Config (add-version) ONLY — the register endpoint never reads this
|
|
51
|
+
* field from the request body. Free-form JSON. */
|
|
52
|
+
extensionConfig?: unknown;
|
|
25
53
|
}
|
|
54
|
+
interface OldResponseMatch {
|
|
55
|
+
value: string;
|
|
56
|
+
type: string;
|
|
57
|
+
isOptional: boolean;
|
|
58
|
+
order: number;
|
|
59
|
+
}
|
|
60
|
+
interface OldResponseRedaction {
|
|
61
|
+
xPath?: string;
|
|
62
|
+
jsonPath?: string;
|
|
63
|
+
regex?: string;
|
|
64
|
+
/** OPRF mode (`oprf` / `oprf-raw` / `oprf-mpc`) — the backend stores it as
|
|
65
|
+
* a free-form string and the attestor interprets it at verification. */
|
|
66
|
+
hash?: string;
|
|
67
|
+
order: number;
|
|
68
|
+
}
|
|
69
|
+
interface OldRequestData {
|
|
70
|
+
url: string;
|
|
71
|
+
urlType: 'CONSTANT' | 'TEMPLATE';
|
|
72
|
+
method: 'GET' | 'POST';
|
|
73
|
+
responseMatches: OldResponseMatch[];
|
|
74
|
+
responseRedactions: OldResponseRedaction[];
|
|
75
|
+
credentials: 'include';
|
|
76
|
+
bodySniff?: {
|
|
77
|
+
enabled: boolean;
|
|
78
|
+
template: string;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** One `allowedInjectedRequestData` entry — an `OldRequestData` plus the
|
|
82
|
+
* pagination/optionality flags the old backend stores alongside it. */
|
|
83
|
+
export interface OldInjectedRequestData extends OldRequestData {
|
|
84
|
+
required?: boolean;
|
|
85
|
+
multiple?: boolean;
|
|
86
|
+
}
|
|
87
|
+
/** Translate fresh `allowedInjectedRequestData` drafts (the author's input
|
|
88
|
+
* shape, `AllowedJsRequest[]` — same shape the builder translator uses for
|
|
89
|
+
* its own `allowedJsRequests`) into the old backend's shape. A
|
|
90
|
+
* carried-forward value read back from an existing version is ALREADY in
|
|
91
|
+
* this shape and should be passed straight through instead — don't run it
|
|
92
|
+
* through this a second time. */
|
|
93
|
+
export declare function injectedRequestDataFrom(requests: AllowedJsRequest[]): OldInjectedRequestData[];
|
|
26
94
|
/** Body for `POST /api/providers/register` — new provider + v1.0.0. Every
|
|
27
95
|
* drafted request becomes a `requestData` entry; provider-level fields (name)
|
|
28
96
|
* come from the first draft. */
|
|
@@ -50,3 +118,4 @@ export declare function extractVersions(resp: unknown): SemverTriplet[];
|
|
|
50
118
|
* rather than collide with the guaranteed 1.0.0).
|
|
51
119
|
*/
|
|
52
120
|
export declare function bumpPatchString(versions: SemverTriplet[]): string;
|
|
121
|
+
export {};
|
package/lib/old/to-register.js
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* is a Provider-level flag (register only). */
|
|
5
5
|
const DEFAULT_GEO_LOCATION = '{{DYNAMIC_GEO}}';
|
|
6
6
|
const DEFAULT_USE_PROXY = true;
|
|
7
|
-
/** Build the single `requestData` entry shared by register + update.
|
|
7
|
+
/** Build the single `requestData` entry shared by register + update. Takes
|
|
8
|
+
* the loose `DraftRequest` shape (not the full `ReclaimProvider`) so it can
|
|
9
|
+
* build BOTH the top-level `requestData` and `allowedInjectedRequestData`
|
|
10
|
+
* entries — a `ReclaimProvider` satisfies it structurally either way. */
|
|
8
11
|
function requestDataFromProvider(provider) {
|
|
9
12
|
const method = String(provider.method).toUpperCase();
|
|
10
13
|
if (method !== 'GET' && method !== 'POST') {
|
|
@@ -22,7 +25,8 @@ function requestDataFromProvider(provider) {
|
|
|
22
25
|
'consumer-supplied params to the {{context_<name>}} form ' +
|
|
23
26
|
'for this backend.');
|
|
24
27
|
}
|
|
25
|
-
const responseMatches = provider.responseMatches
|
|
28
|
+
const responseMatches = (provider.responseMatches ?? [])
|
|
29
|
+
.map((m, i) => ({
|
|
26
30
|
value: m.value,
|
|
27
31
|
type: m.type ?? 'contains',
|
|
28
32
|
isOptional: false,
|
|
@@ -78,6 +82,19 @@ function requestDataFromProvider(provider) {
|
|
|
78
82
|
function requestDataFrom(providers) {
|
|
79
83
|
return providers.map(requestDataFromProvider);
|
|
80
84
|
}
|
|
85
|
+
/** Translate fresh `allowedInjectedRequestData` drafts (the author's input
|
|
86
|
+
* shape, `AllowedJsRequest[]` — same shape the builder translator uses for
|
|
87
|
+
* its own `allowedJsRequests`) into the old backend's shape. A
|
|
88
|
+
* carried-forward value read back from an existing version is ALREADY in
|
|
89
|
+
* this shape and should be passed straight through instead — don't run it
|
|
90
|
+
* through this a second time. */
|
|
91
|
+
export function injectedRequestDataFrom(requests) {
|
|
92
|
+
return requests.map((req) => ({
|
|
93
|
+
...requestDataFromProvider(req),
|
|
94
|
+
required: req.required ?? true,
|
|
95
|
+
multiple: req.multiple ?? true,
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
81
98
|
/** Body for `POST /api/providers/register` — new provider + v1.0.0. Every
|
|
82
99
|
* drafted request becomes a `requestData` entry; provider-level fields (name)
|
|
83
100
|
* come from the first draft. */
|
|
@@ -94,6 +111,18 @@ export function providerToRegisterBody(providers, opts) {
|
|
|
94
111
|
if (opts.description !== undefined) {
|
|
95
112
|
body.description = opts.description;
|
|
96
113
|
}
|
|
114
|
+
if (opts.customInjection !== undefined) {
|
|
115
|
+
body.customInjection = opts.customInjection;
|
|
116
|
+
}
|
|
117
|
+
// Register also accepts these two (unlike allowedInjectedRequestData /
|
|
118
|
+
// stepsToFollow / useIncognitoWebview / extensionConfig, which the
|
|
119
|
+
// register controller never reads from the body at all).
|
|
120
|
+
if (opts.userAgent !== undefined) {
|
|
121
|
+
body.userAgent = opts.userAgent;
|
|
122
|
+
}
|
|
123
|
+
if (opts.pageTitle !== undefined) {
|
|
124
|
+
body.pageTitle = opts.pageTitle;
|
|
125
|
+
}
|
|
97
126
|
return body;
|
|
98
127
|
}
|
|
99
128
|
/** Body for `POST /api/providers/:providerId/config` — adds a new version to
|
|
@@ -108,6 +137,27 @@ export function providerToConfigBody(providers, opts, version, versionInfo) {
|
|
|
108
137
|
geoLocation: opts.geoLocation ?? DEFAULT_GEO_LOCATION,
|
|
109
138
|
requestData: requestDataFrom(providers),
|
|
110
139
|
};
|
|
140
|
+
if (opts.customInjection !== undefined) {
|
|
141
|
+
providerConfig.customInjection = opts.customInjection;
|
|
142
|
+
}
|
|
143
|
+
if (opts.userAgent !== undefined) {
|
|
144
|
+
providerConfig.userAgent = opts.userAgent;
|
|
145
|
+
}
|
|
146
|
+
if (opts.pageTitle !== undefined) {
|
|
147
|
+
providerConfig.pageTitle = opts.pageTitle;
|
|
148
|
+
}
|
|
149
|
+
if (opts.allowedInjectedRequestData !== undefined) {
|
|
150
|
+
providerConfig.allowedInjectedRequestData = opts.allowedInjectedRequestData;
|
|
151
|
+
}
|
|
152
|
+
if (opts.stepsToFollow !== undefined) {
|
|
153
|
+
providerConfig.stepsToFollow = opts.stepsToFollow;
|
|
154
|
+
}
|
|
155
|
+
if (opts.useIncognitoWebview !== undefined) {
|
|
156
|
+
providerConfig.useIncognitoWebview = opts.useIncognitoWebview;
|
|
157
|
+
}
|
|
158
|
+
if (opts.extensionConfig !== undefined) {
|
|
159
|
+
providerConfig.extensionConfig = opts.extensionConfig;
|
|
160
|
+
}
|
|
111
161
|
return { providerConfig, version, versionInfo };
|
|
112
162
|
}
|
|
113
163
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { agentDisabled } from "../../consts.js";
|
|
2
|
-
import { defineTool
|
|
2
|
+
import { defineTool } from "../../mcp/server.js";
|
|
3
3
|
import { persistIdentity, toEthUid } from "../identity.js";
|
|
4
4
|
import { captureFirebaseTokenViaCdp } from "../login-cdp.js";
|
|
5
5
|
/**
|
|
@@ -69,7 +69,7 @@ export function authenticateTool(client) {
|
|
|
69
69
|
},
|
|
70
70
|
},
|
|
71
71
|
},
|
|
72
|
-
}, async ({ token, ethAddress, loginUrl, timeoutMs, useBrowser }
|
|
72
|
+
}, async ({ token, ethAddress, loginUrl, timeoutMs, useBrowser }) => {
|
|
73
73
|
if (token && ethAddress) {
|
|
74
74
|
throw new Error('Provide only ONE of `token` or `ethAddress`, not both.');
|
|
75
75
|
}
|
|
@@ -113,11 +113,7 @@ export function authenticateTool(client) {
|
|
|
113
113
|
throw new Error('Browser login is unavailable (RECLAIM_AGENT_DISABLED=1). Pass a ' +
|
|
114
114
|
'`token` (Firebase ID token) or `ethAddress` instead.');
|
|
115
115
|
}
|
|
116
|
-
const result = await captureFirebaseTokenViaCdp({
|
|
117
|
-
loginUrl,
|
|
118
|
-
timeoutMs,
|
|
119
|
-
onProgress: ctx.reportProgress,
|
|
120
|
-
});
|
|
116
|
+
const result = await captureFirebaseTokenViaCdp({ loginUrl, timeoutMs });
|
|
121
117
|
const identity = { kind: 'bearer', token: result.token };
|
|
122
118
|
await validate(identity);
|
|
123
119
|
persistIdentity(identity);
|
package/lib/old/tools/build.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import type { RegisteredTool } from '../../mcp/server.ts';
|
|
2
2
|
import type { ReclaimOldClient } from '../client.ts';
|
|
3
|
+
import type { ReclaimSdkClient } from '../sdk-client.ts';
|
|
3
4
|
/**
|
|
4
5
|
* The backend-facing tools for OLD-devtools mode. Mirrors
|
|
5
6
|
* `buildCredentialTools` / `buildAgentTools`: returns a flat `RegisteredTool`
|
|
6
7
|
* list for `registerToolList`. These replace the builder credential + publish
|
|
7
8
|
* tools and the (disabled) generated provider tools.
|
|
9
|
+
*
|
|
10
|
+
* `sdkClient` is a SEPARATE, unauthenticated client for the public
|
|
11
|
+
* production SDK backend (`api.reclaimprotocol.org`) — a different host from
|
|
12
|
+
* `client`'s old-devtools dashboard backend (`devapi.reclaimprotocol.org`).
|
|
8
13
|
*/
|
|
9
|
-
export declare function buildOldBackendTools(client: ReclaimOldClient): RegisteredTool[];
|
|
14
|
+
export declare function buildOldBackendTools(client: ReclaimOldClient, sdkClient: ReclaimSdkClient): RegisteredTool[];
|
package/lib/old/tools/build.js
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import { authenticateTool } from "./authenticate.js";
|
|
2
|
+
import { getProviderInfoTool } from "./get-provider-info.js";
|
|
3
|
+
import { getProviderVersionConfigTool } from "./get-version-config.js";
|
|
2
4
|
import { getMeProvidersTool } from "./me-providers.js";
|
|
3
5
|
import { createVersionFromCaptureTool } from "./publish.js";
|
|
4
6
|
import { sessionAnalyticsLogsTool } from "./session-analytics-logs.js";
|
|
7
|
+
import { updateProviderMetadataTool } from "./update-provider-metadata.js";
|
|
5
8
|
/**
|
|
6
9
|
* The backend-facing tools for OLD-devtools mode. Mirrors
|
|
7
10
|
* `buildCredentialTools` / `buildAgentTools`: returns a flat `RegisteredTool`
|
|
8
11
|
* list for `registerToolList`. These replace the builder credential + publish
|
|
9
12
|
* tools and the (disabled) generated provider tools.
|
|
13
|
+
*
|
|
14
|
+
* `sdkClient` is a SEPARATE, unauthenticated client for the public
|
|
15
|
+
* production SDK backend (`api.reclaimprotocol.org`) — a different host from
|
|
16
|
+
* `client`'s old-devtools dashboard backend (`devapi.reclaimprotocol.org`).
|
|
10
17
|
*/
|
|
11
|
-
export function buildOldBackendTools(client) {
|
|
18
|
+
export function buildOldBackendTools(client, sdkClient) {
|
|
12
19
|
return [
|
|
13
20
|
authenticateTool(client),
|
|
14
21
|
createVersionFromCaptureTool(client),
|
|
15
22
|
getMeProvidersTool(client),
|
|
23
|
+
getProviderVersionConfigTool(client),
|
|
24
|
+
getProviderInfoTool(sdkClient),
|
|
25
|
+
updateProviderMetadataTool(client),
|
|
16
26
|
sessionAnalyticsLogsTool(client),
|
|
17
27
|
];
|
|
18
28
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type RegisteredTool } from '../../mcp/server.ts';
|
|
2
|
+
import type { ReclaimSdkClient } from '../sdk-client.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Read provider info from the PUBLIC production SDK backend
|
|
5
|
+
* (`api.reclaimprotocol.org`) — the service the verification SDK/attestor
|
|
6
|
+
* itself calls at runtime, NOT the old-devtools dashboard backend
|
|
7
|
+
* (`get_provider_version_config`, `devapi.reclaimprotocol.org`, requires
|
|
8
|
+
* auth). Both underlying reads are unauthenticated.
|
|
9
|
+
*
|
|
10
|
+
* Fetches BOTH shapes in one call, since they're complementary:
|
|
11
|
+
* - `provider`: full provider + providerConfig, `customInjection` included.
|
|
12
|
+
* - `configs`: the trimmed runtime recipe the attestor actually consumes —
|
|
13
|
+
* `customInjection` deliberately omitted, `isScriptRequestingClaim`
|
|
14
|
+
* computed instead.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getProviderInfoTool(client: ReclaimSdkClient): RegisteredTool;
|