@reclaimprotocol/agent 0.1.18 → 0.1.20

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 (158) hide show
  1. package/README.md +6 -2
  2. package/lib/authoring/backend.d.ts +4 -0
  3. package/lib/authoring/tools.js +4 -1
  4. package/lib/cdp/capture.d.ts +2 -2
  5. package/lib/cdp/capture.js +25 -21
  6. package/lib/cdp/client.d.ts +39 -1
  7. package/lib/cdp/client.js +125 -1
  8. package/lib/mcp/api/operations.gen.js +33 -9
  9. package/lib/mcp/instructions.js +27 -3
  10. package/lib/mcp/start.js +12 -8
  11. package/lib/mcp/tools/agent/attach.d.ts +18 -4
  12. package/lib/mcp/tools/agent/attach.js +142 -52
  13. package/lib/mcp/tools/agent/build.d.ts +1 -1
  14. package/lib/mcp/tools/agent/build.js +5 -3
  15. package/lib/mcp/tools/agent/capture.d.ts +1 -0
  16. package/lib/mcp/tools/agent/capture.js +8 -1
  17. package/lib/mcp/tools/agent/eval.js +2 -3
  18. package/lib/mcp/tools/agent/how-it-works.d.ts +1 -1
  19. package/lib/mcp/tools/agent/how-it-works.js +1 -1
  20. package/lib/mcp/tools/agent/publish.js +22 -1
  21. package/lib/mcp/tools/agent/session.d.ts +2 -2
  22. package/lib/mcp/tools/agent/session.js +21 -5
  23. package/lib/mcp/tools/agent/shared.js +4 -6
  24. package/lib/mcp/tools/authenticate/authenticate.d.ts +1 -1
  25. package/lib/mcp/tools/authenticate/authenticate.js +10 -4
  26. package/lib/mcp/tools/authenticate/builder-access.d.ts +8 -0
  27. package/lib/mcp/tools/authenticate/builder-access.js +31 -0
  28. package/lib/mcp/tools/authenticate/index.d.ts +1 -0
  29. package/lib/mcp/tools/authenticate/index.js +1 -0
  30. package/lib/old/login-cdp.js +11 -3
  31. package/lib/old/tools/publish.js +23 -1
  32. package/lib/provider/draft.d.ts +20 -0
  33. package/lib/provider/draft.js +75 -51
  34. package/lib/provider/index.d.ts +1 -1
  35. package/lib/provider/index.js +1 -1
  36. package/lib/provider/secrets.d.ts +37 -0
  37. package/lib/provider/secrets.js +147 -6
  38. package/lib/provider/to-version.d.ts +7 -0
  39. package/lib/provider/to-version.js +11 -1
  40. package/package.json +2 -2
  41. package/skill/SKILL.builder.md +43 -95
  42. package/skill/SKILL.common.md +102 -0
  43. package/skill/SKILL.old.md +20 -74
  44. package/skill/builder-mode.md +2 -2
  45. package/skill/injection-api.md +95 -10
  46. package/skill/old-mode.md +2 -2
  47. package/src/authoring/backend.ts +4 -0
  48. package/src/authoring/tools.ts +4 -1
  49. package/src/cdp/capture.ts +24 -24
  50. package/src/cdp/client.ts +197 -2
  51. package/src/mcp/api/operations.gen.ts +33 -9
  52. package/src/mcp/instructions.ts +36 -3
  53. package/src/mcp/start.ts +13 -8
  54. package/src/mcp/tools/agent/attach.ts +198 -57
  55. package/src/mcp/tools/agent/build.ts +9 -2
  56. package/src/mcp/tools/agent/capture.ts +10 -1
  57. package/src/mcp/tools/agent/eval.ts +2 -3
  58. package/src/mcp/tools/agent/how-it-works.ts +1 -1
  59. package/src/mcp/tools/agent/publish.ts +22 -1
  60. package/src/mcp/tools/agent/session.ts +29 -8
  61. package/src/mcp/tools/agent/shared.ts +5 -10
  62. package/src/mcp/tools/authenticate/authenticate.ts +14 -4
  63. package/src/mcp/tools/authenticate/builder-access.ts +42 -0
  64. package/src/mcp/tools/authenticate/index.ts +4 -0
  65. package/src/old/login-cdp.ts +13 -3
  66. package/src/old/tools/publish.ts +25 -3
  67. package/src/provider/draft.ts +98 -56
  68. package/src/provider/index.ts +9 -1
  69. package/src/provider/secrets.ts +171 -6
  70. package/src/provider/to-version.ts +12 -1
  71. package/lib/authoring/find.d.ts +0 -3
  72. package/lib/authoring/find.js +0 -54
  73. package/lib/authoring/propose.d.ts +0 -3
  74. package/lib/authoring/propose.js +0 -109
  75. package/lib/authoring/replay.d.ts +0 -10
  76. package/lib/authoring/replay.js +0 -34
  77. package/lib/cli/args.d.ts +0 -14
  78. package/lib/cli/args.js +0 -19
  79. package/lib/cli/commands.d.ts +0 -1
  80. package/lib/cli/commands.js +0 -19
  81. package/lib/cli.d.ts +0 -1
  82. package/lib/cli.js +0 -7
  83. package/lib/mcp/credentials/authenticate.d.ts +0 -3
  84. package/lib/mcp/credentials/authenticate.js +0 -108
  85. package/lib/mcp/credentials/build.d.ts +0 -5
  86. package/lib/mcp/credentials/build.js +0 -18
  87. package/lib/mcp/credentials/cache.d.ts +0 -70
  88. package/lib/mcp/credentials/cache.js +0 -131
  89. package/lib/mcp/credentials/env-file.d.ts +0 -25
  90. package/lib/mcp/credentials/env-file.js +0 -71
  91. package/lib/mcp/credentials/import.d.ts +0 -2
  92. package/lib/mcp/credentials/import.js +0 -52
  93. package/lib/mcp/credentials/issue.d.ts +0 -2
  94. package/lib/mcp/credentials/issue.js +0 -84
  95. package/lib/mcp/credentials/keypair.d.ts +0 -17
  96. package/lib/mcp/credentials/keypair.js +0 -48
  97. package/lib/mcp/credentials/link.d.ts +0 -3
  98. package/lib/mcp/credentials/link.js +0 -107
  99. package/lib/mcp/credentials/list-cached.d.ts +0 -2
  100. package/lib/mcp/credentials/list-cached.js +0 -55
  101. package/lib/mcp/credentials/owner-key.d.ts +0 -23
  102. package/lib/mcp/credentials/owner-key.js +0 -58
  103. package/lib/mcp/credentials/paths.d.ts +0 -12
  104. package/lib/mcp/credentials/paths.js +0 -35
  105. package/lib/mcp/credentials/poll.d.ts +0 -27
  106. package/lib/mcp/credentials/poll.js +0 -61
  107. package/lib/mcp/credentials/register.d.ts +0 -18
  108. package/lib/mcp/credentials/register.js +0 -30
  109. package/lib/mcp/credentials/resolve-owner-key.d.ts +0 -12
  110. package/lib/mcp/credentials/resolve-owner-key.js +0 -68
  111. package/lib/mcp/credentials/session-cache.d.ts +0 -14
  112. package/lib/mcp/credentials/session-cache.js +0 -42
  113. package/lib/mcp/credentials/status.d.ts +0 -3
  114. package/lib/mcp/credentials/status.js +0 -76
  115. package/lib/mcp/register.d.ts +0 -38
  116. package/lib/mcp/register.js +0 -80
  117. package/lib/mcp/tools/analyze.d.ts +0 -3
  118. package/lib/mcp/tools/analyze.js +0 -26
  119. package/lib/mcp/tools/attach.d.ts +0 -11
  120. package/lib/mcp/tools/attach.js +0 -72
  121. package/lib/mcp/tools/capture.d.ts +0 -10
  122. package/lib/mcp/tools/capture.js +0 -142
  123. package/lib/mcp/tools/eval.d.ts +0 -5
  124. package/lib/mcp/tools/eval.js +0 -127
  125. package/lib/mcp/tools/find.d.ts +0 -3
  126. package/lib/mcp/tools/find.js +0 -60
  127. package/lib/mcp/tools/owner-key.d.ts +0 -2
  128. package/lib/mcp/tools/owner-key.js +0 -63
  129. package/lib/mcp/tools/proof.d.ts +0 -3
  130. package/lib/mcp/tools/proof.js +0 -214
  131. package/lib/mcp/tools/propose.d.ts +0 -9
  132. package/lib/mcp/tools/propose.js +0 -75
  133. package/lib/mcp/tools/publish.d.ts +0 -3
  134. package/lib/mcp/tools/publish.js +0 -59
  135. package/lib/mcp/tools/replay.d.ts +0 -3
  136. package/lib/mcp/tools/replay.js +0 -46
  137. package/lib/mcp/tools/session.d.ts +0 -10
  138. package/lib/mcp/tools/session.js +0 -29
  139. package/lib/mcp/tools/verification/diagnose.d.ts +0 -11
  140. package/lib/mcp/tools/verification/diagnose.js +0 -132
  141. package/lib/mcp/verifications/build.d.ts +0 -5
  142. package/lib/mcp/verifications/build.js +0 -14
  143. package/lib/mcp/verifications/create.d.ts +0 -3
  144. package/lib/mcp/verifications/create.js +0 -57
  145. package/lib/mcp/verifications/find.d.ts +0 -3
  146. package/lib/mcp/verifications/find.js +0 -37
  147. package/lib/mcp/verifications/get.d.ts +0 -3
  148. package/lib/mcp/verifications/get.js +0 -26
  149. package/lib/mcp/verifications/update.d.ts +0 -3
  150. package/lib/mcp/verifications/update.js +0 -78
  151. package/lib/old/tools/get-version-config.d.ts +0 -17
  152. package/lib/old/tools/get-version-config.js +0 -47
  153. package/lib/routes.gen.d.ts +0 -271
  154. package/lib/routes.gen.js +0 -73
  155. package/lib/types/openapi.d.ts +0 -4
  156. package/lib/types/openapi.gen.d.ts +0 -3967
  157. package/lib/types/openapi.gen.js +0 -5
  158. package/lib/types/openapi.js +0 -1
@@ -1,17 +0,0 @@
1
- export type EthKeypair = {
2
- address: string;
3
- publicKey: string;
4
- privateKey: string;
5
- };
6
- /**
7
- * Generate a fresh secp256k1 key-pair and derive its lowercase
8
- * Ethereum address (keccak-256 of the uncompressed pubkey minus the
9
- * `0x04` prefix, last 20 bytes).
10
- */
11
- export declare function generateKeypair(): EthKeypair;
12
- /**
13
- * Recover the address + uncompressed public key from a hex-encoded
14
- * private key. Used by `importCredentials` to derive the public
15
- * surface the server needs from a key the user already holds.
16
- */
17
- export declare function deriveFromPrivateKey(privateKey: string): EthKeypair;
@@ -1,48 +0,0 @@
1
- import { keccak_256 } from '@noble/hashes/sha3.js';
2
- import * as secp from '@noble/secp256k1';
3
- /**
4
- * Generate a fresh secp256k1 key-pair and derive its lowercase
5
- * Ethereum address (keccak-256 of the uncompressed pubkey minus the
6
- * `0x04` prefix, last 20 bytes).
7
- */
8
- export function generateKeypair() {
9
- const priv = secp.utils.randomSecretKey();
10
- const pub = secp.getPublicKey(priv, false);
11
- return {
12
- address: '0x' + bytesToHex(keccak_256(pub.slice(1))).slice(-40),
13
- publicKey: '0x' + bytesToHex(pub),
14
- privateKey: '0x' + bytesToHex(priv),
15
- };
16
- }
17
- /**
18
- * Recover the address + uncompressed public key from a hex-encoded
19
- * private key. Used by `importCredentials` to derive the public
20
- * surface the server needs from a key the user already holds.
21
- */
22
- export function deriveFromPrivateKey(privateKey) {
23
- const hex = privateKey.replace(/^0x/, '');
24
- const priv = hexToBytes(hex);
25
- const pub = secp.getPublicKey(priv, false);
26
- return {
27
- address: '0x' + bytesToHex(keccak_256(pub.slice(1))).slice(-40),
28
- publicKey: '0x' + bytesToHex(pub),
29
- privateKey: '0x' + hex,
30
- };
31
- }
32
- function bytesToHex(bytes) {
33
- let out = '';
34
- for (const b of bytes) {
35
- out += b.toString(16).padStart(2, '0');
36
- }
37
- return out;
38
- }
39
- function hexToBytes(hex) {
40
- if (hex.length % 2 !== 0) {
41
- throw new Error('private key hex must have even length');
42
- }
43
- const out = new Uint8Array(hex.length / 2);
44
- for (let i = 0; i < out.length; i++) {
45
- out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
46
- }
47
- return out;
48
- }
@@ -1,3 +0,0 @@
1
- import type { ReclaimClient } from '@reclaimprotocol/client';
2
- import { type RegisteredTool } from '../server.ts';
3
- export declare function linkTool(client: ReclaimClient): RegisteredTool;
@@ -1,107 +0,0 @@
1
- import { defineTool } from "../server.js";
2
- import { patchCached } from "./cache.js";
3
- import { writeSession } from "./session-cache.js";
4
- export function linkTool(client) {
5
- return defineTool({
6
- name: 'link_credential_via_dashboard',
7
- description: 'Pair a Reclaim app credential to an organization via the dashboard. '
8
- + 'Two steps:\n\n'
9
- + 'Step 1 — call with the credential `address`. Returns a `code` '
10
- + 'and `attachUrl`. Tell the user to open the URL, sign in, pick '
11
- + 'an organization, and click "Confirm link".\n\n'
12
- + 'Step 2 — once the user has confirmed, call again with the same '
13
- + '`code` from step 1 to check the result. Returns `LINKED` with '
14
- + 'the linked `orgId` when done, `PENDING` if not yet.',
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 '
22
- + 'process.env.RECLAIM_APP_PK.',
23
- },
24
- code: {
25
- type: 'string',
26
- pattern: '^[A-HJ-NP-Z2-9]{5}$',
27
- description: '5-character code from a previous step-1 call. '
28
- + 'When provided, checks whether the user has '
29
- + 'confirmed that session.',
30
- },
31
- },
32
- },
33
- }, async (args) => {
34
- // Step 2: check an existing session
35
- if (args.code) {
36
- const address = (args.address || process.env.RECLAIM_APP_PK || '').toLowerCase();
37
- let session;
38
- try {
39
- session = await client.call('GetAttachSession', {
40
- params: { code: args.code },
41
- query: {}, body: {},
42
- });
43
- }
44
- catch (err) {
45
- const msg = err.message;
46
- if (msg.includes('404') || msg.includes('Not Found')) {
47
- return {
48
- status: 'NOT_FOUND',
49
- hint: 'Session not found or expired. '
50
- + 'Call link_credential_via_dashboard with just the address to start over.',
51
- };
52
- }
53
- throw new Error(`Failed to check session: ${msg}`);
54
- }
55
- if (session.status === 'LINKED') {
56
- if (session.linkedOrgId && address) {
57
- patchCached(address, { orgId: session.linkedOrgId });
58
- }
59
- const token = session.token?.trim();
60
- if (token) {
61
- writeSession({
62
- token,
63
- expiresAt: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString(),
64
- });
65
- client.setToken(token);
66
- }
67
- return {
68
- status: 'LINKED',
69
- address,
70
- ...(session.linkedOrgId ? { orgId: session.linkedOrgId } : {}),
71
- ...(token ? { token } : {}),
72
- };
73
- }
74
- if (session.status === 'EXPIRED') {
75
- return {
76
- status: 'EXPIRED',
77
- hint: 'Session expired. Call link_credential_via_dashboard with the address to start over.',
78
- };
79
- }
80
- // Still PENDING
81
- return {
82
- status: 'PENDING',
83
- code: session.code,
84
- attachUrl: session.attachUrl,
85
- hint: `Still waiting. Open ${session.attachUrl}, sign in, pick an org, and confirm. `
86
- + `Then call link_credential_via_dashboard with code: "${session.code}" again.`,
87
- };
88
- }
89
- // Step 1: open a new session
90
- const address = (args.address || process.env.RECLAIM_APP_PK || '').toLowerCase();
91
- if (!address) {
92
- throw new Error('No address provided and RECLAIM_APP_PK is not set.');
93
- }
94
- const session = await client.call('CreateAttachSession', {
95
- params: {}, query: {},
96
- body: { address },
97
- });
98
- return {
99
- status: 'PENDING',
100
- code: session.code,
101
- attachUrl: session.attachUrl,
102
- expiresAt: session.expiresAt,
103
- instructions: `Open ${session.attachUrl} in your browser, sign in, and pick an organization. `
104
- + `Once done, call link_credential_via_dashboard again with code: "${session.code}" to confirm.`,
105
- };
106
- });
107
- }
@@ -1,2 +0,0 @@
1
- import { type RegisteredTool } from '../server.ts';
2
- export declare function listCachedTool(): RegisteredTool;
@@ -1,55 +0,0 @@
1
- import { findProjectRoot } from "../../paths.js";
2
- import { defineTool } from "../server.js";
3
- import { readCache, suggestCredentialForDir } from "./cache.js";
4
- export function listCachedTool() {
5
- return defineTool({
6
- name: 'list_cached_credentials',
7
- description: 'List Reclaim app credentials (ETH key-pairs) cached ' +
8
- 'locally in ~/.reclaim/credentials.json. Returns one ' +
9
- 'row per credential with address, optional label and ' +
10
- 'orgId, createdAt, createdIn (where it was first created), ' +
11
- 'and the project directories that have used it. Private keys ' +
12
- 'are stripped. The response also reports `currentDir` and ' +
13
- 'flags the best credential for it (`suggested`, plus ' +
14
- '`suggestedAddress`/`suggestionReason`) — prefer reusing that ' +
15
- 'one over minting a new credential. When the cache is empty a ' +
16
- '`suggestion` string explains how to create one.',
17
- inputSchema: {
18
- type: 'object',
19
- properties: {},
20
- },
21
- }, async () => {
22
- const doc = readCache();
23
- const currentDir = findProjectRoot(process.cwd());
24
- const suggestion = suggestCredentialForDir(currentDir, doc.credentials);
25
- const suggestedAddress = suggestion?.credential.address;
26
- const items = doc.credentials.map((c) => ({
27
- address: c.address,
28
- label: c.label,
29
- orgId: c.orgId,
30
- createdAt: c.createdAt,
31
- createdIn: c.createdIn,
32
- projectDirs: c.projectDirs,
33
- matchesCurrentDir: c.createdIn === currentDir || c.projectDirs.includes(currentDir),
34
- suggested: c.address === suggestedAddress,
35
- }));
36
- // Promote the single suggested row to the top; stable sort keeps
37
- // the rest in insertion (newest-first) order.
38
- items.sort((a, b) => Number(b.suggested) - Number(a.suggested));
39
- if (items.length === 0) {
40
- return {
41
- items,
42
- currentDir,
43
- suggestion: 'No cached credentials. Call issue_credentials to mint a new ' +
44
- 'app key-pair, or import_credentials if you already hold a ' +
45
- 'private key. Do not invent credentials.',
46
- };
47
- }
48
- return {
49
- items,
50
- currentDir,
51
- suggestedAddress,
52
- suggestionReason: suggestion?.reason,
53
- };
54
- });
55
- }
@@ -1,23 +0,0 @@
1
- export type OwnerKey = {
2
- privateKey: string;
3
- address: string;
4
- publicKey: string;
5
- /** ISO timestamp when the key was generated. */
6
- createdAt: string;
7
- };
8
- export declare function ownerKeyPath(): string;
9
- /** Read the saved owner key, or undefined if absent/unreadable. */
10
- export declare function readOwnerKey(): OwnerKey | undefined;
11
- /** Persist the owner key to ~/.reclaim/owner-key.json (0600). */
12
- export declare function writeOwnerKey(key: OwnerKey): void;
13
- /**
14
- * Resolve the owner private key for proof generation. Priority:
15
- * explicit `RECLAIM_AGENT_OWNER_PRIVATE_KEY` env var → saved
16
- * ~/.reclaim/owner-key.json → undefined. Mirrors the token-resolution
17
- * order used for the auth session.
18
- */
19
- export declare function resolveOwnerPrivateKey(): string | undefined;
20
- /** True when the env var is set and therefore shadows any saved file. */
21
- export declare function ownerKeyEnvOverrideActive(): boolean;
22
- /** Generate a fresh owner key, persist it, and return it. */
23
- export declare function generateOwnerKey(): OwnerKey;
@@ -1,58 +0,0 @@
1
- import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { generateKeypair } from "./keypair.js";
4
- import { reclaimHome } from "./paths.js";
5
- const ENV_VAR = 'RECLAIM_AGENT_OWNER_PRIVATE_KEY';
6
- export function ownerKeyPath() {
7
- return join(reclaimHome(), 'owner-key.json');
8
- }
9
- /** Read the saved owner key, or undefined if absent/unreadable. */
10
- export function readOwnerKey() {
11
- const path = ownerKeyPath();
12
- if (!existsSync(path)) {
13
- return undefined;
14
- }
15
- try {
16
- const k = JSON.parse(readFileSync(path, 'utf8'));
17
- return k.privateKey ? k : undefined;
18
- }
19
- catch {
20
- return undefined;
21
- }
22
- }
23
- /** Persist the owner key to ~/.reclaim/owner-key.json (0600). */
24
- export function writeOwnerKey(key) {
25
- const dir = reclaimHome();
26
- if (!existsSync(dir)) {
27
- mkdirSync(dir, { recursive: true, mode: 0o700 });
28
- }
29
- chmodSync(dir, 0o700);
30
- const path = ownerKeyPath();
31
- writeFileSync(path, JSON.stringify(key, null, 2) + '\n', { mode: 0o600 });
32
- chmodSync(path, 0o600);
33
- }
34
- /**
35
- * Resolve the owner private key for proof generation. Priority:
36
- * explicit `RECLAIM_AGENT_OWNER_PRIVATE_KEY` env var → saved
37
- * ~/.reclaim/owner-key.json → undefined. Mirrors the token-resolution
38
- * order used for the auth session.
39
- */
40
- export function resolveOwnerPrivateKey() {
41
- return process.env[ENV_VAR] || readOwnerKey()?.privateKey;
42
- }
43
- /** True when the env var is set and therefore shadows any saved file. */
44
- export function ownerKeyEnvOverrideActive() {
45
- return Boolean(process.env[ENV_VAR]);
46
- }
47
- /** Generate a fresh owner key, persist it, and return it. */
48
- export function generateOwnerKey() {
49
- const kp = generateKeypair();
50
- const key = {
51
- privateKey: kp.privateKey,
52
- address: kp.address,
53
- publicKey: kp.publicKey,
54
- createdAt: new Date().toISOString(),
55
- };
56
- writeOwnerKey(key);
57
- return key;
58
- }
@@ -1,12 +0,0 @@
1
- /**
2
- * Resolve the Reclaim cache directory (`~/.reclaim`). Honours the
3
- * `RECLAIM_HOME` env var as an override so tests can sandbox writes.
4
- */
5
- export declare function reclaimHome(): string;
6
- export declare function credentialsCachePath(): string;
7
- /**
8
- * Walk up from `start` until we find a directory containing one of
9
- * the project-root markers, then return that directory. Falls back
10
- * to `start` if no marker is found within 8 levels.
11
- */
12
- export declare function findProjectRoot(start?: string): string;
@@ -1,35 +0,0 @@
1
- import { existsSync } from 'node:fs';
2
- import { homedir } from 'node:os';
3
- import { dirname, join, resolve } from 'node:path';
4
- /**
5
- * Resolve the Reclaim cache directory (`~/.reclaim`). Honours the
6
- * `RECLAIM_HOME` env var as an override so tests can sandbox writes.
7
- */
8
- export function reclaimHome() {
9
- return process.env.RECLAIM_HOME || join(homedir(), '.reclaim');
10
- }
11
- export function credentialsCachePath() {
12
- return join(reclaimHome(), 'credentials.json');
13
- }
14
- /**
15
- * Walk up from `start` until we find a directory containing one of
16
- * the project-root markers, then return that directory. Falls back
17
- * to `start` if no marker is found within 8 levels.
18
- */
19
- export function findProjectRoot(start = process.cwd()) {
20
- const markers = ['.git', 'package.json', 'pyproject.toml', 'Cargo.toml'];
21
- let dir = resolve(start);
22
- for (let i = 0; i < 8; i++) {
23
- for (const m of markers) {
24
- if (existsSync(join(dir, m))) {
25
- return dir;
26
- }
27
- }
28
- const parent = dirname(dir);
29
- if (parent === dir) {
30
- break;
31
- }
32
- dir = parent;
33
- }
34
- return start;
35
- }
@@ -1,27 +0,0 @@
1
- import type { ReclaimClient } from '@reclaimprotocol/client';
2
- import type { components } from '../../types/openapi.gen.ts';
3
- type Session = components['schemas']['CredentialAttachSession'];
4
- type PollResult = {
5
- outcome: 'linked';
6
- token?: string;
7
- orgId?: string;
8
- } | {
9
- outcome: 'expired';
10
- } | {
11
- outcome: 'timeout';
12
- elapsed: number;
13
- attachUrl: string;
14
- } | {
15
- outcome: 'error';
16
- message: string;
17
- };
18
- /**
19
- * Poll GET /credentials/attach/sessions/{code} until the session is
20
- * LINKED or we give up, whichever comes first.
21
- *
22
- * Errors inside the loop are surfaced immediately after MAX_CONSECUTIVE_ERRORS
23
- * rather than being silently swallowed — this is what tells the agent that
24
- * e.g. the confirmation POST failed or the server restarted.
25
- */
26
- export declare function pollUntilLinked(client: ReclaimClient, session: Pick<Session, 'code' | 'attachUrl' | 'expiresAt'>, onToken?: (token: string) => void): Promise<PollResult>;
27
- export {};
@@ -1,61 +0,0 @@
1
- // How long to poll before giving up and telling the user what to do.
2
- // Shorter than the 15-min server TTL on purpose — agents can't block long.
3
- const POLL_TIMEOUT_MS = 3 * 60_000; // 3 minutes
4
- const POLL_INTERVAL_MS = 5_000; // 5 seconds
5
- const MAX_CONSECUTIVE_ERRORS = 5;
6
- /**
7
- * Poll GET /credentials/attach/sessions/{code} until the session is
8
- * LINKED or we give up, whichever comes first.
9
- *
10
- * Errors inside the loop are surfaced immediately after MAX_CONSECUTIVE_ERRORS
11
- * rather than being silently swallowed — this is what tells the agent that
12
- * e.g. the confirmation POST failed or the server restarted.
13
- */
14
- export async function pollUntilLinked(client, session, onToken) {
15
- const sessionExpiry = new Date(session.expiresAt).getTime();
16
- const deadline = Math.min(sessionExpiry, Date.now() + POLL_TIMEOUT_MS);
17
- const startedAt = Date.now();
18
- let consecutiveErrors = 0;
19
- while (Date.now() < deadline) {
20
- await new Promise(r => setTimeout(r, POLL_INTERVAL_MS));
21
- let polled;
22
- try {
23
- polled = await client.call('GetAttachSession', {
24
- params: { code: session.code },
25
- query: {}, body: {},
26
- });
27
- consecutiveErrors = 0;
28
- }
29
- catch (err) {
30
- consecutiveErrors++;
31
- if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
32
- return {
33
- outcome: 'error',
34
- message: `Polling failed ${consecutiveErrors} times in a row: `
35
- + err.message
36
- + '. The session confirmation may have failed — try opening '
37
- + session.attachUrl
38
- + ' again and confirming, then retry.',
39
- };
40
- }
41
- continue; // transient error, retry
42
- }
43
- if (polled.status === 'LINKED') {
44
- const token = polled.token?.trim();
45
- if (token) {
46
- onToken?.(token);
47
- }
48
- return {
49
- outcome: 'linked',
50
- token,
51
- orgId: polled.linkedOrgId,
52
- };
53
- }
54
- if (polled.status === 'EXPIRED') {
55
- return { outcome: 'expired' };
56
- }
57
- // status === 'PENDING' → keep polling
58
- }
59
- const elapsed = Math.round((Date.now() - startedAt) / 60_000);
60
- return { outcome: 'timeout', elapsed, attachUrl: session.attachUrl };
61
- }
@@ -1,18 +0,0 @@
1
- import type { EthKeypair } from '@reclaimprotocol/client';
2
- import { type CachedCredential } from './cache.ts';
3
- import { type EnvWriteResult } from './env-file.ts';
4
- /** Shape returned by `persistCredentialLocally` — the fields both
5
- * `issue_credentials` and `import_credentials` expose in their MCP
6
- * responses. The env-file fields are present only when the credential
7
- * was persisted to `.env` (`writeEnv: true`); a memory-only credential
8
- * lives in the cache and omits them. */
9
- export type PersistedCredential = Pick<CachedCredential, 'address' | 'label'> & Partial<EnvWriteResult> & {
10
- wroteEnv: boolean;
11
- };
12
- /** Local persistence step shared by `issue_credentials` and
13
- * `import_credentials`. Always upserts the credentials cache; when
14
- * `writeEnv` is true (default) it also writes `RECLAIM_PRIVATE_KEY` to
15
- * the project `.env` (0600) and appends `.env` to `.gitignore`. Pass
16
- * `writeEnv: false` to keep the credential in the cache (memory) only,
17
- * without touching the project's `.env`. */
18
- export declare function persistCredentialLocally(keypair: EthKeypair, label: string | undefined, projectDir: string, writeEnv?: boolean): PersistedCredential;
@@ -1,30 +0,0 @@
1
- import { upsertCredential } from "./cache.js";
2
- import { writeAppCredentialsToEnv } from "./env-file.js";
3
- /** Local persistence step shared by `issue_credentials` and
4
- * `import_credentials`. Always upserts the credentials cache; when
5
- * `writeEnv` is true (default) it also writes `RECLAIM_PRIVATE_KEY` to
6
- * the project `.env` (0600) and appends `.env` to `.gitignore`. Pass
7
- * `writeEnv: false` to keep the credential in the cache (memory) only,
8
- * without touching the project's `.env`. */
9
- export function persistCredentialLocally(keypair, label, projectDir, writeEnv = true) {
10
- const cached = upsertCredential({
11
- address: keypair.address,
12
- privateKey: keypair.privateKey,
13
- label,
14
- createdAt: new Date().toISOString(),
15
- createdIn: projectDir,
16
- projectDirs: [projectDir],
17
- });
18
- if (!writeEnv) {
19
- return { address: cached.address, label: cached.label, wroteEnv: false };
20
- }
21
- const env = writeAppCredentialsToEnv(projectDir, keypair.privateKey);
22
- return {
23
- address: cached.address,
24
- label: cached.label,
25
- wroteEnv: true,
26
- envPath: env.envPath,
27
- gitignorePath: env.gitignorePath,
28
- gitignoreAppended: env.gitignoreAppended,
29
- };
30
- }
@@ -1,12 +0,0 @@
1
- import { type RegisteredTool } from '../server.ts';
2
- /**
3
- * Decide which app credential `run_proof` should sign with, BEFORE
4
- * calling it. `run_proof` requires an `ownerAddress` and does no
5
- * resolution of its own — this is the single place that "checks memory,
6
- * env, etc and offers options".
7
- *
8
- * Checks, in order: `RECLAIM_PRIVATE_KEY` in the MCP env → the project
9
- * `.env` → the best cached credential for this directory. Never returns
10
- * the private key — only the address to pass through as `ownerAddress`.
11
- */
12
- export declare function resolveOwnerKeyTool(): RegisteredTool;
@@ -1,68 +0,0 @@
1
- import { deriveFromPrivateKey } from '@reclaimprotocol/client';
2
- import { findProjectRoot } from "../../paths.js";
3
- import { defineTool } from "../server.js";
4
- import { suggestCredentialForDir } from "./cache.js";
5
- import { readPrivateKeyFromEnvFile } from "./env-file.js";
6
- /**
7
- * Decide which app credential `run_proof` should sign with, BEFORE
8
- * calling it. `run_proof` requires an `ownerAddress` and does no
9
- * resolution of its own — this is the single place that "checks memory,
10
- * env, etc and offers options".
11
- *
12
- * Checks, in order: `RECLAIM_PRIVATE_KEY` in the MCP env → the project
13
- * `.env` → the best cached credential for this directory. Never returns
14
- * the private key — only the address to pass through as `ownerAddress`.
15
- */
16
- export function resolveOwnerKeyTool() {
17
- return defineTool({
18
- name: 'resolve_owner_key',
19
- description: 'Resolve which app credential run_proof should sign with — ' +
20
- 'call this BEFORE run_proof. Checks, in order: ' +
21
- 'RECLAIM_PRIVATE_KEY in the MCP env, RECLAIM_PRIVATE_KEY in the ' +
22
- 'project .env, then the best cached credential for this ' +
23
- 'directory. Returns { ready: true, address, source } when one ' +
24
- 'is available — pass `address` to run_proof as `ownerAddress`. ' +
25
- 'Returns { ready: false } when none exists: then ask the dev to ' +
26
- 'provide a private key (import_credentials) or create one ' +
27
- '(issue_credentials) — optionally with writeEnv:false to keep ' +
28
- 'it in the local cache (memory) only — and call resolve_owner_key ' +
29
- 'again. Never exposes the private key.',
30
- inputSchema: { type: 'object', properties: {} },
31
- }, async () => {
32
- const dir = findProjectRoot(process.cwd());
33
- const envKey = process.env.RECLAIM_PRIVATE_KEY;
34
- if (envKey) {
35
- return {
36
- ready: true,
37
- address: deriveFromPrivateKey(envKey).address,
38
- source: 'env',
39
- };
40
- }
41
- const dotenvKey = readPrivateKeyFromEnvFile(dir);
42
- if (dotenvKey) {
43
- return {
44
- ready: true,
45
- address: deriveFromPrivateKey(dotenvKey).address,
46
- source: 'dotenv',
47
- };
48
- }
49
- const suggestion = suggestCredentialForDir(dir);
50
- if (suggestion) {
51
- return {
52
- ready: true,
53
- address: suggestion.credential.address,
54
- source: 'cache',
55
- reason: suggestion.reason,
56
- };
57
- }
58
- return {
59
- ready: false,
60
- message: 'No app credential available (no RECLAIM_PRIVATE_KEY in env ' +
61
- 'or .env, and the cache is empty). Ask the dev to provide ' +
62
- 'their private key via import_credentials, or create one via ' +
63
- 'issue_credentials (pass writeEnv:false to keep it out of ' +
64
- '.env). Then call resolve_owner_key again and pass its ' +
65
- 'address to run_proof as ownerAddress.',
66
- };
67
- });
68
- }
@@ -1,14 +0,0 @@
1
- export type CachedSession = {
2
- token: string;
3
- /** ISO timestamp when the token expires (2 days from issue). */
4
- expiresAt: string;
5
- /** Email of the user who authorized (if available). */
6
- email?: string;
7
- };
8
- /** Persist a session token to ~/.reclaim/session.json (0600). */
9
- export declare function writeSession(session: CachedSession): void;
10
- /**
11
- * Read the cached session, returning it only if the token is still
12
- * valid (not yet expired). Returns undefined on any error.
13
- */
14
- export declare function readSession(): CachedSession | undefined;
@@ -1,42 +0,0 @@
1
- import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { reclaimHomeDirPath } from "../../paths.js";
4
- // Separate from credentials.json — sessions expire, credentials don't.
5
- function sessionCachePath() {
6
- return join(reclaimHomeDirPath(), 'session.json');
7
- }
8
- /** Persist a session token to ~/.reclaim/session.json (0600). */
9
- export function writeSession(session) {
10
- const dir = reclaimHomeDirPath();
11
- if (!existsSync(dir)) {
12
- mkdirSync(dir, { recursive: true, mode: 0o700 });
13
- }
14
- chmodSync(dir, 0o700);
15
- const path = sessionCachePath();
16
- writeFileSync(path, JSON.stringify(session, null, 2) + '\n', { mode: 0o600 });
17
- chmodSync(path, 0o600);
18
- }
19
- /**
20
- * Read the cached session, returning it only if the token is still
21
- * valid (not yet expired). Returns undefined on any error.
22
- */
23
- export function readSession() {
24
- const path = sessionCachePath();
25
- if (!existsSync(path)) {
26
- return undefined;
27
- }
28
- try {
29
- const raw = readFileSync(path, 'utf8');
30
- const s = JSON.parse(raw);
31
- if (!s.token || !s.expiresAt) {
32
- return undefined;
33
- }
34
- if (new Date(s.expiresAt) <= new Date()) {
35
- return undefined; // expired
36
- }
37
- return s;
38
- }
39
- catch {
40
- return undefined;
41
- }
42
- }
@@ -1,3 +0,0 @@
1
- import { type ReclaimClient } from '@reclaimprotocol/client';
2
- import { type RegisteredTool } from '../server.ts';
3
- export declare function statusTool(client: ReclaimClient): RegisteredTool;