@reclaimprotocol/agent 0.1.0 → 0.1.1

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 (204) hide show
  1. package/README.md +1 -1
  2. package/lib/cdp/capture-engine.d.ts +66 -0
  3. package/lib/cdp/capture-engine.js +153 -0
  4. package/lib/cdp/capture.d.ts +9 -20
  5. package/lib/cdp/capture.js +14 -138
  6. package/lib/cdp/index.d.ts +3 -0
  7. package/lib/cdp/index.js +6 -0
  8. package/lib/config/store.d.ts +7 -6
  9. package/lib/config/store.js +2 -2
  10. package/lib/index.d.ts +2 -2
  11. package/lib/index.js +1 -1
  12. package/lib/mcp/api/operations.gen.d.ts +101 -45
  13. package/lib/mcp/api/operations.gen.js +996 -126
  14. package/lib/mcp/api/to-zod.d.ts +24 -0
  15. package/lib/mcp/api/to-zod.js +94 -0
  16. package/lib/mcp/api/tools.js +1 -1
  17. package/lib/mcp/credentials/authenticate.d.ts +3 -0
  18. package/lib/mcp/{tools/credentials → credentials}/authenticate.js +3 -3
  19. package/lib/mcp/credentials/build.d.ts +5 -0
  20. package/lib/mcp/credentials/build.js +18 -0
  21. package/lib/mcp/{tools/credentials → credentials}/cache.d.ts +9 -9
  22. package/lib/mcp/{tools/credentials → credentials}/cache.js +36 -19
  23. package/lib/mcp/credentials/import.d.ts +2 -0
  24. package/lib/mcp/{tools/credentials → credentials}/import.js +2 -2
  25. package/lib/mcp/credentials/issue.d.ts +2 -0
  26. package/lib/mcp/{tools/credentials → credentials}/issue.js +4 -4
  27. package/lib/mcp/credentials/keypair.d.ts +17 -0
  28. package/lib/mcp/credentials/keypair.js +48 -0
  29. package/lib/mcp/credentials/link.d.ts +3 -0
  30. package/lib/mcp/credentials/link.js +107 -0
  31. package/lib/mcp/{tools/credentials → credentials}/list-cached.d.ts +1 -1
  32. package/lib/mcp/{tools/credentials → credentials}/list-cached.js +5 -5
  33. package/lib/mcp/credentials/owner-key.d.ts +23 -0
  34. package/lib/mcp/credentials/owner-key.js +58 -0
  35. package/lib/mcp/credentials/paths.d.ts +12 -0
  36. package/lib/mcp/credentials/paths.js +35 -0
  37. package/lib/mcp/credentials/poll.d.ts +27 -0
  38. package/lib/mcp/credentials/poll.js +61 -0
  39. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.d.ts +1 -1
  40. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.js +2 -2
  41. package/lib/mcp/credentials/session-cache.d.ts +14 -0
  42. package/lib/mcp/credentials/session-cache.js +42 -0
  43. package/lib/mcp/{tools/credentials → credentials}/status.d.ts +1 -1
  44. package/lib/mcp/credentials/status.js +76 -0
  45. package/lib/mcp/instructions.d.ts +1 -0
  46. package/lib/mcp/instructions.js +11 -0
  47. package/lib/mcp/register.d.ts +38 -0
  48. package/lib/mcp/register.js +80 -0
  49. package/lib/mcp/server.d.ts +2 -2
  50. package/lib/mcp/server.js +3 -3
  51. package/lib/mcp/start.js +10 -4
  52. package/lib/mcp/tools/agent/build.js +2 -15
  53. package/lib/mcp/tools/agent/index.d.ts +2 -0
  54. package/lib/mcp/tools/agent/index.js +3 -0
  55. package/lib/mcp/tools/agent/proof.js +1 -6
  56. package/lib/mcp/tools/agent/propose.js +13 -2
  57. package/lib/mcp/tools/agent/publish.js +10 -2
  58. package/lib/mcp/tools/agent/replay.js +0 -5
  59. package/lib/mcp/tools/analyze.d.ts +3 -0
  60. package/lib/mcp/tools/analyze.js +26 -0
  61. package/lib/mcp/tools/attach.d.ts +11 -0
  62. package/lib/mcp/tools/attach.js +72 -0
  63. package/lib/mcp/tools/authenticate/authenticate.js +107 -0
  64. package/lib/mcp/tools/authenticate/build.d.ts +15 -0
  65. package/lib/mcp/tools/authenticate/build.js +28 -0
  66. package/lib/mcp/tools/authenticate/decrypt-key.d.ts +32 -0
  67. package/lib/mcp/tools/authenticate/decrypt-key.js +49 -0
  68. package/lib/mcp/tools/authenticate/eth-key.d.ts +38 -0
  69. package/lib/mcp/tools/authenticate/eth-key.js +130 -0
  70. package/lib/mcp/tools/authenticate/import.js +57 -0
  71. package/lib/mcp/tools/authenticate/index.d.ts +2 -0
  72. package/lib/mcp/tools/authenticate/index.js +4 -0
  73. package/lib/mcp/tools/authenticate/issue.js +42 -0
  74. package/lib/mcp/tools/authenticate/resolve-owner-key.d.ts +13 -0
  75. package/lib/mcp/tools/authenticate/resolve-owner-key.js +53 -0
  76. package/lib/mcp/tools/capture.d.ts +10 -0
  77. package/lib/mcp/tools/capture.js +142 -0
  78. package/lib/mcp/tools/eval.d.ts +5 -0
  79. package/lib/mcp/tools/eval.js +127 -0
  80. package/lib/mcp/tools/find.d.ts +3 -0
  81. package/lib/mcp/tools/find.js +60 -0
  82. package/lib/mcp/tools/index.d.ts +3 -0
  83. package/lib/mcp/tools/index.js +6 -0
  84. package/lib/mcp/tools/owner-key.d.ts +2 -0
  85. package/lib/mcp/tools/owner-key.js +63 -0
  86. package/lib/mcp/tools/proof.d.ts +3 -0
  87. package/lib/mcp/tools/proof.js +214 -0
  88. package/lib/mcp/tools/propose.d.ts +9 -0
  89. package/lib/mcp/tools/propose.js +75 -0
  90. package/lib/mcp/tools/publish.d.ts +3 -0
  91. package/lib/mcp/tools/publish.js +59 -0
  92. package/lib/mcp/tools/replay.d.ts +3 -0
  93. package/lib/mcp/tools/replay.js +46 -0
  94. package/lib/mcp/tools/session.d.ts +10 -0
  95. package/lib/mcp/tools/session.js +29 -0
  96. package/lib/mcp/tools/verification/build.d.ts +12 -0
  97. package/lib/mcp/tools/verification/build.js +14 -0
  98. package/lib/mcp/tools/verification/diagnose.d.ts +11 -0
  99. package/lib/mcp/tools/verification/diagnose.js +132 -0
  100. package/lib/mcp/tools/verification/index.d.ts +1 -0
  101. package/lib/mcp/tools/verification/index.js +2 -0
  102. package/lib/mcp/tools/verification/result.d.ts +12 -0
  103. package/lib/mcp/tools/verification/result.js +126 -0
  104. package/lib/mcp/verifications/build.d.ts +5 -0
  105. package/lib/mcp/verifications/build.js +14 -0
  106. package/lib/mcp/verifications/create.d.ts +3 -0
  107. package/lib/mcp/verifications/create.js +57 -0
  108. package/lib/mcp/verifications/find.d.ts +3 -0
  109. package/lib/mcp/verifications/find.js +37 -0
  110. package/lib/mcp/verifications/get.d.ts +3 -0
  111. package/lib/mcp/verifications/get.js +26 -0
  112. package/lib/mcp/verifications/update.d.ts +3 -0
  113. package/lib/mcp/verifications/update.js +78 -0
  114. package/lib/old/client.d.ts +3 -0
  115. package/lib/old/instructions.d.ts +0 -6
  116. package/lib/old/instructions.js +6 -24
  117. package/lib/old/to-register.d.ts +3 -0
  118. package/lib/old/to-register.js +35 -7
  119. package/lib/old/tools/publish.js +13 -1
  120. package/lib/proof/attestor.d.ts +33 -1
  121. package/lib/proof/attestor.js +67 -61
  122. package/lib/proof/index.d.ts +5 -0
  123. package/lib/proof/index.js +6 -0
  124. package/lib/proof/proof-core.d.ts +35 -0
  125. package/lib/proof/proof-core.js +73 -0
  126. package/lib/proof/zk-files.d.ts +14 -0
  127. package/lib/proof/zk-files.js +73 -0
  128. package/lib/provider/index.d.ts +5 -0
  129. package/lib/provider/index.js +9 -0
  130. package/lib/provider/to-version.d.ts +9 -12
  131. package/lib/provider/to-version.js +38 -4
  132. package/lib/provider/verification-checks.d.ts +12 -0
  133. package/lib/provider/verification-checks.js +30 -0
  134. package/lib/routes.gen.d.ts +271 -0
  135. package/lib/routes.gen.js +73 -0
  136. package/lib/types/openapi.gen.d.ts +3967 -0
  137. package/lib/types/openapi.gen.js +5 -0
  138. package/package.json +18 -10
  139. package/skill/SKILL.md +35 -14
  140. package/skill/builder-mode.md +9 -0
  141. package/skill/old-mode.md +13 -0
  142. package/src/cdp/capture-engine.ts +256 -0
  143. package/src/cdp/capture.ts +25 -185
  144. package/src/cdp/index.ts +6 -0
  145. package/src/config/store.ts +7 -6
  146. package/src/index.ts +2 -4
  147. package/src/mcp/api/operations.gen.ts +1023 -121
  148. package/src/mcp/api/to-zod.ts +115 -0
  149. package/src/mcp/api/tools.ts +9 -3
  150. package/src/mcp/instructions.ts +15 -0
  151. package/src/mcp/server.ts +5 -4
  152. package/src/mcp/start.ts +14 -4
  153. package/src/mcp/tools/agent/build.ts +2 -18
  154. package/src/mcp/tools/agent/index.ts +4 -0
  155. package/src/mcp/tools/agent/proof.ts +1 -7
  156. package/src/mcp/tools/agent/propose.ts +13 -2
  157. package/src/mcp/tools/agent/publish.ts +10 -2
  158. package/src/mcp/tools/agent/replay.ts +0 -5
  159. package/src/mcp/tools/{credentials → authenticate}/authenticate.ts +9 -8
  160. package/src/mcp/tools/authenticate/build.ts +32 -0
  161. package/src/mcp/tools/authenticate/decrypt-key.ts +64 -0
  162. package/src/mcp/tools/authenticate/eth-key.ts +182 -0
  163. package/src/mcp/tools/authenticate/import.ts +73 -0
  164. package/src/mcp/tools/authenticate/index.ts +4 -0
  165. package/src/mcp/tools/authenticate/issue.ts +50 -0
  166. package/src/mcp/tools/authenticate/resolve-owner-key.ts +60 -0
  167. package/src/mcp/tools/index.ts +6 -0
  168. package/src/mcp/tools/verification/build.ts +16 -0
  169. package/src/mcp/tools/verification/index.ts +2 -0
  170. package/src/mcp/tools/verification/result.ts +155 -0
  171. package/src/old/client.ts +3 -0
  172. package/src/old/instructions.ts +10 -25
  173. package/src/old/to-register.ts +62 -27
  174. package/src/old/tools/publish.ts +17 -1
  175. package/src/proof/attestor.ts +115 -69
  176. package/src/proof/index.ts +21 -0
  177. package/src/proof/proof-core.ts +109 -0
  178. package/src/proof/zk-files.ts +83 -0
  179. package/src/provider/index.ts +20 -0
  180. package/src/provider/to-version.ts +52 -11
  181. package/src/provider/verification-checks.ts +36 -0
  182. package/lib/mcp/tools/credentials/build.d.ts +0 -16
  183. package/lib/mcp/tools/credentials/build.js +0 -33
  184. package/lib/mcp/tools/credentials/status.js +0 -62
  185. package/src/mcp/tools/credentials/build.ts +0 -37
  186. package/src/mcp/tools/credentials/cache.ts +0 -161
  187. package/src/mcp/tools/credentials/env-file.ts +0 -101
  188. package/src/mcp/tools/credentials/import.ts +0 -72
  189. package/src/mcp/tools/credentials/issue.ts +0 -107
  190. package/src/mcp/tools/credentials/list-cached.ts +0 -66
  191. package/src/mcp/tools/credentials/register.ts +0 -49
  192. package/src/mcp/tools/credentials/resolve-owner-key.ts +0 -78
  193. package/src/mcp/tools/credentials/status.ts +0 -82
  194. package/src/types/openapi.ts +0 -7
  195. /package/lib/mcp/{tools/credentials → credentials}/env-file.d.ts +0 -0
  196. /package/lib/mcp/{tools/credentials → credentials}/env-file.js +0 -0
  197. /package/lib/mcp/{tools/credentials → credentials}/register.d.ts +0 -0
  198. /package/lib/mcp/{tools/credentials → credentials}/register.js +0 -0
  199. /package/lib/mcp/tools/{credentials → authenticate}/authenticate.d.ts +0 -0
  200. /package/lib/mcp/tools/{credentials → authenticate}/import.d.ts +0 -0
  201. /package/lib/mcp/tools/{credentials → authenticate}/issue.d.ts +0 -0
  202. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.d.ts +0 -0
  203. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.js +0 -0
  204. /package/src/mcp/tools/{credentials → authenticate}/session-cache.ts +0 -0
@@ -1,8 +1,8 @@
1
1
  import type { ReclaimClient } from '@reclaimprotocol/client'
2
2
  import type {
3
- CreateProviderVersionRequest as CreateVersionBody,
4
- ProviderVersionRef as VersionRef,
5
- } from '../types/openapi.ts'
3
+ CreateProviderVersionRequest,
4
+ ProviderVersionRef,
5
+ } from '@reclaimprotocol/client/openapi'
6
6
  import type { ReclaimProvider } from './schema.ts'
7
7
 
8
8
  /**
@@ -16,12 +16,47 @@ import type { ReclaimProvider } from './schema.ts'
16
16
  * Secrets are NOT persisted — the verification client re-supplies
17
17
  * them at runtime via `credentials: 'include'`.
18
18
  */
19
+ /** `{{context.<name>}}` placeholders in a request URL/body: values the
20
+ * CONSUMER supplies at verification time via the session `context`. Bare
21
+ * `{{name}}` placeholders (extraction params) are not consumer-supplied
22
+ * and are ignored. */
23
+ const CONTEXT_PARAM_RE = /\{\{context\.([A-Za-z_][A-Za-z0-9_]*)\}\}/g
24
+
25
+ /**
26
+ * Derive the version's `requiredContext` JSON Schema from the
27
+ * `{{context.<name>}}` placeholders in the provider's url/body — mechanical,
28
+ * so a published recipe can never reference a context value the Builder
29
+ * doesn't require. Returns undefined when the recipe has none (the field is
30
+ * optional). Authors can refine types/descriptions in the version editor.
31
+ */
32
+ export function deriveRequiredContext(
33
+ provider: ReclaimProvider,
34
+ ): CreateProviderVersionRequest['requiredContext'] {
35
+ const haystack = `${provider.url}\n${provider.body ?? ''}`
36
+ const names = new Set<string>()
37
+ for(const m of haystack.matchAll(CONTEXT_PARAM_RE)) {
38
+ names.add(m[1])
39
+ }
40
+
41
+ if(!names.size) {
42
+ return undefined
43
+ }
44
+
45
+ const properties: Record<string, unknown> = {}
46
+ for(const name of names) {
47
+ properties[name] = { type: 'string' }
48
+ }
49
+
50
+ return { type: 'object', required: [...names], properties }
51
+ }
52
+
19
53
  export function providerToCreateVersionRequest(
20
54
  provider: ReclaimProvider,
21
55
  initialUrl: string,
22
56
  notes?: string,
23
- version?: VersionRef,
24
- ): CreateVersionBody {
57
+ version?: ProviderVersionRef,
58
+ ): CreateProviderVersionRequest {
59
+ const requiredContext = deriveRequiredContext(provider)
25
60
  // Inline responseRedactions into matches[*].extract (1-to-1 by index).
26
61
  const matches = (provider.responseMatches ?? []).map((match, i) => {
27
62
  const redaction = provider.responseRedactions?.[i]
@@ -46,10 +81,16 @@ export function providerToCreateVersionRequest(
46
81
  },
47
82
  ],
48
83
  allowedJsRequests: [],
49
- webSettings: { geoLocation: '{{DYNAMIC_GEO}}' },
50
- ...(provider.paramValues
51
- ? { requiredParameters: provider.paramValues }
52
- : {}),
84
+ // Mirror the builder openapi defaults explicitly — the server does not
85
+ // apply schema defaults to request bodies.
86
+ webSettings: {
87
+ geoLocation: '{{DYNAMIC_GEO}}',
88
+ clientOptions: { portal: { useProxy: true } },
89
+ },
90
+ // Derived from `{{context.<name>}}` placeholders only — a captured
91
+ // provider's `paramValues` are extracted response parameters, not
92
+ // consumer context, and never become requiredContext.
93
+ ...(requiredContext ? { requiredContext } : {}),
53
94
  ...(notes ? { notes } : {}),
54
95
  }
55
96
  }
@@ -63,11 +104,11 @@ export function providerToCreateVersionRequest(
63
104
  export async function nextVersion(
64
105
  client: ReclaimClient,
65
106
  providerId: string,
66
- ): Promise<VersionRef> {
107
+ ): Promise<ProviderVersionRef> {
67
108
  const list = (await client.call('ListProviderVersions', {
68
109
  params: { providerId },
69
110
  query: {},
70
- })) as { items?: Array<{ version: VersionRef }> }
111
+ })) as { items?: Array<{ version: ProviderVersionRef }> }
71
112
 
72
113
  if(!list.items?.length) {
73
114
  return { major: 1, minor: 0, patch: 0 }
@@ -0,0 +1,36 @@
1
+ import type { ReclaimProvider } from './schema.ts'
2
+
3
+ /**
4
+ * Advisory pre-publish lint for a drafted provider — never a gate. Surfaces
5
+ * only what the AUTHOR must decide; everything testable is tested by the
6
+ * verification client itself (CDP capture sees all traffic — navigations,
7
+ * XHRs, fetches — and `checkCandidate` in packages/app src/verify/run.ts
8
+ * selects the captured request that satisfies the recipe, skipping the
9
+ * rest). Interpolation and matching are plain text and work for any
10
+ * captured content; extract selectors apply client-side in spec order
11
+ * xPath → jsonPath → regex, each to the prior selection.
12
+ */
13
+ export function verificationWarnings(provider: ReclaimProvider): string[] {
14
+ const out: string[] = []
15
+
16
+ // Untemplated user-specific URL parts: any captured param value baked
17
+ // literally into the URL makes the provider user-specific. Only the dev
18
+ // can decide: consumer-supplied → {{context.<name>}} (declared in
19
+ // requiredContext; consumers then send the exact value and the VC matches
20
+ // only that value), bare {{<name>}} (resolved from the end-user's own
21
+ // traffic), or re-capture a non-user-specific endpoint.
22
+ for(const [name, value] of Object.entries(provider.paramValues ?? {})) {
23
+ if(value.length >= 3 && provider.url.includes(value)) {
24
+ out.push(
25
+ `The URL contains the captured value "${value}" (param ` +
26
+ `"${name}") as a literal. Decide with the dev: if consumers ` +
27
+ 'supply it, replace with {{context.' + name + '}} (published ' +
28
+ 'requiredContext will require it; verification matches only ' +
29
+ 'the supplied value). Leaving it literal pins the provider ' +
30
+ 'to this user.',
31
+ )
32
+ }
33
+ }
34
+
35
+ return out
36
+ }
@@ -1,16 +0,0 @@
1
- import type { ReclaimClient } from '@reclaimprotocol/client';
2
- import type { RegisteredTool } from '../../server.ts';
3
- /**
4
- * Local-only credential tools — no backend client needed. These manage app
5
- * key-pairs entirely on disk / in the env (mint, import, list, resolve which
6
- * key to sign proofs with), so they are useful in BOTH builder and
7
- * OLD-devtools mode.
8
- */
9
- export declare function buildLocalCredentialTools(): RegisteredTool[];
10
- /**
11
- * Full builder-mode credential set: the local tools plus the two that talk to
12
- * the builder backend (`reclaim_authenticate` device-code flow,
13
- * `reclaim_status`). Old mode does NOT use these — it has its own
14
- * authenticate and no credential-status analog.
15
- */
16
- export declare function buildCredentialTools(client: ReclaimClient): RegisteredTool[];
@@ -1,33 +0,0 @@
1
- import { authenticateTool } from "./authenticate.js";
2
- import { importTool } from "./import.js";
3
- import { issueTool } from "./issue.js";
4
- import { listCachedTool } from "./list-cached.js";
5
- import { resolveOwnerKeyTool } from "./resolve-owner-key.js";
6
- import { statusTool } from "./status.js";
7
- /**
8
- * Local-only credential tools — no backend client needed. These manage app
9
- * key-pairs entirely on disk / in the env (mint, import, list, resolve which
10
- * key to sign proofs with), so they are useful in BOTH builder and
11
- * OLD-devtools mode.
12
- */
13
- export function buildLocalCredentialTools() {
14
- return [
15
- listCachedTool(),
16
- resolveOwnerKeyTool(),
17
- issueTool(),
18
- importTool(),
19
- ];
20
- }
21
- /**
22
- * Full builder-mode credential set: the local tools plus the two that talk to
23
- * the builder backend (`reclaim_authenticate` device-code flow,
24
- * `reclaim_status`). Old mode does NOT use these — it has its own
25
- * authenticate and no credential-status analog.
26
- */
27
- export function buildCredentialTools(client) {
28
- return [
29
- authenticateTool(client),
30
- statusTool(client),
31
- ...buildLocalCredentialTools(),
32
- ];
33
- }
@@ -1,62 +0,0 @@
1
- import { deriveFromPrivateKey, } from '@reclaimprotocol/client';
2
- import assert from 'node:assert';
3
- import { findProjectRoot } from "../../../paths.js";
4
- import { defineTool } from "../../server.js";
5
- import { suggestCredentialForDir } from "./cache.js";
6
- export function statusTool(client) {
7
- return defineTool({
8
- name: 'reclaim_status',
9
- description: 'Report the live mode (SANDBOX | PRODUCTION | REVOKED) ' +
10
- 'of a Reclaim app credential. Pass `address` — or omit it ' +
11
- 'to derive the address from RECLAIM_PRIVATE_KEY, falling ' +
12
- 'back to the best cached credential for this directory. ' +
13
- 'Useful to confirm a credential is linked to an org ' +
14
- 'with billing before running a production flow.',
15
- inputSchema: {
16
- type: 'object',
17
- properties: {
18
- address: {
19
- type: 'string',
20
- pattern: '^0x[0-9a-fA-F]{40}$',
21
- description: 'Lowercase ETH address. Defaults to the address ' +
22
- 'derived from RECLAIM_PRIVATE_KEY, then the cached ' +
23
- 'credential suggested for the current directory.',
24
- },
25
- },
26
- },
27
- }, async (args) => {
28
- let address = args.address?.toLowerCase();
29
- if (!address && process.env.RECLAIM_PRIVATE_KEY) {
30
- address = deriveFromPrivateKey(process.env.RECLAIM_PRIVATE_KEY).address;
31
- }
32
- if (!address) {
33
- address = suggestCredentialForDir(findProjectRoot(process.cwd()))
34
- ?.credential.address;
35
- }
36
- assert(address, new Error('No address given, RECLAIM_PRIVATE_KEY is not set, and the ' +
37
- 'cache is empty. Run issue_credentials or import_credentials ' +
38
- 'first.'));
39
- const credResult = await client
40
- .call('GetCredentialByAddress', { params: { address } });
41
- const cred = credResult.data;
42
- // A registered credential always carries an `id`; its absence means
43
- // the server had no row for this address — a local-only credential.
44
- const localOnly = !cred.id;
45
- return {
46
- address: cred.address,
47
- mode: cred.mode,
48
- orgId: cred.orgId,
49
- label: cred.label,
50
- revokedAt: cred.revokedAt,
51
- lastUsedAt: cred.lastUsedAt,
52
- ...(localOnly ? {
53
- note: 'Local-only credential — not registered with the server. '
54
- + 'It operates in SANDBOX, which is all you need to publish '
55
- + 'a verification (only an auth token is required — no '
56
- + 'credential link). Linking it to an org with billing (via '
57
- + 'link_credential) is only needed for the upcoming feature '
58
- + 'that runs verifications for your own app’s users.',
59
- } : {}),
60
- };
61
- });
62
- }
@@ -1,37 +0,0 @@
1
- import type { ReclaimClient } from '@reclaimprotocol/client'
2
- import type { RegisteredTool } from '../../server.ts'
3
- import { authenticateTool } from './authenticate.ts'
4
- import { importTool } from './import.ts'
5
- import { issueTool } from './issue.ts'
6
- import { listCachedTool } from './list-cached.ts'
7
- import { resolveOwnerKeyTool } from './resolve-owner-key.ts'
8
- import { statusTool } from './status.ts'
9
-
10
- /**
11
- * Local-only credential tools — no backend client needed. These manage app
12
- * key-pairs entirely on disk / in the env (mint, import, list, resolve which
13
- * key to sign proofs with), so they are useful in BOTH builder and
14
- * OLD-devtools mode.
15
- */
16
- export function buildLocalCredentialTools(): RegisteredTool[] {
17
- return [
18
- listCachedTool(),
19
- resolveOwnerKeyTool(),
20
- issueTool(),
21
- importTool(),
22
- ]
23
- }
24
-
25
- /**
26
- * Full builder-mode credential set: the local tools plus the two that talk to
27
- * the builder backend (`reclaim_authenticate` device-code flow,
28
- * `reclaim_status`). Old mode does NOT use these — it has its own
29
- * authenticate and no credential-status analog.
30
- */
31
- export function buildCredentialTools(client: ReclaimClient): RegisteredTool[] {
32
- return [
33
- authenticateTool(client),
34
- statusTool(client),
35
- ...buildLocalCredentialTools(),
36
- ]
37
- }
@@ -1,161 +0,0 @@
1
- import { deriveFromPrivateKey } from '@reclaimprotocol/client'
2
- import { ConfigStore } from '../../../config/store.ts'
3
- import { findProjectRoot } from '../../../paths.ts'
4
- import { readPrivateKeyFromEnvFile } from './env-file.ts'
5
-
6
- export type CachedCredential = {
7
- address: string
8
- privateKey: string
9
- label?: string
10
- orgId?: string
11
- createdAt: string
12
- /** Project dir where this credential was first created. */
13
- createdIn: string
14
- /** All project dirs this credential has been used in ("used by"). */
15
- projectDirs: string[]
16
- }
17
-
18
- type CacheFile = {
19
- version: 1
20
- credentials: CachedCredential[]
21
- }
22
-
23
- const EMPTY: CacheFile = { version: 1, credentials: [] }
24
-
25
- /**
26
- * Read the `credentials` section of the unified config. Returns an empty
27
- * doc if missing or malformed — we'd rather lose the cache than crash the
28
- * CLI on a corrupted file the user can re-create.
29
- */
30
- export function readCredentialsFromCache(): CacheFile {
31
- const creds = new ConfigStore().read()?.credentials
32
- if(!Array.isArray(creds)) {
33
- return { ...EMPTY }
34
- }
35
-
36
- return { version: 1, credentials: creds }
37
- }
38
-
39
- /**
40
- * Persist the `credentials` section, merging into the unified config so
41
- * other sections (browser_agent, session) survive untouched. We rewrite
42
- * the whole list each save; it's tiny and the alternative (append-only)
43
- * loses our ability to deduplicate `projectDirs`.
44
- */
45
- export function writeCache(doc: CacheFile) {
46
- new ConfigStore().write({ credentials: doc.credentials })
47
- }
48
-
49
- /**
50
- * Upsert a credential entry. If a row already exists for `address`,
51
- * we merge the new `projectDirs` into the existing list, refresh
52
- * mutable fields, and keep the original `createdAt` / `createdIn`
53
- * (backfilling `createdIn` from the new entry when an old row lacks it).
54
- */
55
- export function upsertCredential(entry: CachedCredential) {
56
- const doc = readCredentialsFromCache()
57
- const idx = doc.credentials.findIndex(
58
- (c) => c.address === entry.address.toLowerCase(),
59
- )
60
- const merged: CachedCredential = {
61
- ...entry,
62
- address: entry.address.toLowerCase(),
63
- projectDirs: Array.from(
64
- new Set([
65
- ...(doc.credentials[idx]?.projectDirs ?? []),
66
- ...entry.projectDirs,
67
- ]),
68
- ),
69
- createdAt: doc.credentials[idx]?.createdAt ?? entry.createdAt,
70
- createdIn: doc.credentials[idx]?.createdIn ?? entry.createdIn,
71
- }
72
- if(idx >= 0) {
73
- doc.credentials[idx] = merged
74
- } else {
75
- doc.credentials.unshift(merged)
76
- }
77
-
78
- writeCache(doc)
79
- return merged
80
- }
81
-
82
- /** Look up a cached credential by address. Case-insensitive. */
83
- export function findCredentialFromCachedBy(
84
- address: string
85
- ): CachedCredential | undefined {
86
- const doc = readCredentialsFromCache()
87
- const lc = address.toLowerCase()
88
- return doc.credentials.find((c) => c.address === lc)
89
- }
90
-
91
- export type SuggestionReason = 'createdIn' | 'usedIn' | 'newest'
92
-
93
- export type CredentialSuggestion = {
94
- credential: CachedCredential
95
- reason: SuggestionReason
96
- }
97
-
98
- /**
99
- * Pick the best cached credential for `dir`. Ranking tiers:
100
- * 1. created in `dir` (reason: 'createdIn')
101
- * 2. previously used in `dir` (reason: 'usedIn')
102
- * 3. most recently created (reason: 'newest')
103
- * Within every tier, newest `createdAt` wins; address ascending is the
104
- * final deterministic tie-break. Returns undefined only on an empty cache.
105
- *
106
- * Pass a pre-read `creds` array to avoid a redundant `readCache()` (and to
107
- * rank against the same snapshot the caller already holds).
108
- */
109
- export function suggestCredentialForDir(
110
- dir: string,
111
- creds: CachedCredential[] = readCredentialsFromCache().credentials,
112
- ): CredentialSuggestion | undefined {
113
- if(creds.length === 0) {
114
- return undefined
115
- }
116
-
117
- const byNewest = (a: CachedCredential, b: CachedCredential) => {
118
- return (b.createdAt ?? '').localeCompare(a.createdAt ?? '') ||
119
- a.address.localeCompare(b.address)
120
- }
121
-
122
- const createdHere = creds.filter((c) => c.createdIn === dir).sort(byNewest)
123
- if(createdHere[0]) {
124
- return { credential: createdHere[0], reason: 'createdIn' }
125
- }
126
-
127
- const usedHere = creds
128
- .filter((c) => c.projectDirs.includes(dir))
129
- .sort(byNewest)
130
- if(usedHere[0]) {
131
- return { credential: usedHere[0], reason: 'usedIn' }
132
- }
133
-
134
- return { credential: [...creds].sort(byNewest)[0], reason: 'newest' }
135
- }
136
-
137
- /**
138
- * Resolve the private key to sign attestor proofs with (the claim
139
- * "owner"). Reuses the app credential rather than a dedicated key — for
140
- * build-time test proofs the developer is both the app and the proof
141
- * owner, and the owner identity is immaterial to whether the provider's
142
- * matching works.
143
- *
144
- * The owner is chosen up front (see the `resolve_owner_key` tool) and
145
- * passed by `address`; this just fetches the matching private key:
146
- * - the `RECLAIM_PRIVATE_KEY` env / project `.env` key, if it derives
147
- * to `address` (the dev pinned it), else
148
- * - the cached credential with that address.
149
- * Returns undefined when no key for `address` is known.
150
- */
151
- export function resolveOwnerPrivateKey(address: string): string | undefined {
152
- const target = address.toLowerCase()
153
- const envKey =
154
- process.env.RECLAIM_PRIVATE_KEY ??
155
- readPrivateKeyFromEnvFile(findProjectRoot(process.cwd()))
156
- if(envKey && deriveFromPrivateKey(envKey).address === target) {
157
- return envKey
158
- }
159
-
160
- return findCredentialFromCachedBy(target)?.privateKey
161
- }
@@ -1,101 +0,0 @@
1
- import {
2
- appendFileSync,
3
- chmodSync,
4
- existsSync,
5
- readFileSync,
6
- writeFileSync,
7
- } from 'node:fs'
8
- import { join } from 'node:path'
9
-
10
- const PRIVATE_KEY = 'RECLAIM_PRIVATE_KEY'
11
- const GITIGNORE_LINE = '.env'
12
-
13
- export type EnvWriteResult = {
14
- envPath: string
15
- gitignorePath: string
16
- gitignoreAppended: boolean
17
- }
18
-
19
- /**
20
- * Idempotently set `RECLAIM_PRIVATE_KEY` in the project's `.env`, then
21
- * `chmod 0600` it. The private key is the only secret we write — the
22
- * ETH address (and public key) are derived from it on demand, so there
23
- * is no separate address variable to keep in sync. Existing variables
24
- * are replaced in place; other lines are preserved verbatim so we
25
- * don't trample on unrelated config the user wrote by hand.
26
- *
27
- * Also appends `.env` to the project's `.gitignore` if it isn't
28
- * already listed.
29
- */
30
- export function writeAppCredentialsToEnv(
31
- projectDir: string,
32
- privateKey: string,
33
- ): EnvWriteResult {
34
- const envPath = join(projectDir, '.env')
35
- const lines = existsSync(envPath)
36
- ? readFileSync(envPath, 'utf8').split('\n')
37
- : []
38
-
39
- setOrInsert(lines, PRIVATE_KEY, privateKey)
40
-
41
- const body = lines.join('\n').replace(/\n+$/, '') + '\n'
42
- writeFileSync(envPath, body, { mode: 0o600 })
43
- chmodSync(envPath, 0o600)
44
-
45
- const gitignorePath = join(projectDir, '.gitignore')
46
- const gitignoreAppended = ensureLine(gitignorePath, GITIGNORE_LINE)
47
-
48
- return { envPath, gitignorePath, gitignoreAppended }
49
- }
50
-
51
- /**
52
- * Read `RECLAIM_PRIVATE_KEY` back out of the project's `.env`, if set.
53
- * Read live (not from `process.env`) so a credential written by
54
- * `issue_credentials` / `import_credentials` is picked up immediately,
55
- * without a Claude Code restart. Returns undefined when the file or the
56
- * variable is absent.
57
- */
58
- export function readPrivateKeyFromEnvFile(
59
- projectDir: string,
60
- ): string | undefined {
61
- const envPath = join(projectDir, '.env')
62
- if(!existsSync(envPath)) {
63
- return undefined
64
- }
65
-
66
- const prefix = `${PRIVATE_KEY}=`
67
- const line = readFileSync(envPath, 'utf8')
68
- .split('\n')
69
- .find((l) => l.startsWith(prefix))
70
- const value = line?.slice(prefix.length).trim()
71
- return value || undefined
72
- }
73
-
74
- function setOrInsert(lines: string[], key: string, value: string) {
75
- const prefix = `${key}=`
76
- const idx = lines.findIndex((l) => l.startsWith(prefix))
77
- const next = `${key}=${value}`
78
- if(idx >= 0) {
79
- lines[idx] = next
80
- return
81
- }
82
-
83
- lines.push(next)
84
- }
85
-
86
- function ensureLine(path: string, line: string): boolean {
87
- if(!existsSync(path)) {
88
- writeFileSync(path, line + '\n', { mode: 0o644 })
89
- return true
90
- }
91
-
92
- const body = readFileSync(path, 'utf8')
93
- const present = body.split('\n').some((l) => l.trim() === line)
94
- if(present) {
95
- return false
96
- }
97
-
98
- const suffix = body.endsWith('\n') ? '' : '\n'
99
- appendFileSync(path, suffix + line + '\n')
100
- return true
101
- }
@@ -1,72 +0,0 @@
1
- import { deriveFromPrivateKey } from '@reclaimprotocol/client'
2
- import { findProjectRoot } from '../../../paths.ts'
3
- import { defineTool, type RegisteredTool } from '../../server.ts'
4
- import { persistCredentialLocally } from './register.ts'
5
-
6
- export function importTool(): RegisteredTool {
7
- return defineTool<{
8
- privateKey: string
9
- label?: string
10
- projectDir?: string
11
- writeEnv?: boolean
12
- }>(
13
- {
14
- name: 'import_credentials',
15
- description:
16
- 'Import an existing Reclaim app credential the user ' +
17
- 'already holds elsewhere. Provide the private key ' +
18
- '(0x-prefixed hex, 64 chars); we derive the address ' +
19
- 'locally and cache it. By default also writes ' +
20
- 'RECLAIM_PRIVATE_KEY to the project .env (0600) and appends ' +
21
- '.env to .gitignore; pass `writeEnv: false` to keep it in ' +
22
- 'the local cache (memory) only and leave .env untouched. ' +
23
- 'ALWAYS ask the dev whether to save to .env before writing. ' +
24
- 'The credential stays local-only (SANDBOX); link it to an ' +
25
- 'org to use it in production.',
26
- inputSchema: {
27
- type: 'object',
28
- properties: {
29
- privateKey: {
30
- type: 'string',
31
- pattern: '^0x[0-9a-fA-F]{64}$',
32
- description:
33
- '0x-prefixed hex private key (64 hex chars / 32 bytes).',
34
- },
35
- label: {
36
- type: 'string',
37
- minLength: 1,
38
- maxLength: 100,
39
- },
40
- projectDir: {
41
- type: 'string',
42
- description:
43
- 'Project directory to write .env into. Defaults ' +
44
- 'to the closest ancestor of cwd containing ' +
45
- '.git or package.json.',
46
- },
47
- writeEnv: {
48
- type: 'boolean',
49
- description:
50
- 'Write RECLAIM_PRIVATE_KEY to the project .env ' +
51
- '(default true). Set false to cache in memory only, ' +
52
- 'without touching .env.',
53
- },
54
- },
55
- required: ['privateKey'],
56
- },
57
- },
58
- async(args) => {
59
- const keypair = deriveFromPrivateKey(args.privateKey)
60
- const projectDir = findProjectRoot(args.projectDir || process.cwd())
61
-
62
- const persisted = persistCredentialLocally(
63
- keypair,
64
- args.label,
65
- projectDir,
66
- args.writeEnv ?? true,
67
- )
68
-
69
- return { ...persisted }
70
- },
71
- )
72
- }