@reclaimprotocol/agent 0.1.5 → 0.1.6
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/lib/authoring/backend.d.ts +0 -24
- package/lib/authoring/find.d.ts +3 -0
- package/lib/authoring/find.js +54 -0
- package/lib/authoring/index.d.ts +3 -3
- package/lib/authoring/index.js +4 -3
- package/lib/authoring/propose.d.ts +3 -0
- package/lib/authoring/propose.js +109 -0
- package/lib/authoring/replay.d.ts +10 -0
- package/lib/authoring/replay.js +34 -0
- package/lib/authoring/tools.d.ts +13 -6
- package/lib/authoring/tools.js +108 -293
- package/lib/authoring/util.d.ts +27 -0
- package/lib/authoring/util.js +21 -0
- package/lib/consts.d.ts +6 -1
- package/lib/consts.js +9 -2
- package/lib/mcp/server.d.ts +2 -41
- package/lib/mcp/server.js +6 -34
- package/lib/mcp/start.js +4 -2
- package/lib/mcp/tools/agent/analyze.js +3 -3
- package/lib/mcp/tools/agent/build.d.ts +4 -4
- package/lib/mcp/tools/agent/build.js +13 -15
- package/lib/mcp/tools/agent/capture.d.ts +13 -1
- package/lib/mcp/tools/agent/capture.js +18 -85
- package/lib/mcp/tools/agent/eval.d.ts +5 -2
- package/lib/mcp/tools/agent/eval.js +11 -121
- package/lib/mcp/tools/agent/find.d.ts +10 -3
- package/lib/mcp/tools/agent/find.js +25 -31
- package/lib/mcp/tools/agent/proof.d.ts +13 -3
- package/lib/mcp/tools/agent/proof.js +81 -132
- package/lib/mcp/tools/agent/propose.d.ts +10 -9
- package/lib/mcp/tools/agent/propose.js +87 -64
- package/lib/mcp/tools/agent/replay.d.ts +10 -3
- package/lib/mcp/tools/agent/replay.js +29 -36
- package/lib/mcp/tools/agent/session.d.ts +1 -3
- package/lib/mcp/tools/agent/session.js +5 -5
- package/lib/mcp/tools/agent/shared.d.ts +5 -3
- package/lib/mcp/tools/agent/shared.js +29 -19
- package/lib/old/client.d.ts +35 -0
- package/lib/old/client.js +18 -0
- package/lib/old/login-cdp.d.ts +24 -5
- package/lib/old/login-cdp.js +29 -15
- package/lib/old/sdk-client.d.ts +39 -0
- package/lib/old/sdk-client.js +85 -0
- package/lib/old/to-register.d.ts +69 -0
- package/lib/old/to-register.js +52 -2
- package/lib/old/tools/authenticate.js +3 -7
- package/lib/old/tools/build.d.ts +6 -1
- package/lib/old/tools/build.js +11 -1
- package/lib/old/tools/get-provider-info.d.ts +16 -0
- package/lib/old/tools/get-provider-info.js +73 -0
- package/lib/old/tools/get-version-config.d.ts +17 -0
- package/lib/old/tools/get-version-config.js +47 -0
- package/lib/old/tools/publish.js +189 -2
- package/lib/old/tools/update-provider-metadata.d.ts +16 -0
- package/lib/old/tools/update-provider-metadata.js +61 -0
- package/lib/proof/attestor.d.ts +6 -3
- package/lib/proof/attestor.js +10 -1
- package/lib/proof/debug-response-dump.d.ts +4 -0
- package/lib/proof/debug-response-dump.js +55 -0
- package/lib/proof/patch-domhandler.d.ts +1 -0
- package/lib/proof/patch-domhandler.js +59 -0
- package/lib/proof/zk-files.js +7 -6
- package/lib/provider/dom-xpath.d.ts +17 -0
- package/lib/provider/dom-xpath.js +187 -0
- package/lib/provider/draft.d.ts +30 -2
- package/lib/provider/draft.js +117 -28
- package/lib/provider/index.d.ts +4 -0
- package/lib/provider/index.js +7 -0
- package/lib/provider/injection-doc.js +6 -0
- package/lib/provider/matcher.d.ts +17 -20
- package/lib/provider/matcher.js +394 -280
- package/lib/provider/schema.d.ts +7 -5
- package/lib/tools/registry.d.ts +14 -0
- package/lib/tools/registry.js +32 -0
- package/package.json +9 -1
- package/skill/SKILL.builder.md +42 -23
- package/skill/SKILL.old.md +6 -2
- package/skill/builder-mode.md +1 -1
- package/skill/injection-api.md +117 -0
- package/src/authoring/backend.ts +98 -0
- package/src/authoring/index.ts +20 -0
- package/src/authoring/tools.ts +273 -0
- package/src/authoring/util.ts +42 -0
- package/src/consts.ts +15 -2
- package/src/mcp/server.ts +11 -78
- package/src/mcp/start.ts +4 -1
- package/src/mcp/tools/agent/analyze.ts +3 -3
- package/src/mcp/tools/agent/build.ts +13 -21
- package/src/mcp/tools/agent/capture.ts +31 -115
- package/src/mcp/tools/agent/eval.ts +19 -155
- package/src/mcp/tools/agent/find.ts +33 -36
- package/src/mcp/tools/agent/proof.ts +87 -160
- package/src/mcp/tools/agent/propose.ts +93 -85
- package/src/mcp/tools/agent/replay.ts +32 -42
- package/src/mcp/tools/agent/session.ts +4 -5
- package/src/mcp/tools/agent/shared.ts +152 -0
- package/src/old/client.ts +56 -0
- package/src/old/login-cdp.ts +29 -23
- package/src/old/sdk-client.ts +105 -0
- package/src/old/to-register.ts +98 -6
- package/src/old/tools/authenticate.ts +3 -11
- package/src/old/tools/build.ts +12 -0
- package/src/old/tools/get-provider-info.ts +91 -0
- package/src/old/tools/get-version-config.ts +56 -0
- package/src/old/tools/publish.ts +244 -1
- package/src/old/tools/update-provider-metadata.ts +72 -0
- package/src/proof/attestor.ts +16 -4
- package/src/proof/debug-response-dump.ts +70 -0
- package/src/proof/patch-domhandler.ts +68 -0
- package/src/proof/zk-files.ts +7 -6
- package/src/provider/dom-xpath.ts +225 -0
- package/src/provider/draft.ts +168 -35
- package/src/provider/index.ts +8 -0
- package/src/provider/injection-doc.ts +6 -0
- package/src/provider/matcher.ts +477 -357
- package/src/provider/schema.ts +7 -5
- package/src/tools/registry.ts +92 -0
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
import { type DraftStore, makeDraftStore } from '../../../authoring/index.ts'
|
|
1
2
|
import { type CaptureSession, startCapture } from '../../../cdp/capture.ts'
|
|
2
3
|
import { defineTool, type RegisteredTool } from '../../server.ts'
|
|
3
4
|
import type { AttachState } from './attach.ts'
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* One capture == a session (network requests) + the tab CDP client it runs on +
|
|
8
|
+
* a draft store. Bundling the tab client is what lets the shared authoring
|
|
9
|
+
* tools treat a `captureId` as a full session (drive the browser AND read
|
|
10
|
+
* capture AND hold drafts) behind one id.
|
|
11
|
+
*/
|
|
12
|
+
export interface CaptureEntry {
|
|
13
|
+
session: CaptureSession
|
|
14
|
+
tabClient: Awaited<ReturnType<AttachState['handles']['attachToTab']>>
|
|
15
|
+
drafts: DraftStore
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
export interface CaptureStore {
|
|
6
|
-
sessions: Map<string,
|
|
19
|
+
sessions: Map<string, CaptureEntry>
|
|
7
20
|
activeTab?: string
|
|
8
21
|
}
|
|
9
22
|
|
|
@@ -23,8 +36,9 @@ export function captureTools(
|
|
|
23
36
|
{
|
|
24
37
|
name: 'start_capture',
|
|
25
38
|
description:
|
|
26
|
-
'Begin recording network requests on the specified tab. '
|
|
27
|
-
'Returns a captureId
|
|
39
|
+
'Begin recording network requests on the specified tab. '
|
|
40
|
+
+ 'Returns a captureId — the session handle the authoring tools '
|
|
41
|
+
+ '(navigate, propose_provider, run_proof, …) all take.',
|
|
28
42
|
inputSchema: {
|
|
29
43
|
type: 'object',
|
|
30
44
|
properties: { tabId: { type: 'string' } },
|
|
@@ -35,7 +49,9 @@ export function captureTools(
|
|
|
35
49
|
const attached = requireAttached()
|
|
36
50
|
const tabClient = await attached.handles.attachToTab(tabId)
|
|
37
51
|
const session = await startCapture(tabClient)
|
|
38
|
-
captures.sessions.set(session.captureId,
|
|
52
|
+
captures.sessions.set(session.captureId, {
|
|
53
|
+
session, tabClient, drafts: makeDraftStore(),
|
|
54
|
+
})
|
|
39
55
|
captures.activeTab = tabId
|
|
40
56
|
return { captureId: session.captureId }
|
|
41
57
|
},
|
|
@@ -45,8 +61,8 @@ export function captureTools(
|
|
|
45
61
|
{
|
|
46
62
|
name: 'stop_capture',
|
|
47
63
|
description:
|
|
48
|
-
'Stop recording on the given capture. '
|
|
49
|
-
'Captured data remains queryable until reset_session.',
|
|
64
|
+
'Stop recording on the given capture. '
|
|
65
|
+
+ 'Captured data remains queryable until reset_session.',
|
|
50
66
|
inputSchema: {
|
|
51
67
|
type: 'object',
|
|
52
68
|
properties: { captureId: { type: 'string' } },
|
|
@@ -54,104 +70,13 @@ export function captureTools(
|
|
|
54
70
|
},
|
|
55
71
|
},
|
|
56
72
|
async({ captureId }) => {
|
|
57
|
-
const
|
|
58
|
-
if(!
|
|
73
|
+
const entry = captures.sessions.get(captureId)
|
|
74
|
+
if(!entry) {
|
|
59
75
|
throw new Error(`no capture ${captureId}`)
|
|
60
76
|
}
|
|
61
77
|
|
|
62
|
-
await session.stop()
|
|
63
|
-
return { stopped: true, requestCount: session.requests.size }
|
|
64
|
-
},
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
const listTool = defineTool<{
|
|
68
|
-
captureId: string
|
|
69
|
-
filter?: {
|
|
70
|
-
method?: string
|
|
71
|
-
urlGlob?: string
|
|
72
|
-
statusMin?: number
|
|
73
|
-
statusMax?: number
|
|
74
|
-
contentType?: string
|
|
75
|
-
graphqlOp?: string
|
|
76
|
-
}
|
|
77
|
-
}>(
|
|
78
|
-
{
|
|
79
|
-
name: 'list_requests',
|
|
80
|
-
description:
|
|
81
|
-
'List metadata for captured requests. ' +
|
|
82
|
-
'Filter by method, URL glob, status, content-type, ' +
|
|
83
|
-
'or graphqlOp. Returns only metadata, never bodies — ' +
|
|
84
|
-
'use get_request for those.',
|
|
85
|
-
inputSchema: {
|
|
86
|
-
type: 'object',
|
|
87
|
-
properties: {
|
|
88
|
-
captureId: { type: 'string' },
|
|
89
|
-
filter: {
|
|
90
|
-
type: 'object',
|
|
91
|
-
properties: {
|
|
92
|
-
method: { type: 'string' },
|
|
93
|
-
urlGlob: { type: 'string' },
|
|
94
|
-
statusMin: { type: 'number' },
|
|
95
|
-
statusMax: { type: 'number' },
|
|
96
|
-
contentType: { type: 'string' },
|
|
97
|
-
graphqlOp: { type: 'string' },
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
required: ['captureId'],
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
async({ captureId, filter }) => {
|
|
105
|
-
const session = captures.sessions.get(captureId)
|
|
106
|
-
if(!session) {
|
|
107
|
-
throw new Error(`no capture ${captureId}`)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const out = []
|
|
111
|
-
for(const r of session.requests.values()) {
|
|
112
|
-
if(filter?.method && r.method !== filter.method.toUpperCase()) {
|
|
113
|
-
continue
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if(filter?.urlGlob && !globMatch(filter.urlGlob, r.url)) {
|
|
117
|
-
continue
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if(filter?.statusMin !== undefined && r.status < filter.statusMin) {
|
|
121
|
-
continue
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if(filter?.statusMax !== undefined && r.status > filter.statusMax) {
|
|
125
|
-
continue
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if(
|
|
129
|
-
filter?.contentType &&
|
|
130
|
-
!r.contentType.includes(filter.contentType)
|
|
131
|
-
) {
|
|
132
|
-
continue
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if(filter?.graphqlOp && r.graphqlOp !== filter.graphqlOp) {
|
|
136
|
-
continue
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const item: Record<string, unknown> = {
|
|
140
|
-
requestId: r.requestId,
|
|
141
|
-
method: r.method,
|
|
142
|
-
url: r.url,
|
|
143
|
-
status: r.status,
|
|
144
|
-
contentType: r.contentType,
|
|
145
|
-
size: r.size,
|
|
146
|
-
}
|
|
147
|
-
if(r.graphqlOp !== undefined) {
|
|
148
|
-
item.graphqlOp = r.graphqlOp
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
out.push(item)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return { requests: out }
|
|
78
|
+
await entry.session.stop()
|
|
79
|
+
return { stopped: true, requestCount: entry.session.requests.size }
|
|
155
80
|
},
|
|
156
81
|
)
|
|
157
82
|
|
|
@@ -170,17 +95,17 @@ export function captureTools(
|
|
|
170
95
|
},
|
|
171
96
|
},
|
|
172
97
|
async({ captureId, requestId }) => {
|
|
173
|
-
const
|
|
174
|
-
if(!
|
|
98
|
+
const entry = captures.sessions.get(captureId)
|
|
99
|
+
if(!entry) {
|
|
175
100
|
throw new Error(`no capture ${captureId}`)
|
|
176
101
|
}
|
|
177
102
|
|
|
178
|
-
const r = session.requests.get(requestId)
|
|
103
|
+
const r = entry.session.requests.get(requestId)
|
|
179
104
|
if(!r) {
|
|
180
105
|
throw new Error(`no request ${requestId} in capture ${captureId}`)
|
|
181
106
|
}
|
|
182
107
|
|
|
183
|
-
const body = session.bodies.get(requestId)
|
|
108
|
+
const body = entry.session.bodies.get(requestId)
|
|
184
109
|
if(!body.available) {
|
|
185
110
|
return { ...r, bodyAvailable: false, reason: body.reason }
|
|
186
111
|
}
|
|
@@ -189,14 +114,5 @@ export function captureTools(
|
|
|
189
114
|
},
|
|
190
115
|
)
|
|
191
116
|
|
|
192
|
-
return [startTool, stopTool,
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function globMatch(pattern: string, str: string): boolean {
|
|
196
|
-
const re = new RegExp(
|
|
197
|
-
'^' +
|
|
198
|
-
pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') +
|
|
199
|
-
'$',
|
|
200
|
-
)
|
|
201
|
-
return re.test(str)
|
|
117
|
+
return [startTool, stopTool, getTool]
|
|
202
118
|
}
|
|
@@ -2,160 +2,24 @@ import { listTabs } from '../../../cdp/tabs.ts'
|
|
|
2
2
|
import { defineTool, type RegisteredTool } from '../../server.ts'
|
|
3
3
|
import type { AttachState } from './attach.ts'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/** The one browser tool that isn't session-bound: enumerate tabs so the caller
|
|
6
|
+
* can pick one to `start_capture` on. Navigate/eval/wait are the shared
|
|
7
|
+
* authoring tools (keyed by captureId — see `shared.ts`). */
|
|
8
|
+
export function listTabsTool(attachRef: {
|
|
6
9
|
current?: AttachState
|
|
7
|
-
}): RegisteredTool
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
},
|
|
23
|
-
async() => {
|
|
24
|
-
const attached = requireAttached()
|
|
25
|
-
return { tabs: await listTabs(attached.port) }
|
|
26
|
-
},
|
|
27
|
-
),
|
|
28
|
-
defineTool<{ tabId: string, expression: string }>(
|
|
29
|
-
{
|
|
30
|
-
name: 'eval_in_page',
|
|
31
|
-
description:
|
|
32
|
-
'Run a JS expression in a tab via Runtime.evaluate ' +
|
|
33
|
-
'and return the result. ' +
|
|
34
|
-
'Useful for reading visible values from the DOM.',
|
|
35
|
-
inputSchema: {
|
|
36
|
-
type: 'object',
|
|
37
|
-
properties: {
|
|
38
|
-
tabId: { type: 'string' },
|
|
39
|
-
expression: { type: 'string' },
|
|
40
|
-
},
|
|
41
|
-
required: ['tabId', 'expression'],
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
async({ tabId, expression }) => {
|
|
45
|
-
const attached = requireAttached()
|
|
46
|
-
const { Runtime } = await attached.handles.attachToTab(tabId)
|
|
47
|
-
const result = await Runtime.evaluate({
|
|
48
|
-
expression,
|
|
49
|
-
returnByValue: true,
|
|
50
|
-
})
|
|
51
|
-
if(result.exceptionDetails) {
|
|
52
|
-
return { error: result.exceptionDetails.text }
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return { value: result.result.value }
|
|
56
|
-
},
|
|
57
|
-
),
|
|
58
|
-
defineTool<{ tabId: string, url: string, timeoutMs?: number }>(
|
|
59
|
-
{
|
|
60
|
-
name: 'navigate',
|
|
61
|
-
description:
|
|
62
|
-
'Navigate a tab to a URL via Page.navigate, ' +
|
|
63
|
-
'waiting for the load event. ' +
|
|
64
|
-
'Use this to drive the browser yourself ' +
|
|
65
|
-
'instead of asking the developer to navigate. ' +
|
|
66
|
-
'Returns the final URL after navigation ' +
|
|
67
|
-
'(which may differ from the requested URL due to redirects).',
|
|
68
|
-
inputSchema: {
|
|
69
|
-
type: 'object',
|
|
70
|
-
properties: {
|
|
71
|
-
tabId: { type: 'string' },
|
|
72
|
-
url: { type: 'string' },
|
|
73
|
-
timeoutMs: { type: 'number' },
|
|
74
|
-
},
|
|
75
|
-
required: ['tabId', 'url'],
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
async({ tabId, url, timeoutMs }) => {
|
|
79
|
-
const attached = requireAttached()
|
|
80
|
-
const { Page, Runtime } = await attached.handles.attachToTab(tabId)
|
|
81
|
-
|
|
82
|
-
await Page.enable()
|
|
83
|
-
const loadPromise = new Promise<void>((resolve) => {
|
|
84
|
-
Page.loadEventFired(() => resolve())
|
|
85
|
-
})
|
|
86
|
-
const nav = await Page.navigate({ url })
|
|
87
|
-
if(nav.errorText) {
|
|
88
|
-
return { ok: false, error: nav.errorText }
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const t = timeoutMs ?? 30_000
|
|
92
|
-
const raced = await Promise.race([
|
|
93
|
-
loadPromise.then(() => 'loaded' as const),
|
|
94
|
-
new Promise<'timeout'>(
|
|
95
|
-
(resolve) => setTimeout(() => resolve('timeout'), t),
|
|
96
|
-
),
|
|
97
|
-
])
|
|
98
|
-
const final = await Runtime.evaluate({
|
|
99
|
-
expression: 'location.href',
|
|
100
|
-
returnByValue: true,
|
|
101
|
-
})
|
|
102
|
-
return {
|
|
103
|
-
ok: true,
|
|
104
|
-
loadStatus: raced,
|
|
105
|
-
finalUrl: final.result.value,
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
),
|
|
109
|
-
defineTool<{
|
|
110
|
-
tabId: string
|
|
111
|
-
expression: string
|
|
112
|
-
timeoutMs?: number
|
|
113
|
-
pollMs?: number
|
|
114
|
-
}>(
|
|
115
|
-
{
|
|
116
|
-
name: 'wait_for_page',
|
|
117
|
-
description:
|
|
118
|
-
'Poll a JS expression in a tab until it ' +
|
|
119
|
-
'returns truthy (or timeout). ' +
|
|
120
|
-
'Use this for steps that require the ' +
|
|
121
|
-
'developer to do something in the browser — ' +
|
|
122
|
-
'most commonly waiting for them to finish logging in. ' +
|
|
123
|
-
'Pass an expression that ' +
|
|
124
|
-
'evaluates true once the desired state is reached, e.g. ' +
|
|
125
|
-
'`!!document.querySelector' +
|
|
126
|
-
'(\'summary[aria-label*="View profile"]\')`' +
|
|
127
|
-
' for GitHub post-login.',
|
|
128
|
-
inputSchema: {
|
|
129
|
-
type: 'object',
|
|
130
|
-
properties: {
|
|
131
|
-
tabId: { type: 'string' },
|
|
132
|
-
expression: { type: 'string' },
|
|
133
|
-
timeoutMs: { type: 'number' },
|
|
134
|
-
pollMs: { type: 'number' },
|
|
135
|
-
},
|
|
136
|
-
required: ['tabId', 'expression'],
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
async({ tabId, expression, timeoutMs, pollMs }) => {
|
|
140
|
-
const attached = requireAttached()
|
|
141
|
-
const { Runtime } = await attached.handles.attachToTab(tabId)
|
|
142
|
-
const deadline = Date.now() + (timeoutMs ?? 120_000)
|
|
143
|
-
const interval = pollMs ?? 1000
|
|
144
|
-
while(Date.now() < deadline) {
|
|
145
|
-
const r = await Runtime.evaluate({ expression, returnByValue: true })
|
|
146
|
-
if(r.exceptionDetails) {
|
|
147
|
-
return { matched: false, error: r.exceptionDetails.text }
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if(r.result.value) {
|
|
151
|
-
return { matched: true, value: r.result.value }
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
await new Promise((resolve) => setTimeout(resolve, interval))
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return { matched: false, reason: 'timeout' }
|
|
158
|
-
},
|
|
159
|
-
),
|
|
160
|
-
]
|
|
10
|
+
}): RegisteredTool {
|
|
11
|
+
return defineTool<Record<string, never>>(
|
|
12
|
+
{
|
|
13
|
+
name: 'list_tabs',
|
|
14
|
+
description: 'List open Chrome tabs.',
|
|
15
|
+
inputSchema: { type: 'object', properties: {} },
|
|
16
|
+
},
|
|
17
|
+
async() => {
|
|
18
|
+
if(!attachRef.current) {
|
|
19
|
+
throw new Error('Not attached. Call attach_chrome first.')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { tabs: await listTabs(attachRef.current.port) }
|
|
23
|
+
},
|
|
24
|
+
)
|
|
161
25
|
}
|
|
@@ -1,36 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `find_requests_containing` — search captured responses for any encoding of a
|
|
3
|
+
* value and rank the requests that return it. Authored once as a plain
|
|
4
|
+
* {@link RegisteredTool}; the MCP and cloud loop each pass their own backend
|
|
5
|
+
* resolver (see authoring/tools.ts / shared.ts). Server-side: response bodies
|
|
6
|
+
* stay here, only metadata + a short snippet leave.
|
|
7
|
+
*/
|
|
8
|
+
import {
|
|
9
|
+
objectSchema,
|
|
10
|
+
publicRequest,
|
|
11
|
+
type ResolveBackend,
|
|
12
|
+
} from '../../../authoring/util.ts'
|
|
1
13
|
import { normalizeCandidates } from '../../../provider/normalize.ts'
|
|
2
|
-
import { defineTool, type RegisteredTool } from '
|
|
3
|
-
import type { CaptureStore } from './capture.ts'
|
|
14
|
+
import { defineTool, type RegisteredTool } from '../../../tools/registry.ts'
|
|
4
15
|
|
|
5
|
-
export function findTool(
|
|
6
|
-
return defineTool<
|
|
16
|
+
export function findTool(resolveBackend: ResolveBackend): RegisteredTool {
|
|
17
|
+
return defineTool<Record<string, unknown>>(
|
|
7
18
|
{
|
|
8
19
|
name: 'find_requests_containing',
|
|
9
20
|
description:
|
|
10
|
-
'Search captured responses for any encoding of `value
|
|
11
|
-
'Returns a ranked candidate list '
|
|
12
|
-
'
|
|
13
|
-
'Server-side; bodies stay here.',
|
|
14
|
-
inputSchema:
|
|
15
|
-
type: '
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
value: { type: 'string' },
|
|
19
|
-
limit: { type: 'number' },
|
|
20
|
-
},
|
|
21
|
-
required: ['captureId', 'value'],
|
|
22
|
-
},
|
|
21
|
+
'Search captured responses for any encoding of `value` (raw, '
|
|
22
|
+
+ 'URL/JSON/HTML-escaped, …). Returns a ranked candidate list with '
|
|
23
|
+
+ 'the matched variant, score, and a short snippet — used to locate '
|
|
24
|
+
+ 'the request that returns a target. Server-side; bodies stay here.',
|
|
25
|
+
inputSchema: objectSchema(
|
|
26
|
+
{ value: { type: 'string' }, limit: { type: 'number' } },
|
|
27
|
+
['value'],
|
|
28
|
+
),
|
|
23
29
|
},
|
|
24
|
-
async(
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
throw new Error(`no capture ${captureId}`)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const variants = normalizeCandidates(value)
|
|
31
|
-
|
|
30
|
+
async(args) => {
|
|
31
|
+
const backend = resolveBackend(args)
|
|
32
|
+
const variants = normalizeCandidates(String(args.value))
|
|
32
33
|
const ranked = []
|
|
33
|
-
for(const r of
|
|
34
|
+
for(const r of backend.capture.requests.values()) {
|
|
34
35
|
if(!r.responseBody) {
|
|
35
36
|
continue
|
|
36
37
|
}
|
|
@@ -50,16 +51,11 @@ export function findTool(captures: CaptureStore): RegisteredTool {
|
|
|
50
51
|
continue
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
//
|
|
54
|
-
const score =
|
|
55
|
-
|
|
56
|
-
Math.max(0, 5 - Math.log10(Math.max(r.size, 1)))
|
|
54
|
+
// Prefer JSON responses + smaller bodies (likely API endpoints).
|
|
55
|
+
const score = (r.contentType.includes('json') ? 10 : 0)
|
|
56
|
+
+ Math.max(0, 5 - Math.log10(Math.max(r.size, 1)))
|
|
57
57
|
ranked.push({
|
|
58
|
-
|
|
59
|
-
url: r.url,
|
|
60
|
-
method: r.method,
|
|
61
|
-
status: r.status,
|
|
62
|
-
contentType: r.contentType,
|
|
58
|
+
...publicRequest(r),
|
|
63
59
|
matchedVariant: hit.variant,
|
|
64
60
|
snippet: hit.snippet,
|
|
65
61
|
score,
|
|
@@ -67,7 +63,8 @@ export function findTool(captures: CaptureStore): RegisteredTool {
|
|
|
67
63
|
}
|
|
68
64
|
|
|
69
65
|
ranked.sort((a, b) => b.score - a.score)
|
|
70
|
-
|
|
66
|
+
const limit = Number(args.limit) || 10
|
|
67
|
+
return { candidates: ranked.slice(0, limit) }
|
|
71
68
|
},
|
|
72
69
|
)
|
|
73
70
|
}
|