@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.
- package/README.md +212 -0
- package/dist/agent/agent-dir.d.ts +14 -0
- package/dist/agent/agent-dir.js +75 -0
- package/dist/agent/agent-manager.d.ts +61 -0
- package/dist/agent/agent-manager.js +257 -0
- package/dist/agent/config-manager.d.ts +25 -0
- package/dist/agent/config-manager.js +84 -0
- package/dist/agent/desktop-config.d.ts +15 -0
- package/dist/agent/desktop-config.js +91 -0
- package/dist/agent/run.d.ts +26 -0
- package/dist/agent/run.js +65 -0
- package/dist/agent/skills.d.ts +20 -0
- package/dist/agent/skills.js +86 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +168 -0
- package/dist/gateway/backend-url.d.ts +2 -0
- package/dist/gateway/backend-url.js +11 -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/desktop-config.d.ts +7 -0
- package/dist/gateway/desktop-config.js +25 -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 +194 -0
- package/dist/gateway/methods/connect.d.ts +8 -0
- package/dist/gateway/methods/connect.js +15 -0
- package/dist/gateway/methods/install-skill-from-path.d.ts +13 -0
- package/dist/gateway/methods/install-skill-from-path.js +48 -0
- package/dist/gateway/methods/run-scheduled-task.d.ts +13 -0
- package/dist/gateway/methods/run-scheduled-task.js +164 -0
- package/dist/gateway/server.d.ts +10 -0
- package/dist/gateway/server.js +268 -0
- package/dist/gateway/types.d.ts +76 -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/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/memory/build-summary.d.ts +6 -0
- package/dist/memory/build-summary.js +27 -0
- package/dist/memory/compaction-extension.d.ts +6 -0
- package/dist/memory/compaction-extension.js +23 -0
- package/dist/memory/embedding.d.ts +10 -0
- package/dist/memory/embedding.js +22 -0
- package/dist/memory/index.d.ts +29 -0
- package/dist/memory/index.js +66 -0
- package/dist/memory/types.d.ts +16 -0
- package/dist/memory/types.js +1 -0
- package/dist/memory/vector-store.d.ts +15 -0
- package/dist/memory/vector-store.js +65 -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 +34 -0
- package/dist/server/agent-config/agent-config.service.js +171 -0
- package/dist/server/agents/agents.controller.d.ts +41 -0
- package/dist/server/agents/agents.controller.js +120 -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 +167 -0
- package/dist/server/app.module.d.ts +2 -0
- package/dist/server/app.module.js +36 -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/config/config.controller.d.ts +51 -0
- package/dist/server/config/config.controller.js +81 -0
- package/dist/server/config/config.module.d.ts +2 -0
- package/dist/server/config/config.module.js +19 -0
- package/dist/server/config/config.service.d.ts +34 -0
- package/dist/server/config/config.service.js +91 -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 +11 -0
- package/dist/server/database/database.service.js +137 -0
- package/dist/server/main.d.ts +1 -0
- package/dist/server/main.js +18 -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 +63 -0
- package/dist/server/skills/skills.service.js +324 -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 +22 -0
- package/dist/server/tasks/tasks.service.d.ts +84 -0
- package/dist/server/tasks/tasks.service.js +313 -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 +25 -0
- package/dist/server/workspace/workspace.service.js +103 -0
- package/dist/tools/browser-tool.d.ts +10 -0
- package/dist/tools/browser-tool.js +362 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/install-skill-tool.d.ts +9 -0
- package/dist/tools/install-skill-tool.js +77 -0
- package/dist/tools/save-experience-tool.d.ts +5 -0
- package/dist/tools/save-experience-tool.js +54 -0
- package/package.json +80 -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/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# OpenBot
|
|
2
|
+
|
|
3
|
+
[](https://nodejs.org/)
|
|
4
|
+
[](https://www.typescriptlang.org/)
|
|
5
|
+
[](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
|
+
}
|