@next-open-ai/openbot 0.2.1 → 0.3.2

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 (67) hide show
  1. package/README.md +117 -17
  2. package/apps/desktop/renderer/dist/assets/index-DKtaRFW4.js +89 -0
  3. package/apps/desktop/renderer/dist/assets/index-QHuqXpWQ.css +10 -0
  4. package/apps/desktop/renderer/dist/index.html +3 -3
  5. package/dist/cli/cli.js +27 -1
  6. package/dist/cli/service.d.ts +13 -0
  7. package/dist/cli/service.js +243 -0
  8. package/dist/core/agent/agent-manager.d.ts +3 -0
  9. package/dist/core/agent/agent-manager.js +12 -6
  10. package/dist/core/config/desktop-config.d.ts +4 -0
  11. package/dist/core/config/desktop-config.js +5 -1
  12. package/dist/core/installer/index.d.ts +1 -1
  13. package/dist/core/installer/index.js +1 -1
  14. package/dist/core/installer/skill-installer.d.ts +9 -0
  15. package/dist/core/installer/skill-installer.js +94 -0
  16. package/dist/core/mcp/adapter.d.ts +17 -0
  17. package/dist/core/mcp/adapter.js +49 -0
  18. package/dist/core/mcp/client.d.ts +24 -0
  19. package/dist/core/mcp/client.js +70 -0
  20. package/dist/core/mcp/config.d.ts +22 -0
  21. package/dist/core/mcp/config.js +69 -0
  22. package/dist/core/mcp/index.d.ts +18 -0
  23. package/dist/core/mcp/index.js +20 -0
  24. package/dist/core/mcp/operator.d.ts +15 -0
  25. package/dist/core/mcp/operator.js +72 -0
  26. package/dist/core/mcp/transport/index.d.ts +11 -0
  27. package/dist/core/mcp/transport/index.js +16 -0
  28. package/dist/core/mcp/transport/sse.d.ts +20 -0
  29. package/dist/core/mcp/transport/sse.js +82 -0
  30. package/dist/core/mcp/transport/stdio.d.ts +32 -0
  31. package/dist/core/mcp/transport/stdio.js +132 -0
  32. package/dist/core/mcp/types.d.ts +72 -0
  33. package/dist/core/mcp/types.js +5 -0
  34. package/dist/core/tools/bookmark-tool.d.ts +9 -0
  35. package/dist/core/tools/bookmark-tool.js +118 -0
  36. package/dist/core/tools/index.d.ts +1 -0
  37. package/dist/core/tools/index.js +1 -0
  38. package/dist/gateway/methods/agent-chat.js +1 -0
  39. package/dist/gateway/methods/install-skill-from-upload.d.ts +14 -0
  40. package/dist/gateway/methods/install-skill-from-upload.js +13 -0
  41. package/dist/gateway/methods/run-scheduled-task.js +1 -0
  42. package/dist/gateway/server.js +26 -1
  43. package/dist/server/agent-config/agent-config.controller.d.ts +1 -1
  44. package/dist/server/agent-config/agent-config.service.d.ts +4 -1
  45. package/dist/server/agent-config/agent-config.service.js +2 -0
  46. package/dist/server/agents/agents.controller.js +3 -5
  47. package/dist/server/agents/agents.service.d.ts +1 -1
  48. package/dist/server/agents/agents.service.js +4 -2
  49. package/dist/server/app.module.js +2 -0
  50. package/dist/server/database/database.service.d.ts +7 -0
  51. package/dist/server/database/database.service.js +54 -5
  52. package/dist/server/saved-items/saved-items.controller.d.ts +26 -0
  53. package/dist/server/saved-items/saved-items.controller.js +78 -0
  54. package/dist/server/saved-items/saved-items.module.d.ts +2 -0
  55. package/dist/server/saved-items/saved-items.module.js +23 -0
  56. package/dist/server/saved-items/saved-items.service.d.ts +31 -0
  57. package/dist/server/saved-items/saved-items.service.js +105 -0
  58. package/dist/server/saved-items/tags.controller.d.ts +30 -0
  59. package/dist/server/saved-items/tags.controller.js +85 -0
  60. package/dist/server/saved-items/tags.service.d.ts +24 -0
  61. package/dist/server/saved-items/tags.service.js +84 -0
  62. package/dist/server/skills/skills.service.d.ts +2 -0
  63. package/dist/server/skills/skills.service.js +80 -16
  64. package/package.json +8 -2
  65. package/skills/url-bookmark/SKILL.md +36 -0
  66. package/apps/desktop/renderer/dist/assets/index-BOS-F8a4.js +0 -89
  67. package/apps/desktop/renderer/dist/assets/index-DxqxayUL.css +0 -10
@@ -20,6 +20,24 @@ import { getOpenbotAgentDir, getOpenbotWorkspaceDir } from '../../core/agent/age
20
20
  const execAsync = promisify(exec);
21
21
  /** 工作区技能名仅允许英文、数字、下划线、连字符 */
22
22
  const SKILL_NAME_REGEX = /^[a-zA-Z0-9_-]+$/;
23
+ /**
24
+ * 系统技能目录:仅两条规则,不做复杂搜索。
25
+ * - 运行时/打包:由桌面 main 或部署方设置 OPENBOT_SYSTEM_SKILLS_DIR,用该目录。
26
+ * - 开发/测试:未设置时仅尝试「当前工作目录下的 skills」(从项目根启动时即项目目录下的 skills)。
27
+ */
28
+ function resolveSystemSkillsDir() {
29
+ const envDir = process.env.OPENBOT_SYSTEM_SKILLS_DIR?.trim();
30
+ if (envDir) {
31
+ const abs = resolve(envDir);
32
+ if (existsSync(abs))
33
+ return abs;
34
+ return null;
35
+ }
36
+ const cwdSkills = resolve(process.cwd(), 'skills');
37
+ if (existsSync(cwdSkills))
38
+ return cwdSkills;
39
+ return null;
40
+ }
23
41
  /** 技能目录与来源:全局=OPENBOT_AGENT_DIR/skills,系统=项目根/skills,工作区=OPENBOT_WORKSPACE_DIR/xxx/skills */
24
42
  let SkillsService = class SkillsService {
25
43
  /** 待扫描的目录列表 */
@@ -29,14 +47,14 @@ let SkillsService = class SkillsService {
29
47
  const workspaceName = process.env.OPENBOT_WORKSPACE || 'default';
30
48
  // 全局技能:OPENBOT_AGENT_DIR/skills(默认 ~/.openbot/agent/skills)
31
49
  const globalSkillsDir = join(getOpenbotAgentDir(), 'skills');
32
- // 系统技能:当前项目代码根目录下的 skills
33
- const systemSkillsDir = resolve(cwd, 'skills');
50
+ // 系统技能:OPENBOT_SYSTEM_SKILLS_DIR 或 cwd/skills(仅此两种)
51
+ const systemSkillsDir = resolveSystemSkillsDir();
34
52
  // 工作区技能:OPENBOT_WORKSPACE_DIR/<name>/skills(默认 ~/.openbot/workspace/xxx/skills)
35
53
  const workspaceSkillsDir = join(getOpenbotWorkspaceDir(), workspaceName, 'skills');
36
54
  if (existsSync(globalSkillsDir)) {
37
55
  this.skillPaths.push({ path: globalSkillsDir, source: 'global' });
38
56
  }
39
- if (existsSync(systemSkillsDir)) {
57
+ if (systemSkillsDir) {
40
58
  this.skillPaths.push({ path: systemSkillsDir, source: 'system' });
41
59
  }
42
60
  if (existsSync(workspaceSkillsDir)) {
@@ -220,8 +238,20 @@ let SkillsService = class SkillsService {
220
238
  /** 仅返回指定工作区下的技能(文件目录管理,不涉及 SQLite) */
221
239
  async getSkillsForWorkspace(workspaceName) {
222
240
  const skillPath = this.getWorkspaceSkillsDir(workspaceName);
223
- if (!existsSync(skillPath))
224
- return [];
241
+ if (!existsSync(skillPath)) {
242
+ if (workspaceName === DEFAULT_AGENT_ID || workspaceName === 'default') {
243
+ try {
244
+ await mkdir(skillPath, { recursive: true });
245
+ }
246
+ catch (e) {
247
+ console.warn(`[skills] 创建 default 工作区技能目录失败: ${skillPath}`, e);
248
+ }
249
+ }
250
+ if (!existsSync(skillPath)) {
251
+ console.warn(`[skills] 工作区技能目录不存在,返回空列表: workspace=${workspaceName}, path=${skillPath}`);
252
+ return [];
253
+ }
254
+ }
225
255
  const skills = [];
226
256
  try {
227
257
  const entries = await readdir(skillPath);
@@ -232,13 +262,24 @@ let SkillsService = class SkillsService {
232
262
  if (!stats.isDirectory())
233
263
  continue;
234
264
  const skillMdPath = join(fullPath, 'SKILL.md');
235
- if (!existsSync(skillMdPath))
236
- continue;
237
- const content = await readFile(skillMdPath, 'utf-8');
238
- skills.push(this.parseSkillFile(entry, content, fullPath, 'workspace'));
265
+ const skillJsonPath = join(fullPath, 'skill.json');
266
+ const packageJsonPath = join(fullPath, 'package.json');
267
+ if (existsSync(skillMdPath)) {
268
+ const content = await readFile(skillMdPath, 'utf-8');
269
+ skills.push(this.parseSkillFile(entry, content, fullPath, 'workspace'));
270
+ }
271
+ else if (existsSync(skillJsonPath)) {
272
+ const skill = await this.parseSkillJson(entry, skillJsonPath, fullPath);
273
+ if (skill)
274
+ skills.push(skill);
275
+ }
276
+ else if (existsSync(packageJsonPath)) {
277
+ const skill = await this.parseSkillJson(entry, packageJsonPath, fullPath);
278
+ if (skill)
279
+ skills.push(skill);
280
+ }
239
281
  }
240
282
  catch (entryError) {
241
- // 跳过无效条目:ENOENT(目录已删/损坏的符号链接)、无权限等
242
283
  if (entryError?.code !== 'ENOENT') {
243
284
  console.warn(`Skipping workspace skill entry ${entry}:`, entryError?.message ?? entryError);
244
285
  }
@@ -250,18 +291,41 @@ let SkillsService = class SkillsService {
250
291
  }
251
292
  return skills;
252
293
  }
294
+ /** 从 skill.json 或 package.json 解析出 Skill(兼容无 SKILL.md 的 npm 风格技能目录) */
295
+ async parseSkillJson(entryName, jsonPath, fullPath) {
296
+ try {
297
+ const content = await readFile(jsonPath, 'utf-8');
298
+ const data = JSON.parse(content);
299
+ const name = (data.name || entryName).trim() || entryName;
300
+ const description = (data.description || '').trim() || 'No description available';
301
+ return { name: entryName, description, path: fullPath, source: 'workspace' };
302
+ }
303
+ catch {
304
+ return null;
305
+ }
306
+ }
253
307
  async getSkillContentForWorkspace(workspaceName, name) {
254
308
  const skillPath = this.getWorkspaceSkillsDir(workspaceName);
255
309
  const fullPath = join(skillPath, name);
256
310
  const skillMdPath = join(fullPath, 'SKILL.md');
257
- if (!existsSync(skillMdPath))
258
- return null;
259
- try {
260
- return await readFile(skillMdPath, 'utf-8');
311
+ const readmePath = join(fullPath, 'README.md');
312
+ if (existsSync(skillMdPath)) {
313
+ try {
314
+ return await readFile(skillMdPath, 'utf-8');
315
+ }
316
+ catch {
317
+ return null;
318
+ }
261
319
  }
262
- catch {
263
- return null;
320
+ if (existsSync(readmePath)) {
321
+ try {
322
+ return await readFile(readmePath, 'utf-8');
323
+ }
324
+ catch {
325
+ return null;
326
+ }
264
327
  }
328
+ return null;
265
329
  }
266
330
  /** 在工作区下新增技能(创建目录 + SKILL.md) */
267
331
  async addSkill(workspaceName, name, options) {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.2.1",
6
+ "version": "0.3.2",
7
7
  "description": "CLI and library to run prompts with skill paths (Agent Skills style). Use as npm package or openbot CLI.",
8
8
  "type": "module",
9
9
  "main": "dist/index.js",
@@ -33,9 +33,11 @@
33
33
  "@nestjs/platform-socket.io": "^10.3.0",
34
34
  "@nestjs/websockets": "^10.3.0",
35
35
  "@sinclair/typebox": "^0.34.41",
36
+ "adm-zip": "^0.5.16",
36
37
  "agent-browser": "^0.8.5",
37
38
  "commander": "^14.0.2",
38
39
  "croner": "^9.1.0",
40
+ "multer": "^1.4.5-lts.1",
39
41
  "reflect-metadata": "^0.2.1",
40
42
  "rxjs": "^7.8.1",
41
43
  "sql.js": "^1.13.0",
@@ -44,17 +46,20 @@
44
46
  },
45
47
  "devDependencies": {
46
48
  "@nestjs/testing": "^10.3.0",
49
+ "@types/adm-zip": "^0.5.7",
47
50
  "@types/express": "^4.17.21",
48
51
  "@types/jest": "^29.5.14",
52
+ "@types/multer": "^2.0.0",
49
53
  "@types/node": "^22.0.0",
50
54
  "@types/sql.js": "^1.4.9",
51
55
  "@types/ws": "^8.18.1",
52
56
  "jest": "^29.7.0",
57
+ "rimraf": "^5.0.5",
53
58
  "ts-jest": "^29.2.5",
54
59
  "typescript": "^5.7.0"
55
60
  },
56
61
  "scripts": {
57
- "clean": "rm -rf dist",
62
+ "clean": "rimraf dist",
58
63
  "build": "npm run clean && tsc",
59
64
  "build:web": "cd apps/desktop && npm run build:renderer",
60
65
  "build:all": "npm run build && npm run build:web",
@@ -64,6 +69,7 @@
64
69
  "dev": "npm run build && node dist/cli/cli.js",
65
70
  "desktop:dev": "cd apps/desktop && npm run dev",
66
71
  "desktop:build": "cd apps/desktop && npm run build",
72
+ "desktop:pack": "npm run build && cd apps/desktop && npm run build",
67
73
  "desktop:install": "cd apps/desktop && npm install",
68
74
  "test": "jest --config test/jest.config.cjs",
69
75
  "test:memory": "node test/run-memory.mjs",
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: url-bookmark
3
+ description: 根据用户指令将 URL 保存为收藏,并匹配系统中已维护的标签。当用户说「收藏这个链接」「把这条存到技术标签」「保存网址」等时使用。先调用 get_bookmark_tags 获取可用标签列表,再根据用户意图选择匹配的标签,最后用 save_bookmark 保存。
4
+ allowed-tools: get_bookmark_tags, save_bookmark
5
+ ---
6
+
7
+ # URL 收藏技能
8
+
9
+ ## 何时使用
10
+
11
+ 当用户希望**保存一个链接/URL 到收藏**时使用本技能,例如:
12
+
13
+ - 「收藏这个链接」「把这条链接存一下」
14
+ - 「把这个网址加到技术标签」「存到待读里」
15
+ - 「保存当前页面」「记一下这个地址」
16
+
17
+ ## 工作流程
18
+
19
+ 1. **获取可用标签**:先调用工具 `get_bookmark_tags`,获取系统中已维护的标签列表(用户在「设置 → 标签管理」中维护)。
20
+ 2. **理解用户意图**:从用户话里提取:
21
+ - 要收藏的 **URL**(用户可能直接给出,或说「当前页面」「刚发的那条」等,需结合上下文确定);
22
+ - 用户指定的**标签**(如「技术」「待读」),须与 `get_bookmark_tags` 返回的标签名**完全一致**;若用户未指定,可根据内容推断一个已有标签或留空。
23
+ 3. **保存收藏**:调用工具 `save_bookmark`,传入:
24
+ - `url`:必填,要收藏的链接;
25
+ - `title`:可选,可取自页面标题或用户描述;
26
+ - `tagNames`:可选,从可用标签中选出的名称数组(须与系统标签一致)。
27
+
28
+ ## 工具说明
29
+
30
+ - **get_bookmark_tags**:无参数。返回当前可用的标签名称列表,供后续匹配。
31
+ - **save_bookmark**:参数 `url`(必填)、`title`(可选)、`tagNames`(可选,字符串数组)。标签名必须来自 get_bookmark_tags,否则不会关联成功。
32
+
33
+ ## 注意
34
+
35
+ - 标签由用户在「设置 → 标签管理」中维护;若尚无标签,可只保存 URL,不传 `tagNames`。
36
+ - 用户说「存到某某分类」时,将「某某」与 get_bookmark_tags 返回的标签名做匹配(同义或相近即可选对应标签)。