@reclaimprotocol/agent 0.1.18 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/lib/authoring/backend.d.ts +4 -0
- package/lib/authoring/tools.js +4 -1
- package/lib/cdp/capture.d.ts +2 -2
- package/lib/cdp/capture.js +25 -21
- package/lib/cdp/client.d.ts +39 -1
- package/lib/cdp/client.js +125 -1
- package/lib/mcp/api/operations.gen.js +33 -9
- package/lib/mcp/instructions.js +27 -3
- package/lib/mcp/remap_errors.js +2 -1
- package/lib/mcp/start.js +16 -9
- package/lib/mcp/tools/agent/attach.d.ts +18 -4
- package/lib/mcp/tools/agent/attach.js +142 -52
- package/lib/mcp/tools/agent/build.d.ts +9 -2
- package/lib/mcp/tools/agent/build.js +15 -7
- package/lib/mcp/tools/agent/capture.d.ts +1 -0
- package/lib/mcp/tools/agent/capture.js +8 -1
- package/lib/mcp/tools/agent/eval.js +2 -3
- package/lib/mcp/tools/agent/how-it-works.d.ts +1 -1
- package/lib/mcp/tools/agent/how-it-works.js +1 -1
- package/lib/mcp/tools/agent/index.d.ts +1 -1
- package/lib/mcp/tools/agent/index.js +1 -1
- package/lib/mcp/tools/agent/publish.d.ts +4 -1
- package/lib/mcp/tools/agent/publish.js +42 -3
- package/lib/mcp/tools/agent/session.d.ts +2 -2
- package/lib/mcp/tools/agent/session.js +21 -5
- package/lib/mcp/tools/agent/shared.js +4 -6
- package/lib/mcp/tools/authenticate/authenticate.d.ts +1 -1
- package/lib/mcp/tools/authenticate/authenticate.js +13 -6
- package/lib/mcp/tools/authenticate/builder-access.d.ts +8 -0
- package/lib/mcp/tools/authenticate/builder-access.js +31 -0
- package/lib/mcp/tools/authenticate/index.d.ts +1 -0
- package/lib/mcp/tools/authenticate/index.js +1 -0
- package/lib/old/login-cdp.js +11 -3
- package/lib/old/tools/build.d.ts +8 -1
- package/lib/old/tools/build.js +6 -2
- package/lib/old/tools/publish.d.ts +10 -1
- package/lib/old/tools/publish.js +49 -3
- package/lib/provider/draft.d.ts +20 -0
- package/lib/provider/draft.js +75 -51
- package/lib/provider/index.d.ts +1 -1
- package/lib/provider/index.js +1 -1
- package/lib/provider/secrets.d.ts +37 -0
- package/lib/provider/secrets.js +147 -6
- package/lib/provider/to-version.d.ts +7 -0
- package/lib/provider/to-version.js +11 -1
- package/package.json +2 -2
- package/skill/SKILL.builder.md +44 -94
- package/skill/SKILL.common.md +102 -0
- package/skill/SKILL.old.md +21 -73
- package/skill/builder-mode.md +2 -2
- package/skill/injection-api.md +95 -10
- package/skill/old-mode.md +2 -2
- package/src/authoring/backend.ts +4 -0
- package/src/authoring/tools.ts +4 -1
- package/src/cdp/capture.ts +24 -24
- package/src/cdp/client.ts +197 -2
- package/src/mcp/api/operations.gen.ts +33 -9
- package/src/mcp/instructions.ts +36 -3
- package/src/mcp/remap_errors.ts +2 -1
- package/src/mcp/start.ts +24 -9
- package/src/mcp/tools/agent/attach.ts +198 -57
- package/src/mcp/tools/agent/build.ts +20 -6
- package/src/mcp/tools/agent/capture.ts +10 -1
- package/src/mcp/tools/agent/eval.ts +2 -3
- package/src/mcp/tools/agent/how-it-works.ts +1 -1
- package/src/mcp/tools/agent/index.ts +1 -1
- package/src/mcp/tools/agent/publish.ts +46 -2
- package/src/mcp/tools/agent/session.ts +29 -8
- package/src/mcp/tools/agent/shared.ts +5 -10
- package/src/mcp/tools/authenticate/authenticate.ts +17 -9
- package/src/mcp/tools/authenticate/builder-access.ts +42 -0
- package/src/mcp/tools/authenticate/index.ts +4 -0
- package/src/old/login-cdp.ts +13 -3
- package/src/old/tools/build.ts +7 -1
- package/src/old/tools/publish.ts +55 -4
- package/src/provider/draft.ts +98 -56
- package/src/provider/index.ts +9 -1
- package/src/provider/secrets.ts +171 -6
- package/src/provider/to-version.ts +12 -1
- package/lib/authoring/find.d.ts +0 -3
- package/lib/authoring/find.js +0 -54
- package/lib/authoring/propose.d.ts +0 -3
- package/lib/authoring/propose.js +0 -109
- package/lib/authoring/replay.d.ts +0 -10
- package/lib/authoring/replay.js +0 -34
- package/lib/cli/args.d.ts +0 -14
- package/lib/cli/args.js +0 -19
- package/lib/cli/commands.d.ts +0 -1
- package/lib/cli/commands.js +0 -19
- package/lib/cli.d.ts +0 -1
- package/lib/cli.js +0 -7
- package/lib/mcp/credentials/authenticate.d.ts +0 -3
- package/lib/mcp/credentials/authenticate.js +0 -108
- package/lib/mcp/credentials/build.d.ts +0 -5
- package/lib/mcp/credentials/build.js +0 -18
- package/lib/mcp/credentials/cache.d.ts +0 -70
- package/lib/mcp/credentials/cache.js +0 -131
- package/lib/mcp/credentials/env-file.d.ts +0 -25
- package/lib/mcp/credentials/env-file.js +0 -71
- package/lib/mcp/credentials/import.d.ts +0 -2
- package/lib/mcp/credentials/import.js +0 -52
- package/lib/mcp/credentials/issue.d.ts +0 -2
- package/lib/mcp/credentials/issue.js +0 -84
- package/lib/mcp/credentials/keypair.d.ts +0 -17
- package/lib/mcp/credentials/keypair.js +0 -48
- package/lib/mcp/credentials/link.d.ts +0 -3
- package/lib/mcp/credentials/link.js +0 -107
- package/lib/mcp/credentials/list-cached.d.ts +0 -2
- package/lib/mcp/credentials/list-cached.js +0 -55
- package/lib/mcp/credentials/owner-key.d.ts +0 -23
- package/lib/mcp/credentials/owner-key.js +0 -58
- package/lib/mcp/credentials/paths.d.ts +0 -12
- package/lib/mcp/credentials/paths.js +0 -35
- package/lib/mcp/credentials/poll.d.ts +0 -27
- package/lib/mcp/credentials/poll.js +0 -61
- package/lib/mcp/credentials/register.d.ts +0 -18
- package/lib/mcp/credentials/register.js +0 -30
- package/lib/mcp/credentials/resolve-owner-key.d.ts +0 -12
- package/lib/mcp/credentials/resolve-owner-key.js +0 -68
- package/lib/mcp/credentials/session-cache.d.ts +0 -14
- package/lib/mcp/credentials/session-cache.js +0 -42
- package/lib/mcp/credentials/status.d.ts +0 -3
- package/lib/mcp/credentials/status.js +0 -76
- package/lib/mcp/register.d.ts +0 -38
- package/lib/mcp/register.js +0 -80
- package/lib/mcp/tools/analyze.d.ts +0 -3
- package/lib/mcp/tools/analyze.js +0 -26
- package/lib/mcp/tools/attach.d.ts +0 -11
- package/lib/mcp/tools/attach.js +0 -72
- package/lib/mcp/tools/capture.d.ts +0 -10
- package/lib/mcp/tools/capture.js +0 -142
- package/lib/mcp/tools/eval.d.ts +0 -5
- package/lib/mcp/tools/eval.js +0 -127
- package/lib/mcp/tools/find.d.ts +0 -3
- package/lib/mcp/tools/find.js +0 -60
- package/lib/mcp/tools/owner-key.d.ts +0 -2
- package/lib/mcp/tools/owner-key.js +0 -63
- package/lib/mcp/tools/proof.d.ts +0 -3
- package/lib/mcp/tools/proof.js +0 -214
- package/lib/mcp/tools/propose.d.ts +0 -9
- package/lib/mcp/tools/propose.js +0 -75
- package/lib/mcp/tools/publish.d.ts +0 -3
- package/lib/mcp/tools/publish.js +0 -59
- package/lib/mcp/tools/replay.d.ts +0 -3
- package/lib/mcp/tools/replay.js +0 -46
- package/lib/mcp/tools/session.d.ts +0 -10
- package/lib/mcp/tools/session.js +0 -29
- package/lib/mcp/tools/verification/diagnose.d.ts +0 -11
- package/lib/mcp/tools/verification/diagnose.js +0 -132
- package/lib/mcp/verifications/build.d.ts +0 -5
- package/lib/mcp/verifications/build.js +0 -14
- package/lib/mcp/verifications/create.d.ts +0 -3
- package/lib/mcp/verifications/create.js +0 -57
- package/lib/mcp/verifications/find.d.ts +0 -3
- package/lib/mcp/verifications/find.js +0 -37
- package/lib/mcp/verifications/get.d.ts +0 -3
- package/lib/mcp/verifications/get.js +0 -26
- package/lib/mcp/verifications/update.d.ts +0 -3
- package/lib/mcp/verifications/update.js +0 -78
- package/lib/old/tools/get-version-config.d.ts +0 -17
- package/lib/old/tools/get-version-config.js +0 -47
- package/lib/routes.gen.d.ts +0 -271
- package/lib/routes.gen.js +0 -73
- package/lib/types/openapi.d.ts +0 -4
- package/lib/types/openapi.gen.d.ts +0 -3967
- package/lib/types/openapi.gen.js +0 -5
- package/lib/types/openapi.js +0 -1
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export type EnvWriteResult = {
|
|
2
|
-
envPath: string;
|
|
3
|
-
gitignorePath: string;
|
|
4
|
-
gitignoreAppended: boolean;
|
|
5
|
-
};
|
|
6
|
-
/**
|
|
7
|
-
* Idempotently set `RECLAIM_PRIVATE_KEY` in the project's `.env`, then
|
|
8
|
-
* `chmod 0600` it. The private key is the only secret we write — the
|
|
9
|
-
* ETH address (and public key) are derived from it on demand, so there
|
|
10
|
-
* is no separate address variable to keep in sync. Existing variables
|
|
11
|
-
* are replaced in place; other lines are preserved verbatim so we
|
|
12
|
-
* don't trample on unrelated config the user wrote by hand.
|
|
13
|
-
*
|
|
14
|
-
* Also appends `.env` to the project's `.gitignore` if it isn't
|
|
15
|
-
* already listed.
|
|
16
|
-
*/
|
|
17
|
-
export declare function writeAppCredentialsToEnv(projectDir: string, privateKey: string): EnvWriteResult;
|
|
18
|
-
/**
|
|
19
|
-
* Read `RECLAIM_PRIVATE_KEY` back out of the project's `.env`, if set.
|
|
20
|
-
* Read live (not from `process.env`) so a credential written by
|
|
21
|
-
* `issue_credentials` / `import_credentials` is picked up immediately,
|
|
22
|
-
* without a Claude Code restart. Returns undefined when the file or the
|
|
23
|
-
* variable is absent.
|
|
24
|
-
*/
|
|
25
|
-
export declare function readPrivateKeyFromEnvFile(projectDir: string): string | undefined;
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { appendFileSync, chmodSync, existsSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
const PRIVATE_KEY = 'RECLAIM_PRIVATE_KEY';
|
|
4
|
-
const GITIGNORE_LINE = '.env';
|
|
5
|
-
/**
|
|
6
|
-
* Idempotently set `RECLAIM_PRIVATE_KEY` in the project's `.env`, then
|
|
7
|
-
* `chmod 0600` it. The private key is the only secret we write — the
|
|
8
|
-
* ETH address (and public key) are derived from it on demand, so there
|
|
9
|
-
* is no separate address variable to keep in sync. Existing variables
|
|
10
|
-
* are replaced in place; other lines are preserved verbatim so we
|
|
11
|
-
* don't trample on unrelated config the user wrote by hand.
|
|
12
|
-
*
|
|
13
|
-
* Also appends `.env` to the project's `.gitignore` if it isn't
|
|
14
|
-
* already listed.
|
|
15
|
-
*/
|
|
16
|
-
export function writeAppCredentialsToEnv(projectDir, privateKey) {
|
|
17
|
-
const envPath = join(projectDir, '.env');
|
|
18
|
-
const lines = existsSync(envPath)
|
|
19
|
-
? readFileSync(envPath, 'utf8').split('\n')
|
|
20
|
-
: [];
|
|
21
|
-
setOrInsert(lines, PRIVATE_KEY, privateKey);
|
|
22
|
-
const body = lines.join('\n').replace(/\n+$/, '') + '\n';
|
|
23
|
-
writeFileSync(envPath, body, { mode: 0o600 });
|
|
24
|
-
chmodSync(envPath, 0o600);
|
|
25
|
-
const gitignorePath = join(projectDir, '.gitignore');
|
|
26
|
-
const gitignoreAppended = ensureLine(gitignorePath, GITIGNORE_LINE);
|
|
27
|
-
return { envPath, gitignorePath, gitignoreAppended };
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Read `RECLAIM_PRIVATE_KEY` back out of the project's `.env`, if set.
|
|
31
|
-
* Read live (not from `process.env`) so a credential written by
|
|
32
|
-
* `issue_credentials` / `import_credentials` is picked up immediately,
|
|
33
|
-
* without a Claude Code restart. Returns undefined when the file or the
|
|
34
|
-
* variable is absent.
|
|
35
|
-
*/
|
|
36
|
-
export function readPrivateKeyFromEnvFile(projectDir) {
|
|
37
|
-
const envPath = join(projectDir, '.env');
|
|
38
|
-
if (!existsSync(envPath)) {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
const prefix = `${PRIVATE_KEY}=`;
|
|
42
|
-
const line = readFileSync(envPath, 'utf8')
|
|
43
|
-
.split('\n')
|
|
44
|
-
.find((l) => l.startsWith(prefix));
|
|
45
|
-
const value = line?.slice(prefix.length).trim();
|
|
46
|
-
return value || undefined;
|
|
47
|
-
}
|
|
48
|
-
function setOrInsert(lines, key, value) {
|
|
49
|
-
const prefix = `${key}=`;
|
|
50
|
-
const idx = lines.findIndex((l) => l.startsWith(prefix));
|
|
51
|
-
const next = `${key}=${value}`;
|
|
52
|
-
if (idx >= 0) {
|
|
53
|
-
lines[idx] = next;
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
lines.push(next);
|
|
57
|
-
}
|
|
58
|
-
function ensureLine(path, line) {
|
|
59
|
-
if (!existsSync(path)) {
|
|
60
|
-
writeFileSync(path, line + '\n', { mode: 0o644 });
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
const body = readFileSync(path, 'utf8');
|
|
64
|
-
const present = body.split('\n').some((l) => l.trim() === line);
|
|
65
|
-
if (present) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
const suffix = body.endsWith('\n') ? '' : '\n';
|
|
69
|
-
appendFileSync(path, suffix + line + '\n');
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { deriveFromPrivateKey } from '@reclaimprotocol/client';
|
|
2
|
-
import { findProjectRoot } from "../../paths.js";
|
|
3
|
-
import { defineTool } from "../server.js";
|
|
4
|
-
import { persistCredentialLocally } from "./register.js";
|
|
5
|
-
export function importTool() {
|
|
6
|
-
return defineTool({
|
|
7
|
-
name: 'import_credentials',
|
|
8
|
-
description: 'Import an existing Reclaim app credential the user ' +
|
|
9
|
-
'already holds elsewhere. Provide the private key ' +
|
|
10
|
-
'(0x-prefixed hex, 64 chars); we derive the address ' +
|
|
11
|
-
'locally and cache it. By default also writes ' +
|
|
12
|
-
'RECLAIM_PRIVATE_KEY to the project .env (0600) and appends ' +
|
|
13
|
-
'.env to .gitignore; pass `writeEnv: false` to keep it in ' +
|
|
14
|
-
'the local cache (memory) only and leave .env untouched. ' +
|
|
15
|
-
'ALWAYS ask the dev whether to save to .env before writing. ' +
|
|
16
|
-
'The credential stays local-only (SANDBOX); link it to an ' +
|
|
17
|
-
'org to use it in production.',
|
|
18
|
-
inputSchema: {
|
|
19
|
-
type: 'object',
|
|
20
|
-
properties: {
|
|
21
|
-
privateKey: {
|
|
22
|
-
type: 'string',
|
|
23
|
-
pattern: '^0x[0-9a-fA-F]{64}$',
|
|
24
|
-
description: '0x-prefixed hex private key (64 hex chars / 32 bytes).',
|
|
25
|
-
},
|
|
26
|
-
label: {
|
|
27
|
-
type: 'string',
|
|
28
|
-
minLength: 1,
|
|
29
|
-
maxLength: 100,
|
|
30
|
-
},
|
|
31
|
-
projectDir: {
|
|
32
|
-
type: 'string',
|
|
33
|
-
description: 'Project directory to write .env into. Defaults ' +
|
|
34
|
-
'to the closest ancestor of cwd containing ' +
|
|
35
|
-
'.git or package.json.',
|
|
36
|
-
},
|
|
37
|
-
writeEnv: {
|
|
38
|
-
type: 'boolean',
|
|
39
|
-
description: 'Write RECLAIM_PRIVATE_KEY to the project .env ' +
|
|
40
|
-
'(default true). Set false to cache in memory only, ' +
|
|
41
|
-
'without touching .env.',
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
required: ['privateKey'],
|
|
45
|
-
},
|
|
46
|
-
}, async (args) => {
|
|
47
|
-
const keypair = deriveFromPrivateKey(args.privateKey);
|
|
48
|
-
const projectDir = findProjectRoot(args.projectDir || process.cwd());
|
|
49
|
-
const persisted = persistCredentialLocally(keypair, args.label, projectDir, args.writeEnv ?? true);
|
|
50
|
-
return { ...persisted };
|
|
51
|
-
});
|
|
52
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { generateKeypair } from '@reclaimprotocol/client';
|
|
2
|
-
import { findProjectRoot } from "../../paths.js";
|
|
3
|
-
import { defineTool } from "../server.js";
|
|
4
|
-
import { findCached } from "./cache.js";
|
|
5
|
-
import { persistCredentialLocally } from "./register.js";
|
|
6
|
-
export function issueTool() {
|
|
7
|
-
return defineTool({
|
|
8
|
-
name: 'issue_credentials',
|
|
9
|
-
description: 'Mint a fresh anonymous Reclaim app key-pair (or reuse ' +
|
|
10
|
-
'a cached one via `reuseCachedAppPk`) and cache it locally. ' +
|
|
11
|
-
'By default also writes RECLAIM_PRIVATE_KEY to the project ' +
|
|
12
|
-
'.env (mode 0600) and appends .env to .gitignore; pass ' +
|
|
13
|
-
'`writeEnv: false` to keep it in the local cache (memory) ' +
|
|
14
|
-
'only and leave .env untouched. ALWAYS ask the dev whether ' +
|
|
15
|
-
'to save to .env before writing it. The key-pair is ' +
|
|
16
|
-
'generated locally and is NOT registered with the server — ' +
|
|
17
|
-
'anonymous credentials operate in SANDBOX mode entirely ' +
|
|
18
|
-
'client-side. PREFER REUSE over generating a new credential ' +
|
|
19
|
-
'unless the user explicitly asks for a new app identity. To ' +
|
|
20
|
-
'use a credential for verification in production, link it to ' +
|
|
21
|
-
'an organization (via link_credential) and add billing.',
|
|
22
|
-
inputSchema: {
|
|
23
|
-
type: 'object',
|
|
24
|
-
properties: {
|
|
25
|
-
reuseCachedAppPk: {
|
|
26
|
-
type: 'string',
|
|
27
|
-
pattern: '^0x[0-9a-fA-F]{40}$',
|
|
28
|
-
description: 'Lowercase ETH address of a cached credential ' +
|
|
29
|
-
'to reuse instead of minting a new one.',
|
|
30
|
-
},
|
|
31
|
-
label: {
|
|
32
|
-
type: 'string',
|
|
33
|
-
minLength: 1,
|
|
34
|
-
maxLength: 100,
|
|
35
|
-
description: 'Human label stored in the local cache.',
|
|
36
|
-
},
|
|
37
|
-
projectDir: {
|
|
38
|
-
type: 'string',
|
|
39
|
-
description: 'Project directory to write .env into. Defaults ' +
|
|
40
|
-
'to the closest ancestor of cwd containing ' +
|
|
41
|
-
'.git or package.json.',
|
|
42
|
-
},
|
|
43
|
-
writeEnv: {
|
|
44
|
-
type: 'boolean',
|
|
45
|
-
description: 'Write RECLAIM_PRIVATE_KEY to the project .env ' +
|
|
46
|
-
'(default true). Set false to cache the credential ' +
|
|
47
|
-
'in memory only, without touching .env.',
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
}, async (args) => {
|
|
52
|
-
const projectDir = findProjectRoot(args.projectDir || process.cwd());
|
|
53
|
-
let keypair;
|
|
54
|
-
let reused = false;
|
|
55
|
-
if (args.reuseCachedAppPk) {
|
|
56
|
-
const cached = findCached(args.reuseCachedAppPk);
|
|
57
|
-
if (!cached) {
|
|
58
|
-
throw new Error(`No cached credential for ${args.reuseCachedAppPk}. ` +
|
|
59
|
-
'Call list_cached_credentials first.');
|
|
60
|
-
}
|
|
61
|
-
keypair = {
|
|
62
|
-
address: cached.address,
|
|
63
|
-
privateKey: cached.privateKey,
|
|
64
|
-
};
|
|
65
|
-
reused = true;
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
keypair = generateKeypair();
|
|
69
|
-
}
|
|
70
|
-
const writeEnv = args.writeEnv ?? true;
|
|
71
|
-
const persisted = persistCredentialLocally(keypair, args.label, projectDir, writeEnv);
|
|
72
|
-
return {
|
|
73
|
-
reused,
|
|
74
|
-
...persisted,
|
|
75
|
-
note: writeEnv
|
|
76
|
-
? 'Private key written to .env (and the local cache); it is ' +
|
|
77
|
-
'NOT in this response on purpose. The credential is ' +
|
|
78
|
-
'local-only (SANDBOX); link it to an org for production.'
|
|
79
|
-
: 'Private key cached in memory only (.env untouched). Sign ' +
|
|
80
|
-
'proofs with it by passing run_proof({ ownerAddress }). The ' +
|
|
81
|
-
'credential is local-only (SANDBOX).',
|
|
82
|
-
};
|
|
83
|
-
});
|
|
84
|
-
}
|
|
@@ -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,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,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 {};
|