@reclaimprotocol/agent 0.1.1 → 0.1.3
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/cdp/launcher.js +4 -3
- package/lib/consts.d.ts +68 -0
- package/lib/consts.js +134 -0
- package/lib/logger.js +2 -1
- package/lib/mcp/api/operations.gen.d.ts +11 -7
- package/lib/mcp/api/operations.gen.js +356 -199
- package/lib/mcp/server.d.ts +0 -6
- package/lib/mcp/server.js +0 -30
- package/lib/mcp/start.js +7 -7
- package/lib/mcp/tools/agent/build.d.ts +5 -9
- package/lib/mcp/tools/agent/build.js +5 -9
- package/lib/mcp/tools/agent/proof.js +2 -1
- package/lib/mcp/tools/agent/publish.js +166 -77
- package/lib/mcp/tools/authenticate/decrypt-key.d.ts +10 -23
- package/lib/mcp/tools/authenticate/decrypt-key.js +23 -26
- package/lib/mcp/tools/authenticate/eth-key.d.ts +0 -7
- package/lib/mcp/tools/authenticate/eth-key.js +5 -19
- package/lib/mcp/tools/authenticate/issue.js +1 -1
- package/lib/mcp/tools/verification/build.d.ts +1 -1
- package/lib/mcp/tools/verification/build.js +1 -1
- package/lib/mcp/tools/verification/result.d.ts +4 -5
- package/lib/mcp/tools/verification/result.js +17 -17
- 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/login-cdp.d.ts +0 -3
- package/lib/old/login-cdp.js +2 -4
- package/lib/old/to-register.d.ts +5 -3
- package/lib/old/to-register.js +20 -15
- package/lib/old/tools/authenticate.js +2 -1
- package/lib/old/tools/publish.js +97 -13
- package/lib/paths.js +2 -2
- package/lib/proof/attestor.js +2 -1
- package/lib/proof/proof-core.js +13 -2
- 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 +59 -7
- package/lib/provider/to-version.js +79 -28
- package/package.json +1 -1
- package/skill/builder-mode.md +8 -2
- package/skill/injection-api.md +42 -0
- package/skill/old-mode.md +4 -2
- package/src/cdp/launcher.ts +5 -4
- package/src/consts.ts +164 -0
- package/src/logger.ts +2 -1
- package/src/mcp/api/operations.gen.ts +358 -203
- package/src/mcp/server.ts +0 -46
- package/src/mcp/start.ts +13 -7
- package/src/mcp/tools/agent/build.ts +5 -9
- package/src/mcp/tools/agent/proof.ts +2 -1
- package/src/mcp/tools/agent/publish.ts +212 -94
- package/src/mcp/tools/authenticate/decrypt-key.ts +24 -29
- package/src/mcp/tools/authenticate/eth-key.ts +5 -25
- package/src/mcp/tools/authenticate/issue.ts +1 -1
- package/src/mcp/tools/verification/build.ts +1 -1
- package/src/mcp/tools/verification/result.ts +16 -17
- package/src/old/flag.ts +4 -3
- package/src/old/identity.ts +8 -7
- package/src/old/login-cdp.ts +2 -5
- package/src/old/to-register.ts +22 -16
- package/src/old/tools/authenticate.ts +2 -1
- package/src/old/tools/publish.ts +124 -20
- package/src/paths.ts +2 -2
- package/src/proof/attestor.ts +2 -1
- package/src/proof/proof-core.ts +13 -3
- 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 +137 -31
package/lib/mcp/server.d.ts
CHANGED
|
@@ -30,9 +30,3 @@ export declare function defineTool<TArgs>(definition: ToolDefinition, handler: (
|
|
|
30
30
|
* stringification and error catching for tool execution.
|
|
31
31
|
*/
|
|
32
32
|
export declare function registerToolList(server: McpServer, tools: RegisteredTool[]): void;
|
|
33
|
-
export declare class ReclaimMcpServer {
|
|
34
|
-
#private;
|
|
35
|
-
constructor();
|
|
36
|
-
register(tool: RegisteredTool): void;
|
|
37
|
-
listen(): Promise<void>;
|
|
38
|
-
}
|
package/lib/mcp/server.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
4
1
|
import { toZod } from "./api/to-zod.js";
|
|
5
2
|
import { remapErrorAsResponse } from "./remap_errors.js";
|
|
6
3
|
/**
|
|
@@ -47,30 +44,3 @@ export function registerToolList(server, tools) {
|
|
|
47
44
|
});
|
|
48
45
|
}
|
|
49
46
|
}
|
|
50
|
-
export class ReclaimMcpServer {
|
|
51
|
-
#tools = new Map();
|
|
52
|
-
#server;
|
|
53
|
-
constructor() {
|
|
54
|
-
this.#server = new Server({ name: 'reclaim-agent', version: '0.1.0' }, { capabilities: { tools: {} } });
|
|
55
|
-
this.#server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
56
|
-
// Double-cast: our `ToolDefinition` carries the same fields as the
|
|
57
|
-
// SDK's `Tool` but the SDK's type is strict-narrowed and won't
|
|
58
|
-
// accept ours without going through `unknown`.
|
|
59
|
-
tools: [...this.#tools.values()].map((t) => t.definition),
|
|
60
|
-
}));
|
|
61
|
-
this.#server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
62
|
-
const tool = this.#tools.get(req.params.name);
|
|
63
|
-
if (!tool) {
|
|
64
|
-
throw new Error(`unknown tool: ${req.params.name}`);
|
|
65
|
-
}
|
|
66
|
-
const result = await tool.handler(req.params.arguments ?? {});
|
|
67
|
-
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
register(tool) {
|
|
71
|
-
this.#tools.set(tool.definition.name, tool);
|
|
72
|
-
}
|
|
73
|
-
async listen() {
|
|
74
|
-
await this.#server.connect(new StdioServerTransport());
|
|
75
|
-
}
|
|
76
|
-
}
|
package/lib/mcp/start.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { ReclaimClient } from '@reclaimprotocol/client';
|
|
4
|
+
import { agentDisabled, apiToken, apiUrl, oldApiUrl, useOldDevtools, } from "../consts.js";
|
|
5
|
+
import { ReclaimOldClient } from "../old/client.js";
|
|
6
6
|
import { resolveInitialIdentity } from "../old/identity.js";
|
|
7
7
|
import { OLD_MODE_INSTRUCTIONS } from "../old/instructions.js";
|
|
8
8
|
import { buildOldBackendTools } from "../old/tools/build.js";
|
|
@@ -22,14 +22,14 @@ export async function startMcpServer() {
|
|
|
22
22
|
// backend-independent and registered in both modes. Skipped in hosted
|
|
23
23
|
// deployments — set RECLAIM_AGENT_DISABLED=1 to register only the API
|
|
24
24
|
// surface.
|
|
25
|
-
const agentToolsEnabled =
|
|
25
|
+
const agentToolsEnabled = !agentDisabled();
|
|
26
26
|
if (oldMode) {
|
|
27
27
|
// OLD-devtools mode (the default): the legacy backend is still in
|
|
28
28
|
// production while builder is built. All auto-generated builder tools
|
|
29
29
|
// are disabled; a small ReclaimOldClient serves authenticate / publish /
|
|
30
30
|
// get_me_providers instead.
|
|
31
31
|
const oldClient = new ReclaimOldClient({
|
|
32
|
-
baseUrl:
|
|
32
|
+
baseUrl: oldApiUrl(),
|
|
33
33
|
identity: resolveInitialIdentity(),
|
|
34
34
|
});
|
|
35
35
|
registerToolList(server, buildLocalCredentialTools());
|
|
@@ -42,9 +42,9 @@ export async function startMcpServer() {
|
|
|
42
42
|
}
|
|
43
43
|
else {
|
|
44
44
|
const client = new ReclaimClient({
|
|
45
|
-
baseUrl:
|
|
45
|
+
baseUrl: apiUrl(),
|
|
46
46
|
// Priority: explicit env var → cached session token → unauthenticated
|
|
47
|
-
token:
|
|
47
|
+
token: apiToken() || readSession()?.token,
|
|
48
48
|
headers: { 'User-Agent': `${NAME}/${VERSION}` },
|
|
49
49
|
});
|
|
50
50
|
// Tool names must stay disjoint across these three groups: hand-written
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Factory entry-point for embedding the reclaim-agent's MCP tools into
|
|
3
|
-
* a host MCP server.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* the low-level wrapper around `@modelcontextprotocol/sdk`'s Server).
|
|
7
|
-
* - `@reclaimprotocol/client`'s MCP server (uses `McpServer`, the
|
|
8
|
-
* high-level SDK wrapper). Calls `registerAgentTools` to fold the
|
|
9
|
-
* agent's 16 tools alongside its auto-generated API tools.
|
|
3
|
+
* a host MCP server via `registerToolList` (server.ts) on the high-level
|
|
4
|
+
* `@modelcontextprotocol/sdk` `McpServer`. Folds the agent's tools
|
|
5
|
+
* alongside the auto-generated API tools.
|
|
10
6
|
*
|
|
11
7
|
* Shared state — Chrome attach state, capture store, secret store — is
|
|
12
8
|
* built once per server and threaded through every tool factory.
|
|
@@ -25,8 +21,8 @@ export interface AgentState {
|
|
|
25
21
|
captures: CaptureStore;
|
|
26
22
|
secrets: SecretStore;
|
|
27
23
|
}
|
|
28
|
-
/** Build the flat list of `RegisteredTool` entries
|
|
29
|
-
*
|
|
24
|
+
/** Build the flat list of `RegisteredTool` entries, ready for
|
|
25
|
+
* `registerToolList`.
|
|
30
26
|
* Pass `client` to include the builder `create_provider_version_from_capture`;
|
|
31
27
|
* omit it (e.g. OLD-devtools mode) to register only the backend-independent
|
|
32
28
|
* capture/proof tools — old mode supplies its own publish tool. */
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Factory entry-point for embedding the reclaim-agent's MCP tools into
|
|
3
|
-
* a host MCP server.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* the low-level wrapper around `@modelcontextprotocol/sdk`'s Server).
|
|
7
|
-
* - `@reclaimprotocol/client`'s MCP server (uses `McpServer`, the
|
|
8
|
-
* high-level SDK wrapper). Calls `registerAgentTools` to fold the
|
|
9
|
-
* agent's 16 tools alongside its auto-generated API tools.
|
|
3
|
+
* a host MCP server via `registerToolList` (server.ts) on the high-level
|
|
4
|
+
* `@modelcontextprotocol/sdk` `McpServer`. Folds the agent's tools
|
|
5
|
+
* alongside the auto-generated API tools.
|
|
10
6
|
*
|
|
11
7
|
* Shared state — Chrome attach state, capture store, secret store — is
|
|
12
8
|
* built once per server and threaded through every tool factory.
|
|
@@ -30,8 +26,8 @@ function buildAgentState() {
|
|
|
30
26
|
secrets: makeSecretStore(),
|
|
31
27
|
};
|
|
32
28
|
}
|
|
33
|
-
/** Build the flat list of `RegisteredTool` entries
|
|
34
|
-
*
|
|
29
|
+
/** Build the flat list of `RegisteredTool` entries, ready for
|
|
30
|
+
* `registerToolList`.
|
|
35
31
|
* Pass `client` to include the builder `create_provider_version_from_capture`;
|
|
36
32
|
* omit it (e.g. OLD-devtools mode) to register only the backend-independent
|
|
37
33
|
* capture/proof tools — old mode supplies its own publish tool. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { agentTraceEnabled } from "../../../consts.js";
|
|
3
4
|
import { reclaimHomeDirPath } from "../../../paths.js";
|
|
4
5
|
import { runAttestorProof, tailLogs, warnsAndErrors, } from "../../../proof/attestor.js";
|
|
5
6
|
import { defineTool } from "../../server.js";
|
|
@@ -182,7 +183,7 @@ export function proofTool(secrets) {
|
|
|
182
183
|
// when investigating proof failures and the file at runLogPath
|
|
183
184
|
// will carry the input provider, sentParams, full attestor log
|
|
184
185
|
// stream, and result.
|
|
185
|
-
if (
|
|
186
|
+
if (agentTraceEnabled()) {
|
|
186
187
|
const runLogPath = writeProofRunDump({
|
|
187
188
|
provider,
|
|
188
189
|
hasSecretParamsRef: secretParamsRef !== undefined,
|
|
@@ -1,23 +1,118 @@
|
|
|
1
|
+
import { INJECTION_GUIDE } from "../../../provider/injection-doc.js";
|
|
2
|
+
import { countsFor, matchRedactionWarnings, } from "../../../provider/match-redaction-warning.js";
|
|
1
3
|
import { nextVersion, providerToCreateVersionRequest, } from "../../../provider/to-version.js";
|
|
2
4
|
import { defineTool } from "../../server.js";
|
|
5
|
+
/** Schemas shared by the top-level `provider`/`providers` and the lighter
|
|
6
|
+
* `allowedJsRequests` drafts — keeps the request shape from drifting between
|
|
7
|
+
* them. */
|
|
8
|
+
const RESPONSE_MATCHES_SCHEMA = {
|
|
9
|
+
type: 'array',
|
|
10
|
+
description: 'What the attestor checks on the revealed response slice.',
|
|
11
|
+
items: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
type: { type: 'string', enum: ['regex', 'contains'] },
|
|
15
|
+
value: { type: 'string' },
|
|
16
|
+
},
|
|
17
|
+
required: ['type', 'value'],
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
const RESPONSE_REDACTIONS_SCHEMA = {
|
|
21
|
+
type: 'array',
|
|
22
|
+
description: 'Which portions of the response the prover reveals ' +
|
|
23
|
+
'(xPath / jsonPath / regex; hash to OPRF the value). Paired with ' +
|
|
24
|
+
'responseMatches by index — keep them aligned 1-to-1. If you list more ' +
|
|
25
|
+
'redactions than matches, the tool pads matches with no-op optional ' +
|
|
26
|
+
'entries so older index-paired claim creators stay happy.',
|
|
27
|
+
items: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
xPath: { type: 'string' },
|
|
31
|
+
jsonPath: { type: 'string' },
|
|
32
|
+
regex: { type: 'string' },
|
|
33
|
+
hash: { type: 'string', enum: ['oprf', 'oprf-mpc', 'oprf-raw'] },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/** Properties every drafted request shares (top-level providers and the
|
|
38
|
+
* injected `allowedJsRequests`). */
|
|
39
|
+
const REQUEST_DRAFT_PROPERTIES = {
|
|
40
|
+
url: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
description: 'Exact API URL the attestor replays, with {{param}} placeholders ' +
|
|
43
|
+
'for any templated values.',
|
|
44
|
+
},
|
|
45
|
+
method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'PATCH'] },
|
|
46
|
+
body: { type: 'string', description: 'Request body template, if any.' },
|
|
47
|
+
responseMatches: RESPONSE_MATCHES_SCHEMA,
|
|
48
|
+
responseRedactions: RESPONSE_REDACTIONS_SCHEMA,
|
|
49
|
+
};
|
|
50
|
+
/** A full provider draft (from propose_provider), passed through verbatim. */
|
|
51
|
+
const PROVIDER_SCHEMA = {
|
|
52
|
+
type: 'object',
|
|
53
|
+
description: 'A `provider` object from propose_provider, passed through verbatim — ' +
|
|
54
|
+
'the tool builds the version body (the requests[] entry etc.) for you.',
|
|
55
|
+
properties: {
|
|
56
|
+
name: { type: 'string', description: 'Verification name.' },
|
|
57
|
+
...REQUEST_DRAFT_PROPERTIES,
|
|
58
|
+
headers: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
description: 'Request headers from the capture. Secret headers are stripped ' +
|
|
61
|
+
'before persistence; the client re-supplies them at runtime.',
|
|
62
|
+
},
|
|
63
|
+
paramValues: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
description: 'Real values for the {{param}} placeholders, e.g. ' +
|
|
66
|
+
'{ "username": "Syed" }.',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
required: ['name', 'url', 'method', 'responseMatches'],
|
|
70
|
+
};
|
|
71
|
+
/** One `allowedJsRequests` template: a request the injection script may fire,
|
|
72
|
+
* plus pagination/optionality flags. */
|
|
73
|
+
const ALLOWED_JS_REQUEST_SCHEMA = {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
...REQUEST_DRAFT_PROPERTIES,
|
|
77
|
+
multiple: {
|
|
78
|
+
type: 'boolean',
|
|
79
|
+
description: 'Allow the template to match many requests (e.g. pagination). ' +
|
|
80
|
+
'Default true.',
|
|
81
|
+
},
|
|
82
|
+
required: {
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
description: 'Fail validation if no request matches this template. Default true.',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: ['url', 'method'],
|
|
88
|
+
};
|
|
3
89
|
export function createVersionFromCaptureTool(client) {
|
|
4
90
|
return defineTool({
|
|
5
91
|
name: 'create_provider_version_from_capture',
|
|
6
|
-
description: 'Persist
|
|
7
|
-
'Call this after run_proof succeeds, passing the `provider`
|
|
8
|
-
'from propose_provider VERBATIM. This tool builds the
|
|
92
|
+
description: 'Persist drafted ReclaimProvider(s) as a verification version. ' +
|
|
93
|
+
'Call this after run_proof succeeds, passing the `provider` ' +
|
|
94
|
+
'object(s) from propose_provider VERBATIM. This tool builds the ' +
|
|
9
95
|
'body (the requests[] array, webSettings, etc.) for you — do NOT ' +
|
|
10
96
|
'hand-construct that shape or call create_provider_version directly. ' +
|
|
97
|
+
'For a MULTI-REQUEST provider, pass `providers: [...]` (each entry a ' +
|
|
98
|
+
'proven draft) — every entry becomes one request in the version, and ' +
|
|
99
|
+
'all are required at verification time. Pass a single `provider` ' +
|
|
100
|
+
'for the one-request case (exactly one of `provider`/`providers`). ' +
|
|
101
|
+
'For data gated behind login/navigation, supply `jsUserScripts` (a ' +
|
|
102
|
+
'page-injection script that drives the page to where the request ' +
|
|
103
|
+
'fires) and/or `allowedJsRequests` (extra requests the script may ' +
|
|
104
|
+
'fire, e.g. pagination). ' +
|
|
11
105
|
'Omit `version` to have the tool auto-select the next available ' +
|
|
12
106
|
'version (it lists existing versions and bumps past the highest). ' +
|
|
13
107
|
'The version lands on the draft branch — to go live, call ' +
|
|
14
108
|
'`publish_provider_version` (PRIVATE) or ' +
|
|
15
109
|
'`submit_provider_version_for_review` (PUBLIC) afterwards. ' +
|
|
16
110
|
'`requiredContext` is derived from any {{context.<name>}} ' +
|
|
17
|
-
'placeholders
|
|
18
|
-
'
|
|
19
|
-
'MUST send when creating
|
|
20
|
-
'values exactly; a missing
|
|
111
|
+
'placeholders across ALL requests (plus injected requests / script) ' +
|
|
112
|
+
'and returned on the created version — when present, tell the dev ' +
|
|
113
|
+
'verbatim which context fields consumers MUST send when creating ' +
|
|
114
|
+
'sessions (verification matches those values exactly; a missing ' +
|
|
115
|
+
'field is a 400 at session create). ' +
|
|
21
116
|
'Refine types/descriptions via `update_provider_version` while ' +
|
|
22
117
|
'still on draft.',
|
|
23
118
|
inputSchema: {
|
|
@@ -27,71 +122,14 @@ export function createVersionFromCaptureTool(client) {
|
|
|
27
122
|
type: 'string',
|
|
28
123
|
description: 'UUID of the verification to publish the version on.',
|
|
29
124
|
},
|
|
30
|
-
provider:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
'
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
type: 'string',
|
|
39
|
-
description: 'Exact API URL the attestor replays, with {{param}} ' +
|
|
40
|
-
'placeholders for any templated values.',
|
|
41
|
-
},
|
|
42
|
-
method: {
|
|
43
|
-
type: 'string',
|
|
44
|
-
enum: ['GET', 'POST', 'PUT', 'PATCH'],
|
|
45
|
-
},
|
|
46
|
-
headers: {
|
|
47
|
-
type: 'object',
|
|
48
|
-
description: 'Request headers from the capture. Secret headers are ' +
|
|
49
|
-
'stripped before persistence; the client re-supplies them ' +
|
|
50
|
-
'at runtime.',
|
|
51
|
-
},
|
|
52
|
-
body: {
|
|
53
|
-
type: 'string',
|
|
54
|
-
description: 'Request body template, if any.',
|
|
55
|
-
},
|
|
56
|
-
responseMatches: {
|
|
57
|
-
type: 'array',
|
|
58
|
-
description: 'What the attestor checks on the revealed response slice.',
|
|
59
|
-
items: {
|
|
60
|
-
type: 'object',
|
|
61
|
-
properties: {
|
|
62
|
-
type: { type: 'string', enum: ['regex', 'contains'] },
|
|
63
|
-
value: { type: 'string' },
|
|
64
|
-
},
|
|
65
|
-
required: ['type', 'value'],
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
responseRedactions: {
|
|
69
|
-
type: 'array',
|
|
70
|
-
description: 'Which portions of the response the prover reveals ' +
|
|
71
|
-
'(xPath / jsonPath / regex; hash to OPRF the value). ' +
|
|
72
|
-
'Paired with responseMatches by index — the tool folds ' +
|
|
73
|
-
'each one into the matching entry\'s `extract` in the ' +
|
|
74
|
-
'persisted version, so keep them aligned 1-to-1.',
|
|
75
|
-
items: {
|
|
76
|
-
type: 'object',
|
|
77
|
-
properties: {
|
|
78
|
-
xPath: { type: 'string' },
|
|
79
|
-
jsonPath: { type: 'string' },
|
|
80
|
-
regex: { type: 'string' },
|
|
81
|
-
hash: {
|
|
82
|
-
type: 'string',
|
|
83
|
-
enum: ['oprf', 'oprf-mpc', 'oprf-raw'],
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
paramValues: {
|
|
89
|
-
type: 'object',
|
|
90
|
-
description: 'Real values for the {{param}} placeholders, ' +
|
|
91
|
-
'e.g. { "username": "Syed" }.',
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
required: ['name', 'url', 'method', 'responseMatches'],
|
|
125
|
+
provider: PROVIDER_SCHEMA,
|
|
126
|
+
providers: {
|
|
127
|
+
type: 'array',
|
|
128
|
+
description: 'Multiple proven drafts → one multi-request version. Each ' +
|
|
129
|
+
'entry has the same shape as `provider`. Pass this OR ' +
|
|
130
|
+
'`provider`, not both.',
|
|
131
|
+
items: PROVIDER_SCHEMA,
|
|
132
|
+
minItems: 1,
|
|
95
133
|
},
|
|
96
134
|
initialUrl: {
|
|
97
135
|
type: 'string',
|
|
@@ -114,17 +152,68 @@ export function createVersionFromCaptureTool(client) {
|
|
|
114
152
|
},
|
|
115
153
|
required: ['major', 'minor', 'patch'],
|
|
116
154
|
},
|
|
155
|
+
jsUserScripts: {
|
|
156
|
+
type: 'string',
|
|
157
|
+
description: 'Plain JavaScript injected into the provider page before every ' +
|
|
158
|
+
'page load (stored as webSettings.jsUserScripts). Use standard ' +
|
|
159
|
+
'browser APIs (window.location, element.click(), forms) to ' +
|
|
160
|
+
'drive login/navigation so the target request fires — the ' +
|
|
161
|
+
'client already intercepts page requests, and window.Reclaim ' +
|
|
162
|
+
'is only for rare edge cases, not navigation. The Builder ' +
|
|
163
|
+
'verification client installs this to run before every page ' +
|
|
164
|
+
'load (before navigating to initialUrl), matching old-devtool ' +
|
|
165
|
+
'customInjection; window.Reclaim itself is not yet exposed ' +
|
|
166
|
+
'there, so prefer plain browser JS. Guide:\n\n' +
|
|
167
|
+
INJECTION_GUIDE,
|
|
168
|
+
},
|
|
169
|
+
allowedJsRequests: {
|
|
170
|
+
type: 'array',
|
|
171
|
+
description: 'Extra requests the injection script is allowed to fire ' +
|
|
172
|
+
'(e.g. paginated API calls), as RequestSelectionTemplates. ' +
|
|
173
|
+
'`multiple`/`required` default to true.',
|
|
174
|
+
items: ALLOWED_JS_REQUEST_SCHEMA,
|
|
175
|
+
},
|
|
117
176
|
},
|
|
118
|
-
required: ['providerId', '
|
|
177
|
+
required: ['providerId', 'initialUrl'],
|
|
119
178
|
},
|
|
120
179
|
}, async (args) => {
|
|
180
|
+
// Exactly one of `provider` / `providers` — normalize to an array.
|
|
181
|
+
const hasMany = Array.isArray(args.providers) && args.providers.length > 0;
|
|
182
|
+
const hasOne = !!args.provider;
|
|
183
|
+
if (hasMany === hasOne) {
|
|
184
|
+
throw new Error('Pass exactly one of `provider` (a single draft) or ' +
|
|
185
|
+
'`providers` (an array of 1+ drafts).');
|
|
186
|
+
}
|
|
187
|
+
const providers = hasMany ? args.providers : [args.provider];
|
|
121
188
|
// Auto-pick the next version if not explicit
|
|
122
189
|
const ver = args.version ?? (await nextVersion(client, args.providerId));
|
|
123
|
-
const body = providerToCreateVersionRequest(
|
|
124
|
-
|
|
190
|
+
const body = providerToCreateVersionRequest(providers, args.initialUrl, {
|
|
191
|
+
notes: args.notes,
|
|
192
|
+
version: ver,
|
|
193
|
+
jsUserScripts: args.jsUserScripts,
|
|
194
|
+
allowedJsRequests: args.allowedJsRequests,
|
|
195
|
+
});
|
|
196
|
+
const result = await client.call('CreateProviderVersion', {
|
|
125
197
|
params: { providerId: args.providerId },
|
|
126
198
|
body,
|
|
127
|
-
})
|
|
128
|
-
|
|
199
|
+
});
|
|
200
|
+
const created = result.data;
|
|
201
|
+
// Aggregate non-blocking advisories for the dev — nothing here blocks
|
|
202
|
+
// or alters what was published; the author keeps full control.
|
|
203
|
+
const notes = [];
|
|
204
|
+
// Match/redaction advisory across every request (+ injected requests).
|
|
205
|
+
const requestCounts = [
|
|
206
|
+
...providers.map((p, i) => countsFor(`Request ${i + 1}`, p)),
|
|
207
|
+
...(args.allowedJsRequests ?? []).map((r, i) => countsFor(`Injected request ${i + 1}`, r)),
|
|
208
|
+
];
|
|
209
|
+
notes.push(...matchRedactionWarnings(requestCounts));
|
|
210
|
+
// Let the dev know how/when the script runs (and the window.Reclaim gap).
|
|
211
|
+
if (args.jsUserScripts?.trim()) {
|
|
212
|
+
notes.push('jsUserScripts will run in the Builder verification client before ' +
|
|
213
|
+
'every page load (before navigating to initialUrl), matching ' +
|
|
214
|
+
'old-devtool customInjection. window.Reclaim is not yet exposed ' +
|
|
215
|
+
'there, so prefer plain browser JS for navigation/interaction.');
|
|
216
|
+
}
|
|
217
|
+
return notes.length ? { ...created, _notes: notes } : created;
|
|
129
218
|
});
|
|
130
219
|
}
|
|
@@ -1,32 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Optional
|
|
3
|
-
* payloads in the consumer role.
|
|
4
|
-
*
|
|
2
|
+
* Optional eth decryption key — only needed when testing ENCRYPTED callback
|
|
3
|
+
* payloads in the consumer role. The org's results are ECIES-encrypted to its
|
|
4
|
+
* eth public key; this is the matching eth PRIVATE key (`0x`-hex). Resolution:
|
|
5
5
|
*
|
|
6
|
-
* 1.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* openssl genpkey -algorithm X25519 -out reclaim-decrypt.pem
|
|
10
|
-
*
|
|
11
|
-
* 2. Absent — plaintext-only testing (no decryption key; encrypted
|
|
6
|
+
* 1. The `RECLAIM_DECRYPT_KEY` env var (a `0x`-prefixed 32-byte hex key), or
|
|
7
|
+
* 2. A file named `reclaim-decrypt.key` in the project root holding that hex.
|
|
8
|
+
* 3. Absent — plaintext-only testing (no decryption key; encrypted
|
|
12
9
|
* deliveries surface a clear error).
|
|
13
10
|
*
|
|
14
|
-
* The
|
|
15
|
-
*
|
|
16
|
-
* `decryptCallback`.
|
|
11
|
+
* The hex is loaded via the client's `loadDecryptionKey` and passed straight
|
|
12
|
+
* through as `ReclaimConfig.credential` / to `decryptCallback`.
|
|
17
13
|
*/
|
|
18
14
|
import { type DecryptionKey } from '@reclaimprotocol/client';
|
|
19
|
-
/** Default X25519 PEM filename the agent looks for in the project root. */
|
|
20
|
-
export declare const DEFAULT_DECRYPT_PEM = "reclaim-decrypt.pem";
|
|
21
|
-
/** Resolve the decrypt-PEM path (the default file in the project root). */
|
|
22
|
-
export declare function decryptPemPath(projectDir?: string): string;
|
|
23
|
-
/**
|
|
24
|
-
* Read the raw decryption-key PEM string from the default file, without loading
|
|
25
|
-
* it. Returns `undefined` when the file is absent (plaintext-only).
|
|
26
|
-
*/
|
|
27
|
-
export declare function resolveDecryptKeyInput(projectDir?: string): string | undefined;
|
|
28
15
|
/**
|
|
29
|
-
* Load the optional decryption key. Returns `undefined` when
|
|
30
|
-
*
|
|
16
|
+
* Load the optional decryption key. Returns `undefined` when no key is
|
|
17
|
+
* configured (the consumer then handles only plaintext deliveries).
|
|
31
18
|
*/
|
|
32
19
|
export declare function resolveDecryptionKey(projectDir?: string): Promise<DecryptionKey | undefined>;
|
|
@@ -1,44 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Optional
|
|
3
|
-
* payloads in the consumer role.
|
|
4
|
-
*
|
|
2
|
+
* Optional eth decryption key — only needed when testing ENCRYPTED callback
|
|
3
|
+
* payloads in the consumer role. The org's results are ECIES-encrypted to its
|
|
4
|
+
* eth public key; this is the matching eth PRIVATE key (`0x`-hex). Resolution:
|
|
5
5
|
*
|
|
6
|
-
* 1.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* openssl genpkey -algorithm X25519 -out reclaim-decrypt.pem
|
|
10
|
-
*
|
|
11
|
-
* 2. Absent — plaintext-only testing (no decryption key; encrypted
|
|
6
|
+
* 1. The `RECLAIM_DECRYPT_KEY` env var (a `0x`-prefixed 32-byte hex key), or
|
|
7
|
+
* 2. A file named `reclaim-decrypt.key` in the project root holding that hex.
|
|
8
|
+
* 3. Absent — plaintext-only testing (no decryption key; encrypted
|
|
12
9
|
* deliveries surface a clear error).
|
|
13
10
|
*
|
|
14
|
-
* The
|
|
15
|
-
*
|
|
16
|
-
* `decryptCallback`.
|
|
11
|
+
* The hex is loaded via the client's `loadDecryptionKey` and passed straight
|
|
12
|
+
* through as `ReclaimConfig.credential` / to `decryptCallback`.
|
|
17
13
|
*/
|
|
18
14
|
import { loadDecryptionKey, } from '@reclaimprotocol/client';
|
|
19
15
|
import { existsSync, readFileSync } from 'node:fs';
|
|
20
16
|
import { join } from 'node:path';
|
|
21
17
|
import { findProjectRoot } from "../../../paths.js";
|
|
22
|
-
/** Default
|
|
23
|
-
|
|
24
|
-
/** Resolve the decrypt-PEM path (the default file in the project root). */
|
|
25
|
-
export function decryptPemPath(projectDir = findProjectRoot(process.cwd())) {
|
|
26
|
-
return join(projectDir, DEFAULT_DECRYPT_PEM);
|
|
27
|
-
}
|
|
18
|
+
/** Default eth decrypt-key filename the agent looks for in the project root. */
|
|
19
|
+
const DEFAULT_DECRYPT_KEY_FILE = 'reclaim-decrypt.key';
|
|
28
20
|
/**
|
|
29
|
-
* Read the raw decryption-key
|
|
30
|
-
* it. Returns `undefined` when
|
|
21
|
+
* Read the raw eth decryption-key hex — from `RECLAIM_DECRYPT_KEY` or the
|
|
22
|
+
* default file — without loading it. Returns `undefined` when neither is
|
|
23
|
+
* present (plaintext-only).
|
|
31
24
|
*/
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
return
|
|
25
|
+
function resolveDecryptKeyInput(projectDir = findProjectRoot(process.cwd())) {
|
|
26
|
+
const fromEnv = process.env.RECLAIM_DECRYPT_KEY?.trim();
|
|
27
|
+
if (fromEnv) {
|
|
28
|
+
return fromEnv;
|
|
29
|
+
}
|
|
30
|
+
const keyPath = join(projectDir, DEFAULT_DECRYPT_KEY_FILE);
|
|
31
|
+
if (existsSync(keyPath)) {
|
|
32
|
+
return readFileSync(keyPath, 'utf8').trim();
|
|
36
33
|
}
|
|
37
34
|
return undefined;
|
|
38
35
|
}
|
|
39
36
|
/**
|
|
40
|
-
* Load the optional decryption key. Returns `undefined` when
|
|
41
|
-
*
|
|
37
|
+
* Load the optional decryption key. Returns `undefined` when no key is
|
|
38
|
+
* configured (the consumer then handles only plaintext deliveries).
|
|
42
39
|
*/
|
|
43
40
|
export async function resolveDecryptionKey(projectDir = findProjectRoot(process.cwd())) {
|
|
44
41
|
const input = resolveDecryptKeyInput(projectDir);
|
|
@@ -13,19 +13,12 @@ export type ResolvedEthKey = {
|
|
|
13
13
|
};
|
|
14
14
|
/** Validate + normalize a raw Ethereum private key to lowercase 0x-hex. */
|
|
15
15
|
export declare function normalizeEthHex(value: string): string;
|
|
16
|
-
/** The explicit raw-hex key-file path, if `RECLAIM_PRIVATE_KEY_FILE` is set. */
|
|
17
|
-
export declare function ethKeyFilePath(projectDir?: string): string | undefined;
|
|
18
16
|
/**
|
|
19
17
|
* Resolve the eth proof-owner key without prompting. Returns `undefined` when
|
|
20
18
|
* no key is configured anywhere — the caller decides whether to generate or
|
|
21
19
|
* ask for one (see `resolve_owner_key`).
|
|
22
20
|
*/
|
|
23
21
|
export declare function resolveEthKey(projectDir?: string): ResolvedEthKey | undefined;
|
|
24
|
-
/**
|
|
25
|
-
* Resolve the eth proof-owner private key (0x-hex) or throw a clear, actionable
|
|
26
|
-
* error. Used by the proof flow that can't proceed without a signing key.
|
|
27
|
-
*/
|
|
28
|
-
export declare function requireEthPrivateKey(projectDir?: string): string;
|
|
29
22
|
/**
|
|
30
23
|
* Persist a raw eth private key to the project `.env` as
|
|
31
24
|
* `RECLAIM_PRIVATE_KEY=0x…` (upsert; mode 0600; `.env` added to `.gitignore`).
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
* Generated/imported keys are written to the project `.env` (mode 0600, and the
|
|
13
13
|
* file is added to `.gitignore`). NO `~/.reclaim` caching. This is a LOCAL
|
|
14
14
|
* signing key only — unrelated to org auth (the org secret) or encryption
|
|
15
|
-
* (the
|
|
15
|
+
* (the eth decrypt key).
|
|
16
16
|
*/
|
|
17
17
|
import { deriveFromPrivateKey } from '@reclaimprotocol/client';
|
|
18
18
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
19
19
|
import { isAbsolute, join } from 'node:path';
|
|
20
|
+
import { privateKey, privateKeyFile } from "../../../consts.js";
|
|
20
21
|
import { findProjectRoot } from "../../../paths.js";
|
|
21
22
|
const ENV_VAR = 'RECLAIM_PRIVATE_KEY';
|
|
22
23
|
/** The dotenv file the agent reads from / writes the key to. */
|
|
@@ -43,8 +44,8 @@ function readDotenvKey(path) {
|
|
|
43
44
|
return undefined;
|
|
44
45
|
}
|
|
45
46
|
/** The explicit raw-hex key-file path, if `RECLAIM_PRIVATE_KEY_FILE` is set. */
|
|
46
|
-
|
|
47
|
-
const p =
|
|
47
|
+
function ethKeyFilePath(projectDir = findProjectRoot(process.cwd())) {
|
|
48
|
+
const p = privateKeyFile();
|
|
48
49
|
if (!p) {
|
|
49
50
|
return undefined;
|
|
50
51
|
}
|
|
@@ -64,7 +65,7 @@ function resolved(privateKey, source, path) {
|
|
|
64
65
|
* ask for one (see `resolve_owner_key`).
|
|
65
66
|
*/
|
|
66
67
|
export function resolveEthKey(projectDir = findProjectRoot(process.cwd())) {
|
|
67
|
-
const env =
|
|
68
|
+
const env = privateKey();
|
|
68
69
|
if (env?.trim()) {
|
|
69
70
|
return resolved(normalizeEthHex(env), 'env');
|
|
70
71
|
}
|
|
@@ -79,21 +80,6 @@ export function resolveEthKey(projectDir = findProjectRoot(process.cwd())) {
|
|
|
79
80
|
}
|
|
80
81
|
return undefined;
|
|
81
82
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Resolve the eth proof-owner private key (0x-hex) or throw a clear, actionable
|
|
84
|
-
* error. Used by the proof flow that can't proceed without a signing key.
|
|
85
|
-
*/
|
|
86
|
-
export function requireEthPrivateKey(projectDir = findProjectRoot(process.cwd())) {
|
|
87
|
-
const r = resolveEthKey(projectDir);
|
|
88
|
-
if (!r) {
|
|
89
|
-
throw new Error('No eth proof-owner key found. Set RECLAIM_PRIVATE_KEY (0x-hex), add a '
|
|
90
|
-
+ `RECLAIM_PRIVATE_KEY=0x… line to ${join(projectDir, ENV_FILE)}, or `
|
|
91
|
-
+ 'point RECLAIM_PRIVATE_KEY_FILE at a file holding the raw hex. Run '
|
|
92
|
-
+ 'issue_credentials to generate one, or import_credentials to use a '
|
|
93
|
-
+ 'key you already hold.');
|
|
94
|
-
}
|
|
95
|
-
return r.privateKey;
|
|
96
|
-
}
|
|
97
83
|
/** Append `entry` to the project `.gitignore` if not already present. */
|
|
98
84
|
function ensureGitignored(projectDir, entry) {
|
|
99
85
|
const path = join(projectDir, '.gitignore');
|
|
@@ -10,7 +10,7 @@ export function issueTool() {
|
|
|
10
10
|
'the project `.env` as RECLAIM_PRIVATE_KEY=0x… (mode 0600; `.env` ' +
|
|
11
11
|
'added to .gitignore). This is a LOCAL signing key only — it is NOT ' +
|
|
12
12
|
'registered with any server and is unrelated to org auth (the org ' +
|
|
13
|
-
'secret) or encryption (the
|
|
13
|
+
'secret) or encryption (the eth decrypt key). ALWAYS confirm with ' +
|
|
14
14
|
'the dev before generating a new key. After writing, run_proof picks ' +
|
|
15
15
|
'it up automatically via resolve_owner_key. The private key is never ' +
|
|
16
16
|
'returned in the response.',
|