@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
package/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # OpenBot
2
+
3
+ [![Node.js](https://img.shields.io/badge/Node.js-20+-green.svg)](https://nodejs.org/)
4
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ **OpenBot** 是基于 Agent Skills 与编码智能体(Coding Agent)的**一体化 AI 助手平台**,支持 CLI、WebSocket 网关与桌面端。通过可插拔技能(Skills)、浏览器自动化、代码执行与长期记忆,为开发与日常任务提供可扩展的 AI 工作流。
8
+
9
+ ---
10
+
11
+ ## 特性概览
12
+
13
+ | 能力 | 说明 |
14
+ |------|------|
15
+ | **技能架构** | 基于 Cursor Agent Skills 规范,支持多路径加载、本地安装与动态扩展 |
16
+ | **编码智能体** | 集成 [pi-coding-agent](https://www.npmjs.com/package/@mariozechner/pi-coding-agent),支持多轮工具调用与代码执行 |
17
+ | **浏览器自动化** | 内置 [agent-browser](https://www.npmjs.com/package/agent-browser),可导航、填表、截图与数据抓取 |
18
+ | **长期记忆** | 向量存储(Vectra)+ 本地嵌入,支持经验总结与会话压缩(compaction) |
19
+ | **多端接入** | CLI、WebSocket 网关、Electron 桌面端,同一套 Agent 核心 |
20
+
21
+ ---
22
+
23
+ ## 技术架构
24
+
25
+ ```
26
+ ┌─────────────────────────────────────────────────────────────────────────────┐
27
+ │ 客户端 / 接入层 │
28
+ ├─────────────────┬─────────────────────────────┬─────────────────────────────┤
29
+ │ CLI (openbot) │ WebSocket Gateway (JSON-RPC) │ OpenBot Desktop (Electron) │
30
+ │ Commander │ ws, 端口 3000 │ Vue 3 + Pinia + Vite │
31
+ └────────┬────────┴──────────────┬──────────────┴──────────────┬──────────────┘
32
+ │ │ │
33
+ │ │ HTTP + Socket.io │
34
+ ▼ ▼ ▼
35
+ ┌─────────────────────────────────────────────────────────────────────────────┐
36
+ │ Gateway Server (Node) │
37
+ │ • 静态资源 • 自动发现端口 • 子进程拉起 Desktop Server │
38
+ └────────────────────────────────────┬────────────────────────────────────────┘
39
+
40
+ ┌────────────────────────────┼────────────────────────────┐
41
+ ▼ ▼ ▼
42
+ ┌─────────────────┐ ┌─────────────────────────────┐ ┌─────────────────────┐
43
+ │ Agent 核心 │ │ Desktop Backend (NestJS) │ │ Memory / 向量存储 │
44
+ │ AgentManager │ │ server-api/* │ │ Vectra + 嵌入 │
45
+ │ pi-coding-agent│ │ Agents · Skills · Tasks │ │ compaction 扩展 │
46
+ │ pi-ai 多模型 │ │ Auth · Users · Workspace │ │ better-sqlite3 │
47
+ └────────┬────────┘ └─────────────────────────────┘ └─────────────────────┘
48
+
49
+
50
+ ┌─────────────────────────────────────────────────────────────────────────────┐
51
+ │ Tools: read/write/edit · bash · find/grep/ls · browser · install-skill · │
52
+ │ save-experience (写入记忆) │
53
+ └─────────────────────────────────────────────────────────────────────────────┘
54
+ ```
55
+
56
+ - **CLI**:直接调用 Agent 核心,单次提示或批量脚本。
57
+ - **Gateway**:对外提供 WebSocket(JSON-RPC),供 Web/移动端连接;内部负责起端口、拉 Nest 后端、路由请求。
58
+ - **Desktop**:Electron 包一层 Vue 前端 + 本地 Nest 后端,通过 Gateway 或直连后端与 Agent 通信;技能、会话、任务、工作区等由 Nest 模块管理。
59
+ - **Agent 核心**:统一由 `AgentManager` 管理会话、技能注入与工具注册;记忆与 compaction 作为扩展参与 system prompt 与经验写入。
60
+
61
+ ---
62
+
63
+ ## 各端技术栈
64
+
65
+ ### CLI
66
+
67
+ | 类别 | 技术 |
68
+ |------|------|
69
+ | 运行时 | Node.js 20+ |
70
+ | 语言 | TypeScript 5.7 |
71
+ | 入口 | `openbot`(bin → `dist/cli.js`) |
72
+ | 框架 | Commander(子命令:`gateway`、`login`、`config`) |
73
+ | 配置 | `~/.openbot/agent`(API Key、模型、技能等) |
74
+
75
+ ### WebSocket Gateway
76
+
77
+ | 类别 | 技术 |
78
+ |------|------|
79
+ | 协议 | JSON-RPC over WebSocket(`ws`) |
80
+ | 端口 | 默认 3000,可 `-p` 指定 |
81
+ | 职责 | 连接管理、消息路由、静态资源、拉 Nest 子进程 |
82
+ | 方法 | `connect`、`agent.chat`、`agent.cancel`、`subscribe_session`、`unsubscribe_session` 等 |
83
+
84
+ ### Agent 核心
85
+
86
+ | 类别 | 技术 |
87
+ |------|------|
88
+ | 智能体 | @mariozechner/pi-coding-agent |
89
+ | 模型/Provider | @mariozechner/pi-ai(DeepSeek、DashScope、OpenAI 等) |
90
+ | 工具 | read/write/edit、bash、find/grep/ls、browser、install-skill、save-experience |
91
+ | 技能 | SKILL.md 规范,多路径加载,formatSkillsForPrompt 注入 system prompt |
92
+
93
+ ### Desktop 后端(NestJS)
94
+
95
+ | 类别 | 技术 |
96
+ |------|------|
97
+ | 框架 | NestJS 10、Express、Socket.io |
98
+ | 前缀 | `server-api` |
99
+ | 模块 | Database · Agents · AgentConfig · Skills · Config · Auth · Users · Workspace · Tasks · Usage |
100
+ | 数据 | better-sqlite3(若使用本地库) |
101
+
102
+ ### Desktop 前端(Electron + Vue)
103
+
104
+ | 类别 | 技术 |
105
+ |------|------|
106
+ | 壳子 | Electron 28 |
107
+ | 前端 | Vue 3、Vue Router、Pinia |
108
+ | 构建 | Vite 5 |
109
+ | 通信 | axios、socket.io-client |
110
+ | 视图 | Dashboard、Agents、AgentChat/AgentDetail、Sessions、Skills、Settings、Tasks、WorkResults、Workspace、Login |
111
+ | 国际化 | 自研 useI18n + locales (zh/en) |
112
+
113
+ ### 记忆与向量
114
+
115
+ | 类别 | 技术 |
116
+ |------|------|
117
+ | 向量索引 | Vectra(LocalIndex) |
118
+ | 嵌入 | @xenova/transformers(本地模型) |
119
+ | 扩展 | compaction-extension(会话压缩、摘要入 prompt) |
120
+ | 持久化 | 与 agent 目录一致的 memory 目录、better-sqlite3(若用于元数据) |
121
+
122
+ ### 内置技能
123
+
124
+ | 技能 | 说明 |
125
+ |------|------|
126
+ | find-skills | 发现与安装 Cursor/Agent 技能 |
127
+ | agent-browser | 浏览器自动化(Playwright/agent-browser CLI) |
128
+
129
+ ---
130
+
131
+ ## 快速开始
132
+
133
+ ### 环境要求
134
+
135
+ - **Node.js** ≥ 20
136
+ - 可选:`OPENAI_API_KEY` 或 `DEEPSEEK_API_KEY` 等(按所用 provider 配置)
137
+
138
+ ### 安装与构建
139
+
140
+ ```bash
141
+ npm install
142
+ npm run build
143
+ ```
144
+
145
+ ### CLI 使用
146
+
147
+ ```bash
148
+ # 直接对话(使用默认 workspace 与技能)
149
+ openbot "总结一下当前有哪些技能"
150
+
151
+ # 指定技能路径
152
+ openbot -s ./skills "用 find-skills 搜一下 PDF 相关技能"
153
+
154
+ # 仅打印 system/user prompt,不调 LLM
155
+ openbot --dry-run --prompt "查北京天气"
156
+
157
+ # 指定模型与 provider
158
+ openbot --model deepseek-chat --provider deepseek "写一段 TypeScript 示例"
159
+ ```
160
+
161
+ ### 启动 WebSocket 网关
162
+
163
+ ```bash
164
+ openbot gateway --port 3000
165
+ ```
166
+
167
+ 客户端通过 `ws://localhost:3000` 连接,使用 JSON-RPC 调用 `connect`、`agent.chat` 等。
168
+
169
+ ### 启动桌面端
170
+
171
+ ```bash
172
+ # 先构建核心(若未构建)
173
+ npm run build
174
+
175
+ # 开发模式(Vite 热更 + Electron)
176
+ npm run desktop:dev
177
+
178
+ # 仅安装桌面依赖
179
+ npm run desktop:install
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Gateway API 简述
185
+
186
+ - **请求**:`{ "type": "request", "id": "<id>", "method": "<method>", "params": { ... } }`
187
+ - **成功响应**:`{ "type": "response", "id": "<id>", "result": { ... } }`
188
+ - **错误响应**:`{ "type": "response", "id": "<id>", "error": { "message": "..." } }`
189
+ - **服务端事件**:如 `agent.chunk`(流式输出)、`agent.tool`(工具调用)等,格式为 `{ "type": "event", "event": "...", "payload": { ... } }`
190
+
191
+ 常用方法:先 `connect` 建立会话,再通过 `agent.chat` 发送消息并接收流式/事件;`agent.cancel` 取消当前任务。
192
+
193
+ ---
194
+
195
+ ## 开发
196
+
197
+ ```bash
198
+ # 单元/集成测试
199
+ npm test
200
+
201
+ # 仅 e2e
202
+ npm run test:e2e
203
+
204
+ # 记忆相关测试
205
+ npm run test:memory
206
+ ```
207
+
208
+ ---
209
+
210
+ ## 许可证
211
+
212
+ MIT
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 获取 openbot agent 配置目录(默认 ~/.openbot/agent)
3
+ * 可通过环境变量 OPENBOT_AGENT_DIR 覆盖
4
+ */
5
+ export declare function getOpenbotAgentDir(): string;
6
+ /**
7
+ * 获取 openbot 工作空间根目录(默认 ~/.openbot/workspace)
8
+ * 可通过环境变量 OPENBOT_WORKSPACE_DIR 覆盖
9
+ */
10
+ export declare function getOpenbotWorkspaceDir(): string;
11
+ /**
12
+ * 确保 agent 目录存在,并创建默认配置文件
13
+ */
14
+ export declare function ensureDefaultAgentDir(agentDir: string): void;
@@ -0,0 +1,75 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ /**
5
+ * 获取 openbot agent 配置目录(默认 ~/.openbot/agent)
6
+ * 可通过环境变量 OPENBOT_AGENT_DIR 覆盖
7
+ */
8
+ export function getOpenbotAgentDir() {
9
+ return process.env.OPENBOT_AGENT_DIR ?? join(homedir(), ".openbot", "agent");
10
+ }
11
+ /**
12
+ * 获取 openbot 工作空间根目录(默认 ~/.openbot/workspace)
13
+ * 可通过环境变量 OPENBOT_WORKSPACE_DIR 覆盖
14
+ */
15
+ export function getOpenbotWorkspaceDir() {
16
+ return process.env.OPENBOT_WORKSPACE_DIR ?? join(homedir(), ".openbot", "workspace");
17
+ }
18
+ /**
19
+ * 确保 agent 目录存在,并创建默认配置文件
20
+ */
21
+ export function ensureDefaultAgentDir(agentDir) {
22
+ if (!existsSync(agentDir)) {
23
+ mkdirSync(agentDir, { recursive: true });
24
+ }
25
+ const modelsJsonPath = join(agentDir, "models.json");
26
+ if (!existsSync(modelsJsonPath)) {
27
+ const defaultModels = {
28
+ providers: {
29
+ deepseek: {
30
+ name: "DeepSeek",
31
+ apiKey: "OPENAI_API_KEY",
32
+ api: "openai-completions",
33
+ baseUrl: "https://api.deepseek.com/v1",
34
+ authHeader: true,
35
+ models: [
36
+ {
37
+ id: "deepseek-chat",
38
+ name: "DeepSeek Chat",
39
+ contextWindow: 64000,
40
+ supportsTools: true
41
+ },
42
+ {
43
+ id: "deepseek-reasoner",
44
+ name: "DeepSeek Reasoner",
45
+ contextWindow: 64000,
46
+ supportsTools: true
47
+ }
48
+ ]
49
+ },
50
+ dashscope: {
51
+ name: "DashScope (Alibaba)",
52
+ apiKey: "DASHSCOPE_API_KEY",
53
+ api: "openai-completions",
54
+ baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
55
+ authHeader: true,
56
+ models: [
57
+ {
58
+ id: "qwen-max",
59
+ name: "Qwen Max",
60
+ contextWindow: 30000,
61
+ supportsTools: true
62
+ },
63
+ {
64
+ id: "qwen-plus",
65
+ name: "Qwen Plus",
66
+ contextWindow: 128000,
67
+ supportsTools: true
68
+ }
69
+ ]
70
+ }
71
+ }
72
+ };
73
+ writeFileSync(modelsJsonPath, JSON.stringify(defaultModels, null, 2), "utf-8");
74
+ }
75
+ }
@@ -0,0 +1,61 @@
1
+ import type { AgentSession } from "@mariozechner/pi-coding-agent";
2
+ import type { Skill } from "./skills.js";
3
+ export interface AgentManagerOptions {
4
+ agentDir?: string;
5
+ workspace?: string;
6
+ skillPaths?: string[];
7
+ skills?: Skill[];
8
+ }
9
+ /**
10
+ * Unified Agent Manager for both CLI and Gateway
11
+ */
12
+ export declare class AgentManager {
13
+ private sessions;
14
+ /** 每个 session 最后被使用的时间戳,用于 LRU 淘汰 */
15
+ private sessionLastActiveAt;
16
+ private agentDir;
17
+ private workspaceDir;
18
+ private skillPaths;
19
+ private preLoadedSkills;
20
+ constructor(options?: AgentManagerOptions);
21
+ /**
22
+ * Re-configure the manager
23
+ */
24
+ configure(options: AgentManagerOptions): void;
25
+ /**
26
+ * Build system prompt with skills and browser tool description
27
+ */
28
+ buildSystemPrompt(skills: Skill[]): string;
29
+ /**
30
+ * Get the initial context (prompt and skills)
31
+ */
32
+ getContext(): Promise<{
33
+ systemPrompt: string;
34
+ skills: Skill[];
35
+ }>;
36
+ private createResourceLoader;
37
+ /**
38
+ * Resolve all relevant skill paths (Global, Project, Workspace)
39
+ * @param workspaceDir 当前会话使用的工作区目录,不传则用 manager 默认
40
+ */
41
+ private resolveSkillPaths;
42
+ /**
43
+ * Get or create an agent session.
44
+ * @param options.workspace 该会话绑定的工作区名(来自 agent 配置),不传则用 default,创建时 cwd/技能路径依此
45
+ * @param options.provider / options.modelId 来自 agent 配置的大模型,不传则用环境变量默认
46
+ * @param options.maxSessions 若提供且当前 session 数 >= 该值,会先淘汰最后调用时间最早的 session 再创建新的
47
+ * @param options.targetAgentId 创建时绑定到 install_skill 工具,用于安装目标(具体 agentId 或 global)
48
+ */
49
+ getOrCreateSession(sessionId: string, options?: {
50
+ workspace?: string;
51
+ provider?: string;
52
+ modelId?: string;
53
+ apiKey?: string;
54
+ maxSessions?: number;
55
+ targetAgentId?: string;
56
+ }): Promise<AgentSession>;
57
+ getSession(sessionId: string): AgentSession | undefined;
58
+ deleteSession(sessionId: string): boolean;
59
+ clearAll(): void;
60
+ }
61
+ export declare const agentManager: AgentManager;
@@ -0,0 +1,257 @@
1
+ import { createAgentSession, AuthStorage, DefaultResourceLoader, ModelRegistry, SessionManager as CoreSessionManager, createReadTool, createWriteTool, createEditTool, createBashTool, createFindTool, createGrepTool, createLsTool } from "@mariozechner/pi-coding-agent";
2
+ import { join } from "node:path";
3
+ import { existsSync, mkdirSync } from "node:fs";
4
+ import { createCompactionMemoryExtensionFactory } from "../memory/compaction-extension.js";
5
+ import { getCompactionContextForSystemPrompt } from "../memory/index.js";
6
+ import { createBrowserTool, createSaveExperienceTool, createInstallSkillTool } from "../tools/index.js";
7
+ import { registerBuiltInApiProviders } from "@mariozechner/pi-ai/dist/providers/register-builtins.js";
8
+ import { getOpenbotAgentDir, getOpenbotWorkspaceDir, ensureDefaultAgentDir } from "./agent-dir.js";
9
+ import { formatSkillsForPrompt } from "./skills.js";
10
+ // Ensure all built-in providers are registered
11
+ registerBuiltInApiProviders();
12
+ /** system prompt 中每个技能描述最大字符数,超出截断以省 token */
13
+ const MAX_SKILL_DESC_IN_PROMPT = 250;
14
+ /**
15
+ * Unified Agent Manager for both CLI and Gateway
16
+ */
17
+ export class AgentManager {
18
+ sessions = new Map();
19
+ /** 每个 session 最后被使用的时间戳,用于 LRU 淘汰 */
20
+ sessionLastActiveAt = new Map();
21
+ agentDir;
22
+ workspaceDir;
23
+ skillPaths = [];
24
+ preLoadedSkills = [];
25
+ constructor(options = {}) {
26
+ this.agentDir = options.agentDir || getOpenbotAgentDir();
27
+ // Centralized workspace root: ~/.openbot/workspace/
28
+ const workspaceRoot = getOpenbotWorkspaceDir();
29
+ const workspaceName = options.workspace || "default";
30
+ this.workspaceDir = join(workspaceRoot, workspaceName);
31
+ this.skillPaths = options.skillPaths || [];
32
+ this.preLoadedSkills = options.skills || [];
33
+ // Ensure workspace directory exists
34
+ if (!existsSync(this.workspaceDir)) {
35
+ mkdirSync(this.workspaceDir, { recursive: true });
36
+ }
37
+ }
38
+ /**
39
+ * Re-configure the manager
40
+ */
41
+ configure(options) {
42
+ if (options.agentDir)
43
+ this.agentDir = options.agentDir;
44
+ if (options.workspace) {
45
+ const workspaceRoot = getOpenbotWorkspaceDir();
46
+ this.workspaceDir = join(workspaceRoot, options.workspace);
47
+ if (!existsSync(this.workspaceDir)) {
48
+ mkdirSync(this.workspaceDir, { recursive: true });
49
+ }
50
+ }
51
+ if (options.skillPaths)
52
+ this.skillPaths = options.skillPaths;
53
+ if (options.skills)
54
+ this.preLoadedSkills = options.skills;
55
+ }
56
+ /**
57
+ * Build system prompt with skills and browser tool description
58
+ */
59
+ buildSystemPrompt(skills) {
60
+ const shortSkills = skills.map((s) => ({
61
+ ...s,
62
+ description: s.description.length <= MAX_SKILL_DESC_IN_PROMPT
63
+ ? s.description
64
+ : s.description.slice(0, MAX_SKILL_DESC_IN_PROMPT) + "…",
65
+ }));
66
+ const skillsBlock = formatSkillsForPrompt(shortSkills);
67
+ const browserToolDesc = `
68
+ ## Browser Tool
69
+
70
+ You have access to a \`browser\` tool for web automation:
71
+ - **navigate**: Navigate to a URL
72
+ - **snapshot**: Get page content with element refs (@e1, @e2, etc.)
73
+ - **screenshot**: Capture page image
74
+ - **click**: Click element (use selector or ref from snapshot)
75
+ - **type**: Type text into element
76
+ - **fill**: Clear and fill input field
77
+ - **scroll**: Scroll page (up/down/left/right)
78
+ - **extract**: Get text from element
79
+ - **wait**: Wait for element to appear
80
+ - **download**: Download file from URL or by clicking download button/link
81
+ - **back/forward**: Navigate browser history
82
+ - **close**: Close browser
83
+
84
+ Use refs from snapshots (e.g., @e1) for reliable element selection.
85
+ For downloads, provide either a direct URL or a selector to click.`;
86
+ const parts = [
87
+ "You are a helpful assistant. When users ask about skills, explain what skills are available.",
88
+ browserToolDesc,
89
+ skillsBlock,
90
+ ].filter(Boolean);
91
+ return parts.join("\n\n");
92
+ }
93
+ /**
94
+ * Get the initial context (prompt and skills)
95
+ */
96
+ async getContext() {
97
+ const loader = this.createResourceLoader(this.workspaceDir);
98
+ await loader.reload();
99
+ const loadedSkills = loader.getSkills().skills;
100
+ const systemPrompt = this.buildSystemPrompt(loadedSkills);
101
+ return { systemPrompt, skills: loadedSkills };
102
+ }
103
+ createResourceLoader(workspaceDir, sessionId, compactionBlock) {
104
+ const loader = new DefaultResourceLoader({
105
+ cwd: workspaceDir,
106
+ agentDir: this.agentDir,
107
+ noSkills: true, // Disable SDK's built-in skills logic to take full control
108
+ additionalSkillPaths: this.resolveSkillPaths(workspaceDir),
109
+ extensionFactories: sessionId ? [createCompactionMemoryExtensionFactory(sessionId)] : [],
110
+ systemPromptOverride: (base) => {
111
+ const loadedSkills = loader.getSkills().skills;
112
+ let customPrompt = this.buildSystemPrompt(loadedSkills);
113
+ if (compactionBlock?.trim()) {
114
+ customPrompt = customPrompt + "\n\n" + compactionBlock.trim();
115
+ }
116
+ return customPrompt;
117
+ },
118
+ });
119
+ return loader;
120
+ }
121
+ /**
122
+ * Resolve all relevant skill paths (Global, Project, Workspace)
123
+ * @param workspaceDir 当前会话使用的工作区目录,不传则用 manager 默认
124
+ */
125
+ resolveSkillPaths(workspaceDir) {
126
+ const paths = new Set();
127
+ const wsDir = workspaceDir ?? this.workspaceDir;
128
+ // 1. Managed skills (Global: ~/.openbot/agent/skills)
129
+ const managedSkillsDir = join(this.agentDir, "skills");
130
+ if (existsSync(managedSkillsDir))
131
+ paths.add(managedSkillsDir);
132
+ // 2. Extra paths (CLI -s / Config)
133
+ this.skillPaths.forEach(p => paths.add(p));
134
+ // 3. Project skills (./skills)
135
+ const projectSkillsDir = join(process.cwd(), "skills");
136
+ if (existsSync(projectSkillsDir))
137
+ paths.add(projectSkillsDir);
138
+ // 4. Workspace skills (./workspace/<name>/skills)
139
+ const workspaceSkillsDir = join(wsDir, "skills");
140
+ if (existsSync(workspaceSkillsDir))
141
+ paths.add(workspaceSkillsDir);
142
+ return Array.from(paths);
143
+ }
144
+ /**
145
+ * Get or create an agent session.
146
+ * @param options.workspace 该会话绑定的工作区名(来自 agent 配置),不传则用 default,创建时 cwd/技能路径依此
147
+ * @param options.provider / options.modelId 来自 agent 配置的大模型,不传则用环境变量默认
148
+ * @param options.maxSessions 若提供且当前 session 数 >= 该值,会先淘汰最后调用时间最早的 session 再创建新的
149
+ * @param options.targetAgentId 创建时绑定到 install_skill 工具,用于安装目标(具体 agentId 或 global)
150
+ */
151
+ async getOrCreateSession(sessionId, options = {}) {
152
+ const now = Date.now();
153
+ if (this.sessions.has(sessionId)) {
154
+ this.sessionLastActiveAt.set(sessionId, now);
155
+ return this.sessions.get(sessionId);
156
+ }
157
+ const { maxSessions } = options;
158
+ if (typeof maxSessions === "number" && maxSessions > 0 && this.sessions.size >= maxSessions) {
159
+ let oldestId = null;
160
+ let oldestAt = Infinity;
161
+ for (const [id, at] of this.sessionLastActiveAt) {
162
+ if (this.sessions.has(id) && at < oldestAt) {
163
+ oldestAt = at;
164
+ oldestId = id;
165
+ }
166
+ }
167
+ if (oldestId != null) {
168
+ this.deleteSession(oldestId);
169
+ }
170
+ }
171
+ const workspaceRoot = getOpenbotWorkspaceDir();
172
+ const workspaceName = options.workspace ?? "default";
173
+ const sessionWorkspaceDir = join(workspaceRoot, workspaceName);
174
+ if (!existsSync(sessionWorkspaceDir)) {
175
+ mkdirSync(sessionWorkspaceDir, { recursive: true });
176
+ }
177
+ const provider = options.provider ?? process.env.OPENBOT_PROVIDER ?? "deepseek";
178
+ const modelId = options.modelId ?? process.env.OPENBOT_MODEL ?? "deepseek-chat";
179
+ const apiKey = options.apiKey;
180
+ ensureDefaultAgentDir(this.agentDir);
181
+ const authPath = join(this.agentDir, "auth.json");
182
+ const modelsPath = join(this.agentDir, "models.json");
183
+ const authStorage = new AuthStorage(authPath);
184
+ if (apiKey) {
185
+ authStorage.setRuntimeApiKey(provider, apiKey);
186
+ }
187
+ if (await authStorage.hasAuth(provider)) {
188
+ const key = await authStorage.getApiKey(provider);
189
+ if (key) {
190
+ if (provider === "deepseek") {
191
+ process.env.OPENAI_API_KEY = key;
192
+ }
193
+ else if (provider === "dashscope") {
194
+ process.env.DASHSCOPE_API_KEY = key;
195
+ }
196
+ if (!process.env.OPENAI_API_KEY) {
197
+ process.env.OPENAI_API_KEY = key;
198
+ }
199
+ }
200
+ }
201
+ const modelRegistry = new ModelRegistry(authStorage, modelsPath);
202
+ authStorage.setFallbackResolver((p) => {
203
+ if (p === "deepseek")
204
+ return process.env.OPENAI_API_KEY || process.env.DEEPSEEK_API_KEY;
205
+ if (p === "dashscope")
206
+ return process.env.DASHSCOPE_API_KEY || process.env.OPENAI_API_KEY;
207
+ return process.env.OPENAI_API_KEY;
208
+ });
209
+ const compactionBlock = await getCompactionContextForSystemPrompt(sessionId);
210
+ const loader = this.createResourceLoader(sessionWorkspaceDir, sessionId, compactionBlock);
211
+ await loader.reload();
212
+ const coreTools = {
213
+ read: createReadTool(sessionWorkspaceDir),
214
+ write: createWriteTool(sessionWorkspaceDir),
215
+ edit: createEditTool(sessionWorkspaceDir),
216
+ bash: createBashTool(sessionWorkspaceDir),
217
+ find: createFindTool(sessionWorkspaceDir),
218
+ grep: createGrepTool(sessionWorkspaceDir),
219
+ ls: createLsTool(sessionWorkspaceDir),
220
+ };
221
+ const { session } = await createAgentSession({
222
+ agentDir: this.agentDir,
223
+ sessionManager: CoreSessionManager.inMemory(),
224
+ authStorage,
225
+ modelRegistry,
226
+ cwd: sessionWorkspaceDir,
227
+ resourceLoader: loader,
228
+ customTools: [
229
+ createBrowserTool(sessionWorkspaceDir),
230
+ createSaveExperienceTool(sessionId),
231
+ createInstallSkillTool(options.targetAgentId),
232
+ ],
233
+ baseToolsOverride: coreTools,
234
+ });
235
+ const model = modelRegistry.find(provider, modelId);
236
+ if (model) {
237
+ console.log(`Setting model to ${model.provider}/${model.id} (workspace: ${workspaceName})`);
238
+ await session.setModel(model);
239
+ }
240
+ this.sessions.set(sessionId, session);
241
+ this.sessionLastActiveAt.set(sessionId, now);
242
+ return session;
243
+ }
244
+ getSession(sessionId) {
245
+ return this.sessions.get(sessionId);
246
+ }
247
+ deleteSession(sessionId) {
248
+ this.sessionLastActiveAt.delete(sessionId);
249
+ return this.sessions.delete(sessionId);
250
+ }
251
+ clearAll() {
252
+ this.sessions.clear();
253
+ this.sessionLastActiveAt.clear();
254
+ }
255
+ }
256
+ // Singleton for easy access (e.g., from Gateway)
257
+ export const agentManager = new AgentManager();
@@ -0,0 +1,25 @@
1
+ export interface ConfigInfo {
2
+ provider: string;
3
+ model: string;
4
+ hasKey: boolean;
5
+ }
6
+ /**
7
+ * Manage agent configuration (auth.json, models.json)
8
+ */
9
+ export declare class ConfigManager {
10
+ private agentDir;
11
+ private authStorage;
12
+ constructor(agentDir?: string);
13
+ /**
14
+ * Save API key for a provider
15
+ */
16
+ login(provider: string, apiKey: string): Promise<void>;
17
+ /**
18
+ * Update default model for a provider in models.json
19
+ */
20
+ setModel(provider: string, modelId: string): Promise<void>;
21
+ /**
22
+ * List current configurations
23
+ */
24
+ list(): ConfigInfo[];
25
+ }