@next-open-ai/openclawx 0.6.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/README.md +523 -0
- package/apps/desktop/README.md +210 -0
- package/apps/desktop/renderer/dist/assets/index-CYkSfhcp.css +10 -0
- package/apps/desktop/renderer/dist/assets/index-FI6O25Ms.js +89 -0
- package/apps/desktop/renderer/dist/index.html +22 -0
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.js +198 -0
- package/dist/cli/service.d.ts +13 -0
- package/dist/cli/service.js +243 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +5 -0
- package/dist/core/agent/agent-dir.d.ts +14 -0
- package/dist/core/agent/agent-dir.js +75 -0
- package/dist/core/agent/agent-manager.d.ts +64 -0
- package/dist/core/agent/agent-manager.js +278 -0
- package/dist/core/agent/config-manager.d.ts +25 -0
- package/dist/core/agent/config-manager.js +84 -0
- package/dist/core/agent/run.d.ts +26 -0
- package/dist/core/agent/run.js +65 -0
- package/dist/core/agent/skills.d.ts +20 -0
- package/dist/core/agent/skills.js +86 -0
- package/dist/core/config/desktop-config.d.ts +90 -0
- package/dist/core/config/desktop-config.js +521 -0
- package/dist/core/config/provider-support-default.d.ts +21 -0
- package/dist/core/config/provider-support-default.js +57 -0
- package/dist/core/installer/index.d.ts +1 -0
- package/dist/core/installer/index.js +1 -0
- package/dist/core/installer/skill-installer.d.ts +39 -0
- package/dist/core/installer/skill-installer.js +215 -0
- package/dist/core/mcp/adapter.d.ts +17 -0
- package/dist/core/mcp/adapter.js +49 -0
- package/dist/core/mcp/client.d.ts +24 -0
- package/dist/core/mcp/client.js +70 -0
- package/dist/core/mcp/config.d.ts +22 -0
- package/dist/core/mcp/config.js +69 -0
- package/dist/core/mcp/index.d.ts +18 -0
- package/dist/core/mcp/index.js +20 -0
- package/dist/core/mcp/operator.d.ts +15 -0
- package/dist/core/mcp/operator.js +72 -0
- package/dist/core/mcp/transport/index.d.ts +11 -0
- package/dist/core/mcp/transport/index.js +16 -0
- package/dist/core/mcp/transport/sse.d.ts +20 -0
- package/dist/core/mcp/transport/sse.js +82 -0
- package/dist/core/mcp/transport/stdio.d.ts +32 -0
- package/dist/core/mcp/transport/stdio.js +132 -0
- package/dist/core/mcp/types.d.ts +72 -0
- package/dist/core/mcp/types.js +5 -0
- package/dist/core/memory/build-summary.d.ts +6 -0
- package/dist/core/memory/build-summary.js +27 -0
- package/dist/core/memory/compaction-extension.d.ts +6 -0
- package/dist/core/memory/compaction-extension.js +23 -0
- package/dist/core/memory/embedding.d.ts +4 -0
- package/dist/core/memory/embedding.js +15 -0
- package/dist/core/memory/index.d.ts +29 -0
- package/dist/core/memory/index.js +70 -0
- package/dist/core/memory/remote-embedding.d.ts +10 -0
- package/dist/core/memory/remote-embedding.js +36 -0
- package/dist/core/memory/types.d.ts +16 -0
- package/dist/core/memory/types.js +1 -0
- package/dist/core/memory/vector-store.d.ts +15 -0
- package/dist/core/memory/vector-store.js +65 -0
- package/dist/core/tools/bookmark-tool.d.ts +9 -0
- package/dist/core/tools/bookmark-tool.js +118 -0
- package/dist/core/tools/browser-tool.d.ts +10 -0
- package/dist/core/tools/browser-tool.js +362 -0
- package/dist/core/tools/index.d.ts +4 -0
- package/dist/core/tools/index.js +4 -0
- package/dist/core/tools/install-skill-tool.d.ts +6 -0
- package/dist/core/tools/install-skill-tool.js +53 -0
- package/dist/core/tools/save-experience-tool.d.ts +5 -0
- package/dist/core/tools/save-experience-tool.js +54 -0
- package/dist/gateway/auth-hooks.d.ts +17 -0
- package/dist/gateway/auth-hooks.js +19 -0
- package/dist/gateway/backend-url.d.ts +2 -0
- package/dist/gateway/backend-url.js +11 -0
- package/dist/gateway/channel-handler.d.ts +6 -0
- package/dist/gateway/channel-handler.js +3 -0
- package/dist/gateway/clients.d.ts +5 -0
- package/dist/gateway/clients.js +4 -0
- package/dist/gateway/connection-handler.d.ts +6 -0
- package/dist/gateway/connection-handler.js +48 -0
- package/dist/gateway/index.d.ts +3 -0
- package/dist/gateway/index.js +2 -0
- package/dist/gateway/message-handler.d.ts +5 -0
- package/dist/gateway/message-handler.js +65 -0
- package/dist/gateway/methods/agent-cancel.d.ts +10 -0
- package/dist/gateway/methods/agent-cancel.js +17 -0
- package/dist/gateway/methods/agent-chat.d.ts +8 -0
- package/dist/gateway/methods/agent-chat.js +148 -0
- package/dist/gateway/methods/connect.d.ts +9 -0
- package/dist/gateway/methods/connect.js +18 -0
- package/dist/gateway/methods/install-skill-from-path.d.ts +13 -0
- package/dist/gateway/methods/install-skill-from-path.js +15 -0
- package/dist/gateway/methods/install-skill-from-upload.d.ts +14 -0
- package/dist/gateway/methods/install-skill-from-upload.js +13 -0
- package/dist/gateway/methods/run-scheduled-task.d.ts +15 -0
- package/dist/gateway/methods/run-scheduled-task.js +127 -0
- package/dist/gateway/paths.d.ts +20 -0
- package/dist/gateway/paths.js +19 -0
- package/dist/gateway/server.d.ts +8 -0
- package/dist/gateway/server.js +190 -0
- package/dist/gateway/sse-handler.d.ts +6 -0
- package/dist/gateway/sse-handler.js +3 -0
- package/dist/gateway/types.d.ts +90 -0
- package/dist/gateway/types.js +1 -0
- package/dist/gateway/utils.d.ts +22 -0
- package/dist/gateway/utils.js +67 -0
- package/dist/gateway/voice-handler.d.ts +12 -0
- package/dist/gateway/voice-handler.js +18 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/server/agent-config/agent-config.controller.d.ts +30 -0
- package/dist/server/agent-config/agent-config.controller.js +83 -0
- package/dist/server/agent-config/agent-config.module.d.ts +2 -0
- package/dist/server/agent-config/agent-config.module.js +19 -0
- package/dist/server/agent-config/agent-config.service.d.ts +53 -0
- package/dist/server/agent-config/agent-config.service.js +213 -0
- package/dist/server/agents/agents.controller.d.ts +41 -0
- package/dist/server/agents/agents.controller.js +118 -0
- package/dist/server/agents/agents.gateway.d.ts +21 -0
- package/dist/server/agents/agents.gateway.js +103 -0
- package/dist/server/agents/agents.module.d.ts +2 -0
- package/dist/server/agents/agents.module.js +20 -0
- package/dist/server/agents/agents.service.d.ts +63 -0
- package/dist/server/agents/agents.service.js +169 -0
- package/dist/server/app.module.d.ts +2 -0
- package/dist/server/app.module.js +38 -0
- package/dist/server/auth/auth.controller.d.ts +20 -0
- package/dist/server/auth/auth.controller.js +64 -0
- package/dist/server/auth/auth.module.d.ts +2 -0
- package/dist/server/auth/auth.module.js +19 -0
- package/dist/server/bootstrap.d.ts +15 -0
- package/dist/server/bootstrap.js +38 -0
- package/dist/server/config/config.controller.d.ts +73 -0
- package/dist/server/config/config.controller.js +95 -0
- package/dist/server/config/config.module.d.ts +2 -0
- package/dist/server/config/config.module.js +21 -0
- package/dist/server/config/config.service.d.ts +82 -0
- package/dist/server/config/config.service.js +123 -0
- package/dist/server/database/database.module.d.ts +2 -0
- package/dist/server/database/database.module.js +18 -0
- package/dist/server/database/database.service.d.ts +26 -0
- package/dist/server/database/database.service.js +253 -0
- package/dist/server/main.d.ts +1 -0
- package/dist/server/main.js +9 -0
- package/dist/server/saved-items/saved-items.controller.d.ts +57 -0
- package/dist/server/saved-items/saved-items.controller.js +229 -0
- package/dist/server/saved-items/saved-items.module.d.ts +2 -0
- package/dist/server/saved-items/saved-items.module.js +25 -0
- package/dist/server/saved-items/saved-items.service.d.ts +31 -0
- package/dist/server/saved-items/saved-items.service.js +105 -0
- package/dist/server/saved-items/tags.controller.d.ts +30 -0
- package/dist/server/saved-items/tags.controller.js +85 -0
- package/dist/server/saved-items/tags.service.d.ts +24 -0
- package/dist/server/saved-items/tags.service.js +84 -0
- package/dist/server/skills/skills.controller.d.ts +63 -0
- package/dist/server/skills/skills.controller.js +194 -0
- package/dist/server/skills/skills.module.d.ts +2 -0
- package/dist/server/skills/skills.module.js +22 -0
- package/dist/server/skills/skills.service.d.ts +65 -0
- package/dist/server/skills/skills.service.js +388 -0
- package/dist/server/tasks/tasks.controller.d.ts +52 -0
- package/dist/server/tasks/tasks.controller.js +163 -0
- package/dist/server/tasks/tasks.module.d.ts +2 -0
- package/dist/server/tasks/tasks.module.js +23 -0
- package/dist/server/tasks/tasks.service.d.ts +86 -0
- package/dist/server/tasks/tasks.service.js +327 -0
- package/dist/server/usage/usage.controller.d.ts +12 -0
- package/dist/server/usage/usage.controller.js +46 -0
- package/dist/server/usage/usage.module.d.ts +2 -0
- package/dist/server/usage/usage.module.js +19 -0
- package/dist/server/usage/usage.service.d.ts +21 -0
- package/dist/server/usage/usage.service.js +55 -0
- package/dist/server/users/users.controller.d.ts +35 -0
- package/dist/server/users/users.controller.js +69 -0
- package/dist/server/users/users.module.d.ts +2 -0
- package/dist/server/users/users.module.js +19 -0
- package/dist/server/users/users.service.d.ts +39 -0
- package/dist/server/users/users.service.js +140 -0
- package/dist/server/workspace/workspace.controller.d.ts +24 -0
- package/dist/server/workspace/workspace.controller.js +132 -0
- package/dist/server/workspace/workspace.module.d.ts +2 -0
- package/dist/server/workspace/workspace.module.js +21 -0
- package/dist/server/workspace/workspace.service.d.ts +36 -0
- package/dist/server/workspace/workspace.service.js +142 -0
- package/package.json +90 -0
- package/skills/agent-browser/SKILL.md +207 -0
- package/skills/agent-browser/references/authentication.md +202 -0
- package/skills/agent-browser/references/commands.md +259 -0
- package/skills/agent-browser/references/proxy-support.md +188 -0
- package/skills/agent-browser/references/session-management.md +193 -0
- package/skills/agent-browser/references/snapshot-refs.md +194 -0
- package/skills/agent-browser/references/video-recording.md +173 -0
- package/skills/agent-browser/templates/authenticated-session.sh +97 -0
- package/skills/agent-browser/templates/capture-workflow.sh +69 -0
- package/skills/agent-browser/templates/form-automation.sh +62 -0
- package/skills/find-skills/SKILL.md +140 -0
- package/skills/url-bookmark/SKILL.md +36 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
/**
|
|
3
|
+
* 获取当前系统中已维护的标签列表,供保存 URL 时选择匹配的标签。
|
|
4
|
+
*/
|
|
5
|
+
export declare function createGetBookmarkTagsTool(): ToolDefinition;
|
|
6
|
+
/**
|
|
7
|
+
* 将 URL 保存为收藏,并关联指定标签(标签名须与 get_bookmark_tags 返回的一致)。
|
|
8
|
+
*/
|
|
9
|
+
export declare function createSaveBookmarkTool(): ToolDefinition;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { getBackendBaseUrl } from "../../gateway/backend-url.js";
|
|
3
|
+
const GetBookmarkTagsSchema = Type.Object({});
|
|
4
|
+
const SaveBookmarkSchema = Type.Object({
|
|
5
|
+
url: Type.String({ description: "要收藏的 URL" }),
|
|
6
|
+
title: Type.Optional(Type.String({ description: "可选标题" })),
|
|
7
|
+
tagNames: Type.Optional(Type.Array(Type.String(), {
|
|
8
|
+
description: "标签名称列表,须与系统中已维护的标签一致。保存前应先用 get_bookmark_tags 获取可用标签。",
|
|
9
|
+
})),
|
|
10
|
+
});
|
|
11
|
+
async function apiGet(path) {
|
|
12
|
+
const base = getBackendBaseUrl();
|
|
13
|
+
if (!base) {
|
|
14
|
+
throw new Error("收藏功能需要在前端/桌面环境中使用,当前无法访问后端。");
|
|
15
|
+
}
|
|
16
|
+
const res = await fetch(`${base.replace(/\/$/, "")}/server-api${path}`, {
|
|
17
|
+
method: "GET",
|
|
18
|
+
headers: { "Content-Type": "application/json" },
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok) {
|
|
21
|
+
const text = await res.text();
|
|
22
|
+
throw new Error(`请求失败: ${res.status} ${text}`);
|
|
23
|
+
}
|
|
24
|
+
return res.json();
|
|
25
|
+
}
|
|
26
|
+
async function apiPost(path, body) {
|
|
27
|
+
const base = getBackendBaseUrl();
|
|
28
|
+
if (!base) {
|
|
29
|
+
throw new Error("收藏功能需要在前端/桌面环境中使用,当前无法访问后端。");
|
|
30
|
+
}
|
|
31
|
+
const res = await fetch(`${base.replace(/\/$/, "")}/server-api${path}`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "Content-Type": "application/json" },
|
|
34
|
+
body: JSON.stringify(body),
|
|
35
|
+
});
|
|
36
|
+
if (!res.ok) {
|
|
37
|
+
const text = await res.text();
|
|
38
|
+
throw new Error(`请求失败: ${res.status} ${text}`);
|
|
39
|
+
}
|
|
40
|
+
return res.json();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 获取当前系统中已维护的标签列表,供保存 URL 时选择匹配的标签。
|
|
44
|
+
*/
|
|
45
|
+
export function createGetBookmarkTagsTool() {
|
|
46
|
+
return {
|
|
47
|
+
name: "get_bookmark_tags",
|
|
48
|
+
label: "Get Bookmark Tags",
|
|
49
|
+
description: "获取系统中已维护的收藏标签列表。在用户要求保存链接时,应先调用本工具获取可用标签,再根据用户意图匹配最合适的标签名,并用 save_bookmark 保存。",
|
|
50
|
+
parameters: GetBookmarkTagsSchema,
|
|
51
|
+
execute: async (_toolCallId, _params, _signal, _onUpdate, _ctx) => {
|
|
52
|
+
try {
|
|
53
|
+
const json = await apiGet("/tags");
|
|
54
|
+
const data = json.data ?? [];
|
|
55
|
+
const names = data.map((t) => t.name);
|
|
56
|
+
const text = names.length > 0
|
|
57
|
+
? `当前可用标签:${names.join("、")}。请根据用户意图选择匹配的标签名用于 save_bookmark。`
|
|
58
|
+
: "当前暂无标签。请在设置中先添加标签,或使用 save_bookmark 时不传 tagNames。";
|
|
59
|
+
return {
|
|
60
|
+
content: [{ type: "text", text }],
|
|
61
|
+
details: { tags: data },
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
66
|
+
return {
|
|
67
|
+
content: [{ type: "text", text: `获取标签失败: ${msg}` }],
|
|
68
|
+
details: undefined,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 将 URL 保存为收藏,并关联指定标签(标签名须与 get_bookmark_tags 返回的一致)。
|
|
76
|
+
*/
|
|
77
|
+
export function createSaveBookmarkTool() {
|
|
78
|
+
return {
|
|
79
|
+
name: "save_bookmark",
|
|
80
|
+
label: "Save Bookmark",
|
|
81
|
+
description: "将用户提供的 URL 保存到收藏库,并可关联一个或多个标签。标签名必须使用 get_bookmark_tags 返回的已有标签;若用户未指定标签,可根据上下文推断或留空。",
|
|
82
|
+
parameters: SaveBookmarkSchema,
|
|
83
|
+
execute: async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
84
|
+
const url = (params.url ?? "").trim();
|
|
85
|
+
if (!url) {
|
|
86
|
+
return {
|
|
87
|
+
content: [{ type: "text", text: "请提供要收藏的 URL。" }],
|
|
88
|
+
details: undefined,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const json = await apiPost("/saved-items", {
|
|
93
|
+
url,
|
|
94
|
+
title: params.title?.trim() || undefined,
|
|
95
|
+
tagNames: params.tagNames?.length ? params.tagNames.map((n) => n.trim()).filter(Boolean) : undefined,
|
|
96
|
+
});
|
|
97
|
+
const data = json.data;
|
|
98
|
+
const tagStr = data?.tagNames?.length ? `,标签:${data.tagNames.join("、")}` : "";
|
|
99
|
+
return {
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: "text",
|
|
103
|
+
text: `已收藏:${data?.url ?? url}${tagStr}`,
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
details: data,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
111
|
+
return {
|
|
112
|
+
content: [{ type: "text", text: `保存失败: ${msg}` }],
|
|
113
|
+
details: undefined,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
|
2
|
+
/**
|
|
3
|
+
* Create browser automation tool
|
|
4
|
+
* @param workspaceDir Optional workspace directory for downloads
|
|
5
|
+
*/
|
|
6
|
+
export declare function createBrowserTool(workspaceDir?: string): AgentTool<any>;
|
|
7
|
+
/**
|
|
8
|
+
* Cleanup function to close browser on process exit
|
|
9
|
+
*/
|
|
10
|
+
export declare function closeBrowser(): Promise<void>;
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { BrowserManager } from "agent-browser/dist/browser.js";
|
|
3
|
+
/**
|
|
4
|
+
* Browser tool schema - defines all possible browser actions
|
|
5
|
+
*/
|
|
6
|
+
const BrowserToolSchema = Type.Object({
|
|
7
|
+
action: Type.Union([
|
|
8
|
+
Type.Literal("navigate"),
|
|
9
|
+
Type.Literal("snapshot"),
|
|
10
|
+
Type.Literal("screenshot"),
|
|
11
|
+
Type.Literal("click"),
|
|
12
|
+
Type.Literal("type"),
|
|
13
|
+
Type.Literal("fill"),
|
|
14
|
+
Type.Literal("scroll"),
|
|
15
|
+
Type.Literal("extract"),
|
|
16
|
+
Type.Literal("wait"),
|
|
17
|
+
Type.Literal("back"),
|
|
18
|
+
Type.Literal("forward"),
|
|
19
|
+
Type.Literal("download"),
|
|
20
|
+
Type.Literal("close"),
|
|
21
|
+
], { description: "Browser action to perform" }),
|
|
22
|
+
url: Type.Optional(Type.String({ description: "URL to navigate to (for navigate action) or download from (for download action)" })),
|
|
23
|
+
selector: Type.Optional(Type.String({ description: "CSS selector or ref (e.g., @e1) to target element" })),
|
|
24
|
+
text: Type.Optional(Type.String({ description: "Text to type or fill (for type/fill actions)" })),
|
|
25
|
+
direction: Type.Optional(Type.Union([
|
|
26
|
+
Type.Literal("up"),
|
|
27
|
+
Type.Literal("down"),
|
|
28
|
+
Type.Literal("left"),
|
|
29
|
+
Type.Literal("right"),
|
|
30
|
+
], { description: "Scroll direction" })),
|
|
31
|
+
pixels: Type.Optional(Type.Number({ description: "Number of pixels to scroll" })),
|
|
32
|
+
interactive: Type.Optional(Type.Boolean({ description: "Get only interactive elements in snapshot" })),
|
|
33
|
+
fullPage: Type.Optional(Type.Boolean({ description: "Take full page screenshot" })),
|
|
34
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in milliseconds for wait action" })),
|
|
35
|
+
downloadPath: Type.Optional(Type.String({ description: "Local path to save downloaded file (for download action)" })),
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Shared browser manager instance
|
|
39
|
+
*/
|
|
40
|
+
let browserManager = null;
|
|
41
|
+
/**
|
|
42
|
+
* Get or create browser manager instance
|
|
43
|
+
*/
|
|
44
|
+
async function getBrowserManager() {
|
|
45
|
+
if (!browserManager) {
|
|
46
|
+
browserManager = new BrowserManager();
|
|
47
|
+
}
|
|
48
|
+
// Launch browser if not already launched
|
|
49
|
+
if (!browserManager.isLaunched()) {
|
|
50
|
+
await browserManager.launch({
|
|
51
|
+
action: "launch",
|
|
52
|
+
id: "default",
|
|
53
|
+
headless: true,
|
|
54
|
+
viewport: { width: 1280, height: 720 },
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return browserManager;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create browser automation tool
|
|
61
|
+
* @param workspaceDir Optional workspace directory for downloads
|
|
62
|
+
*/
|
|
63
|
+
export function createBrowserTool(workspaceDir) {
|
|
64
|
+
return {
|
|
65
|
+
name: "browser",
|
|
66
|
+
label: "Browser",
|
|
67
|
+
description: [
|
|
68
|
+
"Control a headless browser for web automation.",
|
|
69
|
+
"Actions: navigate (go to URL), snapshot (get page content with refs), screenshot (capture image),",
|
|
70
|
+
"click (click element), type (type text), fill (clear and fill input), scroll (scroll page),",
|
|
71
|
+
"extract (get text from element), wait (wait for element), download (download file from URL or by clicking),",
|
|
72
|
+
"back/forward (navigation), close (close browser).",
|
|
73
|
+
"Use refs from snapshot (e.g., @e1, @e2) to interact with elements reliably.",
|
|
74
|
+
].join(" "),
|
|
75
|
+
parameters: BrowserToolSchema,
|
|
76
|
+
execute: async (_toolCallId, args) => {
|
|
77
|
+
const params = args;
|
|
78
|
+
console.log(`[Browser Tool] Executing action: ${params.action}`);
|
|
79
|
+
try {
|
|
80
|
+
// Ensure browser is ready
|
|
81
|
+
const browser = await getBrowserManager();
|
|
82
|
+
console.log(`[Browser Tool] Browser manager ready`);
|
|
83
|
+
switch (params.action) {
|
|
84
|
+
case "navigate": {
|
|
85
|
+
if (!params.url) {
|
|
86
|
+
throw new Error("URL is required for navigate action");
|
|
87
|
+
}
|
|
88
|
+
const page = browser.getPage();
|
|
89
|
+
await page.goto(params.url, { waitUntil: "domcontentloaded" });
|
|
90
|
+
const url = page.url();
|
|
91
|
+
const title = await page.title();
|
|
92
|
+
return {
|
|
93
|
+
content: [{
|
|
94
|
+
type: "text",
|
|
95
|
+
text: `Navigated to: ${title}\nURL: ${url}`
|
|
96
|
+
}],
|
|
97
|
+
details: { url, title },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
case "snapshot": {
|
|
101
|
+
const snapshot = await browser.getSnapshot({
|
|
102
|
+
interactive: params.interactive ?? true,
|
|
103
|
+
compact: true,
|
|
104
|
+
});
|
|
105
|
+
const page = browser.getPage();
|
|
106
|
+
const url = page.url();
|
|
107
|
+
const title = await page.title();
|
|
108
|
+
return {
|
|
109
|
+
content: [{
|
|
110
|
+
type: "text",
|
|
111
|
+
text: `Page: ${title}\nURL: ${url}\n\n${snapshot.tree}`
|
|
112
|
+
}],
|
|
113
|
+
details: {
|
|
114
|
+
url,
|
|
115
|
+
title,
|
|
116
|
+
snapshot: snapshot.tree,
|
|
117
|
+
refs: snapshot.refs
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
case "screenshot": {
|
|
122
|
+
const page = browser.getPage();
|
|
123
|
+
const screenshot = await page.screenshot({
|
|
124
|
+
fullPage: params.fullPage ?? false,
|
|
125
|
+
type: "png",
|
|
126
|
+
});
|
|
127
|
+
const base64 = screenshot.toString("base64");
|
|
128
|
+
return {
|
|
129
|
+
content: [
|
|
130
|
+
{ type: "text", text: "Screenshot captured" },
|
|
131
|
+
{
|
|
132
|
+
type: "image",
|
|
133
|
+
data: base64,
|
|
134
|
+
mimeType: "image/png"
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
details: { fullPage: params.fullPage ?? false },
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
case "click": {
|
|
141
|
+
if (!params.selector) {
|
|
142
|
+
throw new Error("Selector is required for click action");
|
|
143
|
+
}
|
|
144
|
+
const locator = browser.getLocator(params.selector);
|
|
145
|
+
await locator.click();
|
|
146
|
+
return {
|
|
147
|
+
content: [{
|
|
148
|
+
type: "text",
|
|
149
|
+
text: `Clicked element: ${params.selector}`
|
|
150
|
+
}],
|
|
151
|
+
details: { selector: params.selector },
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
case "type": {
|
|
155
|
+
if (!params.selector || !params.text) {
|
|
156
|
+
throw new Error("Selector and text are required for type action");
|
|
157
|
+
}
|
|
158
|
+
const locator = browser.getLocator(params.selector);
|
|
159
|
+
await locator.pressSequentially(params.text);
|
|
160
|
+
return {
|
|
161
|
+
content: [{
|
|
162
|
+
type: "text",
|
|
163
|
+
text: `Typed text into: ${params.selector}`
|
|
164
|
+
}],
|
|
165
|
+
details: { selector: params.selector, text: params.text },
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
case "fill": {
|
|
169
|
+
if (!params.selector || !params.text) {
|
|
170
|
+
throw new Error("Selector and text are required for fill action");
|
|
171
|
+
}
|
|
172
|
+
const locator = browser.getLocator(params.selector);
|
|
173
|
+
await locator.fill(params.text);
|
|
174
|
+
return {
|
|
175
|
+
content: [{
|
|
176
|
+
type: "text",
|
|
177
|
+
text: `Filled text into: ${params.selector}`
|
|
178
|
+
}],
|
|
179
|
+
details: { selector: params.selector, text: params.text },
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
case "scroll": {
|
|
183
|
+
const page = browser.getPage();
|
|
184
|
+
const direction = params.direction ?? "down";
|
|
185
|
+
const pixels = params.pixels ?? 500;
|
|
186
|
+
const scrollMap = {
|
|
187
|
+
down: { x: 0, y: pixels },
|
|
188
|
+
up: { x: 0, y: -pixels },
|
|
189
|
+
right: { x: pixels, y: 0 },
|
|
190
|
+
left: { x: -pixels, y: 0 },
|
|
191
|
+
};
|
|
192
|
+
const { x, y } = scrollMap[direction];
|
|
193
|
+
await page.evaluate(({ x, y }) => {
|
|
194
|
+
window.scrollBy(x, y);
|
|
195
|
+
}, { x, y });
|
|
196
|
+
return {
|
|
197
|
+
content: [{
|
|
198
|
+
type: "text",
|
|
199
|
+
text: `Scrolled ${direction} by ${pixels}px`
|
|
200
|
+
}],
|
|
201
|
+
details: { direction, pixels },
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
case "extract": {
|
|
205
|
+
if (!params.selector) {
|
|
206
|
+
throw new Error("Selector is required for extract action");
|
|
207
|
+
}
|
|
208
|
+
const locator = browser.getLocator(params.selector);
|
|
209
|
+
const text = await locator.textContent();
|
|
210
|
+
return {
|
|
211
|
+
content: [{
|
|
212
|
+
type: "text",
|
|
213
|
+
text: `Extracted text: ${text || "(empty)"}`
|
|
214
|
+
}],
|
|
215
|
+
details: { selector: params.selector, text },
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
case "wait": {
|
|
219
|
+
if (!params.selector) {
|
|
220
|
+
throw new Error("Selector is required for wait action");
|
|
221
|
+
}
|
|
222
|
+
const locator = browser.getLocator(params.selector);
|
|
223
|
+
await locator.waitFor({
|
|
224
|
+
state: "visible",
|
|
225
|
+
timeout: params.timeout ?? 30000
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
content: [{
|
|
229
|
+
type: "text",
|
|
230
|
+
text: `Element is now visible: ${params.selector}`
|
|
231
|
+
}],
|
|
232
|
+
details: { selector: params.selector },
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
case "back": {
|
|
236
|
+
const page = browser.getPage();
|
|
237
|
+
await page.goBack();
|
|
238
|
+
const url = page.url();
|
|
239
|
+
return {
|
|
240
|
+
content: [{
|
|
241
|
+
type: "text",
|
|
242
|
+
text: `Navigated back to: ${url}`
|
|
243
|
+
}],
|
|
244
|
+
details: { url },
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
case "forward": {
|
|
248
|
+
const page = browser.getPage();
|
|
249
|
+
await page.goForward();
|
|
250
|
+
const url = page.url();
|
|
251
|
+
return {
|
|
252
|
+
content: [{
|
|
253
|
+
type: "text",
|
|
254
|
+
text: `Navigated forward to: ${url}`
|
|
255
|
+
}],
|
|
256
|
+
details: { url },
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
case "download": {
|
|
260
|
+
const fs = await import("fs/promises");
|
|
261
|
+
const path = await import("path");
|
|
262
|
+
// Create downloads directory if it doesn't exist
|
|
263
|
+
const downloadsDir = workspaceDir ? path.join(workspaceDir, "downloads") : path.join(process.cwd(), "downloads");
|
|
264
|
+
try {
|
|
265
|
+
await fs.mkdir(downloadsDir, { recursive: true });
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
// Directory might already exist, ignore
|
|
269
|
+
}
|
|
270
|
+
// Hybrid approach: URL download or click-triggered download
|
|
271
|
+
if (params.url) {
|
|
272
|
+
// Direct URL download
|
|
273
|
+
const response = await fetch(params.url);
|
|
274
|
+
if (!response.ok) {
|
|
275
|
+
throw new Error(`Download failed: ${response.statusText}`);
|
|
276
|
+
}
|
|
277
|
+
const buffer = await response.arrayBuffer();
|
|
278
|
+
const filename = params.downloadPath || path.basename(params.url) || `download_${Date.now()}`;
|
|
279
|
+
const fullPath = path.isAbsolute(filename)
|
|
280
|
+
? filename
|
|
281
|
+
: path.join(downloadsDir, filename);
|
|
282
|
+
await fs.writeFile(fullPath, Buffer.from(buffer));
|
|
283
|
+
return {
|
|
284
|
+
content: [{
|
|
285
|
+
type: "text",
|
|
286
|
+
text: `Downloaded file to: ${fullPath}\nSize: ${buffer.byteLength} bytes`
|
|
287
|
+
}],
|
|
288
|
+
details: {
|
|
289
|
+
path: fullPath,
|
|
290
|
+
size: buffer.byteLength,
|
|
291
|
+
url: params.url
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
else if (params.selector) {
|
|
296
|
+
// Click-triggered download using Playwright
|
|
297
|
+
const page = browser.getPage();
|
|
298
|
+
const downloadPromise = page.waitForEvent('download', { timeout: 30000 });
|
|
299
|
+
const locator = browser.getLocator(params.selector);
|
|
300
|
+
await locator.click();
|
|
301
|
+
const download = await downloadPromise;
|
|
302
|
+
const suggestedFilename = download.suggestedFilename();
|
|
303
|
+
const filename = params.downloadPath || suggestedFilename || `download_${Date.now()}`;
|
|
304
|
+
const fullPath = path.isAbsolute(filename)
|
|
305
|
+
? filename
|
|
306
|
+
: path.join(downloadsDir, filename);
|
|
307
|
+
await download.saveAs(fullPath);
|
|
308
|
+
return {
|
|
309
|
+
content: [{
|
|
310
|
+
type: "text",
|
|
311
|
+
text: `Downloaded file to: ${fullPath}\nOriginal filename: ${suggestedFilename}`
|
|
312
|
+
}],
|
|
313
|
+
details: {
|
|
314
|
+
path: fullPath,
|
|
315
|
+
originalFilename: suggestedFilename,
|
|
316
|
+
selector: params.selector
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
throw new Error("Download action requires either url or selector parameter");
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
case "close": {
|
|
325
|
+
if (browserManager) {
|
|
326
|
+
await browserManager.close();
|
|
327
|
+
browserManager = null;
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
content: [{
|
|
331
|
+
type: "text",
|
|
332
|
+
text: "Browser closed"
|
|
333
|
+
}],
|
|
334
|
+
details: { closed: true },
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
default:
|
|
338
|
+
throw new Error(`Unknown action: ${params.action}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
343
|
+
return {
|
|
344
|
+
content: [{
|
|
345
|
+
type: "text",
|
|
346
|
+
text: `Browser action failed: ${errorMessage}`
|
|
347
|
+
}],
|
|
348
|
+
details: { error: errorMessage },
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Cleanup function to close browser on process exit
|
|
356
|
+
*/
|
|
357
|
+
export async function closeBrowser() {
|
|
358
|
+
if (browserManager) {
|
|
359
|
+
await browserManager.close();
|
|
360
|
+
browserManager = null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createBrowserTool, closeBrowser } from "./browser-tool.js";
|
|
2
|
+
export { createSaveExperienceTool } from "./save-experience-tool.js";
|
|
3
|
+
export { createInstallSkillTool } from "./install-skill-tool.js";
|
|
4
|
+
export { createGetBookmarkTagsTool, createSaveBookmarkTool } from "./bookmark-tool.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createBrowserTool, closeBrowser } from "./browser-tool.js";
|
|
2
|
+
export { createSaveExperienceTool } from "./save-experience-tool.js";
|
|
3
|
+
export { createInstallSkillTool } from "./install-skill-tool.js";
|
|
4
|
+
export { createGetBookmarkTagsTool, createSaveBookmarkTool } from "./bookmark-tool.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
/**
|
|
3
|
+
* 创建 install_skill 工具:通过核心 installer 安装技能,不依赖 Nest。
|
|
4
|
+
* @param targetAgentId 安装目标(具体 agentId / "global"|"all" / 未传则当前/default)
|
|
5
|
+
*/
|
|
6
|
+
export declare function createInstallSkillTool(targetAgentId: string | undefined): ToolDefinition;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { resolveInstallTarget, installSkillByUrl } from "../installer/index.js";
|
|
3
|
+
const InstallSkillSchema = Type.Object({
|
|
4
|
+
url: Type.String({
|
|
5
|
+
description: "Skill 安装地址,如 GitHub 简写 owner/repo、完整 URL 或 owner/repo@skillName",
|
|
6
|
+
}),
|
|
7
|
+
});
|
|
8
|
+
/**
|
|
9
|
+
* 创建 install_skill 工具:通过核心 installer 安装技能,不依赖 Nest。
|
|
10
|
+
* @param targetAgentId 安装目标(具体 agentId / "global"|"all" / 未传则当前/default)
|
|
11
|
+
*/
|
|
12
|
+
export function createInstallSkillTool(targetAgentId) {
|
|
13
|
+
return {
|
|
14
|
+
name: "install_skill",
|
|
15
|
+
label: "Install Skill",
|
|
16
|
+
description: "将指定地址的技能安装到当前智能体工作区目录。在 OpenBot 中为用户安装技能时," +
|
|
17
|
+
"应使用本工具而非 npx skills add,以保证安装到当前智能体对应的技能目录。",
|
|
18
|
+
parameters: InstallSkillSchema,
|
|
19
|
+
execute: async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
20
|
+
const url = (params.url ?? "").trim();
|
|
21
|
+
if (!url) {
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: "请提供技能安装地址(如 owner/repo 或 owner/repo@skillName)。" }],
|
|
24
|
+
details: undefined,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const target = await resolveInstallTarget(targetAgentId);
|
|
29
|
+
const result = await installSkillByUrl(url, {
|
|
30
|
+
scope: target.scope,
|
|
31
|
+
workspace: target.workspace,
|
|
32
|
+
});
|
|
33
|
+
const out = result.stdout?.trim() ?? "";
|
|
34
|
+
const err = result.stderr?.trim() ?? "";
|
|
35
|
+
const installDir = result.installDir;
|
|
36
|
+
const dirLine = installDir ? `\n安装目录:${installDir}` : "";
|
|
37
|
+
const baseText = out || (err ? `安装完成。\n${err}` : "技能已安装完成。");
|
|
38
|
+
const text = dirLine ? `${baseText}${dirLine}` : baseText;
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text", text }],
|
|
41
|
+
details: { stdout: result.stdout, stderr: result.stderr, installDir: result.installDir },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text: `安装失败: ${msg}` }],
|
|
48
|
+
details: undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { addMemory } from "../memory/index.js";
|
|
3
|
+
const SaveExperienceSchema = Type.Object({
|
|
4
|
+
summary: Type.String({
|
|
5
|
+
description: "本轮对话中总结出的经验、要点或可复用的结论,用于存入长期记忆",
|
|
6
|
+
}),
|
|
7
|
+
});
|
|
8
|
+
/**
|
|
9
|
+
* 创建 save_experience 工具:对话结束时由 Agent 调用,将经验总结存入向量库(infotype: experience)
|
|
10
|
+
*/
|
|
11
|
+
export function createSaveExperienceTool(sessionId) {
|
|
12
|
+
return {
|
|
13
|
+
name: "save_experience",
|
|
14
|
+
label: "Save Experience",
|
|
15
|
+
description: "在对话结束时调用,将本轮对话中总结出的经验、要点或可复用的结论存入长期记忆,便于后续会话检索。",
|
|
16
|
+
parameters: SaveExperienceSchema,
|
|
17
|
+
execute: async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
18
|
+
const text = (params.summary ?? "").trim();
|
|
19
|
+
if (!text) {
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: "text", text: "未提供总结内容,未写入记忆。" }],
|
|
22
|
+
details: undefined,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const id = await addMemory(text, {
|
|
27
|
+
infotype: "experience",
|
|
28
|
+
sessionId,
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: "text",
|
|
34
|
+
text: `经验已存入长期记忆(id: ${id})。`,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
details: { id },
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
42
|
+
return {
|
|
43
|
+
content: [
|
|
44
|
+
{
|
|
45
|
+
type: "text",
|
|
46
|
+
text: `存入记忆失败: ${msg}`,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
details: undefined,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway 各通道入口的鉴权/安全钩子(占位模块)。
|
|
3
|
+
* 连接之初可在此做 token 校验、限流等,当前暂不加逻辑,仅透传。
|
|
4
|
+
*/
|
|
5
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
6
|
+
import type { IncomingMessage } from 'http';
|
|
7
|
+
/** HTTP:/server-api 进入 Nest 前的鉴权钩子(占位,直接 next) */
|
|
8
|
+
export declare function authHookServerApi(req: Request, res: Response, next: NextFunction): void;
|
|
9
|
+
/** HTTP:/channel 进入通道模块前的鉴权钩子(占位,直接 next) */
|
|
10
|
+
export declare function authHookChannel(req: Request, res: Response, next: NextFunction): void;
|
|
11
|
+
/** HTTP:/sse 进入 SSE 前的鉴权钩子(占位,直接 next) */
|
|
12
|
+
export declare function authHookSse(req: Request, res: Response, next: NextFunction): void;
|
|
13
|
+
/**
|
|
14
|
+
* WebSocket upgrade:/ws 或 /ws/voice 连接前的鉴权钩子(占位)。
|
|
15
|
+
* 返回 true 表示放行,false 表示拒绝(调用方应关闭 socket)。
|
|
16
|
+
*/
|
|
17
|
+
export declare function authHookWs(_req: IncomingMessage, _path: string): boolean;
|