@downcity/plugins 1.0.248 → 1.0.253
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/bin/image/ImagePlugin.d.ts +1 -1
- package/bin/image/ImagePlugin.d.ts.map +1 -1
- package/bin/image/ImagePlugin.js +23 -8
- package/bin/image/ImagePlugin.js.map +1 -1
- package/bin/image/runtime/ImageResultStorage.d.ts +14 -0
- package/bin/image/runtime/ImageResultStorage.d.ts.map +1 -0
- package/bin/image/runtime/ImageResultStorage.js +167 -0
- package/bin/image/runtime/ImageResultStorage.js.map +1 -0
- package/bin/image/types/ImagePlugin.d.ts +22 -0
- package/bin/image/types/ImagePlugin.d.ts.map +1 -1
- package/bin/web/Plugin.d.ts +25 -30
- package/bin/web/Plugin.d.ts.map +1 -1
- package/bin/web/Plugin.js +254 -110
- package/bin/web/Plugin.js.map +1 -1
- package/bin/web/providers/ComputerUseBrowserProviderAdapter.d.ts +35 -0
- package/bin/web/providers/ComputerUseBrowserProviderAdapter.d.ts.map +1 -0
- package/bin/web/providers/ComputerUseBrowserProviderAdapter.js +62 -0
- package/bin/web/providers/ComputerUseBrowserProviderAdapter.js.map +1 -0
- package/bin/web/providers/PlaywrightBrowserProvider.d.ts +43 -0
- package/bin/web/providers/PlaywrightBrowserProvider.d.ts.map +1 -0
- package/bin/web/providers/PlaywrightBrowserProvider.js +184 -0
- package/bin/web/providers/PlaywrightBrowserProvider.js.map +1 -0
- package/bin/web/providers/SemanticBrowserProviderAdapter.d.ts +39 -0
- package/bin/web/providers/SemanticBrowserProviderAdapter.d.ts.map +1 -0
- package/bin/web/providers/SemanticBrowserProviderAdapter.js +61 -0
- package/bin/web/providers/SemanticBrowserProviderAdapter.js.map +1 -0
- package/bin/web/types/ComputerUseBrowserProviderAdapter.d.ts +43 -0
- package/bin/web/types/ComputerUseBrowserProviderAdapter.d.ts.map +1 -0
- package/bin/web/types/ComputerUseBrowserProviderAdapter.js +8 -0
- package/bin/web/types/ComputerUseBrowserProviderAdapter.js.map +1 -0
- package/bin/web/types/PlaywrightBrowserProvider.d.ts +17 -0
- package/bin/web/types/PlaywrightBrowserProvider.d.ts.map +1 -0
- package/bin/web/types/PlaywrightBrowserProvider.js +7 -0
- package/bin/web/types/PlaywrightBrowserProvider.js.map +1 -0
- package/bin/web/types/SemanticBrowserProviderAdapter.d.ts +19 -0
- package/bin/web/types/SemanticBrowserProviderAdapter.d.ts.map +1 -0
- package/bin/web/types/SemanticBrowserProviderAdapter.js +8 -0
- package/bin/web/types/SemanticBrowserProviderAdapter.js.map +1 -0
- package/bin/web/types/WebPlugin.d.ts +213 -63
- package/bin/web/types/WebPlugin.d.ts.map +1 -1
- package/bin/web/types/WebPlugin.js +23 -11
- package/bin/web/types/WebPlugin.js.map +1 -1
- package/bin/web.d.ts +9 -3
- package/bin/web.d.ts.map +1 -1
- package/bin/web.js +5 -2
- package/bin/web.js.map +1 -1
- package/package.json +4 -3
- package/scripts/web-plugin.test.mjs +195 -57
- package/src/image/ImagePlugin.ts +23 -8
- package/src/image/runtime/ImageResultStorage.ts +198 -0
- package/src/image/types/ImagePlugin.ts +24 -0
- package/src/web/Plugin.ts +289 -125
- package/src/web/providers/ComputerUseBrowserProviderAdapter.ts +93 -0
- package/src/web/providers/PlaywrightBrowserProvider.ts +239 -0
- package/src/web/providers/SemanticBrowserProviderAdapter.ts +92 -0
- package/src/web/types/ComputerUseBrowserProviderAdapter.ts +58 -0
- package/src/web/types/PlaywrightBrowserProvider.ts +17 -0
- package/src/web/types/SemanticBrowserProviderAdapter.ts +30 -0
- package/src/web/types/WebPlugin.ts +236 -63
- package/src/web.ts +30 -8
- package/bin/web/PROMPT.d.ts +0 -7
- package/bin/web/PROMPT.d.ts.map +0 -1
- package/bin/web/PROMPT.js +0 -8
- package/bin/web/PROMPT.js.map +0 -1
- package/bin/web/WebPromptAssets.d.ts +0 -12
- package/bin/web/WebPromptAssets.d.ts.map +0 -1
- package/bin/web/WebPromptAssets.js +0 -13
- package/bin/web/WebPromptAssets.js.map +0 -1
- package/bin/web/runtime/Prompt.d.ts +0 -14
- package/bin/web/runtime/Prompt.d.ts.map +0 -1
- package/bin/web/runtime/Prompt.js +0 -77
- package/bin/web/runtime/Prompt.js.map +0 -1
- package/src/web/PROMPT.ts +0 -9
- package/src/web/PROMPT.ts.txt +0 -61
- package/src/web/WebPromptAssets.ts +0 -14
- package/src/web/runtime/Prompt.ts +0 -99
package/src/web/Plugin.ts
CHANGED
|
@@ -1,151 +1,315 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WebPlugin
|
|
2
|
+
* WebPlugin:provider-neutral 的联网与浏览器能力边界。
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* - 具体执行能力由当前 agent 已注册的 tools、skills 或外部 plugin 决定。
|
|
5
|
+
* - Plugin 只编排已显式注入的 provider,不选择云厂商或模型。
|
|
6
|
+
* - 搜索、文档读取与浏览器 session 是三个独立能力。
|
|
7
|
+
* - 浏览器长期资源由 provider 拥有,并在 Plugin lifecycle.stop 时统一释放。
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
|
-
import { BasePlugin } from "@downcity/agent";
|
|
12
|
-
import {
|
|
10
|
+
import { BasePlugin, create_action } from "@downcity/agent";
|
|
11
|
+
import type { JsonObject, PluginActionResult } from "@downcity/agent";
|
|
13
12
|
import { z } from "zod";
|
|
14
|
-
import type {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
import type {
|
|
14
|
+
BrowserActInput,
|
|
15
|
+
BrowserCloseSessionInput,
|
|
16
|
+
BrowserCreateSessionInput,
|
|
17
|
+
BrowserExtractInput,
|
|
18
|
+
BrowserObserveInput,
|
|
19
|
+
BrowserSemanticActInput,
|
|
20
|
+
BrowserSemanticExtractInput,
|
|
21
|
+
WebOpenInput,
|
|
22
|
+
WebPluginOptions,
|
|
23
|
+
WebSearchInput,
|
|
24
|
+
} from "@/web/types/WebPlugin.js";
|
|
19
25
|
import { WEB_PLUGIN_ACTIONS } from "@/web/types/WebPlugin.js";
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
function get_string_opt(
|
|
25
|
-
opts: Record<string, JsonValue>,
|
|
26
|
-
key: string,
|
|
27
|
-
): string | undefined {
|
|
28
|
-
const value = opts[key];
|
|
29
|
-
if (typeof value !== "string") return undefined;
|
|
30
|
-
const trimmed = value.trim();
|
|
31
|
-
return trimmed || undefined;
|
|
32
|
-
}
|
|
27
|
+
const URL_SCHEMA = z.string().url();
|
|
28
|
+
const SESSION_ID_SCHEMA = z.string().trim().min(1);
|
|
29
|
+
const MAX_CHARS_SCHEMA = z.number().int().min(1).max(100_000).optional();
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
31
|
+
const BROWSER_ACTION_SCHEMA = z.discriminatedUnion("type", [
|
|
32
|
+
z.object({ type: z.literal("goto"), url: URL_SCHEMA }),
|
|
33
|
+
z.object({ type: z.literal("click"), selector: z.string().trim().min(1) }),
|
|
34
|
+
z.object({
|
|
35
|
+
type: z.literal("fill"),
|
|
36
|
+
selector: z.string().trim().min(1),
|
|
37
|
+
value: z.string(),
|
|
38
|
+
}),
|
|
39
|
+
z.object({
|
|
40
|
+
type: z.literal("press"),
|
|
41
|
+
selector: z.string().trim().min(1),
|
|
42
|
+
key: z.string().trim().min(1),
|
|
43
|
+
}),
|
|
44
|
+
z.object({
|
|
45
|
+
type: z.literal("scroll"),
|
|
46
|
+
x: z.number().optional(),
|
|
47
|
+
y: z.number().optional(),
|
|
48
|
+
}),
|
|
49
|
+
z.object({
|
|
50
|
+
type: z.literal("wait"),
|
|
51
|
+
milliseconds: z.number().int().min(0).max(60_000).optional(),
|
|
52
|
+
}),
|
|
53
|
+
]);
|
|
46
54
|
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
opts: Record<string, JsonValue>,
|
|
52
|
-
): "user" | "project" | undefined {
|
|
53
|
-
const value = get_string_opt(opts, "scope");
|
|
54
|
-
if (value === "user" || value === "project") return value;
|
|
55
|
-
return undefined;
|
|
55
|
+
/** 把异常转换为 action 的稳定失败结果。 */
|
|
56
|
+
function failure_result(error: unknown): PluginActionResult<JsonObject> {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
return { success: false, error: message, message };
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
/**
|
|
59
|
-
* WebPlugin:为 agent 注入联网任务的方法论。
|
|
60
|
-
*/
|
|
61
|
+
/** WebPlugin:组合显式注入的 Web provider。 */
|
|
61
62
|
export class WebPlugin extends BasePlugin {
|
|
62
|
-
/**
|
|
63
|
-
* 当前 plugin 稳定名称。
|
|
64
|
-
*/
|
|
63
|
+
/** 当前 plugin 稳定名称。 */
|
|
65
64
|
readonly name = "web";
|
|
66
65
|
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
*/
|
|
70
|
-
readonly title = "Web Methodology";
|
|
66
|
+
/** 插件标题。 */
|
|
67
|
+
readonly title = "Web";
|
|
71
68
|
|
|
72
|
-
/**
|
|
73
|
-
* 插件说明。
|
|
74
|
-
*/
|
|
69
|
+
/** 插件说明。 */
|
|
75
70
|
readonly description =
|
|
76
|
-
"
|
|
71
|
+
"Provides structured web search, document reading, and browser sessions through configured providers.";
|
|
72
|
+
|
|
73
|
+
/** 搜索 provider。 */
|
|
74
|
+
private readonly search_provider?: WebPluginOptions["search"];
|
|
77
75
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
/** 文档读取 provider。 */
|
|
77
|
+
private readonly open_provider?: WebPluginOptions["open"];
|
|
78
|
+
|
|
79
|
+
/** 浏览器 provider。 */
|
|
80
|
+
private readonly browser_provider?: WebPluginOptions["browser"];
|
|
81
|
+
|
|
82
|
+
constructor(options: WebPluginOptions) {
|
|
83
|
+
super();
|
|
84
|
+
if (!options.search && !options.open && !options.browser) {
|
|
85
|
+
throw new TypeError("WebPlugin requires at least one provider");
|
|
86
|
+
}
|
|
87
|
+
this.search_provider = options.search;
|
|
88
|
+
this.open_provider = options.open;
|
|
89
|
+
this.browser_provider = options.browser;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Agent 释放或卸载 plugin 时关闭全部浏览器资源。 */
|
|
93
|
+
readonly lifecycle = {
|
|
94
|
+
stop: async () => {
|
|
95
|
+
await this.browser_provider?.dispose();
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/** 给模型注入最小且与实际 actions 一致的使用说明。 */
|
|
100
|
+
system(): string {
|
|
101
|
+
return [
|
|
102
|
+
"# Web Plugin",
|
|
103
|
+
"",
|
|
104
|
+
"Use direct search or document reading before starting a browser session.",
|
|
105
|
+
"Use browser actions only for dynamic pages, navigation, or interaction.",
|
|
106
|
+
"Browser actions are deterministic CSS-selector operations; inspect the page before acting.",
|
|
107
|
+
"Do not perform consequential actions such as submitting, sending, purchasing, uploading, or deleting without host approval.",
|
|
108
|
+
"Close every browser session created for the task when it is no longer needed.",
|
|
109
|
+
].join("\n");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** WebPlugin 结构化 actions。 */
|
|
81
113
|
readonly actions = {
|
|
82
|
-
[WEB_PLUGIN_ACTIONS.
|
|
83
|
-
description:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
enum: ["user", "project"],
|
|
101
|
-
description: "Installation scope for the agent-browser CLI.",
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
},
|
|
114
|
+
[WEB_PLUGIN_ACTIONS.search]: create_action({
|
|
115
|
+
description: "Search the web with the configured search provider.",
|
|
116
|
+
input_schema: z.object({
|
|
117
|
+
query: z.string().trim().min(1),
|
|
118
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
119
|
+
domains: z.array(z.string().trim().min(1)).max(50).optional(),
|
|
120
|
+
}),
|
|
121
|
+
examples: [{ title: "Search official sources", payload: { query: "Playwright CDP documentation", limit: 5 } }],
|
|
122
|
+
execute: async ({ input }) => {
|
|
123
|
+
if (!this.search_provider) {
|
|
124
|
+
return failure_result("WebPlugin search provider is not configured");
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const result = await this.search_provider(input as WebSearchInput);
|
|
128
|
+
return { success: true, data: result, message: "web search completed" };
|
|
129
|
+
} catch (error) {
|
|
130
|
+
return failure_result(error);
|
|
131
|
+
}
|
|
105
132
|
},
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
],
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
"
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
}),
|
|
134
|
+
|
|
135
|
+
[WEB_PLUGIN_ACTIONS.open]: create_action({
|
|
136
|
+
description: "Read a known URL with the configured document provider.",
|
|
137
|
+
input_schema: z.object({ url: URL_SCHEMA, max_chars: MAX_CHARS_SCHEMA }),
|
|
138
|
+
examples: [{ title: "Read official documentation", payload: { url: "https://playwright.dev/docs/api/class-playwright" } }],
|
|
139
|
+
execute: async ({ input }) => {
|
|
140
|
+
if (!this.open_provider) {
|
|
141
|
+
return failure_result("WebPlugin document provider is not configured");
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const result = await this.open_provider(input as WebOpenInput);
|
|
145
|
+
return { success: true, data: result, message: "web document opened" };
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return failure_result(error);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
}),
|
|
151
|
+
|
|
152
|
+
[WEB_PLUGIN_ACTIONS.browser_create_session]: create_action({
|
|
153
|
+
description: "Create a browser session with an optional initial URL.",
|
|
154
|
+
input_schema: z.object({
|
|
155
|
+
url: URL_SCHEMA.optional(),
|
|
156
|
+
include_screenshot: z.boolean().optional(),
|
|
157
|
+
}),
|
|
158
|
+
examples: [{ title: "Open a page", payload: { url: "https://example.com" } }],
|
|
159
|
+
execute: async ({ input }) => {
|
|
160
|
+
if (!this.browser_provider) {
|
|
161
|
+
return failure_result("WebPlugin browser provider is not configured");
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const result = await this.browser_provider.create_session(
|
|
165
|
+
input as BrowserCreateSessionInput,
|
|
166
|
+
);
|
|
167
|
+
return { success: true, data: result, message: "browser session created" };
|
|
168
|
+
} catch (error) {
|
|
169
|
+
return failure_result(error);
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
}),
|
|
173
|
+
|
|
174
|
+
[WEB_PLUGIN_ACTIONS.browser_observe]: create_action({
|
|
175
|
+
description: "Observe the current browser page as text and an optional screenshot.",
|
|
176
|
+
input_schema: z.object({
|
|
177
|
+
session_id: SESSION_ID_SCHEMA,
|
|
178
|
+
include_screenshot: z.boolean().optional(),
|
|
179
|
+
}),
|
|
180
|
+
execute: async ({ input }) => {
|
|
181
|
+
if (!this.browser_provider) {
|
|
182
|
+
return failure_result("WebPlugin browser provider is not configured");
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
const result = await this.browser_provider.observe(
|
|
186
|
+
input as BrowserObserveInput,
|
|
187
|
+
);
|
|
188
|
+
return { success: true, data: result, message: "browser observed" };
|
|
189
|
+
} catch (error) {
|
|
190
|
+
return failure_result(error);
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
}),
|
|
194
|
+
|
|
195
|
+
[WEB_PLUGIN_ACTIONS.browser_act]: create_action({
|
|
196
|
+
description: "Execute one deterministic browser action and return the resulting observation.",
|
|
197
|
+
input_schema: z.object({
|
|
198
|
+
session_id: SESSION_ID_SCHEMA,
|
|
199
|
+
action: BROWSER_ACTION_SCHEMA,
|
|
200
|
+
include_screenshot: z.boolean().optional(),
|
|
201
|
+
}),
|
|
202
|
+
examples: [{ title: "Click a link", payload: { session_id: "session-id", action: { type: "click", selector: "a" } } }],
|
|
203
|
+
execute: async ({ input }) => {
|
|
204
|
+
if (!this.browser_provider) {
|
|
205
|
+
return failure_result("WebPlugin browser provider is not configured");
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const result = await this.browser_provider.act(input as BrowserActInput);
|
|
209
|
+
return { success: true, data: result, message: "browser action completed" };
|
|
210
|
+
} catch (error) {
|
|
211
|
+
return failure_result(error);
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
}),
|
|
215
|
+
|
|
216
|
+
[WEB_PLUGIN_ACTIONS.browser_semantic_act]: create_action({
|
|
217
|
+
description: "Execute one natural-language browser action through a semantic provider.",
|
|
218
|
+
input_schema: z.object({
|
|
219
|
+
session_id: SESSION_ID_SCHEMA,
|
|
220
|
+
instruction: z.string().trim().min(1),
|
|
221
|
+
include_screenshot: z.boolean().optional(),
|
|
222
|
+
}),
|
|
223
|
+
examples: [{
|
|
224
|
+
title: "Use a semantic browser action",
|
|
225
|
+
payload: {
|
|
226
|
+
session_id: "session-id",
|
|
227
|
+
instruction: "Click the sign in button",
|
|
129
228
|
},
|
|
229
|
+
}],
|
|
230
|
+
execute: async ({ input }) => {
|
|
231
|
+
if (!this.browser_provider?.semantic_act) {
|
|
232
|
+
return failure_result(
|
|
233
|
+
"WebPlugin semantic browser action provider is not configured",
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
const result = await this.browser_provider.semantic_act(
|
|
238
|
+
input as BrowserSemanticActInput,
|
|
239
|
+
);
|
|
240
|
+
return { success: true, data: result, message: "semantic browser action completed" };
|
|
241
|
+
} catch (error) {
|
|
242
|
+
return failure_result(error);
|
|
243
|
+
}
|
|
130
244
|
},
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
245
|
+
}),
|
|
246
|
+
|
|
247
|
+
[WEB_PLUGIN_ACTIONS.browser_extract]: create_action({
|
|
248
|
+
description: "Extract text from the page body or a CSS selector.",
|
|
249
|
+
input_schema: z.object({
|
|
250
|
+
session_id: SESSION_ID_SCHEMA,
|
|
251
|
+
selector: z.string().trim().min(1).optional(),
|
|
252
|
+
max_chars: MAX_CHARS_SCHEMA,
|
|
253
|
+
}),
|
|
254
|
+
execute: async ({ input }) => {
|
|
255
|
+
if (!this.browser_provider) {
|
|
256
|
+
return failure_result("WebPlugin browser provider is not configured");
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
const result = await this.browser_provider.extract(
|
|
260
|
+
input as BrowserExtractInput,
|
|
261
|
+
);
|
|
262
|
+
return { success: true, data: result, message: "browser content extracted" };
|
|
263
|
+
} catch (error) {
|
|
264
|
+
return failure_result(error);
|
|
265
|
+
}
|
|
141
266
|
},
|
|
142
267
|
}),
|
|
143
|
-
};
|
|
144
268
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
269
|
+
[WEB_PLUGIN_ACTIONS.browser_semantic_extract]: create_action({
|
|
270
|
+
description: "Extract page content through a semantic provider.",
|
|
271
|
+
input_schema: z.object({
|
|
272
|
+
session_id: SESSION_ID_SCHEMA,
|
|
273
|
+
instruction: z.string().trim().min(1),
|
|
274
|
+
max_chars: MAX_CHARS_SCHEMA,
|
|
275
|
+
}),
|
|
276
|
+
execute: async ({ input }) => {
|
|
277
|
+
if (!this.browser_provider?.semantic_extract) {
|
|
278
|
+
return failure_result(
|
|
279
|
+
"WebPlugin semantic browser extract provider is not configured",
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
const result = await this.browser_provider.semantic_extract(
|
|
284
|
+
input as BrowserSemanticExtractInput,
|
|
285
|
+
);
|
|
286
|
+
return { success: true, data: result, message: "semantic browser content extracted" };
|
|
287
|
+
} catch (error) {
|
|
288
|
+
return failure_result(error);
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
}),
|
|
292
|
+
|
|
293
|
+
[WEB_PLUGIN_ACTIONS.browser_close_session]: create_action({
|
|
294
|
+
description: "Close a browser session and release its page resources.",
|
|
295
|
+
input_schema: z.object({ session_id: SESSION_ID_SCHEMA }),
|
|
296
|
+
execute: async ({ input }) => {
|
|
297
|
+
if (!this.browser_provider) {
|
|
298
|
+
return failure_result("WebPlugin browser provider is not configured");
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
await this.browser_provider.close_session(
|
|
302
|
+
input as BrowserCloseSessionInput,
|
|
303
|
+
);
|
|
304
|
+
return {
|
|
305
|
+
success: true,
|
|
306
|
+
data: { session_id: input.session_id, closed: true },
|
|
307
|
+
message: "browser session closed",
|
|
308
|
+
};
|
|
309
|
+
} catch (error) {
|
|
310
|
+
return failure_result(error);
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
}),
|
|
314
|
+
} as unknown as BasePlugin["actions"];
|
|
151
315
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-neutral Computer Use 浏览器 adapter。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - adapter 不依赖 OpenAI、Anthropic 或 Google SDK。
|
|
6
|
+
* - 每次语义动作先获取带截图的当前状态,再交给宿主模型循环。
|
|
7
|
+
* - 模型回调不能直接接触 Playwright 对象,只能返回 JSON observation。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
BrowserActInput,
|
|
12
|
+
BrowserCloseSessionInput,
|
|
13
|
+
BrowserCreateSessionInput,
|
|
14
|
+
BrowserExtractInput,
|
|
15
|
+
BrowserExtractResult,
|
|
16
|
+
BrowserObservation,
|
|
17
|
+
BrowserObserveInput,
|
|
18
|
+
BrowserProvider,
|
|
19
|
+
BrowserSemanticActInput,
|
|
20
|
+
} from "@/web/types/WebPlugin.js";
|
|
21
|
+
import type {
|
|
22
|
+
ComputerUseBrowserProviderAdapterOptions,
|
|
23
|
+
ComputerUseBrowserProviderMethods,
|
|
24
|
+
} from "@/web/types/ComputerUseBrowserProviderAdapter.js";
|
|
25
|
+
|
|
26
|
+
/** 为基础浏览器组合 Computer Use 视觉回退。 */
|
|
27
|
+
export class ComputerUseBrowserProviderAdapter
|
|
28
|
+
implements ComputerUseBrowserProviderMethods
|
|
29
|
+
{
|
|
30
|
+
/** adapter 稳定名称。 */
|
|
31
|
+
readonly name: string;
|
|
32
|
+
|
|
33
|
+
/** 拥有 session 的基础浏览器 provider。 */
|
|
34
|
+
private readonly browser: BrowserProvider;
|
|
35
|
+
|
|
36
|
+
/** Computer Use 模型回调。 */
|
|
37
|
+
private readonly run_callback: ComputerUseBrowserProviderAdapterOptions["run"];
|
|
38
|
+
|
|
39
|
+
constructor(options: ComputerUseBrowserProviderAdapterOptions) {
|
|
40
|
+
const name = String(options.name || "").trim();
|
|
41
|
+
if (!name) throw new TypeError("ComputerUseBrowserProviderAdapter requires name");
|
|
42
|
+
this.name = name;
|
|
43
|
+
this.browser = options.browser;
|
|
44
|
+
this.run_callback = options.run;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 委托创建浏览器 session。 */
|
|
48
|
+
async create_session(
|
|
49
|
+
input: BrowserCreateSessionInput,
|
|
50
|
+
): Promise<BrowserObservation> {
|
|
51
|
+
return await this.browser.create_session(input);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** 委托观察页面。 */
|
|
55
|
+
async observe(input: BrowserObserveInput): Promise<BrowserObservation> {
|
|
56
|
+
return await this.browser.observe(input);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** 委托确定性动作。 */
|
|
60
|
+
async act(input: BrowserActInput): Promise<BrowserObservation> {
|
|
61
|
+
return await this.browser.act(input);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** 获取截图并运行一次 Computer Use 模型循环。 */
|
|
65
|
+
async semantic_act(
|
|
66
|
+
input: BrowserSemanticActInput,
|
|
67
|
+
): Promise<BrowserObservation> {
|
|
68
|
+
const observation = await this.browser.observe({
|
|
69
|
+
session_id: input.session_id,
|
|
70
|
+
include_screenshot: true,
|
|
71
|
+
});
|
|
72
|
+
return await this.run_callback({
|
|
73
|
+
instruction: input.instruction,
|
|
74
|
+
session_id: input.session_id,
|
|
75
|
+
observation,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** 委托页面文本抽取。 */
|
|
80
|
+
async extract(input: BrowserExtractInput): Promise<BrowserExtractResult> {
|
|
81
|
+
return await this.browser.extract(input);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Computer Use adapter 不提供结构化语义抽取。 */
|
|
85
|
+
async close_session(input: BrowserCloseSessionInput): Promise<void> {
|
|
86
|
+
await this.browser.close_session(input);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 释放基础浏览器资源。 */
|
|
90
|
+
async dispose(): Promise<void> {
|
|
91
|
+
await this.browser.dispose();
|
|
92
|
+
}
|
|
93
|
+
}
|