@reclaimprotocol/agent 0.1.27 → 0.1.29

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