@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
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Factory entry-point for embedding the reclaim-agent's MCP tools into
|
|
3
|
-
* a host MCP server.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* the low-level wrapper around `@modelcontextprotocol/sdk`'s Server).
|
|
7
|
-
* - `@reclaimprotocol/client`'s MCP server (uses `McpServer`, the
|
|
8
|
-
* high-level SDK wrapper). Calls `registerAgentTools` to fold the
|
|
9
|
-
* agent's 16 tools alongside its auto-generated API tools.
|
|
3
|
+
* a host MCP server via `registerToolList` (server.ts) on the high-level
|
|
4
|
+
* `@modelcontextprotocol/sdk` `McpServer`. Folds the agent's tools
|
|
5
|
+
* alongside the auto-generated API tools.
|
|
10
6
|
*
|
|
11
7
|
* Shared state — Chrome attach state, capture store, secret store — is
|
|
12
8
|
* built once per server and threaded through every tool factory.
|
|
13
9
|
*/
|
|
14
|
-
import { setCryptoImplementation } from '@reclaimprotocol/tls';
|
|
15
|
-
import { webcryptoCrypto } from '@reclaimprotocol/tls/webcrypto';
|
|
16
10
|
import { ConfigStore } from "../../../config/store.js";
|
|
11
|
+
import { ensureAttestorCrypto } from "../../../proof/proof-core.js";
|
|
17
12
|
import { analyzeTool } from "./analyze.js";
|
|
18
13
|
import { attachTool } from "./attach.js";
|
|
19
14
|
import { captureTools } from "./capture.js";
|
|
@@ -24,18 +19,6 @@ import { makeSecretStore, proposeTool, } from "./propose.js";
|
|
|
24
19
|
import { createVersionFromCaptureTool } from "./publish.js";
|
|
25
20
|
import { replayTool } from "./replay.js";
|
|
26
21
|
import { sessionTools } from "./session.js";
|
|
27
|
-
let cryptoInitialized = false;
|
|
28
|
-
/** One-time crypto backend init for @reclaimprotocol/tls. The package
|
|
29
|
-
* ships with an empty `crypto = {}` and defers backend choice to the
|
|
30
|
-
* caller; without this, the first proof call dies inside the SDK with
|
|
31
|
-
* `crypto.randomBytes is not a function`. */
|
|
32
|
-
function ensureCryptoInit() {
|
|
33
|
-
if (cryptoInitialized) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
setCryptoImplementation(webcryptoCrypto);
|
|
37
|
-
cryptoInitialized = true;
|
|
38
|
-
}
|
|
39
22
|
function buildAgentState() {
|
|
40
23
|
return {
|
|
41
24
|
attach: { config: new ConfigStore() },
|
|
@@ -43,14 +26,14 @@ function buildAgentState() {
|
|
|
43
26
|
secrets: makeSecretStore(),
|
|
44
27
|
};
|
|
45
28
|
}
|
|
46
|
-
/** Build the flat list of `RegisteredTool` entries
|
|
47
|
-
*
|
|
29
|
+
/** Build the flat list of `RegisteredTool` entries, ready for
|
|
30
|
+
* `registerToolList`.
|
|
48
31
|
* Pass `client` to include the builder `create_provider_version_from_capture`;
|
|
49
32
|
* omit it (e.g. OLD-devtools mode) to register only the backend-independent
|
|
50
33
|
* capture/proof tools — old mode supplies its own publish tool. */
|
|
51
34
|
export function buildAgentTools(client) {
|
|
52
35
|
const state = buildAgentState();
|
|
53
|
-
|
|
36
|
+
ensureAttestorCrypto();
|
|
54
37
|
const tools = [];
|
|
55
38
|
tools.push(attachTool(state.attach));
|
|
56
39
|
for (const t of captureTools(state.attach, state.captures)) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { agentTraceEnabled } from "../../../consts.js";
|
|
3
4
|
import { reclaimHomeDirPath } from "../../../paths.js";
|
|
4
5
|
import { runAttestorProof, tailLogs, warnsAndErrors, } from "../../../proof/attestor.js";
|
|
5
6
|
import { defineTool } from "../../server.js";
|
|
@@ -103,11 +104,6 @@ export function proofTool(secrets) {
|
|
|
103
104
|
inputSchema: {
|
|
104
105
|
type: 'object',
|
|
105
106
|
properties: {
|
|
106
|
-
// `type: 'object'` is required so MCP clients (the LLM)
|
|
107
|
-
// serialize the value as an object, not a JSON-stringified
|
|
108
|
-
// string. `properties: {}` is required so zod-from-json-schema
|
|
109
|
-
// compiles to a passthrough instead of strict-strip — leaving
|
|
110
|
-
// it off makes the server receive `{}`.
|
|
111
107
|
provider: { type: 'object', properties: {} },
|
|
112
108
|
secretParamsRef: { type: 'string' },
|
|
113
109
|
attestorUrl: { type: 'string' },
|
|
@@ -132,7 +128,7 @@ export function proofTool(secrets) {
|
|
|
132
128
|
}
|
|
133
129
|
resolved = r;
|
|
134
130
|
}
|
|
135
|
-
const result = await runAttestorProof(provider, resolved, attestorUrl, ownerAddress);
|
|
131
|
+
const result = await runAttestorProof(provider, resolved, { attestorUrl, ownerAddress });
|
|
136
132
|
// Persist the full claim to disk for the dev to inspect/share.
|
|
137
133
|
let claimPath;
|
|
138
134
|
if (result.proof !== undefined && result.proof !== null) {
|
|
@@ -187,7 +183,7 @@ export function proofTool(secrets) {
|
|
|
187
183
|
// when investigating proof failures and the file at runLogPath
|
|
188
184
|
// will carry the input provider, sentParams, full attestor log
|
|
189
185
|
// stream, and result.
|
|
190
|
-
if (
|
|
186
|
+
if (agentTraceEnabled()) {
|
|
191
187
|
const runLogPath = writeProofRunDump({
|
|
192
188
|
provider,
|
|
193
189
|
hasSecretParamsRef: secretParamsRef !== undefined,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { draftProvider } from "../../../provider/draft.js";
|
|
3
3
|
import { OPRF_MAX_BYTES } from "../../../provider/matcher.js";
|
|
4
|
+
import { verificationWarnings } from "../../../provider/verification-checks.js";
|
|
4
5
|
import { defineTool } from "../../server.js";
|
|
5
6
|
export function makeSecretStore() {
|
|
6
7
|
const map = new Map();
|
|
@@ -27,7 +28,11 @@ export function proposeTool(captures, secrets) {
|
|
|
27
28
|
'cleartext. Skill should ask the dev first. `oprf-raw` ' +
|
|
28
29
|
'is the only variant that runs ' +
|
|
29
30
|
'without external deps; `oprf` needs gnark (Linux/macOS), ' +
|
|
30
|
-
'`oprf-mpc` needs a TEE attestor.'
|
|
31
|
+
'`oprf-mpc` needs a TEE attestor. ' +
|
|
32
|
+
'The response may include `verificationWarnings` — advisory ' +
|
|
33
|
+
'author-only decisions (e.g. a user-specific URL part needing ' +
|
|
34
|
+
'the {{context.<name>}}-vs-bare call). Surface each to the ' +
|
|
35
|
+
'dev verbatim and resolve before publishing.',
|
|
31
36
|
inputSchema: {
|
|
32
37
|
type: 'object',
|
|
33
38
|
properties: {
|
|
@@ -70,6 +75,12 @@ export function proposeTool(captures, secrets) {
|
|
|
70
75
|
}
|
|
71
76
|
}
|
|
72
77
|
const secretParamsRef = secrets.put(realSecrets);
|
|
73
|
-
|
|
78
|
+
const warnings = verificationWarnings(provider);
|
|
79
|
+
return {
|
|
80
|
+
provider,
|
|
81
|
+
secretRefs,
|
|
82
|
+
secretParamsRef,
|
|
83
|
+
...(warnings.length ? { verificationWarnings: warnings } : {}),
|
|
84
|
+
};
|
|
74
85
|
});
|
|
75
86
|
}
|
|
@@ -1,17 +1,120 @@
|
|
|
1
|
+
import { INJECTION_GUIDE } from "../../../provider/injection-doc.js";
|
|
2
|
+
import { countsFor, matchRedactionWarnings, } from "../../../provider/match-redaction-warning.js";
|
|
1
3
|
import { nextVersion, providerToCreateVersionRequest, } from "../../../provider/to-version.js";
|
|
2
4
|
import { defineTool } from "../../server.js";
|
|
5
|
+
/** Schemas shared by the top-level `provider`/`providers` and the lighter
|
|
6
|
+
* `allowedJsRequests` drafts — keeps the request shape from drifting between
|
|
7
|
+
* them. */
|
|
8
|
+
const RESPONSE_MATCHES_SCHEMA = {
|
|
9
|
+
type: 'array',
|
|
10
|
+
description: 'What the attestor checks on the revealed response slice.',
|
|
11
|
+
items: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
type: { type: 'string', enum: ['regex', 'contains'] },
|
|
15
|
+
value: { type: 'string' },
|
|
16
|
+
},
|
|
17
|
+
required: ['type', 'value'],
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
const RESPONSE_REDACTIONS_SCHEMA = {
|
|
21
|
+
type: 'array',
|
|
22
|
+
description: 'Which portions of the response the prover reveals ' +
|
|
23
|
+
'(xPath / jsonPath / regex; hash to OPRF the value). Paired with ' +
|
|
24
|
+
'responseMatches by index — keep them aligned 1-to-1. If you list more ' +
|
|
25
|
+
'redactions than matches, the tool pads matches with no-op optional ' +
|
|
26
|
+
'entries so older index-paired claim creators stay happy.',
|
|
27
|
+
items: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
xPath: { type: 'string' },
|
|
31
|
+
jsonPath: { type: 'string' },
|
|
32
|
+
regex: { type: 'string' },
|
|
33
|
+
hash: { type: 'string', enum: ['oprf', 'oprf-mpc', 'oprf-raw'] },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/** Properties every drafted request shares (top-level providers and the
|
|
38
|
+
* injected `allowedJsRequests`). */
|
|
39
|
+
const REQUEST_DRAFT_PROPERTIES = {
|
|
40
|
+
url: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
description: 'Exact API URL the attestor replays, with {{param}} placeholders ' +
|
|
43
|
+
'for any templated values.',
|
|
44
|
+
},
|
|
45
|
+
method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'PATCH'] },
|
|
46
|
+
body: { type: 'string', description: 'Request body template, if any.' },
|
|
47
|
+
responseMatches: RESPONSE_MATCHES_SCHEMA,
|
|
48
|
+
responseRedactions: RESPONSE_REDACTIONS_SCHEMA,
|
|
49
|
+
};
|
|
50
|
+
/** A full provider draft (from propose_provider), passed through verbatim. */
|
|
51
|
+
const PROVIDER_SCHEMA = {
|
|
52
|
+
type: 'object',
|
|
53
|
+
description: 'A `provider` object from propose_provider, passed through verbatim — ' +
|
|
54
|
+
'the tool builds the version body (the requests[] entry etc.) for you.',
|
|
55
|
+
properties: {
|
|
56
|
+
name: { type: 'string', description: 'Verification name.' },
|
|
57
|
+
...REQUEST_DRAFT_PROPERTIES,
|
|
58
|
+
headers: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
description: 'Request headers from the capture. Secret headers are stripped ' +
|
|
61
|
+
'before persistence; the client re-supplies them at runtime.',
|
|
62
|
+
},
|
|
63
|
+
paramValues: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
description: 'Real values for the {{param}} placeholders, e.g. ' +
|
|
66
|
+
'{ "username": "Syed" }.',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
required: ['name', 'url', 'method', 'responseMatches'],
|
|
70
|
+
};
|
|
71
|
+
/** One `allowedJsRequests` template: a request the injection script may fire,
|
|
72
|
+
* plus pagination/optionality flags. */
|
|
73
|
+
const ALLOWED_JS_REQUEST_SCHEMA = {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
...REQUEST_DRAFT_PROPERTIES,
|
|
77
|
+
multiple: {
|
|
78
|
+
type: 'boolean',
|
|
79
|
+
description: 'Allow the template to match many requests (e.g. pagination). ' +
|
|
80
|
+
'Default true.',
|
|
81
|
+
},
|
|
82
|
+
required: {
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
description: 'Fail validation if no request matches this template. Default true.',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: ['url', 'method'],
|
|
88
|
+
};
|
|
3
89
|
export function createVersionFromCaptureTool(client) {
|
|
4
90
|
return defineTool({
|
|
5
91
|
name: 'create_provider_version_from_capture',
|
|
6
|
-
description: 'Persist
|
|
7
|
-
'Call this after run_proof succeeds, passing the `provider`
|
|
8
|
-
'from propose_provider VERBATIM. This tool builds the
|
|
92
|
+
description: 'Persist drafted ReclaimProvider(s) as a verification version. ' +
|
|
93
|
+
'Call this after run_proof succeeds, passing the `provider` ' +
|
|
94
|
+
'object(s) from propose_provider VERBATIM. This tool builds the ' +
|
|
9
95
|
'body (the requests[] array, webSettings, etc.) for you — do NOT ' +
|
|
10
96
|
'hand-construct that shape or call create_provider_version directly. ' +
|
|
97
|
+
'For a MULTI-REQUEST provider, pass `providers: [...]` (each entry a ' +
|
|
98
|
+
'proven draft) — every entry becomes one request in the version, and ' +
|
|
99
|
+
'all are required at verification time. Pass a single `provider` ' +
|
|
100
|
+
'for the one-request case (exactly one of `provider`/`providers`). ' +
|
|
101
|
+
'For data gated behind login/navigation, supply `jsUserScripts` (a ' +
|
|
102
|
+
'page-injection script that drives the page to where the request ' +
|
|
103
|
+
'fires) and/or `allowedJsRequests` (extra requests the script may ' +
|
|
104
|
+
'fire, e.g. pagination). ' +
|
|
11
105
|
'Omit `version` to have the tool auto-select the next available ' +
|
|
12
106
|
'version (it lists existing versions and bumps past the highest). ' +
|
|
13
|
-
'
|
|
14
|
-
'
|
|
107
|
+
'The version lands on the draft branch — to go live, call ' +
|
|
108
|
+
'`publish_provider_version` (PRIVATE) or ' +
|
|
109
|
+
'`submit_provider_version_for_review` (PUBLIC) afterwards. ' +
|
|
110
|
+
'`requiredContext` is derived from any {{context.<name>}} ' +
|
|
111
|
+
'placeholders across ALL requests (plus injected requests / script) ' +
|
|
112
|
+
'and returned on the created version — when present, tell the dev ' +
|
|
113
|
+
'verbatim which context fields consumers MUST send when creating ' +
|
|
114
|
+
'sessions (verification matches those values exactly; a missing ' +
|
|
115
|
+
'field is a 400 at session create). ' +
|
|
116
|
+
'Refine types/descriptions via `update_provider_version` while ' +
|
|
117
|
+
'still on draft.',
|
|
15
118
|
inputSchema: {
|
|
16
119
|
type: 'object',
|
|
17
120
|
properties: {
|
|
@@ -19,71 +122,14 @@ export function createVersionFromCaptureTool(client) {
|
|
|
19
122
|
type: 'string',
|
|
20
123
|
description: 'UUID of the verification to publish the version on.',
|
|
21
124
|
},
|
|
22
|
-
provider:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
type: 'string',
|
|
31
|
-
description: 'Exact API URL the attestor replays, with {{param}} ' +
|
|
32
|
-
'placeholders for any templated values.',
|
|
33
|
-
},
|
|
34
|
-
method: {
|
|
35
|
-
type: 'string',
|
|
36
|
-
enum: ['GET', 'POST', 'PUT', 'PATCH'],
|
|
37
|
-
},
|
|
38
|
-
headers: {
|
|
39
|
-
type: 'object',
|
|
40
|
-
description: 'Request headers from the capture. Secret headers are ' +
|
|
41
|
-
'stripped before persistence; the client re-supplies them ' +
|
|
42
|
-
'at runtime.',
|
|
43
|
-
},
|
|
44
|
-
body: {
|
|
45
|
-
type: 'string',
|
|
46
|
-
description: 'Request body template, if any.',
|
|
47
|
-
},
|
|
48
|
-
responseMatches: {
|
|
49
|
-
type: 'array',
|
|
50
|
-
description: 'What the attestor checks on the revealed response slice.',
|
|
51
|
-
items: {
|
|
52
|
-
type: 'object',
|
|
53
|
-
properties: {
|
|
54
|
-
type: { type: 'string', enum: ['regex', 'contains'] },
|
|
55
|
-
value: { type: 'string' },
|
|
56
|
-
},
|
|
57
|
-
required: ['type', 'value'],
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
responseRedactions: {
|
|
61
|
-
type: 'array',
|
|
62
|
-
description: 'Which portions of the response the prover reveals ' +
|
|
63
|
-
'(xPath / jsonPath / regex; hash to OPRF the value). ' +
|
|
64
|
-
'Paired with responseMatches by index — the tool folds ' +
|
|
65
|
-
'each one into the matching entry\'s `extract` in the ' +
|
|
66
|
-
'persisted version, so keep them aligned 1-to-1.',
|
|
67
|
-
items: {
|
|
68
|
-
type: 'object',
|
|
69
|
-
properties: {
|
|
70
|
-
xPath: { type: 'string' },
|
|
71
|
-
jsonPath: { type: 'string' },
|
|
72
|
-
regex: { type: 'string' },
|
|
73
|
-
hash: {
|
|
74
|
-
type: 'string',
|
|
75
|
-
enum: ['oprf', 'oprf-mpc', 'oprf-raw'],
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
paramValues: {
|
|
81
|
-
type: 'object',
|
|
82
|
-
description: 'Real values for the {{param}} placeholders, ' +
|
|
83
|
-
'e.g. { "username": "Syed" }.',
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
required: ['name', 'url', 'method', 'responseMatches'],
|
|
125
|
+
provider: PROVIDER_SCHEMA,
|
|
126
|
+
providers: {
|
|
127
|
+
type: 'array',
|
|
128
|
+
description: 'Multiple proven drafts → one multi-request version. Each ' +
|
|
129
|
+
'entry has the same shape as `provider`. Pass this OR ' +
|
|
130
|
+
'`provider`, not both.',
|
|
131
|
+
items: PROVIDER_SCHEMA,
|
|
132
|
+
minItems: 1,
|
|
87
133
|
},
|
|
88
134
|
initialUrl: {
|
|
89
135
|
type: 'string',
|
|
@@ -106,17 +152,68 @@ export function createVersionFromCaptureTool(client) {
|
|
|
106
152
|
},
|
|
107
153
|
required: ['major', 'minor', 'patch'],
|
|
108
154
|
},
|
|
155
|
+
jsUserScripts: {
|
|
156
|
+
type: 'string',
|
|
157
|
+
description: 'Plain JavaScript injected into the provider page before every ' +
|
|
158
|
+
'page load (stored as webSettings.jsUserScripts). Use standard ' +
|
|
159
|
+
'browser APIs (window.location, element.click(), forms) to ' +
|
|
160
|
+
'drive login/navigation so the target request fires — the ' +
|
|
161
|
+
'client already intercepts page requests, and window.Reclaim ' +
|
|
162
|
+
'is only for rare edge cases, not navigation. The Builder ' +
|
|
163
|
+
'verification client installs this to run before every page ' +
|
|
164
|
+
'load (before navigating to initialUrl), matching old-devtool ' +
|
|
165
|
+
'customInjection; window.Reclaim itself is not yet exposed ' +
|
|
166
|
+
'there, so prefer plain browser JS. Guide:\n\n' +
|
|
167
|
+
INJECTION_GUIDE,
|
|
168
|
+
},
|
|
169
|
+
allowedJsRequests: {
|
|
170
|
+
type: 'array',
|
|
171
|
+
description: 'Extra requests the injection script is allowed to fire ' +
|
|
172
|
+
'(e.g. paginated API calls), as RequestSelectionTemplates. ' +
|
|
173
|
+
'`multiple`/`required` default to true.',
|
|
174
|
+
items: ALLOWED_JS_REQUEST_SCHEMA,
|
|
175
|
+
},
|
|
109
176
|
},
|
|
110
|
-
required: ['providerId', '
|
|
177
|
+
required: ['providerId', 'initialUrl'],
|
|
111
178
|
},
|
|
112
179
|
}, async (args) => {
|
|
180
|
+
// Exactly one of `provider` / `providers` — normalize to an array.
|
|
181
|
+
const hasMany = Array.isArray(args.providers) && args.providers.length > 0;
|
|
182
|
+
const hasOne = !!args.provider;
|
|
183
|
+
if (hasMany === hasOne) {
|
|
184
|
+
throw new Error('Pass exactly one of `provider` (a single draft) or ' +
|
|
185
|
+
'`providers` (an array of 1+ drafts).');
|
|
186
|
+
}
|
|
187
|
+
const providers = hasMany ? args.providers : [args.provider];
|
|
113
188
|
// Auto-pick the next version if not explicit
|
|
114
189
|
const ver = args.version ?? (await nextVersion(client, args.providerId));
|
|
115
|
-
const body = providerToCreateVersionRequest(
|
|
116
|
-
|
|
190
|
+
const body = providerToCreateVersionRequest(providers, args.initialUrl, {
|
|
191
|
+
notes: args.notes,
|
|
192
|
+
version: ver,
|
|
193
|
+
jsUserScripts: args.jsUserScripts,
|
|
194
|
+
allowedJsRequests: args.allowedJsRequests,
|
|
195
|
+
});
|
|
196
|
+
const result = await client.call('CreateProviderVersion', {
|
|
117
197
|
params: { providerId: args.providerId },
|
|
118
198
|
body,
|
|
119
|
-
})
|
|
120
|
-
|
|
199
|
+
});
|
|
200
|
+
const created = result.data;
|
|
201
|
+
// Aggregate non-blocking advisories for the dev — nothing here blocks
|
|
202
|
+
// or alters what was published; the author keeps full control.
|
|
203
|
+
const notes = [];
|
|
204
|
+
// Match/redaction advisory across every request (+ injected requests).
|
|
205
|
+
const requestCounts = [
|
|
206
|
+
...providers.map((p, i) => countsFor(`Request ${i + 1}`, p)),
|
|
207
|
+
...(args.allowedJsRequests ?? []).map((r, i) => countsFor(`Injected request ${i + 1}`, r)),
|
|
208
|
+
];
|
|
209
|
+
notes.push(...matchRedactionWarnings(requestCounts));
|
|
210
|
+
// Let the dev know how/when the script runs (and the window.Reclaim gap).
|
|
211
|
+
if (args.jsUserScripts?.trim()) {
|
|
212
|
+
notes.push('jsUserScripts will run in the Builder verification client before ' +
|
|
213
|
+
'every page load (before navigating to initialUrl), matching ' +
|
|
214
|
+
'old-devtool customInjection. window.Reclaim is not yet exposed ' +
|
|
215
|
+
'there, so prefer plain browser JS for navigation/interaction.');
|
|
216
|
+
}
|
|
217
|
+
return notes.length ? { ...created, _notes: notes } : created;
|
|
121
218
|
});
|
|
122
219
|
}
|
|
@@ -11,11 +11,6 @@ export function replayTool(secrets) {
|
|
|
11
11
|
inputSchema: {
|
|
12
12
|
type: 'object',
|
|
13
13
|
properties: {
|
|
14
|
-
// `type: 'object'` is required so MCP clients (the LLM)
|
|
15
|
-
// serialize the value as an object, not a JSON-stringified
|
|
16
|
-
// string. `properties: {}` is required so zod-from-json-schema
|
|
17
|
-
// compiles to a passthrough instead of strict-strip — leaving
|
|
18
|
-
// it off makes the server receive `{}`.
|
|
19
14
|
provider: { type: 'object', properties: {} },
|
|
20
15
|
secretParamsRef: { type: 'string' },
|
|
21
16
|
},
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { detectConcerns } from "../../provider/constraints.js";
|
|
2
|
+
import { defineTool } from "../server.js";
|
|
3
|
+
export function analyzeTool(captures) {
|
|
4
|
+
return defineTool({
|
|
5
|
+
name: 'analyze_request_constraints',
|
|
6
|
+
description: 'Flag potential provider-authoring concerns for a captured request.',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
captureId: { type: 'string' },
|
|
11
|
+
requestId: { type: 'string' },
|
|
12
|
+
},
|
|
13
|
+
required: ['captureId', 'requestId'],
|
|
14
|
+
},
|
|
15
|
+
}, async ({ captureId, requestId }) => {
|
|
16
|
+
const session = captures.sessions.get(captureId);
|
|
17
|
+
if (!session) {
|
|
18
|
+
throw new Error(`no capture ${captureId}`);
|
|
19
|
+
}
|
|
20
|
+
const r = session.requests.get(requestId);
|
|
21
|
+
if (!r) {
|
|
22
|
+
throw new Error(`no request ${requestId} in capture ${captureId}`);
|
|
23
|
+
}
|
|
24
|
+
return { concerns: detectConcerns(r) };
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CdpHandles } from '../../cdp/client.ts';
|
|
2
|
+
import type { ConfigStore } from '../../config/store.ts';
|
|
3
|
+
import { type RegisteredTool } from '../server.ts';
|
|
4
|
+
export interface AttachState {
|
|
5
|
+
handles: CdpHandles;
|
|
6
|
+
port: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function attachTool(state: {
|
|
9
|
+
current?: AttachState;
|
|
10
|
+
config: ConfigStore;
|
|
11
|
+
}): RegisteredTool;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { connectCdp } from "../../cdp/client.js";
|
|
2
|
+
import { defaultProfileDir, launchChrome, waitForCdpReady, } from "../../cdp/launcher.js";
|
|
3
|
+
import { defaultConfigPath } from "../../config/store.js";
|
|
4
|
+
import { defineTool } from "../server.js";
|
|
5
|
+
export function attachTool(state) {
|
|
6
|
+
return defineTool({
|
|
7
|
+
name: 'attach_chrome',
|
|
8
|
+
description: 'Attach to a Chrome instance via CDP. ' +
|
|
9
|
+
'Auto-launches a dedicated-profile Chrome ' +
|
|
10
|
+
'on first call if no attachMode is persisted. ' +
|
|
11
|
+
'Persists the chosen mode in ~/.reclaim/config.json.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
attachMode: { type: 'string', enum: ['dedicated', 'attach'] },
|
|
16
|
+
port: { type: 'number' },
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
}, async (parsed) => {
|
|
20
|
+
let cfg = state.config.read();
|
|
21
|
+
if (parsed.attachMode || parsed.port) {
|
|
22
|
+
cfg = {
|
|
23
|
+
attachMode: parsed.attachMode ?? cfg?.attachMode ?? 'dedicated',
|
|
24
|
+
port: parsed.port ?? cfg?.port ?? 9222,
|
|
25
|
+
};
|
|
26
|
+
state.config.write(cfg);
|
|
27
|
+
}
|
|
28
|
+
if (!cfg) {
|
|
29
|
+
return {
|
|
30
|
+
needsChoice: true,
|
|
31
|
+
message: 'First-run: choose attach mode. ' +
|
|
32
|
+
'Call attach_chrome again with attachMode="dedicated" ' +
|
|
33
|
+
'(auto-launch a Chrome profile under ~/.reclaim/chrome-profile) ' +
|
|
34
|
+
'or attachMode="attach" ' +
|
|
35
|
+
'(connect to a Chrome you launched yourself with ' +
|
|
36
|
+
'BOTH --remote-debugging-port=9222 AND ' +
|
|
37
|
+
'--user-data-dir=<some-non-default-path>; ' +
|
|
38
|
+
'since Chrome 136 the debug-port flag is silently ' +
|
|
39
|
+
'ignored if the default profile is in use).',
|
|
40
|
+
configPath: defaultConfigPath(),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (cfg.attachMode === 'dedicated') {
|
|
44
|
+
try {
|
|
45
|
+
await waitForCdpReady(cfg.port, 500);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
launchChrome({ port: cfg.port, profileDir: defaultProfileDir() });
|
|
49
|
+
await waitForCdpReady(cfg.port, 10_000);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
await waitForCdpReady(cfg.port, 2_000).catch(() => {
|
|
54
|
+
throw new Error(`attach mode: nothing answering CDP on port ${cfg.port}. ` +
|
|
55
|
+
'Chrome must be launched with BOTH ' +
|
|
56
|
+
`--remote-debugging-port=${cfg.port} AND ` +
|
|
57
|
+
'--user-data-dir=<some-non-default-path>. ' +
|
|
58
|
+
'Since Chrome 136 (May 2025), the debug-port flag is ' +
|
|
59
|
+
'silently ignored when the default ' +
|
|
60
|
+
"user profile is in use — that's why nothing opens the port. " +
|
|
61
|
+
'Either re-launch Chrome with ' +
|
|
62
|
+
'a custom --user-data-dir, or switch to attachMode="dedicated".');
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (state.current) {
|
|
66
|
+
await state.current.handles.close().catch(() => { });
|
|
67
|
+
}
|
|
68
|
+
const handles = await connectCdp(cfg.port);
|
|
69
|
+
state.current = { handles, port: cfg.port };
|
|
70
|
+
return { attached: true, port: cfg.port, mode: cfg.attachMode };
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { defineTool } from "../../server.js";
|
|
2
|
+
import { writeSession } from "./session-cache.js";
|
|
3
|
+
export function authenticateTool(client) {
|
|
4
|
+
return defineTool({
|
|
5
|
+
name: 'reclaim_authenticate',
|
|
6
|
+
description: 'Authorize the CLI / MCP to make authenticated API calls by having ' +
|
|
7
|
+
'the user sign in on the dashboard. Two steps:\n\n' +
|
|
8
|
+
'Step 1 — call with no arguments. Returns a `code` and `attachUrl`. ' +
|
|
9
|
+
'Tell the user to open the URL, sign in, and authorize.\n\n' +
|
|
10
|
+
'Step 2 — once the user says they have authorized, call again ' +
|
|
11
|
+
'with the same `code` from step 1 to check if confirmed. ' +
|
|
12
|
+
'Returns `LINKED` with a 2-day bearer token when confirmed, ' +
|
|
13
|
+
'`PENDING` if not yet, or `EXPIRED` if too long has passed.\n\n' +
|
|
14
|
+
'The token is cached in ~/.reclaim/config.json automatically.\n\n' +
|
|
15
|
+
'This bearer token authenticates dashboard/org-management calls ' +
|
|
16
|
+
'(e.g. list_orgs, issuing an org secret). Programmatic verification ' +
|
|
17
|
+
'calls use a separate org secret (rorg_…, set as RECLAIM_ORG_SECRET).',
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
code: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
pattern: '^[A-HJ-NP-Z2-9]{5}$',
|
|
24
|
+
description: '5-character code from a previous step-1 call. ' +
|
|
25
|
+
'When provided, checks whether the user has ' +
|
|
26
|
+
'confirmed that session instead of opening a new one.',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
}, async (args) => {
|
|
31
|
+
// Step 2: check an existing session
|
|
32
|
+
if (args.code) {
|
|
33
|
+
return checkSession(client, args.code);
|
|
34
|
+
}
|
|
35
|
+
// Step 1: open a new auth-only session
|
|
36
|
+
const sessionResult = (await client.call('CreateAttachSession'));
|
|
37
|
+
const session = sessionResult.data;
|
|
38
|
+
return {
|
|
39
|
+
status: 'PENDING',
|
|
40
|
+
code: session.code,
|
|
41
|
+
attachUrl: session.attachUrl,
|
|
42
|
+
expiresAt: session.expiresAt,
|
|
43
|
+
instructions: `Open ${session.attachUrl} in your browser, sign in, and click ` +
|
|
44
|
+
'"Authorize this device". ' +
|
|
45
|
+
'Once done, call authenticate again with code: ' +
|
|
46
|
+
`"${session.code}" to confirm.`,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async function checkSession(client, code) {
|
|
51
|
+
let session;
|
|
52
|
+
try {
|
|
53
|
+
const sessionResult = (await client.call('GetAttachSession', {
|
|
54
|
+
params: { code }
|
|
55
|
+
}));
|
|
56
|
+
session = sessionResult.data;
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
const status = err.status;
|
|
60
|
+
if (status === 404) {
|
|
61
|
+
return {
|
|
62
|
+
status: 'NOT_FOUND',
|
|
63
|
+
hint: 'Session not found — it may have expired (15-min window). ' +
|
|
64
|
+
'Call authenticate without a code to start a new session.',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`Failed to check session: ${err.message}`);
|
|
68
|
+
}
|
|
69
|
+
if (session.status === 'LINKED') {
|
|
70
|
+
const token = session.token?.trim();
|
|
71
|
+
if (token) {
|
|
72
|
+
writeSession({
|
|
73
|
+
token,
|
|
74
|
+
expiresAt: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString(),
|
|
75
|
+
});
|
|
76
|
+
client.setToken(token);
|
|
77
|
+
return {
|
|
78
|
+
status: 'LINKED',
|
|
79
|
+
token,
|
|
80
|
+
note: 'Token cached in ~/.reclaim/config.json and active now.',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
status: 'LINKED_NO_TOKEN',
|
|
85
|
+
hint: 'Session confirmed but no token issued. ' +
|
|
86
|
+
'Restart the backend (npm run dev) and try again.',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (session.status === 'EXPIRED') {
|
|
90
|
+
return {
|
|
91
|
+
status: 'EXPIRED',
|
|
92
|
+
hint: 'Session expired (15-min window passed). ' +
|
|
93
|
+
'Call authenticate without a code to start a new session.',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Still PENDING
|
|
97
|
+
return {
|
|
98
|
+
status: 'PENDING',
|
|
99
|
+
code: session.code,
|
|
100
|
+
attachUrl: session.attachUrl,
|
|
101
|
+
hint: 'Session is still waiting. ' +
|
|
102
|
+
`Make sure you opened ${session.attachUrl}, ` +
|
|
103
|
+
'signed in, and clicked "Authorize this device". ' +
|
|
104
|
+
`Call authenticate again with code: "${session.code}"` +
|
|
105
|
+
' once you have done so.',
|
|
106
|
+
};
|
|
107
|
+
}
|