@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.
- package/README.md +117 -17
- package/apps/desktop/renderer/dist/assets/index-DKtaRFW4.js +89 -0
- package/apps/desktop/renderer/dist/assets/index-QHuqXpWQ.css +10 -0
- package/apps/desktop/renderer/dist/index.html +3 -3
- package/dist/cli/cli.js +27 -1
- package/dist/cli/service.d.ts +13 -0
- package/dist/cli/service.js +243 -0
- package/dist/core/agent/agent-manager.d.ts +3 -0
- package/dist/core/agent/agent-manager.js +12 -6
- package/dist/core/config/desktop-config.d.ts +4 -0
- package/dist/core/config/desktop-config.js +5 -1
- package/dist/core/installer/index.d.ts +1 -1
- package/dist/core/installer/index.js +1 -1
- package/dist/core/installer/skill-installer.d.ts +9 -0
- package/dist/core/installer/skill-installer.js +94 -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/tools/bookmark-tool.d.ts +9 -0
- package/dist/core/tools/bookmark-tool.js +118 -0
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.js +1 -0
- package/dist/gateway/methods/agent-chat.js +1 -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.js +1 -0
- package/dist/gateway/server.js +26 -1
- package/dist/server/agent-config/agent-config.controller.d.ts +1 -1
- package/dist/server/agent-config/agent-config.service.d.ts +4 -1
- package/dist/server/agent-config/agent-config.service.js +2 -0
- package/dist/server/agents/agents.controller.js +3 -5
- package/dist/server/agents/agents.service.d.ts +1 -1
- package/dist/server/agents/agents.service.js +4 -2
- package/dist/server/app.module.js +2 -0
- package/dist/server/database/database.service.d.ts +7 -0
- package/dist/server/database/database.service.js +54 -5
- package/dist/server/saved-items/saved-items.controller.d.ts +26 -0
- package/dist/server/saved-items/saved-items.controller.js +78 -0
- package/dist/server/saved-items/saved-items.module.d.ts +2 -0
- package/dist/server/saved-items/saved-items.module.js +23 -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.service.d.ts +2 -0
- package/dist/server/skills/skills.service.js +80 -16
- package/package.json +8 -2
- package/skills/url-bookmark/SKILL.md +36 -0
- package/apps/desktop/renderer/dist/assets/index-BOS-F8a4.js +0 -89
- 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
|
-
//
|
|
33
|
-
const systemSkillsDir =
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
263
|
-
|
|
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
|
|
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": "
|
|
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 返回的标签名做匹配(同义或相近即可选对应标签)。
|