@reclaimprotocol/agent 0.1.33 → 0.1.35
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 +3 -3
- package/lib/authoring/backend.d.ts +13 -0
- package/lib/authoring/index.d.ts +1 -1
- package/lib/authoring/tools.js +78 -42
- package/lib/config/store.d.ts +10 -0
- package/lib/mcp/api/operations.gen.d.ts +238 -4
- package/lib/mcp/api/operations.gen.js +6967 -672
- package/lib/mcp/api/tools.js +21 -8
- package/lib/mcp/instructions.js +12 -12
- package/lib/mcp/server.d.ts +1 -1
- package/lib/mcp/server.js +3 -0
- package/lib/mcp/start.js +8 -7
- package/lib/mcp/success-response.js +5 -1
- package/lib/mcp/tools/agent/analyze.js +5 -4
- package/lib/mcp/tools/agent/attach.js +11 -7
- package/lib/mcp/tools/agent/build.js +5 -1
- package/lib/mcp/tools/agent/capture.d.ts +2 -2
- package/lib/mcp/tools/agent/capture.js +15 -11
- package/lib/mcp/tools/agent/check-attestor.d.ts +5 -0
- package/lib/mcp/tools/agent/check-attestor.js +37 -0
- package/lib/mcp/tools/agent/eval.js +4 -3
- package/lib/mcp/tools/agent/find.js +9 -10
- package/lib/mcp/tools/agent/how-it-works.js +1 -0
- package/lib/mcp/tools/agent/live-view.js +49 -22
- package/lib/mcp/tools/agent/pod-proof.js +7 -11
- package/lib/mcp/tools/agent/proof.d.ts +12 -0
- package/lib/mcp/tools/agent/proof.js +32 -0
- package/lib/mcp/tools/agent/propose.js +4 -3
- package/lib/mcp/tools/agent/publish.d.ts +2 -1
- package/lib/mcp/tools/agent/publish.js +12 -6
- package/lib/mcp/tools/agent/replay.js +10 -7
- package/lib/mcp/tools/agent/session.js +16 -8
- package/lib/mcp/tools/agent/shared.d.ts +2 -1
- package/lib/mcp/tools/agent/shared.js +29 -24
- package/lib/mcp/tools/agent/start-integration.d.ts +15 -0
- package/lib/mcp/tools/agent/start-integration.js +62 -0
- package/lib/mcp/tools/authenticate/authenticate.js +9 -11
- package/lib/mcp/tools/authenticate/build.js +2 -0
- package/lib/mcp/tools/authenticate/builder-access.js +1 -0
- package/lib/mcp/tools/authenticate/eth-key.d.ts +8 -0
- package/lib/mcp/tools/authenticate/eth-key.js +27 -15
- package/lib/mcp/tools/authenticate/import.js +18 -31
- package/lib/mcp/tools/authenticate/issue-org-token.d.ts +8 -0
- package/lib/mcp/tools/authenticate/issue-org-token.js +60 -0
- package/lib/mcp/tools/authenticate/issue.js +9 -9
- package/lib/mcp/tools/authenticate/org-key-store.d.ts +10 -0
- package/lib/mcp/tools/authenticate/org-key-store.js +23 -0
- package/lib/mcp/tools/authenticate/resolve-owner-key.js +10 -12
- package/lib/mcp/tools/mode/mode.js +2 -0
- package/lib/mcp/tools/verification/build.d.ts +3 -1
- package/lib/mcp/tools/verification/build.js +10 -2
- package/lib/mcp/tools/verification/callback-listener.d.ts +51 -0
- package/lib/mcp/tools/verification/callback-listener.js +163 -0
- package/lib/mcp/tools/verification/index.d.ts +2 -0
- package/lib/mcp/tools/verification/index.js +2 -0
- package/lib/mcp/tools/verification/proxy-callback-events.d.ts +59 -0
- package/lib/mcp/tools/verification/proxy-callback-events.js +300 -0
- package/lib/mcp/tools/verification/read-callback-events.d.ts +3 -0
- package/lib/mcp/tools/verification/read-callback-events.js +135 -0
- package/lib/mcp/tools/verification/result.d.ts +6 -0
- package/lib/mcp/tools/verification/result.js +11 -8
- package/lib/proof/check-attestor.d.ts +67 -0
- package/lib/proof/check-attestor.js +156 -0
- package/lib/tools/registry.d.ts +12 -0
- package/lib/tools/registry.js +1 -0
- package/package.json +2 -2
- package/skill/SKILL.builder.md +84 -75
- package/skill/SKILL.common.md +23 -10
- package/skill/builder-mode.md +140 -138
- package/skill/start-integration.md +74 -0
- package/skill/user-script.md +64 -0
- package/skill/verification-client-integration.md +40 -28
- package/src/authoring/backend.ts +15 -0
- package/src/authoring/index.ts +2 -0
- package/src/authoring/tools.ts +86 -42
- package/src/config/store.ts +11 -0
- package/src/mcp/api/operations.gen.ts +7011 -704
- package/src/mcp/api/tools.ts +21 -8
- package/src/mcp/instructions.ts +12 -12
- package/src/mcp/server.ts +4 -0
- package/src/mcp/start.ts +9 -7
- package/src/mcp/success-response.ts +5 -1
- package/src/mcp/tools/agent/analyze.ts +5 -4
- package/src/mcp/tools/agent/attach.ts +11 -7
- package/src/mcp/tools/agent/build.ts +7 -1
- package/src/mcp/tools/agent/capture.ts +20 -12
- package/src/mcp/tools/agent/check-attestor.ts +49 -0
- package/src/mcp/tools/agent/eval.ts +4 -3
- package/src/mcp/tools/agent/find.ts +9 -10
- package/src/mcp/tools/agent/how-it-works.ts +1 -0
- package/src/mcp/tools/agent/live-view.ts +49 -22
- package/src/mcp/tools/agent/pod-proof.ts +7 -11
- package/src/mcp/tools/agent/proof.ts +55 -0
- package/src/mcp/tools/agent/propose.ts +4 -3
- package/src/mcp/tools/agent/publish.ts +12 -4
- package/src/mcp/tools/agent/replay.ts +10 -7
- package/src/mcp/tools/agent/session.ts +16 -8
- package/src/mcp/tools/agent/shared.ts +39 -24
- package/src/mcp/tools/agent/start-integration.ts +84 -0
- package/src/mcp/tools/authenticate/authenticate.ts +9 -11
- package/src/mcp/tools/authenticate/build.ts +2 -0
- package/src/mcp/tools/authenticate/builder-access.ts +1 -0
- package/src/mcp/tools/authenticate/eth-key.ts +48 -18
- package/src/mcp/tools/authenticate/import.ts +21 -36
- package/src/mcp/tools/authenticate/issue-org-token.ts +84 -0
- package/src/mcp/tools/authenticate/issue.ts +9 -9
- package/src/mcp/tools/authenticate/org-key-store.ts +25 -0
- package/src/mcp/tools/authenticate/resolve-owner-key.ts +10 -12
- package/src/mcp/tools/mode/mode.ts +2 -0
- package/src/mcp/tools/verification/build.ts +10 -2
- package/src/mcp/tools/verification/callback-listener.ts +203 -0
- package/src/mcp/tools/verification/index.ts +2 -0
- package/src/mcp/tools/verification/proxy-callback-events.ts +374 -0
- package/src/mcp/tools/verification/read-callback-events.ts +160 -0
- package/src/mcp/tools/verification/result.ts +11 -8
- package/src/proof/check-attestor.ts +209 -0
- package/src/tools/registry.ts +14 -0
package/lib/mcp/api/tools.js
CHANGED
|
@@ -4,6 +4,7 @@ import assert from 'node:assert';
|
|
|
4
4
|
import { orgSecret } from "../../consts.js";
|
|
5
5
|
import { remapErrorAsResponse } from "../remap_errors.js";
|
|
6
6
|
import { successResponse } from "../success-response.js";
|
|
7
|
+
import { savedOrgKey } from "../tools/authenticate/org-key-store.js";
|
|
7
8
|
import { MCP_TOOLS } from "./operations.gen.js";
|
|
8
9
|
/**
|
|
9
10
|
* The generated `MCP_TOOLS` are keyed by OpenAPI `operationId` (PascalCase,
|
|
@@ -33,6 +34,11 @@ export function registerTools(server, client, orgClient = (orgToken) => new Recl
|
|
|
33
34
|
const shape = {
|
|
34
35
|
description: tool.description,
|
|
35
36
|
inputSchema: tool.inputSchema,
|
|
37
|
+
// Generated entries gain `annotations` when the spec marks them; read
|
|
38
|
+
// it defensively so an older generated file still registers.
|
|
39
|
+
...('annotations' in tool && tool.annotations
|
|
40
|
+
? { annotations: tool.annotations }
|
|
41
|
+
: {}),
|
|
36
42
|
};
|
|
37
43
|
if (!isOperationId(opId)) {
|
|
38
44
|
throw new Error(`Generated MCP operation is not in the API client: ${opId}`);
|
|
@@ -51,7 +57,7 @@ export function registerTools(server, client, orgClient = (orgToken) => new Recl
|
|
|
51
57
|
throw new Error(`Invalid generated MCP arguments for ${opId}`);
|
|
52
58
|
}
|
|
53
59
|
const caller = orgTokenOnly
|
|
54
|
-
? orgClient(orgTokenFor(toSnakeCase(opId), orgToken))
|
|
60
|
+
? orgClient(orgTokenFor(toSnakeCase(opId), orgToken, args))
|
|
55
61
|
: client;
|
|
56
62
|
const result = await caller.call(opId, args);
|
|
57
63
|
return successResponse(result.data);
|
|
@@ -85,12 +91,19 @@ function isRecord(value) {
|
|
|
85
91
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
86
92
|
}
|
|
87
93
|
/** The org API key a call authenticates with: its `orgToken`, else the
|
|
88
|
-
* server's RECLAIM_ORG_SECRET
|
|
89
|
-
*
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
* server's RECLAIM_ORG_SECRET, else the key `issue_org_token` saved (for the
|
|
95
|
+
* call's `orgId` when it has one, otherwise the most recent). The session
|
|
96
|
+
* token (the developer's sign-in) can't stand in, since these routes accept
|
|
97
|
+
* only an org key. */
|
|
98
|
+
function orgTokenFor(tool, orgToken, args) {
|
|
99
|
+
const orgId = isRecord(args) && isRecord(args.params)
|
|
100
|
+
? args.params.orgId
|
|
101
|
+
: undefined;
|
|
102
|
+
const token = (typeof orgToken === 'string' && orgToken)
|
|
103
|
+
|| orgSecret()
|
|
104
|
+
|| savedOrgKey(typeof orgId === 'string' ? orgId : undefined);
|
|
105
|
+
assert(token, unauthorized(`${tool} authenticates with the organization's API key. Run `
|
|
106
|
+
+ 'issue_org_token to save one, or pass orgToken, or set '
|
|
107
|
+
+ 'RECLAIM_ORG_SECRET for the MCP server.'));
|
|
95
108
|
return token;
|
|
96
109
|
}
|
package/lib/mcp/instructions.js
CHANGED
|
@@ -45,7 +45,7 @@ const BUILDER_TOPIC_FILES = {
|
|
|
45
45
|
* the right one without fetching several. Keyed across both modes; only the
|
|
46
46
|
* topics a mode actually has are listed back to it. */
|
|
47
47
|
const TOPIC_SUMMARIES = {
|
|
48
|
-
'overview': 'Terminology, your stance, and the
|
|
48
|
+
'overview': 'Terminology, your stance, and the authoring checklist.',
|
|
49
49
|
'contributions': 'Contributing a public provider as a volunteer: the '
|
|
50
50
|
+ 'candidate-and-review workflow and the extra rules it adds.',
|
|
51
51
|
'auth': 'Signing in to the backend, and which token authorizes what.',
|
|
@@ -54,23 +54,23 @@ const TOPIC_SUMMARIES = {
|
|
|
54
54
|
'browser': 'Choosing a browser source (local Chrome, the Reclaim runtime in '
|
|
55
55
|
+ 'Docker, a remote Builder browser, or a custom CDP endpoint), '
|
|
56
56
|
+ 'geolocation, live view, and launch troubleshooting.',
|
|
57
|
-
'capture': 'Tabs,
|
|
58
|
-
+ 'authenticated request to prove.',
|
|
57
|
+
'capture': 'Tabs, sign-in detection, capturing through the navigation '
|
|
58
|
+
+ 'script, and picking an authenticated request to prove.',
|
|
59
59
|
'draft': 'The OPRF/privacy decision, propose_provider, and the canonical '
|
|
60
60
|
+ 'drafted shape.',
|
|
61
61
|
'params': 'Extracted vs consumer-supplied vs secret params, the URL '
|
|
62
62
|
+ 'character budget, and runtime resolution.',
|
|
63
63
|
'prove': 'replay_request, the mandatory auth-bound check, run_proof, and '
|
|
64
64
|
+ 'diagnosing failures.',
|
|
65
|
-
'publish': 'Creating
|
|
66
|
-
+ 'and going live.',
|
|
67
|
-
'discover': '
|
|
68
|
-
+ 'session.',
|
|
69
|
-
'integration': '
|
|
70
|
-
+ '
|
|
71
|
-
+ 'verification.',
|
|
72
|
-
'user-script': '
|
|
73
|
-
+ '
|
|
65
|
+
'publish': 'Creating a provider, provider versions, multi-request '
|
|
66
|
+
+ 'providers, and going live.',
|
|
67
|
+
'discover': 'Reusing existing providers: finding, resolving, and '
|
|
68
|
+
+ 'creating a verification session.',
|
|
69
|
+
'integration': 'After start_integration: org key, callback (local '
|
|
70
|
+
+ 'proxy_callback_events or deployed webhook), frontend and backend '
|
|
71
|
+
+ 'over the HTTP session API; `api=2` and result verification.',
|
|
72
|
+
'user-script': 'The navigation script (jsUserScripts): sign-in redirect, '
|
|
73
|
+
+ 'multi-page walk, test_user_script with keep.',
|
|
74
74
|
'interception': 'injectionType / interceptorType, live interception, the '
|
|
75
75
|
+ 'document-replay flag, and what to do when a site won\'t load or '
|
|
76
76
|
+ 'won\'t let the user sign in.',
|
package/lib/mcp/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { type RegisteredTool } from '../tools/registry.ts';
|
|
3
|
-
export { defineTool, type RegisteredTool, type ToolDefinition, } from '../tools/registry.ts';
|
|
3
|
+
export { defineTool, type RegisteredTool, type ToolAnnotations, type ToolDefinition, } from '../tools/registry.ts';
|
|
4
4
|
/**
|
|
5
5
|
* Registers a list of RegisteredTool entries on a high-level McpServer.
|
|
6
6
|
* Converts JSON Schema definitions to Zod and provides consistent
|
package/lib/mcp/server.js
CHANGED
|
@@ -15,6 +15,9 @@ export function registerToolList(server, tools) {
|
|
|
15
15
|
server.registerTool(tool.definition.name, {
|
|
16
16
|
description: tool.definition.description,
|
|
17
17
|
inputSchema: toZod(tool.definition.inputSchema),
|
|
18
|
+
...(tool.definition.annotations
|
|
19
|
+
? { annotations: tool.definition.annotations }
|
|
20
|
+
: {}),
|
|
18
21
|
}, async (args) => {
|
|
19
22
|
try {
|
|
20
23
|
return successResponse(await tool.handler(args));
|
package/lib/mcp/start.js
CHANGED
|
@@ -11,7 +11,7 @@ import { ReclaimSdkClient } from "../old/sdk-client.js";
|
|
|
11
11
|
import { buildOldBackendTools } from "../old/tools/build.js";
|
|
12
12
|
import { NAME, VERSION } from "../pkg.js";
|
|
13
13
|
import { registerTools } from "./api/tools.js";
|
|
14
|
-
import { buildAgentState, buildAgentTools, buildBuilderAuthenticationTools, buildBuilderOrganizationTools, buildCredentialTools, buildLocalCredentialTools, buildModeTools, buildVerificationTools, disposeAgentState, readSession, } from "./tools/index.js";
|
|
14
|
+
import { buildAgentState, buildAgentTools, buildBuilderAuthenticationTools, buildBuilderOrganizationTools, buildCredentialTools, buildLocalCredentialTools, buildModeTools, buildVerificationTools, disposeAgentState, readSession, releaseAllListeners, stopAllForwarders, } from "./tools/index.js";
|
|
15
15
|
import { BUILDER_MODE_INSTRUCTIONS } from "./instructions.js";
|
|
16
16
|
import { registerToolList } from "./server.js";
|
|
17
17
|
export async function startMcpServer() {
|
|
@@ -94,7 +94,7 @@ export async function startMcpServer() {
|
|
|
94
94
|
// the session can end without a single tool call: the developer closes their
|
|
95
95
|
// agent, the client kills the server, stdin drops. Nothing ran here before,
|
|
96
96
|
// which left a container up after the session that made it was gone.
|
|
97
|
-
if (agentToolsEnabled) {
|
|
97
|
+
if (agentToolsEnabled || !oldMode) {
|
|
98
98
|
installShutdownHooks(() => agentState);
|
|
99
99
|
}
|
|
100
100
|
const transport = new StdioServerTransport();
|
|
@@ -117,10 +117,7 @@ function installShutdownHooks(state) {
|
|
|
117
117
|
}
|
|
118
118
|
shuttingDown = true;
|
|
119
119
|
const current = state();
|
|
120
|
-
|
|
121
|
-
process.exit(0);
|
|
122
|
-
}
|
|
123
|
-
LOGGER.info({ reason }, 'releasing the browser before exit');
|
|
120
|
+
LOGGER.info({ reason }, 'releasing resources before exit');
|
|
124
121
|
// Bounded: a wedged `docker stop` must not keep the process alive, but
|
|
125
122
|
// nor should we skip cleanup that would have finished in a second.
|
|
126
123
|
const timer = setTimeout(() => {
|
|
@@ -128,7 +125,11 @@ function installShutdownHooks(state) {
|
|
|
128
125
|
process.exit(0);
|
|
129
126
|
}, SHUTDOWN_GRACE_MS);
|
|
130
127
|
timer.unref();
|
|
131
|
-
void
|
|
128
|
+
void Promise.all([
|
|
129
|
+
stopAllForwarders(),
|
|
130
|
+
releaseAllListeners(),
|
|
131
|
+
current && disposeAgentState(current),
|
|
132
|
+
])
|
|
132
133
|
.catch((err) => {
|
|
133
134
|
LOGGER.warn({ err }, 'teardown failed');
|
|
134
135
|
})
|
|
@@ -6,7 +6,11 @@ export function successResponse(value) {
|
|
|
6
6
|
structuredContent: structured(value),
|
|
7
7
|
content: [{
|
|
8
8
|
type: 'text',
|
|
9
|
-
text
|
|
9
|
+
// A string result is text written for the agent (a plan, a guide):
|
|
10
|
+
// JSON-quoting it would escape every newline.
|
|
11
|
+
text: typeof value === 'string'
|
|
12
|
+
? value
|
|
13
|
+
: JSON.stringify(value, null, 2) ?? 'null',
|
|
10
14
|
}],
|
|
11
15
|
};
|
|
12
16
|
}
|
|
@@ -3,10 +3,10 @@ import { defineTool } from "../../server.js";
|
|
|
3
3
|
export function analyzeTool(captures) {
|
|
4
4
|
return defineTool({
|
|
5
5
|
name: 'analyze_request_constraints',
|
|
6
|
-
description: '
|
|
7
|
-
+ 'request, CSRF dependency, bot challenge, short-lived
|
|
8
|
-
+ 'GraphQL. Call it on each top candidate from '
|
|
9
|
-
+ 'find_requests_containing before drafting, and relay the
|
|
6
|
+
description: 'Returns provider-authoring concerns for one captured request: '
|
|
7
|
+
+ 'signed request, CSRF dependency, bot challenge, short-lived '
|
|
8
|
+
+ 'token, or GraphQL. Call it on each top candidate from '
|
|
9
|
+
+ '`find_requests_containing` before drafting, and relay the '
|
|
10
10
|
+ '`concerns` to the developer verbatim.',
|
|
11
11
|
inputSchema: {
|
|
12
12
|
type: 'object',
|
|
@@ -16,6 +16,7 @@ export function analyzeTool(captures) {
|
|
|
16
16
|
},
|
|
17
17
|
required: ['captureId', 'requestId'],
|
|
18
18
|
},
|
|
19
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
19
20
|
}, async ({ captureId, requestId }) => {
|
|
20
21
|
const entry = captures.sessions.get(captureId);
|
|
21
22
|
if (!entry) {
|
|
@@ -104,13 +104,16 @@ dependencies = {}) {
|
|
|
104
104
|
};
|
|
105
105
|
return defineTool({
|
|
106
106
|
name: 'attach_browser',
|
|
107
|
-
description: 'Connects
|
|
108
|
-
+ '
|
|
109
|
-
+ '
|
|
110
|
-
+ '
|
|
111
|
-
+ '
|
|
112
|
-
+ '
|
|
113
|
-
+ '
|
|
107
|
+
description: 'Connects the authoring tools to a browser that loads the '
|
|
108
|
+
+ 'developer\'s sign-in page and target site. `builder` and '
|
|
109
|
+
+ '`container` run the browser remotely or in Docker, away from the '
|
|
110
|
+
+ 'developer\'s own profile; `dedicated`, `attach`, and `custom` use a '
|
|
111
|
+
+ 'local or supplied Chrome. Call without `mode` to receive the '
|
|
112
|
+
+ 'options, ask the developer to choose, then call again once with '
|
|
113
|
+
+ 'the chosen mode. `builder` consumes quota. Only `container` and '
|
|
114
|
+
+ '`builder` support a shareable live view. Relay returned live-view '
|
|
115
|
+
+ 'URLs verbatim. See how_it_works({ topic: "browser" }) for '
|
|
116
|
+
+ 'selection, geolocation, and lifecycle.',
|
|
114
117
|
inputSchema: {
|
|
115
118
|
type: 'object',
|
|
116
119
|
properties: {
|
|
@@ -153,6 +156,7 @@ dependencies = {}) {
|
|
|
153
156
|
},
|
|
154
157
|
},
|
|
155
158
|
},
|
|
159
|
+
annotations: { openWorldHint: true, destructiveHint: false },
|
|
156
160
|
}, async (parsed) => {
|
|
157
161
|
const persisted = state.config.read()?.browser_agent;
|
|
158
162
|
// Deliberately NOT falling back to the persisted mode: which browser
|
|
@@ -14,6 +14,7 @@ import { ensureAttestorCrypto } from "../../../proof/proof-core.js";
|
|
|
14
14
|
import { analyzeTool } from "./analyze.js";
|
|
15
15
|
import { attachTool } from "./attach.js";
|
|
16
16
|
import { captureTools, resetCaptures } from "./capture.js";
|
|
17
|
+
import { checkAttestorTool } from "./check-attestor.js";
|
|
17
18
|
import { listTabsTool } from "./eval.js";
|
|
18
19
|
import { howItWorksTool } from "./how-it-works.js";
|
|
19
20
|
import { liveViewTools } from "./live-view.js";
|
|
@@ -21,6 +22,7 @@ import { podProofTools } from "./pod-proof.js";
|
|
|
21
22
|
import { createVersionFromCaptureTool } from "./publish.js";
|
|
22
23
|
import { resetAll, sessionTools } from "./session.js";
|
|
23
24
|
import { sharedAuthoringTools } from "./shared.js";
|
|
25
|
+
import { startIntegrationTool } from "./start-integration.js";
|
|
24
26
|
/** Exported so a mode (for example, old-devtools) that supplies its own
|
|
25
27
|
* publish tool can build the attach/capture state up front and share the SAME
|
|
26
28
|
* instance with `buildAgentTools` — letting that publish tool see the live
|
|
@@ -64,6 +66,7 @@ export function buildAgentTools(client, builderClient = client, externalState) {
|
|
|
64
66
|
tools.push(t);
|
|
65
67
|
}
|
|
66
68
|
tools.push(analyzeTool(state.captures));
|
|
69
|
+
tools.push(checkAttestorTool());
|
|
67
70
|
// navigate / eval_in_page / wait_for_page / get_cookies / list_requests /
|
|
68
71
|
// find_requests_containing / propose_provider / replay_request / run_proof —
|
|
69
72
|
// shared with the cloud loop, bound to each capture's local backend.
|
|
@@ -71,7 +74,8 @@ export function buildAgentTools(client, builderClient = client, externalState) {
|
|
|
71
74
|
tools.push(t);
|
|
72
75
|
}
|
|
73
76
|
if (client) {
|
|
74
|
-
tools.push(
|
|
77
|
+
tools.push(startIntegrationTool());
|
|
78
|
+
tools.push(createVersionFromCaptureTool(client, state.attach, state.captures));
|
|
75
79
|
}
|
|
76
80
|
return tools;
|
|
77
81
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DraftStore } from '../../../authoring/index.ts';
|
|
1
|
+
import { type DraftStore, type KeptScriptRef } from '../../../authoring/index.ts';
|
|
2
2
|
import { type CaptureSession } from '../../../cdp/capture.ts';
|
|
3
3
|
import { type RegisteredTool } from '../../server.ts';
|
|
4
4
|
import type { AttachState } from './attach.ts';
|
|
@@ -13,7 +13,7 @@ export interface CaptureEntry {
|
|
|
13
13
|
tabClient: Awaited<ReturnType<AttachState['handles']['attachToTab']>>;
|
|
14
14
|
drafts: DraftStore;
|
|
15
15
|
}
|
|
16
|
-
export interface CaptureStore {
|
|
16
|
+
export interface CaptureStore extends KeptScriptRef {
|
|
17
17
|
sessions: Map<string, CaptureEntry>;
|
|
18
18
|
activeTab?: string;
|
|
19
19
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { makeDraftStore } from "../../../authoring/index.js";
|
|
1
|
+
import { makeDraftStore, } from "../../../authoring/index.js";
|
|
2
2
|
import { startCapture } from "../../../cdp/capture.js";
|
|
3
3
|
import { defineTool } from "../../server.js";
|
|
4
4
|
export async function resetCaptures(captures) {
|
|
@@ -7,6 +7,7 @@ export async function resetCaptures(captures) {
|
|
|
7
7
|
}
|
|
8
8
|
captures.sessions.clear();
|
|
9
9
|
delete captures.activeTab;
|
|
10
|
+
delete captures.keptScript;
|
|
10
11
|
}
|
|
11
12
|
export function captureTools(attachRef, captures) {
|
|
12
13
|
const requireAttached = () => {
|
|
@@ -17,14 +18,15 @@ export function captureTools(attachRef, captures) {
|
|
|
17
18
|
};
|
|
18
19
|
const startTool = defineTool({
|
|
19
20
|
name: 'start_capture',
|
|
20
|
-
description: '
|
|
21
|
-
+ '
|
|
22
|
-
+ '
|
|
21
|
+
description: 'Starts recording network requests on a tab. Returns a captureId, '
|
|
22
|
+
+ 'the session handle that the authoring tools (`navigate`, '
|
|
23
|
+
+ '`propose_provider`, `run_proof`) take.',
|
|
23
24
|
inputSchema: {
|
|
24
25
|
type: 'object',
|
|
25
26
|
properties: { tabId: { type: 'string' } },
|
|
26
27
|
required: ['tabId'],
|
|
27
28
|
},
|
|
29
|
+
annotations: { openWorldHint: true },
|
|
28
30
|
}, async ({ tabId }) => {
|
|
29
31
|
const attached = requireAttached();
|
|
30
32
|
const tabClient = await attached.handles.attachToTab(tabId);
|
|
@@ -37,13 +39,14 @@ export function captureTools(attachRef, captures) {
|
|
|
37
39
|
});
|
|
38
40
|
const stopTool = defineTool({
|
|
39
41
|
name: 'stop_capture',
|
|
40
|
-
description: '
|
|
41
|
-
+ '
|
|
42
|
+
description: 'Stops recording on a capture. Captured requests stay queryable '
|
|
43
|
+
+ 'until `reset_session`.',
|
|
42
44
|
inputSchema: {
|
|
43
45
|
type: 'object',
|
|
44
46
|
properties: { captureId: { type: 'string' } },
|
|
45
47
|
required: ['captureId'],
|
|
46
48
|
},
|
|
49
|
+
annotations: { openWorldHint: false, idempotentHint: true },
|
|
47
50
|
}, async ({ captureId }) => {
|
|
48
51
|
const entry = captures.sessions.get(captureId);
|
|
49
52
|
if (!entry) {
|
|
@@ -54,11 +57,11 @@ export function captureTools(attachRef, captures) {
|
|
|
54
57
|
});
|
|
55
58
|
const getTool = defineTool({
|
|
56
59
|
name: 'get_request',
|
|
57
|
-
description: '
|
|
58
|
-
+ '
|
|
59
|
-
+ '
|
|
60
|
-
+ '
|
|
61
|
-
+ '
|
|
60
|
+
description: 'Returns the full headers and bodies of one captured request, to '
|
|
61
|
+
+ 'inspect a candidate that `find_requests_containing` ranked. Unlike '
|
|
62
|
+
+ 'the other tools, headers are raw: cookie and authorization values '
|
|
63
|
+
+ 'are visible. Read them for diagnosis; do not repeat them to the '
|
|
64
|
+
+ 'developer or write them to a file.',
|
|
62
65
|
inputSchema: {
|
|
63
66
|
type: 'object',
|
|
64
67
|
properties: {
|
|
@@ -67,6 +70,7 @@ export function captureTools(attachRef, captures) {
|
|
|
67
70
|
},
|
|
68
71
|
required: ['captureId', 'requestId'],
|
|
69
72
|
},
|
|
73
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
70
74
|
}, async ({ captureId, requestId }) => {
|
|
71
75
|
const entry = captures.sessions.get(captureId);
|
|
72
76
|
if (!entry) {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { checkAttestor, type CheckAttestorOptions } from '../../../proof/check-attestor.ts';
|
|
2
|
+
import { type RegisteredTool } from '../../server.ts';
|
|
3
|
+
/** `check_attestor`: proves a public request to show whether the attestor
|
|
4
|
+
* works from this machine. `check` is injectable so tests need no network. */
|
|
5
|
+
export declare function checkAttestorTool(check?: (opts: CheckAttestorOptions) => ReturnType<typeof checkAttestor>): RegisteredTool;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { checkAttestor, } from "../../../proof/check-attestor.js";
|
|
2
|
+
import { defineTool } from "../../server.js";
|
|
3
|
+
/** `check_attestor`: proves a public request to show whether the attestor
|
|
4
|
+
* works from this machine. `check` is injectable so tests need no network. */
|
|
5
|
+
export function checkAttestorTool(check = checkAttestor) {
|
|
6
|
+
return defineTool({
|
|
7
|
+
name: 'check_attestor',
|
|
8
|
+
description: 'Checks that the Reclaim attestor works from this machine by proving '
|
|
9
|
+
+ 'a public CoinGecko price request with a throwaway key. It uses no '
|
|
10
|
+
+ 'credential, provider, or logged-in site, and takes up to 45 '
|
|
11
|
+
+ 'seconds (about 8 when healthy). Call it once when you set up a '
|
|
12
|
+
+ 'new provider build, and after `run_proof` fails with a network '
|
|
13
|
+
+ 'error. Returns `{ ok, attestorUrl, durationMs, '
|
|
14
|
+
+ 'extractedParameters, error, errorKind }`. If `ok` is true, the '
|
|
15
|
+
+ 'attestor is fine and a failing proof points to the target site. '
|
|
16
|
+
+ 'If `errorKind` is `attestor-unreachable`, this machine cannot '
|
|
17
|
+
+ 'reach the attestor (network, VPN, firewall, or proxy): tell the '
|
|
18
|
+
+ 'developer. If it is `setup-failed`, the ZK file download failed '
|
|
19
|
+
+ 'before the attestor was contacted: tell the developer to check '
|
|
20
|
+
+ 'network access to the circuit download. If it is '
|
|
21
|
+
+ '`attestor-error`, the attestor rejected the claim: report '
|
|
22
|
+
+ '`error`.',
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
attestorUrl: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Attestor websocket URL. Set it only if the developer '
|
|
29
|
+
+ 'supplied one; the default is the public attestor.',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
annotations: { openWorldHint: true, readOnlyHint: true },
|
|
34
|
+
}, async ({ attestorUrl }) => {
|
|
35
|
+
return check(attestorUrl ? { attestorUrl } : {});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -5,10 +5,11 @@ import { defineTool } from "../../server.js";
|
|
|
5
5
|
export function listTabsTool(attachRef) {
|
|
6
6
|
return defineTool({
|
|
7
7
|
name: 'list_tabs',
|
|
8
|
-
description: '
|
|
9
|
-
+ 'and URL. Call it after attach_browser to pick the tabId
|
|
10
|
-
+ 'start_capture
|
|
8
|
+
description: 'Returns the open tabs of the attached browser, each with its tabId, '
|
|
9
|
+
+ 'title, and URL. Call it after `attach_browser` to pick the tabId '
|
|
10
|
+
+ 'for `start_capture`.',
|
|
11
11
|
inputSchema: { type: 'object', properties: {} },
|
|
12
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
12
13
|
}, async () => {
|
|
13
14
|
if (!attachRef.current) {
|
|
14
15
|
throw new Error('Not attached. Call attach_browser first.');
|
|
@@ -11,17 +11,16 @@ import { defineTool } from "../../../tools/registry.js";
|
|
|
11
11
|
export function findTool(resolveBackend) {
|
|
12
12
|
return defineTool({
|
|
13
13
|
name: 'find_requests_containing',
|
|
14
|
-
description: '
|
|
15
|
-
+ 'URL
|
|
16
|
-
+ '
|
|
17
|
-
+ '
|
|
18
|
-
+ '`value`
|
|
19
|
-
+ '
|
|
20
|
-
+ '
|
|
21
|
-
+ '
|
|
22
|
-
+ 'per-field (a redaction per part) rather than as one string. '
|
|
23
|
-
+ 'Server-side; bodies stay here.',
|
|
14
|
+
description: 'Searches captured responses for any encoding of `value` (raw, '
|
|
15
|
+
+ 'URL-, JSON-, or HTML-escaped). Returns ranked candidates with the '
|
|
16
|
+
+ 'matched variant, score, and a short snippet, to locate the '
|
|
17
|
+
+ 'request that returns a data point. Also finds split matches: when '
|
|
18
|
+
+ '`value` has several words (for example, a full name) and a '
|
|
19
|
+
+ 'response carries each word in a separate field, the candidate has '
|
|
20
|
+
+ '`splitMatch: true`; prove those per field (one redaction per '
|
|
21
|
+
+ 'part). Response bodies stay server-side.',
|
|
24
22
|
inputSchema: objectSchema({ value: { type: 'string' }, limit: { type: 'number' } }, ['value']),
|
|
23
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
25
24
|
}, async (args) => {
|
|
26
25
|
const backend = resolveBackend(args);
|
|
27
26
|
const value = String(args.value);
|
|
@@ -16,6 +16,7 @@ export function howItWorksTool(oldMode) {
|
|
|
16
16
|
+ 'backend. Call without a topic for the overview and index. Before an '
|
|
17
17
|
+ 'unfamiliar or sensitive step, request that topic. Prefer one topic; '
|
|
18
18
|
+ '`all` returns the much larger complete guide.',
|
|
19
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
19
20
|
inputSchema: {
|
|
20
21
|
type: 'object',
|
|
21
22
|
properties: {
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* attached container browser's built-in viewer behind a public URL, so someone
|
|
4
4
|
* who is not at this machine can watch it and optionally drive it.
|
|
5
5
|
*
|
|
6
|
-
* The server never runs, downloads or resolves cloudflared
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* The server never runs, downloads or resolves cloudflared. The developer
|
|
7
|
+
* chooses who runs it: they run it themselves (recommended) or approve the
|
|
8
|
+
* agent running it. Either way the `trycloudflare.com` URL it prints comes
|
|
9
|
+
* back as `publicUrl` to get the link to hand out.
|
|
9
10
|
*
|
|
10
11
|
* Scoped to `container` mode on purpose. A `builder`-mode browser already has
|
|
11
12
|
* a hosted live view at `attach_browser`'s `liveViewUrl`.
|
|
@@ -21,13 +22,19 @@ export function liveViewTools(attachRef) {
|
|
|
21
22
|
return [
|
|
22
23
|
defineTool({
|
|
23
24
|
name: 'share_browser_view',
|
|
24
|
-
description: '
|
|
25
|
-
+ '
|
|
26
|
-
+ '
|
|
27
|
-
+ '
|
|
28
|
-
+ '`
|
|
29
|
-
+ '
|
|
30
|
-
+ '
|
|
25
|
+
description: 'Returns a link that lets someone who is not at this machine watch '
|
|
26
|
+
+ 'and drive the attached `container` browser, for example to sign '
|
|
27
|
+
+ 'in. This server opens no tunnel itself. Step 1: call with no '
|
|
28
|
+
+ 'arguments. It returns the local viewer URL, the `cloudflared` '
|
|
29
|
+
+ 'command, and two `choices`. Ask the developer to pick one before '
|
|
30
|
+
+ 'any tunnel runs: (a, recommended) they run `! cloudflared tunnel '
|
|
31
|
+
+ '--url http://localhost:<port>` themselves and paste the printed '
|
|
32
|
+
+ '`https://*.trycloudflare.com` URL; (b) they explicitly approve '
|
|
33
|
+
+ 'you running the command in the background. Never install '
|
|
34
|
+
+ '`cloudflared` without approval. Step 2: call again with the URL '
|
|
35
|
+
+ 'as `publicUrl` to get the link to relay. The link grants access '
|
|
36
|
+
+ 'to that browser session only, so share it only with the person '
|
|
37
|
+
+ 'who needs it. For `builder`, use the `liveViewUrl` from '
|
|
31
38
|
+ '`attach_browser`.',
|
|
32
39
|
inputSchema: {
|
|
33
40
|
type: 'object',
|
|
@@ -39,6 +46,7 @@ export function liveViewTools(attachRef) {
|
|
|
39
46
|
},
|
|
40
47
|
},
|
|
41
48
|
},
|
|
49
|
+
annotations: { openWorldHint: true, idempotentHint: true },
|
|
42
50
|
}, async (parsed) => {
|
|
43
51
|
const attached = attachRef.current;
|
|
44
52
|
assert(attached, badRequest('No browser attached. Call attach_browser first.'));
|
|
@@ -72,27 +80,46 @@ export function liveViewTools(attachRef) {
|
|
|
72
80
|
+ 'separate window is visible too.',
|
|
73
81
|
'Opening it on a phone lays the page out for that phone, with '
|
|
74
82
|
+ 'touch, pinch-zoom and the soft keyboard.',
|
|
75
|
-
'When they are done, stop the cloudflared process
|
|
76
|
-
+ '
|
|
83
|
+
'When they are done, stop the cloudflared process: yourself if '
|
|
84
|
+
+ 'you started it, otherwise ask the developer. The browser '
|
|
85
|
+
+ 'keeps running until `dispose_browser`.',
|
|
77
86
|
],
|
|
78
87
|
};
|
|
79
88
|
}
|
|
80
89
|
const localUrl = `http://localhost:${viewerPort}`;
|
|
90
|
+
const command = `cloudflared tunnel --url ${localUrl}`;
|
|
81
91
|
return {
|
|
82
92
|
localViewerUrl: ensureLiveViewUrl(localUrl),
|
|
83
93
|
port: viewerPort,
|
|
84
|
-
command
|
|
94
|
+
command,
|
|
95
|
+
choices: [
|
|
96
|
+
{
|
|
97
|
+
id: 'developer-runs',
|
|
98
|
+
recommended: true,
|
|
99
|
+
action: `Run \`! ${command}\` in the prompt, then paste the `
|
|
100
|
+
+ 'https://*.trycloudflare.com URL it prints.',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: 'agent-runs',
|
|
104
|
+
recommended: false,
|
|
105
|
+
action: 'Approve the agent to run the command in the '
|
|
106
|
+
+ 'background and read the URL from its output.',
|
|
107
|
+
},
|
|
108
|
+
],
|
|
85
109
|
_notes: [
|
|
86
|
-
'
|
|
87
|
-
+ '
|
|
88
|
-
|
|
89
|
-
+ '
|
|
110
|
+
'Ask the developer to choose before any tunnel runs. Do not run '
|
|
111
|
+
+ '`command` until they pick `agent-runs`.',
|
|
112
|
+
'If they pick `developer-runs`, wait for the pasted URL. If they '
|
|
113
|
+
+ 'pick `agent-runs`, run `command` in a background process and '
|
|
114
|
+
+ 'read its output for a line containing '
|
|
115
|
+
+ '`https://<words>.trycloudflare.com`.',
|
|
116
|
+
'If `cloudflared` is not installed, tell the developer and ask '
|
|
117
|
+
+ 'them to install it '
|
|
118
|
+
+ '(https://developers.cloudflare.com/cloudflare-one/'
|
|
119
|
+
+ 'connections/connect-networks/downloads/). Never install it '
|
|
90
120
|
+ 'without their approval.',
|
|
91
|
-
'
|
|
92
|
-
+ '
|
|
93
|
-
'Call share_browser_view again with that value as `publicUrl` '
|
|
94
|
-
+ 'to get the link to relay. Never guess or reconstruct the '
|
|
95
|
-
+ 'hostname.',
|
|
121
|
+
'Call share_browser_view again with the URL as `publicUrl` to get '
|
|
122
|
+
+ 'the link to relay. Never guess or reconstruct the hostname.',
|
|
96
123
|
'Expose only this port. Never tunnel the browser\'s CDP port.',
|
|
97
124
|
],
|
|
98
125
|
};
|
|
@@ -22,17 +22,12 @@ import { shapeRichProofResult } from "./proof.js";
|
|
|
22
22
|
export function podProofTools(attachRef, captures) {
|
|
23
23
|
const tool = defineTool({
|
|
24
24
|
name: 'run_proof_via_pod',
|
|
25
|
-
description: '
|
|
26
|
-
+ '(/reclaim/prove) instead of the local attestor SDK
|
|
27
|
-
+ '
|
|
28
|
-
+ '
|
|
29
|
-
+ '
|
|
30
|
-
+ '
|
|
31
|
-
+ 'held the browser\'s TLS session (and its country-routed egress, '
|
|
32
|
-
+ 'if any), so '
|
|
33
|
-
+ 'the proof reaches the target from the browser\'s own exit IP. '
|
|
34
|
-
+ 'No ownerAddress needed — the pod signs with the attestor\'s '
|
|
35
|
-
+ 'own TEE key, not a local credential.',
|
|
25
|
+
description: 'Proves the drafted recipe through the remote browser pod\'s own '
|
|
26
|
+
+ 'TEE (/reclaim/prove) instead of the local attestor SDK. Use it '
|
|
27
|
+
+ 'instead of `run_proof` when the attach mode is "builder": the '
|
|
28
|
+
+ 'proof then reaches the target from the browser\'s own exit IP and '
|
|
29
|
+
+ 'country. Needs no `ownerAddress`; the pod signs with its own TEE '
|
|
30
|
+
+ 'key.',
|
|
36
31
|
inputSchema: {
|
|
37
32
|
type: 'object',
|
|
38
33
|
properties: {
|
|
@@ -41,6 +36,7 @@ export function podProofTools(attachRef, captures) {
|
|
|
41
36
|
},
|
|
42
37
|
required: ['captureId', 'draftId'],
|
|
43
38
|
},
|
|
39
|
+
annotations: { openWorldHint: true, destructiveHint: false },
|
|
44
40
|
}, async ({ captureId, draftId }) => {
|
|
45
41
|
const apiUrl = attachRef.current?.apiUrl;
|
|
46
42
|
assert(apiUrl, badRequest('No pod api available for this attach — run_proof_via_pod needs '
|