@next-open-ai/openbot 0.1.1

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.
Files changed (139) hide show
  1. package/README.md +212 -0
  2. package/dist/agent/agent-dir.d.ts +14 -0
  3. package/dist/agent/agent-dir.js +75 -0
  4. package/dist/agent/agent-manager.d.ts +61 -0
  5. package/dist/agent/agent-manager.js +257 -0
  6. package/dist/agent/config-manager.d.ts +25 -0
  7. package/dist/agent/config-manager.js +84 -0
  8. package/dist/agent/desktop-config.d.ts +15 -0
  9. package/dist/agent/desktop-config.js +91 -0
  10. package/dist/agent/run.d.ts +26 -0
  11. package/dist/agent/run.js +65 -0
  12. package/dist/agent/skills.d.ts +20 -0
  13. package/dist/agent/skills.js +86 -0
  14. package/dist/cli.d.ts +2 -0
  15. package/dist/cli.js +168 -0
  16. package/dist/gateway/backend-url.d.ts +2 -0
  17. package/dist/gateway/backend-url.js +11 -0
  18. package/dist/gateway/clients.d.ts +5 -0
  19. package/dist/gateway/clients.js +4 -0
  20. package/dist/gateway/connection-handler.d.ts +6 -0
  21. package/dist/gateway/connection-handler.js +48 -0
  22. package/dist/gateway/desktop-config.d.ts +7 -0
  23. package/dist/gateway/desktop-config.js +25 -0
  24. package/dist/gateway/index.d.ts +3 -0
  25. package/dist/gateway/index.js +2 -0
  26. package/dist/gateway/message-handler.d.ts +5 -0
  27. package/dist/gateway/message-handler.js +65 -0
  28. package/dist/gateway/methods/agent-cancel.d.ts +10 -0
  29. package/dist/gateway/methods/agent-cancel.js +17 -0
  30. package/dist/gateway/methods/agent-chat.d.ts +8 -0
  31. package/dist/gateway/methods/agent-chat.js +194 -0
  32. package/dist/gateway/methods/connect.d.ts +8 -0
  33. package/dist/gateway/methods/connect.js +15 -0
  34. package/dist/gateway/methods/install-skill-from-path.d.ts +13 -0
  35. package/dist/gateway/methods/install-skill-from-path.js +48 -0
  36. package/dist/gateway/methods/run-scheduled-task.d.ts +13 -0
  37. package/dist/gateway/methods/run-scheduled-task.js +164 -0
  38. package/dist/gateway/server.d.ts +10 -0
  39. package/dist/gateway/server.js +268 -0
  40. package/dist/gateway/types.d.ts +76 -0
  41. package/dist/gateway/types.js +1 -0
  42. package/dist/gateway/utils.d.ts +22 -0
  43. package/dist/gateway/utils.js +67 -0
  44. package/dist/index.d.ts +3 -0
  45. package/dist/index.js +3 -0
  46. package/dist/memory/build-summary.d.ts +6 -0
  47. package/dist/memory/build-summary.js +27 -0
  48. package/dist/memory/compaction-extension.d.ts +6 -0
  49. package/dist/memory/compaction-extension.js +23 -0
  50. package/dist/memory/embedding.d.ts +10 -0
  51. package/dist/memory/embedding.js +22 -0
  52. package/dist/memory/index.d.ts +29 -0
  53. package/dist/memory/index.js +66 -0
  54. package/dist/memory/types.d.ts +16 -0
  55. package/dist/memory/types.js +1 -0
  56. package/dist/memory/vector-store.d.ts +15 -0
  57. package/dist/memory/vector-store.js +65 -0
  58. package/dist/server/agent-config/agent-config.controller.d.ts +30 -0
  59. package/dist/server/agent-config/agent-config.controller.js +83 -0
  60. package/dist/server/agent-config/agent-config.module.d.ts +2 -0
  61. package/dist/server/agent-config/agent-config.module.js +19 -0
  62. package/dist/server/agent-config/agent-config.service.d.ts +34 -0
  63. package/dist/server/agent-config/agent-config.service.js +171 -0
  64. package/dist/server/agents/agents.controller.d.ts +41 -0
  65. package/dist/server/agents/agents.controller.js +120 -0
  66. package/dist/server/agents/agents.gateway.d.ts +21 -0
  67. package/dist/server/agents/agents.gateway.js +103 -0
  68. package/dist/server/agents/agents.module.d.ts +2 -0
  69. package/dist/server/agents/agents.module.js +20 -0
  70. package/dist/server/agents/agents.service.d.ts +63 -0
  71. package/dist/server/agents/agents.service.js +167 -0
  72. package/dist/server/app.module.d.ts +2 -0
  73. package/dist/server/app.module.js +36 -0
  74. package/dist/server/auth/auth.controller.d.ts +20 -0
  75. package/dist/server/auth/auth.controller.js +64 -0
  76. package/dist/server/auth/auth.module.d.ts +2 -0
  77. package/dist/server/auth/auth.module.js +19 -0
  78. package/dist/server/config/config.controller.d.ts +51 -0
  79. package/dist/server/config/config.controller.js +81 -0
  80. package/dist/server/config/config.module.d.ts +2 -0
  81. package/dist/server/config/config.module.js +19 -0
  82. package/dist/server/config/config.service.d.ts +34 -0
  83. package/dist/server/config/config.service.js +91 -0
  84. package/dist/server/database/database.module.d.ts +2 -0
  85. package/dist/server/database/database.module.js +18 -0
  86. package/dist/server/database/database.service.d.ts +11 -0
  87. package/dist/server/database/database.service.js +137 -0
  88. package/dist/server/main.d.ts +1 -0
  89. package/dist/server/main.js +18 -0
  90. package/dist/server/skills/skills.controller.d.ts +63 -0
  91. package/dist/server/skills/skills.controller.js +194 -0
  92. package/dist/server/skills/skills.module.d.ts +2 -0
  93. package/dist/server/skills/skills.module.js +22 -0
  94. package/dist/server/skills/skills.service.d.ts +63 -0
  95. package/dist/server/skills/skills.service.js +324 -0
  96. package/dist/server/tasks/tasks.controller.d.ts +52 -0
  97. package/dist/server/tasks/tasks.controller.js +163 -0
  98. package/dist/server/tasks/tasks.module.d.ts +2 -0
  99. package/dist/server/tasks/tasks.module.js +22 -0
  100. package/dist/server/tasks/tasks.service.d.ts +84 -0
  101. package/dist/server/tasks/tasks.service.js +313 -0
  102. package/dist/server/usage/usage.controller.d.ts +12 -0
  103. package/dist/server/usage/usage.controller.js +46 -0
  104. package/dist/server/usage/usage.module.d.ts +2 -0
  105. package/dist/server/usage/usage.module.js +19 -0
  106. package/dist/server/usage/usage.service.d.ts +21 -0
  107. package/dist/server/usage/usage.service.js +55 -0
  108. package/dist/server/users/users.controller.d.ts +35 -0
  109. package/dist/server/users/users.controller.js +69 -0
  110. package/dist/server/users/users.module.d.ts +2 -0
  111. package/dist/server/users/users.module.js +19 -0
  112. package/dist/server/users/users.service.d.ts +39 -0
  113. package/dist/server/users/users.service.js +140 -0
  114. package/dist/server/workspace/workspace.controller.d.ts +24 -0
  115. package/dist/server/workspace/workspace.controller.js +132 -0
  116. package/dist/server/workspace/workspace.module.d.ts +2 -0
  117. package/dist/server/workspace/workspace.module.js +21 -0
  118. package/dist/server/workspace/workspace.service.d.ts +25 -0
  119. package/dist/server/workspace/workspace.service.js +103 -0
  120. package/dist/tools/browser-tool.d.ts +10 -0
  121. package/dist/tools/browser-tool.js +362 -0
  122. package/dist/tools/index.d.ts +3 -0
  123. package/dist/tools/index.js +3 -0
  124. package/dist/tools/install-skill-tool.d.ts +9 -0
  125. package/dist/tools/install-skill-tool.js +77 -0
  126. package/dist/tools/save-experience-tool.d.ts +5 -0
  127. package/dist/tools/save-experience-tool.js +54 -0
  128. package/package.json +80 -0
  129. package/skills/agent-browser/SKILL.md +207 -0
  130. package/skills/agent-browser/references/authentication.md +202 -0
  131. package/skills/agent-browser/references/commands.md +259 -0
  132. package/skills/agent-browser/references/proxy-support.md +188 -0
  133. package/skills/agent-browser/references/session-management.md +193 -0
  134. package/skills/agent-browser/references/snapshot-refs.md +194 -0
  135. package/skills/agent-browser/references/video-recording.md +173 -0
  136. package/skills/agent-browser/templates/authenticated-session.sh +97 -0
  137. package/skills/agent-browser/templates/capture-workflow.sh +69 -0
  138. package/skills/agent-browser/templates/form-automation.sh +62 -0
  139. package/skills/find-skills/SKILL.md +140 -0
@@ -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,3 @@
1
+ export { createBrowserTool, closeBrowser } from "./browser-tool.js";
2
+ export { createSaveExperienceTool } from "./save-experience-tool.js";
3
+ export { createInstallSkillTool } from "./install-skill-tool.js";
@@ -0,0 +1,3 @@
1
+ export { createBrowserTool, closeBrowser } from "./browser-tool.js";
2
+ export { createSaveExperienceTool } from "./save-experience-tool.js";
3
+ export { createInstallSkillTool } from "./install-skill-tool.js";
@@ -0,0 +1,9 @@
1
+ import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
2
+ /**
3
+ * 创建 install_skill 工具:通过后端 API 安装技能。
4
+ * 创建时绑定 targetAgentId,安装目标由此决定:
5
+ * - 具体 agentId → 安装到该 agent 的 workspace/skills
6
+ * - "global"|"all" → 安装到全局 OPENBOT_AGENT_DIR/skills
7
+ * - 未传则后端按默认/全局处理
8
+ */
9
+ export declare function createInstallSkillTool(targetAgentId: string | undefined): ToolDefinition;
@@ -0,0 +1,77 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { getBackendBaseUrl } from "../gateway/backend-url.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 工具:通过后端 API 安装技能。
10
+ * 创建时绑定 targetAgentId,安装目标由此决定:
11
+ * - 具体 agentId → 安装到该 agent 的 workspace/skills
12
+ * - "global"|"all" → 安装到全局 OPENBOT_AGENT_DIR/skills
13
+ * - 未传则后端按默认/全局处理
14
+ */
15
+ export function createInstallSkillTool(targetAgentId) {
16
+ return {
17
+ name: "install_skill",
18
+ label: "Install Skill",
19
+ description: "将指定地址的技能安装到当前智能体工作区目录。在 OpenBot 中为用户安装技能时," +
20
+ "应使用本工具而非 npx skills add,以保证安装到当前智能体对应的技能目录。",
21
+ parameters: InstallSkillSchema,
22
+ execute: async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
23
+ const url = (params.url ?? "").trim();
24
+ if (!url) {
25
+ return {
26
+ content: [{ type: "text", text: "请提供技能安装地址(如 owner/repo 或 owner/repo@skillName)。" }],
27
+ details: undefined,
28
+ };
29
+ }
30
+ const base = getBackendBaseUrl();
31
+ if (!base) {
32
+ return {
33
+ content: [
34
+ {
35
+ type: "text",
36
+ text: "当前环境未连接后端,无法通过工具安装。请让用户在应用内使用「手动安装」并填入该地址:" + url,
37
+ },
38
+ ],
39
+ details: undefined,
40
+ };
41
+ }
42
+ const installUrl = `${base.replace(/\/$/, "")}/server-api/skills/install`;
43
+ try {
44
+ const res = await fetch(installUrl, {
45
+ method: "POST",
46
+ headers: { "Content-Type": "application/json" },
47
+ body: JSON.stringify({ url, targetAgentId: targetAgentId }),
48
+ });
49
+ const data = (await res.json().catch(() => ({})));
50
+ if (!res.ok) {
51
+ const msg = data?.message ?? data?.data?.stderr ?? res.statusText;
52
+ return {
53
+ content: [{ type: "text", text: `安装请求失败: ${msg}` }],
54
+ details: undefined,
55
+ };
56
+ }
57
+ const out = data?.data?.stdout ?? "";
58
+ const err = data?.data?.stderr ?? "";
59
+ const installDir = data?.data?.installDir;
60
+ const dirLine = installDir ? `\n安装目录:${installDir}` : "";
61
+ const baseText = out.trim() || (err.trim() ? `安装完成。\n${err}` : "技能已安装完成。");
62
+ const text = dirLine ? `${baseText}${dirLine}` : baseText;
63
+ return {
64
+ content: [{ type: "text", text }],
65
+ details: data?.data,
66
+ };
67
+ }
68
+ catch (err) {
69
+ const msg = err instanceof Error ? err.message : String(err);
70
+ return {
71
+ content: [{ type: "text", text: `安装请求异常: ${msg}` }],
72
+ details: undefined,
73
+ };
74
+ }
75
+ },
76
+ };
77
+ }
@@ -0,0 +1,5 @@
1
+ import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
2
+ /**
3
+ * 创建 save_experience 工具:对话结束时由 Agent 调用,将经验总结存入向量库(infotype: experience)
4
+ */
5
+ export declare function createSaveExperienceTool(sessionId: string): ToolDefinition;
@@ -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
+ }
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@next-open-ai/openbot",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.1.1",
7
+ "description": "CLI and library to run prompts with skill paths (Agent Skills style). Use as npm package or openbot CLI.",
8
+ "type": "module",
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "skills",
21
+ "README.md"
22
+ ],
23
+ "bin": {
24
+ "openbot": "./dist/cli.js"
25
+ },
26
+ "dependencies": {
27
+ "better-sqlite3": "^11.6.0",
28
+ "croner": "^9.1.0",
29
+ "@mariozechner/pi-ai": "^0.51.2",
30
+ "@mariozechner/pi-coding-agent": "^0.51.2",
31
+ "@nestjs/common": "^10.3.0",
32
+ "@nestjs/core": "^10.3.0",
33
+ "@nestjs/platform-express": "^10.3.0",
34
+ "@nestjs/platform-socket.io": "^10.3.0",
35
+ "@nestjs/websockets": "^10.3.0",
36
+ "@sinclair/typebox": "^0.34.41",
37
+ "@xenova/transformers": "^2.17.2",
38
+ "agent-browser": "^0.8.5",
39
+ "commander": "^14.0.2",
40
+ "reflect-metadata": "^0.2.1",
41
+ "rxjs": "^7.8.1",
42
+ "vectra": "^0.12.3",
43
+ "ws": "^8.19.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/better-sqlite3": "^7.6.11",
47
+ "@types/express": "^4.17.21",
48
+ "@types/node": "^22.0.0",
49
+ "@types/jest": "^29.5.14",
50
+ "@types/ws": "^8.18.1",
51
+ "@nestjs/testing": "^10.3.0",
52
+ "jest": "^29.7.0",
53
+ "ts-jest": "^29.2.5",
54
+ "typescript": "^5.7.0"
55
+ },
56
+ "scripts": {
57
+ "clean": "rm -rf dist",
58
+ "build": "npm run clean && tsc",
59
+ "start": "node dist/cli.js",
60
+ "run": "node dist/cli.js",
61
+ "dev": "npm run build && node dist/cli.js",
62
+ "desktop:dev": "cd desktop && npm run dev",
63
+ "desktop:build": "cd desktop && npm run build",
64
+ "desktop:install": "cd desktop && npm install",
65
+ "test": "jest --config test/jest.config.cjs",
66
+ "test:memory": "node test/run-memory.mjs",
67
+ "test:e2e": "OPENBOT_DB_PATH=:memory: jest --config test/jest.config.cjs --testPathPattern=e2e"
68
+ },
69
+ "engines": {
70
+ "node": ">=20.0.0"
71
+ },
72
+ "keywords": [
73
+ "skill",
74
+ "agent",
75
+ "cli",
76
+ "llm",
77
+ "openbot"
78
+ ],
79
+ "license": "MIT"
80
+ }