@reclaimprotocol/agent 0.1.1-dev.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/cdp/capture-engine.d.ts +66 -0
- package/lib/cdp/capture-engine.js +153 -0
- package/lib/cdp/capture.d.ts +9 -20
- package/lib/cdp/capture.js +14 -138
- package/lib/cdp/index.d.ts +3 -0
- package/lib/cdp/index.js +6 -0
- package/lib/cdp/launcher.js +4 -3
- package/lib/config/store.d.ts +7 -6
- package/lib/config/store.js +2 -2
- package/lib/consts.d.ts +68 -0
- package/lib/consts.js +134 -0
- package/lib/index.d.ts +2 -2
- package/lib/index.js +1 -1
- package/lib/logger.js +2 -1
- package/lib/mcp/api/operations.gen.d.ts +100 -48
- package/lib/mcp/api/operations.gen.js +1129 -194
- package/lib/mcp/api/to-zod.d.ts +24 -0
- package/lib/mcp/api/to-zod.js +94 -0
- package/lib/mcp/api/tools.js +1 -1
- package/lib/mcp/credentials/authenticate.d.ts +3 -0
- package/lib/mcp/{tools/credentials → credentials}/authenticate.js +3 -3
- package/lib/mcp/credentials/build.d.ts +5 -0
- package/lib/mcp/credentials/build.js +18 -0
- package/lib/mcp/{tools/credentials → credentials}/cache.d.ts +9 -9
- package/lib/mcp/{tools/credentials → credentials}/cache.js +36 -19
- package/lib/mcp/credentials/import.d.ts +2 -0
- package/lib/mcp/{tools/credentials → credentials}/import.js +2 -2
- package/lib/mcp/credentials/issue.d.ts +2 -0
- package/lib/mcp/{tools/credentials → credentials}/issue.js +4 -4
- package/lib/mcp/credentials/keypair.d.ts +17 -0
- package/lib/mcp/credentials/keypair.js +48 -0
- package/lib/mcp/credentials/link.d.ts +3 -0
- package/lib/mcp/credentials/link.js +107 -0
- package/lib/mcp/{tools/credentials → credentials}/list-cached.d.ts +1 -1
- package/lib/mcp/{tools/credentials → credentials}/list-cached.js +5 -5
- package/lib/mcp/credentials/owner-key.d.ts +23 -0
- package/lib/mcp/credentials/owner-key.js +58 -0
- package/lib/mcp/credentials/paths.d.ts +12 -0
- package/lib/mcp/credentials/paths.js +35 -0
- package/lib/mcp/credentials/poll.d.ts +27 -0
- package/lib/mcp/credentials/poll.js +61 -0
- package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.d.ts +1 -1
- package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.js +2 -2
- package/lib/mcp/credentials/session-cache.d.ts +14 -0
- package/lib/mcp/credentials/session-cache.js +42 -0
- package/lib/mcp/{tools/credentials → credentials}/status.d.ts +1 -1
- package/lib/mcp/credentials/status.js +76 -0
- package/lib/mcp/instructions.d.ts +1 -0
- package/lib/mcp/instructions.js +11 -0
- package/lib/mcp/register.d.ts +38 -0
- package/lib/mcp/register.js +80 -0
- package/lib/mcp/server.d.ts +2 -8
- package/lib/mcp/server.js +3 -33
- package/lib/mcp/start.js +17 -11
- package/lib/mcp/tools/agent/build.d.ts +5 -9
- package/lib/mcp/tools/agent/build.js +7 -24
- package/lib/mcp/tools/agent/index.d.ts +2 -0
- package/lib/mcp/tools/agent/index.js +3 -0
- package/lib/mcp/tools/agent/proof.js +3 -7
- package/lib/mcp/tools/agent/propose.js +13 -2
- package/lib/mcp/tools/agent/publish.js +172 -75
- package/lib/mcp/tools/agent/replay.js +0 -5
- package/lib/mcp/tools/analyze.d.ts +3 -0
- package/lib/mcp/tools/analyze.js +26 -0
- package/lib/mcp/tools/attach.d.ts +11 -0
- package/lib/mcp/tools/attach.js +72 -0
- package/lib/mcp/tools/authenticate/authenticate.js +107 -0
- package/lib/mcp/tools/authenticate/build.d.ts +15 -0
- package/lib/mcp/tools/authenticate/build.js +28 -0
- package/lib/mcp/tools/authenticate/decrypt-key.d.ts +19 -0
- package/lib/mcp/tools/authenticate/decrypt-key.js +46 -0
- package/lib/mcp/tools/authenticate/eth-key.d.ts +31 -0
- package/lib/mcp/tools/authenticate/eth-key.js +116 -0
- package/lib/mcp/tools/authenticate/import.js +57 -0
- package/lib/mcp/tools/authenticate/index.d.ts +2 -0
- package/lib/mcp/tools/authenticate/index.js +4 -0
- package/lib/mcp/tools/authenticate/issue.js +42 -0
- package/lib/mcp/tools/authenticate/resolve-owner-key.d.ts +13 -0
- package/lib/mcp/tools/authenticate/resolve-owner-key.js +53 -0
- package/lib/mcp/tools/capture.d.ts +10 -0
- package/lib/mcp/tools/capture.js +142 -0
- package/lib/mcp/tools/eval.d.ts +5 -0
- package/lib/mcp/tools/eval.js +127 -0
- package/lib/mcp/tools/find.d.ts +3 -0
- package/lib/mcp/tools/find.js +60 -0
- package/lib/mcp/tools/index.d.ts +3 -0
- package/lib/mcp/tools/index.js +6 -0
- package/lib/mcp/tools/owner-key.d.ts +2 -0
- package/lib/mcp/tools/owner-key.js +63 -0
- package/lib/mcp/tools/proof.d.ts +3 -0
- package/lib/mcp/tools/proof.js +214 -0
- package/lib/mcp/tools/propose.d.ts +9 -0
- package/lib/mcp/tools/propose.js +75 -0
- package/lib/mcp/tools/publish.d.ts +3 -0
- package/lib/mcp/tools/publish.js +59 -0
- package/lib/mcp/tools/replay.d.ts +3 -0
- package/lib/mcp/tools/replay.js +46 -0
- package/lib/mcp/tools/session.d.ts +10 -0
- package/lib/mcp/tools/session.js +29 -0
- package/lib/mcp/tools/verification/build.d.ts +12 -0
- package/lib/mcp/tools/verification/build.js +14 -0
- package/lib/mcp/tools/verification/diagnose.d.ts +11 -0
- package/lib/mcp/tools/verification/diagnose.js +132 -0
- package/lib/mcp/tools/verification/index.d.ts +1 -0
- package/lib/mcp/tools/verification/index.js +2 -0
- package/lib/mcp/tools/verification/result.d.ts +11 -0
- package/lib/mcp/tools/verification/result.js +126 -0
- package/lib/mcp/verifications/build.d.ts +5 -0
- package/lib/mcp/verifications/build.js +14 -0
- package/lib/mcp/verifications/create.d.ts +3 -0
- package/lib/mcp/verifications/create.js +57 -0
- package/lib/mcp/verifications/find.d.ts +3 -0
- package/lib/mcp/verifications/find.js +37 -0
- package/lib/mcp/verifications/get.d.ts +3 -0
- package/lib/mcp/verifications/get.js +26 -0
- package/lib/mcp/verifications/update.d.ts +3 -0
- package/lib/mcp/verifications/update.js +78 -0
- package/lib/old/client.d.ts +3 -0
- package/lib/old/flag.d.ts +4 -1
- package/lib/old/flag.js +4 -3
- package/lib/old/identity.d.ts +0 -10
- package/lib/old/identity.js +8 -7
- package/lib/old/instructions.d.ts +0 -6
- package/lib/old/instructions.js +6 -24
- package/lib/old/login-cdp.d.ts +0 -3
- package/lib/old/login-cdp.js +2 -4
- package/lib/old/to-register.d.ts +3 -0
- package/lib/old/to-register.js +32 -7
- package/lib/old/tools/authenticate.js +2 -1
- package/lib/old/tools/publish.js +71 -10
- package/lib/paths.js +2 -2
- package/lib/proof/attestor.d.ts +33 -1
- package/lib/proof/attestor.js +67 -64
- package/lib/proof/index.d.ts +5 -0
- package/lib/proof/index.js +6 -0
- package/lib/proof/proof-core.d.ts +35 -0
- package/lib/proof/proof-core.js +84 -0
- package/lib/provider/index.d.ts +5 -0
- package/lib/provider/index.js +9 -0
- package/lib/provider/injection-doc.d.ts +1 -0
- package/lib/provider/injection-doc.js +17 -0
- package/lib/provider/match-redaction-warning.d.ts +25 -0
- package/lib/provider/match-redaction-warning.js +35 -0
- package/lib/provider/schema.d.ts +16 -0
- package/lib/provider/schema.js +11 -1
- package/lib/provider/to-version.d.ts +61 -12
- package/lib/provider/to-version.js +109 -24
- package/lib/provider/verification-checks.d.ts +12 -0
- package/lib/provider/verification-checks.js +30 -0
- package/lib/routes.gen.d.ts +271 -0
- package/lib/routes.gen.js +73 -0
- package/lib/types/openapi.gen.d.ts +3967 -0
- package/lib/types/openapi.gen.js +5 -0
- package/package.json +16 -7
- package/skill/SKILL.md +35 -14
- package/skill/builder-mode.md +13 -0
- package/skill/injection-api.md +42 -0
- package/skill/old-mode.md +15 -0
- package/src/cdp/capture-engine.ts +256 -0
- package/src/cdp/capture.ts +25 -185
- package/src/cdp/index.ts +6 -0
- package/src/cdp/launcher.ts +5 -4
- package/src/config/store.ts +7 -6
- package/src/consts.ts +164 -0
- package/src/index.ts +2 -4
- package/src/logger.ts +2 -1
- package/src/mcp/api/operations.gen.ts +1155 -194
- package/src/mcp/api/to-zod.ts +115 -0
- package/src/mcp/api/tools.ts +9 -3
- package/src/mcp/instructions.ts +15 -0
- package/src/mcp/server.ts +5 -50
- package/src/mcp/start.ts +27 -11
- package/src/mcp/tools/agent/build.ts +7 -27
- package/src/mcp/tools/agent/index.ts +4 -0
- package/src/mcp/tools/agent/proof.ts +3 -8
- package/src/mcp/tools/agent/propose.ts +13 -2
- package/src/mcp/tools/agent/publish.ts +218 -92
- package/src/mcp/tools/agent/replay.ts +0 -5
- package/src/mcp/tools/{credentials → authenticate}/authenticate.ts +9 -8
- package/src/mcp/tools/authenticate/build.ts +32 -0
- package/src/mcp/tools/authenticate/decrypt-key.ts +59 -0
- package/src/mcp/tools/authenticate/eth-key.ts +162 -0
- package/src/mcp/tools/authenticate/import.ts +73 -0
- package/src/mcp/tools/authenticate/index.ts +4 -0
- package/src/mcp/tools/authenticate/issue.ts +50 -0
- package/src/mcp/tools/authenticate/resolve-owner-key.ts +60 -0
- package/src/mcp/tools/index.ts +6 -0
- package/src/mcp/tools/verification/build.ts +16 -0
- package/src/mcp/tools/verification/index.ts +2 -0
- package/src/mcp/tools/verification/result.ts +154 -0
- package/src/old/client.ts +3 -0
- package/src/old/flag.ts +4 -3
- package/src/old/identity.ts +8 -7
- package/src/old/instructions.ts +10 -25
- package/src/old/login-cdp.ts +2 -5
- package/src/old/to-register.ts +58 -26
- package/src/old/tools/authenticate.ts +2 -1
- package/src/old/tools/publish.ts +92 -16
- package/src/paths.ts +2 -2
- package/src/proof/attestor.ts +114 -72
- package/src/proof/index.ts +21 -0
- package/src/proof/proof-core.ts +119 -0
- package/src/provider/index.ts +20 -0
- package/src/provider/injection-doc.ts +21 -0
- package/src/provider/match-redaction-warning.ts +53 -0
- package/src/provider/schema.ts +23 -0
- package/src/provider/to-version.ts +180 -33
- package/src/provider/verification-checks.ts +36 -0
- package/lib/mcp/tools/credentials/build.d.ts +0 -16
- package/lib/mcp/tools/credentials/build.js +0 -33
- package/lib/mcp/tools/credentials/status.js +0 -62
- package/src/mcp/tools/credentials/build.ts +0 -37
- package/src/mcp/tools/credentials/cache.ts +0 -161
- package/src/mcp/tools/credentials/env-file.ts +0 -101
- package/src/mcp/tools/credentials/import.ts +0 -72
- package/src/mcp/tools/credentials/issue.ts +0 -107
- package/src/mcp/tools/credentials/list-cached.ts +0 -66
- package/src/mcp/tools/credentials/register.ts +0 -49
- package/src/mcp/tools/credentials/resolve-owner-key.ts +0 -78
- package/src/mcp/tools/credentials/status.ts +0 -82
- package/src/types/openapi.ts +0 -7
- /package/lib/mcp/{tools/credentials → credentials}/env-file.d.ts +0 -0
- /package/lib/mcp/{tools/credentials → credentials}/env-file.js +0 -0
- /package/lib/mcp/{tools/credentials → credentials}/register.d.ts +0 -0
- /package/lib/mcp/{tools/credentials → credentials}/register.js +0 -0
- /package/lib/mcp/tools/{credentials → authenticate}/authenticate.d.ts +0 -0
- /package/lib/mcp/tools/{credentials → authenticate}/import.d.ts +0 -0
- /package/lib/mcp/tools/{credentials → authenticate}/issue.d.ts +0 -0
- /package/lib/mcp/tools/{credentials → authenticate}/session-cache.d.ts +0 -0
- /package/lib/mcp/tools/{credentials → authenticate}/session-cache.js +0 -0
- /package/src/mcp/tools/{credentials → authenticate}/session-cache.ts +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { JSONSchema } from 'zod/v4/core';
|
|
3
|
+
/**
|
|
4
|
+
* JSON Schema → zod for the generated MCP tool inputs.
|
|
5
|
+
*
|
|
6
|
+
* The generator (`packages/app/scripts/mcp-tools-gen.ts`) inlines `$ref`s it
|
|
7
|
+
* walks through `properties`/`items`, but it does NOT recurse into `allOf` /
|
|
8
|
+
* `anyOf` / `oneOf`. So a schema composed with `allOf` (e.g.
|
|
9
|
+
* `RequestSelectionTemplate =
|
|
10
|
+
* allOf[ RequestSelection, { multiple, required } ]`,
|
|
11
|
+
* surfaced by the proof tools' `allowedJsRequests`) can carry a dangling
|
|
12
|
+
* `#/components/schemas/…` `$ref` that points outside the standalone schema
|
|
13
|
+
* object handed to `z.fromJSONSchema`. zod then throws `Reference not found`
|
|
14
|
+
* at module-load time, taking down every tool.
|
|
15
|
+
*
|
|
16
|
+
* We pre-process here (rather than hand-editing the generated file, which
|
|
17
|
+
* regen would clobber): flatten `allOf` compositions into a single object
|
|
18
|
+
* schema and neutralize any remaining unresolvable `#/components/schemas/…`
|
|
19
|
+
* `$ref` to a permissive `{}`. The schemas affected are deep proof-tool inputs
|
|
20
|
+
* the model rarely hand-builds, so loosening leaf validation there is
|
|
21
|
+
* acceptable; the common tool inputs (uuids, version patterns, contexts) keep
|
|
22
|
+
* their full constraints.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toZod(schema: JSONSchema.Schema): z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* JSON Schema → zod for the generated MCP tool inputs.
|
|
4
|
+
*
|
|
5
|
+
* The generator (`packages/app/scripts/mcp-tools-gen.ts`) inlines `$ref`s it
|
|
6
|
+
* walks through `properties`/`items`, but it does NOT recurse into `allOf` /
|
|
7
|
+
* `anyOf` / `oneOf`. So a schema composed with `allOf` (e.g.
|
|
8
|
+
* `RequestSelectionTemplate =
|
|
9
|
+
* allOf[ RequestSelection, { multiple, required } ]`,
|
|
10
|
+
* surfaced by the proof tools' `allowedJsRequests`) can carry a dangling
|
|
11
|
+
* `#/components/schemas/…` `$ref` that points outside the standalone schema
|
|
12
|
+
* object handed to `z.fromJSONSchema`. zod then throws `Reference not found`
|
|
13
|
+
* at module-load time, taking down every tool.
|
|
14
|
+
*
|
|
15
|
+
* We pre-process here (rather than hand-editing the generated file, which
|
|
16
|
+
* regen would clobber): flatten `allOf` compositions into a single object
|
|
17
|
+
* schema and neutralize any remaining unresolvable `#/components/schemas/…`
|
|
18
|
+
* `$ref` to a permissive `{}`. The schemas affected are deep proof-tool inputs
|
|
19
|
+
* the model rarely hand-builds, so loosening leaf validation there is
|
|
20
|
+
* acceptable; the common tool inputs (uuids, version patterns, contexts) keep
|
|
21
|
+
* their full constraints.
|
|
22
|
+
*/
|
|
23
|
+
export function toZod(schema) {
|
|
24
|
+
return z.fromJSONSchema(sanitize(schema));
|
|
25
|
+
}
|
|
26
|
+
/** True for a `$ref` that can't be resolved in a standalone schema object. */
|
|
27
|
+
function isDanglingComponentRef(node) {
|
|
28
|
+
return (typeof node.$ref === 'string' &&
|
|
29
|
+
(node.$ref).startsWith('#/components/'));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Recursively (a) merge `allOf` members into their parent so composition
|
|
33
|
+
* resolves locally, and (b) replace any dangling `#/components/…` `$ref` with
|
|
34
|
+
* a permissive empty schema. Pure — returns a fresh tree, never mutates input.
|
|
35
|
+
*/
|
|
36
|
+
function sanitize(input) {
|
|
37
|
+
if (Array.isArray(input)) {
|
|
38
|
+
return input.map(sanitize);
|
|
39
|
+
}
|
|
40
|
+
if (input === null || typeof input !== 'object') {
|
|
41
|
+
return input;
|
|
42
|
+
}
|
|
43
|
+
const node = input;
|
|
44
|
+
if (isDanglingComponentRef(node)) {
|
|
45
|
+
// Drop the ref; keep any sibling keys (rare) sanitized.
|
|
46
|
+
const { $ref: _ref, ...rest } = node;
|
|
47
|
+
void _ref;
|
|
48
|
+
return sanitizeEntries(rest);
|
|
49
|
+
}
|
|
50
|
+
// Flatten allOf into the parent object. Each member is sanitized first so
|
|
51
|
+
// its own refs/allOf collapse; object members merge their `properties`,
|
|
52
|
+
// `required`, and scalar keys upward.
|
|
53
|
+
if (Array.isArray(node.allOf)) {
|
|
54
|
+
const { allOf, ...rest } = node;
|
|
55
|
+
let merged = sanitizeEntries(rest);
|
|
56
|
+
for (const member of allOf) {
|
|
57
|
+
merged = mergeObjectSchemas(merged, sanitize(member));
|
|
58
|
+
}
|
|
59
|
+
return merged;
|
|
60
|
+
}
|
|
61
|
+
return sanitizeEntries(node);
|
|
62
|
+
}
|
|
63
|
+
/** Sanitize every value of an object schema's keys. */
|
|
64
|
+
function sanitizeEntries(node) {
|
|
65
|
+
const out = {};
|
|
66
|
+
for (const [k, v] of Object.entries(node)) {
|
|
67
|
+
out[k] = sanitize(v);
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Merge two (already-sanitized) object schemas for `allOf` flattening: union
|
|
73
|
+
* `properties`, union `required`, and let later members win on scalar keys.
|
|
74
|
+
* Both sides are expected to be object-shaped; non-object members fall back to
|
|
75
|
+
* a plain key spread (the rare case).
|
|
76
|
+
*/
|
|
77
|
+
function mergeObjectSchemas(a, b) {
|
|
78
|
+
const out = { ...a, ...b };
|
|
79
|
+
const aProps = a.properties;
|
|
80
|
+
const bProps = b.properties;
|
|
81
|
+
if (aProps || bProps) {
|
|
82
|
+
out.properties = { ...aProps, ...bProps };
|
|
83
|
+
}
|
|
84
|
+
const aReq = Array.isArray(a.required) ? a.required : [];
|
|
85
|
+
const bReq = Array.isArray(b.required) ? b.required : [];
|
|
86
|
+
if (aReq.length || bReq.length) {
|
|
87
|
+
out.required = Array.from(new Set([...aReq, ...bReq]));
|
|
88
|
+
}
|
|
89
|
+
// `type` should stay `object` after merging two object schemas.
|
|
90
|
+
if (a.type === 'object' || b.type === 'object' || out.properties) {
|
|
91
|
+
out.type = 'object';
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
package/lib/mcp/api/tools.js
CHANGED
|
@@ -21,7 +21,7 @@ export function registerTools(server, client) {
|
|
|
21
21
|
// bug to surface, not silently shadow.
|
|
22
22
|
server.registerTool(toSnakeCase(opId), shape, async (args) => {
|
|
23
23
|
try {
|
|
24
|
-
const result =
|
|
24
|
+
const result = await client.call(opId, args);
|
|
25
25
|
return {
|
|
26
26
|
content: [{
|
|
27
27
|
type: 'text', text: JSON.stringify(result.data, null, 2)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineTool } from "
|
|
1
|
+
import { defineTool } from "../server.js";
|
|
2
2
|
import { writeSession } from "./session-cache.js";
|
|
3
3
|
export function authenticateTool(client) {
|
|
4
4
|
return defineTool({
|
|
@@ -11,7 +11,7 @@ export function authenticateTool(client) {
|
|
|
11
11
|
'with the same `code` from step 1 to check if confirmed. ' +
|
|
12
12
|
'Returns `LINKED` with a 2-day bearer token when confirmed, ' +
|
|
13
13
|
'`PENDING` if not yet, or `EXPIRED` if too long has passed.\n\n' +
|
|
14
|
-
'The token is cached in ~/.reclaim/
|
|
14
|
+
'The token is cached in ~/.reclaim/session.json automatically.\n\n' +
|
|
15
15
|
'To then link a credential to an org (needed only to use it ' +
|
|
16
16
|
'for verification in production), use the token with the ' +
|
|
17
17
|
'generated tools: `list_orgs` → `link_credential` with the ' +
|
|
@@ -78,7 +78,7 @@ async function checkSession(client, code) {
|
|
|
78
78
|
return {
|
|
79
79
|
status: 'LINKED',
|
|
80
80
|
token,
|
|
81
|
-
note: 'Token cached in ~/.reclaim/
|
|
81
|
+
note: 'Token cached in ~/.reclaim/session.json and active now.',
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
return {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ReclaimClient } from '@reclaimprotocol/client';
|
|
2
|
+
import type { RegisteredTool } from '../server.ts';
|
|
3
|
+
/** Credential lifecycle tools — build the flat list of RegisteredTool
|
|
4
|
+
* entries. Mirrors buildAgentTools() in mcp/register.ts. */
|
|
5
|
+
export declare function buildCredentialTools(client: ReclaimClient): RegisteredTool[];
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
/** Credential lifecycle tools — build the flat list of RegisteredTool
|
|
8
|
+
* entries. Mirrors buildAgentTools() in mcp/register.ts. */
|
|
9
|
+
export function buildCredentialTools(client) {
|
|
10
|
+
return [
|
|
11
|
+
authenticateTool(client),
|
|
12
|
+
listCachedTool(),
|
|
13
|
+
resolveOwnerKeyTool(),
|
|
14
|
+
issueTool(),
|
|
15
|
+
importTool(),
|
|
16
|
+
statusTool(client),
|
|
17
|
+
];
|
|
18
|
+
}
|
|
@@ -14,16 +14,16 @@ type CacheFile = {
|
|
|
14
14
|
credentials: CachedCredential[];
|
|
15
15
|
};
|
|
16
16
|
/**
|
|
17
|
-
* Read the
|
|
18
|
-
*
|
|
19
|
-
* CLI on a corrupted file the user can re-create.
|
|
17
|
+
* Read the cache file. Returns an empty doc if missing. Surface-level
|
|
18
|
+
* shape errors fall back to empty — we'd rather lose the cache than
|
|
19
|
+
* crash the CLI on a corrupted file the user can re-create.
|
|
20
20
|
*/
|
|
21
|
-
export declare function
|
|
21
|
+
export declare function readCache(): CacheFile;
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* the whole
|
|
26
|
-
* loses our ability to deduplicate `projectDirs`.
|
|
23
|
+
* Write the cache file with strict permissions. The directory gets
|
|
24
|
+
* `0700` and the file `0600` — same posture as `~/.ssh`. We rewrite
|
|
25
|
+
* the whole doc each save; the file is tiny and the alternative
|
|
26
|
+
* (append-only) loses our ability to deduplicate `projectDirs`.
|
|
27
27
|
*/
|
|
28
28
|
export declare function writeCache(doc: CacheFile): void;
|
|
29
29
|
/**
|
|
@@ -34,7 +34,7 @@ export declare function writeCache(doc: CacheFile): void;
|
|
|
34
34
|
*/
|
|
35
35
|
export declare function upsertCredential(entry: CachedCredential): CachedCredential;
|
|
36
36
|
/** Look up a cached credential by address. Case-insensitive. */
|
|
37
|
-
export declare function
|
|
37
|
+
export declare function findCached(address: string): CachedCredential | undefined;
|
|
38
38
|
export type SuggestionReason = 'createdIn' | 'usedIn' | 'newest';
|
|
39
39
|
export type CredentialSuggestion = {
|
|
40
40
|
credential: CachedCredential;
|
|
@@ -1,28 +1,45 @@
|
|
|
1
1
|
import { deriveFromPrivateKey } from '@reclaimprotocol/client';
|
|
2
|
-
import {
|
|
3
|
-
import { findProjectRoot } from "
|
|
2
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
3
|
+
import { credentialsCachePath, findProjectRoot, reclaimHomeDirPath } from "../../paths.js";
|
|
4
4
|
import { readPrivateKeyFromEnvFile } from "./env-file.js";
|
|
5
5
|
const EMPTY = { version: 1, credentials: [] };
|
|
6
6
|
/**
|
|
7
|
-
* Read the
|
|
8
|
-
*
|
|
9
|
-
* CLI on a corrupted file the user can re-create.
|
|
7
|
+
* Read the cache file. Returns an empty doc if missing. Surface-level
|
|
8
|
+
* shape errors fall back to empty — we'd rather lose the cache than
|
|
9
|
+
* crash the CLI on a corrupted file the user can re-create.
|
|
10
10
|
*/
|
|
11
|
-
export function
|
|
12
|
-
const
|
|
13
|
-
if (!
|
|
11
|
+
export function readCache() {
|
|
12
|
+
const path = credentialsCachePath();
|
|
13
|
+
if (!existsSync(path)) {
|
|
14
|
+
return { ...EMPTY };
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const raw = readFileSync(path, 'utf8');
|
|
18
|
+
const parsed = JSON.parse(raw);
|
|
19
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.credentials)) {
|
|
20
|
+
return { ...EMPTY };
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
14
25
|
return { ...EMPTY };
|
|
15
26
|
}
|
|
16
|
-
return { version: 1, credentials: creds };
|
|
17
27
|
}
|
|
18
28
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* the whole
|
|
22
|
-
* loses our ability to deduplicate `projectDirs`.
|
|
29
|
+
* Write the cache file with strict permissions. The directory gets
|
|
30
|
+
* `0700` and the file `0600` — same posture as `~/.ssh`. We rewrite
|
|
31
|
+
* the whole doc each save; the file is tiny and the alternative
|
|
32
|
+
* (append-only) loses our ability to deduplicate `projectDirs`.
|
|
23
33
|
*/
|
|
24
34
|
export function writeCache(doc) {
|
|
25
|
-
|
|
35
|
+
const dir = reclaimHomeDirPath();
|
|
36
|
+
if (!existsSync(dir)) {
|
|
37
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
38
|
+
}
|
|
39
|
+
chmodSync(dir, 0o700);
|
|
40
|
+
const path = credentialsCachePath();
|
|
41
|
+
writeFileSync(path, JSON.stringify(doc, null, 2) + '\n', { mode: 0o600 });
|
|
42
|
+
chmodSync(path, 0o600);
|
|
26
43
|
}
|
|
27
44
|
/**
|
|
28
45
|
* Upsert a credential entry. If a row already exists for `address`,
|
|
@@ -31,7 +48,7 @@ export function writeCache(doc) {
|
|
|
31
48
|
* (backfilling `createdIn` from the new entry when an old row lacks it).
|
|
32
49
|
*/
|
|
33
50
|
export function upsertCredential(entry) {
|
|
34
|
-
const doc =
|
|
51
|
+
const doc = readCache();
|
|
35
52
|
const idx = doc.credentials.findIndex((c) => c.address === entry.address.toLowerCase());
|
|
36
53
|
const merged = {
|
|
37
54
|
...entry,
|
|
@@ -53,8 +70,8 @@ export function upsertCredential(entry) {
|
|
|
53
70
|
return merged;
|
|
54
71
|
}
|
|
55
72
|
/** Look up a cached credential by address. Case-insensitive. */
|
|
56
|
-
export function
|
|
57
|
-
const doc =
|
|
73
|
+
export function findCached(address) {
|
|
74
|
+
const doc = readCache();
|
|
58
75
|
const lc = address.toLowerCase();
|
|
59
76
|
return doc.credentials.find((c) => c.address === lc);
|
|
60
77
|
}
|
|
@@ -69,7 +86,7 @@ export function findCredentialFromCachedBy(address) {
|
|
|
69
86
|
* Pass a pre-read `creds` array to avoid a redundant `readCache()` (and to
|
|
70
87
|
* rank against the same snapshot the caller already holds).
|
|
71
88
|
*/
|
|
72
|
-
export function suggestCredentialForDir(dir, creds =
|
|
89
|
+
export function suggestCredentialForDir(dir, creds = readCache().credentials) {
|
|
73
90
|
if (creds.length === 0) {
|
|
74
91
|
return undefined;
|
|
75
92
|
}
|
|
@@ -110,5 +127,5 @@ export function resolveOwnerPrivateKey(address) {
|
|
|
110
127
|
if (envKey && deriveFromPrivateKey(envKey).address === target) {
|
|
111
128
|
return envKey;
|
|
112
129
|
}
|
|
113
|
-
return
|
|
130
|
+
return findCached(target)?.privateKey;
|
|
114
131
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { deriveFromPrivateKey } from '@reclaimprotocol/client';
|
|
2
|
-
import { findProjectRoot } from "
|
|
3
|
-
import { defineTool } from "
|
|
2
|
+
import { findProjectRoot } from "../../paths.js";
|
|
3
|
+
import { defineTool } from "../server.js";
|
|
4
4
|
import { persistCredentialLocally } from "./register.js";
|
|
5
5
|
export function importTool() {
|
|
6
6
|
return defineTool({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { generateKeypair } from '@reclaimprotocol/client';
|
|
2
|
-
import { findProjectRoot } from "
|
|
3
|
-
import { defineTool } from "
|
|
4
|
-
import {
|
|
2
|
+
import { findProjectRoot } from "../../paths.js";
|
|
3
|
+
import { defineTool } from "../server.js";
|
|
4
|
+
import { findCached } from "./cache.js";
|
|
5
5
|
import { persistCredentialLocally } from "./register.js";
|
|
6
6
|
export function issueTool() {
|
|
7
7
|
return defineTool({
|
|
@@ -53,7 +53,7 @@ export function issueTool() {
|
|
|
53
53
|
let keypair;
|
|
54
54
|
let reused = false;
|
|
55
55
|
if (args.reuseCachedAppPk) {
|
|
56
|
-
const cached =
|
|
56
|
+
const cached = findCached(args.reuseCachedAppPk);
|
|
57
57
|
if (!cached) {
|
|
58
58
|
throw new Error(`No cached credential for ${args.reuseCachedAppPk}. ` +
|
|
59
59
|
'Call list_cached_credentials first.');
|
|
@@ -0,0 +1,17 @@
|
|
|
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;
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
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 +1,2 @@
|
|
|
1
|
-
import { type RegisteredTool } from '
|
|
1
|
+
import { type RegisteredTool } from '../server.ts';
|
|
2
2
|
export declare function listCachedTool(): RegisteredTool;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { findProjectRoot } from "
|
|
2
|
-
import { defineTool } from "
|
|
3
|
-
import {
|
|
1
|
+
import { findProjectRoot } from "../../paths.js";
|
|
2
|
+
import { defineTool } from "../server.js";
|
|
3
|
+
import { readCache, suggestCredentialForDir } from "./cache.js";
|
|
4
4
|
export function listCachedTool() {
|
|
5
5
|
return defineTool({
|
|
6
6
|
name: 'list_cached_credentials',
|
|
7
7
|
description: 'List Reclaim app credentials (ETH key-pairs) cached ' +
|
|
8
|
-
'locally in ~/.reclaim/
|
|
8
|
+
'locally in ~/.reclaim/credentials.json. Returns one ' +
|
|
9
9
|
'row per credential with address, optional label and ' +
|
|
10
10
|
'orgId, createdAt, createdIn (where it was first created), ' +
|
|
11
11
|
'and the project directories that have used it. Private keys ' +
|
|
@@ -19,7 +19,7 @@ export function listCachedTool() {
|
|
|
19
19
|
properties: {},
|
|
20
20
|
},
|
|
21
21
|
}, async () => {
|
|
22
|
-
const doc =
|
|
22
|
+
const doc = readCache();
|
|
23
23
|
const currentDir = findProjectRoot(process.cwd());
|
|
24
24
|
const suggestion = suggestCredentialForDir(currentDir, doc.credentials);
|
|
25
25
|
const suggestedAddress = suggestion?.credential.address;
|
|
@@ -0,0 +1,23 @@
|
|
|
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;
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
}
|