@reclaimprotocol/agent 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/lib/cdp/launcher.js +4 -3
  2. package/lib/consts.d.ts +68 -0
  3. package/lib/consts.js +134 -0
  4. package/lib/logger.js +2 -1
  5. package/lib/mcp/api/operations.gen.d.ts +3 -7
  6. package/lib/mcp/api/operations.gen.js +264 -199
  7. package/lib/mcp/server.d.ts +0 -6
  8. package/lib/mcp/server.js +0 -30
  9. package/lib/mcp/start.js +7 -7
  10. package/lib/mcp/tools/agent/build.d.ts +5 -9
  11. package/lib/mcp/tools/agent/build.js +5 -9
  12. package/lib/mcp/tools/agent/proof.js +2 -1
  13. package/lib/mcp/tools/agent/publish.js +166 -77
  14. package/lib/mcp/tools/authenticate/decrypt-key.d.ts +10 -23
  15. package/lib/mcp/tools/authenticate/decrypt-key.js +23 -26
  16. package/lib/mcp/tools/authenticate/eth-key.d.ts +0 -7
  17. package/lib/mcp/tools/authenticate/eth-key.js +5 -19
  18. package/lib/mcp/tools/authenticate/issue.js +1 -1
  19. package/lib/mcp/tools/verification/build.d.ts +1 -1
  20. package/lib/mcp/tools/verification/build.js +1 -1
  21. package/lib/mcp/tools/verification/result.d.ts +4 -5
  22. package/lib/mcp/tools/verification/result.js +17 -17
  23. package/lib/old/flag.d.ts +4 -1
  24. package/lib/old/flag.js +4 -3
  25. package/lib/old/identity.d.ts +0 -10
  26. package/lib/old/identity.js +8 -7
  27. package/lib/old/login-cdp.d.ts +0 -3
  28. package/lib/old/login-cdp.js +2 -4
  29. package/lib/old/to-register.js +6 -9
  30. package/lib/old/tools/authenticate.js +2 -1
  31. package/lib/old/tools/publish.js +58 -9
  32. package/lib/paths.js +2 -2
  33. package/lib/proof/attestor.js +2 -1
  34. package/lib/proof/proof-core.js +13 -2
  35. package/lib/provider/injection-doc.d.ts +1 -0
  36. package/lib/provider/injection-doc.js +17 -0
  37. package/lib/provider/match-redaction-warning.d.ts +25 -0
  38. package/lib/provider/match-redaction-warning.js +35 -0
  39. package/lib/provider/schema.d.ts +16 -0
  40. package/lib/provider/schema.js +11 -1
  41. package/lib/provider/to-version.d.ts +59 -7
  42. package/lib/provider/to-version.js +79 -28
  43. package/package.json +1 -1
  44. package/skill/builder-mode.md +6 -2
  45. package/skill/injection-api.md +42 -0
  46. package/skill/old-mode.md +2 -0
  47. package/src/cdp/launcher.ts +5 -4
  48. package/src/consts.ts +164 -0
  49. package/src/logger.ts +2 -1
  50. package/src/mcp/api/operations.gen.ts +262 -203
  51. package/src/mcp/server.ts +0 -46
  52. package/src/mcp/start.ts +13 -7
  53. package/src/mcp/tools/agent/build.ts +5 -9
  54. package/src/mcp/tools/agent/proof.ts +2 -1
  55. package/src/mcp/tools/agent/publish.ts +212 -94
  56. package/src/mcp/tools/authenticate/decrypt-key.ts +24 -29
  57. package/src/mcp/tools/authenticate/eth-key.ts +5 -25
  58. package/src/mcp/tools/authenticate/issue.ts +1 -1
  59. package/src/mcp/tools/verification/build.ts +1 -1
  60. package/src/mcp/tools/verification/result.ts +16 -17
  61. package/src/old/flag.ts +4 -3
  62. package/src/old/identity.ts +8 -7
  63. package/src/old/login-cdp.ts +2 -5
  64. package/src/old/to-register.ts +7 -10
  65. package/src/old/tools/authenticate.ts +2 -1
  66. package/src/old/tools/publish.ts +75 -15
  67. package/src/paths.ts +2 -2
  68. package/src/proof/attestor.ts +2 -1
  69. package/src/proof/proof-core.ts +13 -3
  70. package/src/provider/injection-doc.ts +21 -0
  71. package/src/provider/match-redaction-warning.ts +53 -0
  72. package/src/provider/schema.ts +23 -0
  73. package/src/provider/to-version.ts +137 -31
@@ -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 { DEFAULT_BASE_URL, ReclaimClient } from '@reclaimprotocol/client';
4
- import { DEFAULT_OLD_API_URL, ReclaimOldClient } from "../old/client.js";
5
- import { useOldDevtools } from "../old/flag.js";
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 = process.env.RECLAIM_AGENT_DISABLED !== '1';
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: process.env.RECLAIM_OLD_API_URL || DEFAULT_OLD_API_URL,
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: process.env.RECLAIM_API_URL || DEFAULT_BASE_URL,
45
+ baseUrl: apiUrl(),
46
46
  // Priority: explicit env var → cached session token → unauthenticated
47
- token: process.env.RECLAIM_API_TOKEN || readSession()?.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. Two consumers:
4
- *
5
- * - The agent's own standalone `src/index.ts` (uses `ReclaimMcpServer`,
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. Useful when the host
29
- * uses our internal `ReclaimMcpServer` interface (`.register(tool)`).
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. Two consumers:
4
- *
5
- * - The agent's own standalone `src/index.ts` (uses `ReclaimMcpServer`,
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. Useful when the host
34
- * uses our internal `ReclaimMcpServer` interface (`.register(tool)`).
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 (process.env['RECLAIM_AGENT_TRACE'] === '1') {
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 a drafted ReclaimProvider as a verification version. ' +
7
- 'Call this after run_proof succeeds, passing the `provider` object ' +
8
- 'from propose_provider VERBATIM. This tool builds the full version ' +
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 and returned on the created version — when ' +
18
- 'present, tell the dev verbatim which context fields consumers ' +
19
- 'MUST send when creating sessions (verification matches those ' +
20
- 'values exactly; a missing field is a 400 at session create). ' +
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
- type: 'object',
32
- description: 'The `provider` object from propose_provider, passed ' +
33
- 'through verbatim — the tool builds the version body ' +
34
- '(the requests[] array etc.) for you.',
35
- properties: {
36
- name: { type: 'string', description: 'Verification name.' },
37
- url: {
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', 'provider', 'initialUrl'],
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(args.provider, args.initialUrl, args.notes, ver);
124
- const result = (await client.call('CreateProviderVersion', {
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
- return result.data;
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 X25519 decryption key — only needed when testing ENCRYPTED callback
3
- * payloads in the consumer role. Resolution is file-only (no env var, no path
4
- * override, no `~/.reclaim` caching):
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. An X25519 PKCS#8 PEM file named `reclaim-decrypt.pem` in the project
7
- * root. Mint it with openssl:
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 PEM *content* is loaded via the client's `loadDecryptionKey` (jose
15
- * `ECDH-ES`) and passed straight through as `ReclaimConfig.credential` / to
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 the PEM file is
30
- * absent (the consumer then handles only plaintext deliveries).
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 X25519 decryption key — only needed when testing ENCRYPTED callback
3
- * payloads in the consumer role. Resolution is file-only (no env var, no path
4
- * override, no `~/.reclaim` caching):
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. An X25519 PKCS#8 PEM file named `reclaim-decrypt.pem` in the project
7
- * root. Mint it with openssl:
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 PEM *content* is loaded via the client's `loadDecryptionKey` (jose
15
- * `ECDH-ES`) and passed straight through as `ReclaimConfig.credential` / to
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 X25519 PEM filename the agent looks for in the project root. */
23
- export const DEFAULT_DECRYPT_PEM = 'reclaim-decrypt.pem';
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 PEM string from the default file, without loading
30
- * it. Returns `undefined` when the file is absent (plaintext-only).
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
- export function resolveDecryptKeyInput(projectDir = findProjectRoot(process.cwd())) {
33
- const pemPath = decryptPemPath(projectDir);
34
- if (existsSync(pemPath)) {
35
- return readFileSync(pemPath, 'utf8');
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 the PEM file is
41
- * absent (the consumer then handles only plaintext deliveries).
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 X25519 decrypt key).
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
- export function ethKeyFilePath(projectDir = findProjectRoot(process.cwd())) {
47
- const p = process.env.RECLAIM_PRIVATE_KEY_FILE;
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 = process.env.RECLAIM_PRIVATE_KEY;
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 X25519 decrypt key). ALWAYS confirm with ' +
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.',