@next-open-ai/openbot 0.2.1 → 0.2.8
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 +105 -16
- package/apps/desktop/renderer/dist/index.html +1 -1
- package/dist/cli/cli.js +27 -1
- package/dist/cli/service.d.ts +13 -0
- package/dist/cli/service.js +243 -0
- package/dist/gateway/server.js +2 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
▼ ▼ ▼
|
|
37
37
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
38
38
|
│ Gateway Server (Node) │
|
|
39
|
-
│ •
|
|
39
|
+
│ • 内嵌 Nest(/server-api)• 按 path 分流 • 静态资源 • 自动发现端口 │
|
|
40
40
|
└────────────────────────────────────┬────────────────────────────────────────┘
|
|
41
41
|
│
|
|
42
42
|
┌────────────────────────────┼────────────────────────────┐
|
|
@@ -55,21 +55,55 @@
|
|
|
55
55
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
- **CLI**:直接调用 Agent
|
|
59
|
-
- **WebSocket Gateway**(`src/gateway
|
|
60
|
-
- **Desktop 后端**(`src/server/`):NestJS HTTP API,即 **server-api
|
|
58
|
+
- **CLI**:直接调用 Agent 核心,单次提示或批量脚本;可启动 Gateway(`openbot gateway`)及配置开机自启(`openbot service install/uninstall`)。
|
|
59
|
+
- **WebSocket Gateway**(`src/gateway/`):单进程内嵌 Nest,对外提供 WebSocket(JSON-RPC)与 HTTP;按 path 分流:`/server-api` 走 Nest、`/ws` 为 Agent 对话、`/ws/voice`/`/sse`/`/channel` 为扩展占位,其余为静态资源。供 Web/移动端连接;支持以开机/登录自启方式常驻(Linux cron、macOS LaunchAgent、Windows 计划任务)。
|
|
60
|
+
- **Desktop 后端**(`src/server/`):NestJS HTTP API,即 **server-api**;可被 Gateway 内嵌或独立监听(默认端口 38081)。会话、智能体配置、技能、任务、工作区、鉴权等由本模块提供。
|
|
61
61
|
- **Desktop**:Electron 包一层 Vue 前端 + 上述后端;通过 Gateway 或直连 Desktop 后端与 Agent 通信。
|
|
62
62
|
- **Agent 核心**:统一由 `AgentManager` 管理会话、技能注入与工具注册;记忆与 compaction 作为扩展参与 system prompt 与经验写入。
|
|
63
63
|
|
|
64
|
+
### 项目目录结构
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
openbot/
|
|
68
|
+
├── src/ # 源码(构建输出 dist/)
|
|
69
|
+
│ ├── core/ # 公共核心,CLI 与 Gateway 共用
|
|
70
|
+
│ │ ├── agent/ # AgentManager、run、技能与配置
|
|
71
|
+
│ │ ├── config/ # 桌面配置(~/.openbot/desktop)
|
|
72
|
+
│ │ ├── memory/ # 向量存储、嵌入、compaction
|
|
73
|
+
│ │ ├── installer/ # 技能安装
|
|
74
|
+
│ │ └── tools/ # 内置工具(browser、install-skill、save-experience 等)
|
|
75
|
+
│ ├── cli/ # CLI 入口与 service 子命令
|
|
76
|
+
│ │ ├── cli.ts # 主入口,构建为 dist/cli/cli.js
|
|
77
|
+
│ │ └── service.ts # 开机自启 install/uninstall/stop
|
|
78
|
+
│ ├── gateway/ # WebSocket 网关(内嵌 Nest、path 分流)
|
|
79
|
+
│ ├── server/ # Desktop 后端(NestJS)
|
|
80
|
+
│ ├── cli.ts # 兼容入口,仅转发到 cli/cli.js
|
|
81
|
+
│ └── index.ts # 包导出
|
|
82
|
+
├── apps/
|
|
83
|
+
│ ├── desktop/ # Electron + Vue 桌面端
|
|
84
|
+
│ ├── web/ # 预留
|
|
85
|
+
│ ├── mobile/ # 预留
|
|
86
|
+
│ ├── miniprogram/ # 预留
|
|
87
|
+
│ └── browser-extension/ # 预留
|
|
88
|
+
├── deploy/ # Docker、K8s 等部署
|
|
89
|
+
├── test/ # 单元与 e2e 测试
|
|
90
|
+
├── examples/ # 示例(含 workspace、gateway-client)
|
|
91
|
+
└── skills/ # 内置技能(find-skills、agent-browser)
|
|
92
|
+
```
|
|
93
|
+
|
|
64
94
|
### 目录与模块对应
|
|
65
95
|
|
|
66
96
|
| 目录 | 说明 |
|
|
67
97
|
|------|------|
|
|
68
|
-
| `src/
|
|
69
|
-
| `src/
|
|
70
|
-
| `src/
|
|
71
|
-
| `src/
|
|
72
|
-
| `
|
|
98
|
+
| `src/core/` | **公共核心**:`agent/`(AgentManager、pi-coding-agent)、`config/`(桌面配置)、`memory/`、`installer/`、`tools/`;CLI 与 Gateway 共用。 |
|
|
99
|
+
| `src/cli/` | **CLI**:`cli.ts` 主入口(构建为 `dist/cli/cli.js`),`service.ts` 提供开机自启(install/uninstall/stop)。 |
|
|
100
|
+
| `src/gateway/` | **WebSocket 网关**:单进程内嵌 Nest,按 path 分流:`/server-api`、`/ws`、`/ws/voice`、`/sse`、`/channel`、`/health`、静态资源(`apps/desktop/renderer/dist`)。 |
|
|
101
|
+
| `src/server/` | **Desktop 后端**(NestJS),HTTP API 前缀 `server-api`;可内嵌到 Gateway 或独立监听。 |
|
|
102
|
+
| `apps/desktop/` | **桌面端**(Electron + Vue),前端构建产物由 Gateway 提供。 |
|
|
103
|
+
| `deploy/` | Dockerfile、K8s 等部署配置。 |
|
|
104
|
+
| `test/` | 单元与 e2e 测试(config、gateway、server、installer)。 |
|
|
105
|
+
| `examples/` | 示例工作区、gateway 客户端等。真实工作区根目录为 `~/.openbot/workspace/`。 |
|
|
106
|
+
| `skills/` | 内置技能(SKILL.md 规范)。 |
|
|
73
107
|
|
|
74
108
|
---
|
|
75
109
|
|
|
@@ -82,8 +116,9 @@
|
|
|
82
116
|
| 运行时 | Node.js 20+ |
|
|
83
117
|
| 语言 | TypeScript 5.7 |
|
|
84
118
|
| 入口 | `openbot`(bin → `dist/cli/cli.js`) |
|
|
85
|
-
| 框架 | Commander(子命令:`gateway`、`login`、`config`) |
|
|
86
|
-
| 配置 | `~/.openbot/agent`(API Key
|
|
119
|
+
| 框架 | Commander(子命令:`gateway`、`login`、`config`、`service`) |
|
|
120
|
+
| 配置 | `~/.openbot/agent`(API Key、模型、技能等);`~/.openbot/desktop`(与桌面共用) |
|
|
121
|
+
| 开机自启 | `openbot service install` / `uninstall`(Linux cron、macOS LaunchAgent、Windows 计划任务);`openbot service stop` 停止当前 gateway |
|
|
87
122
|
|
|
88
123
|
### WebSocket Gateway
|
|
89
124
|
|
|
@@ -91,7 +126,8 @@
|
|
|
91
126
|
|------|------|
|
|
92
127
|
| 协议 | JSON-RPC over WebSocket(`ws`) |
|
|
93
128
|
| 端口 | 默认 38080,可 `-p` 指定 |
|
|
94
|
-
|
|
|
129
|
+
| 架构 | 单进程内嵌 Nest;按 path 分流:`/server-api`、`/ws`、`/ws/voice`、`/sse`、`/channel`、`/health`、静态资源 |
|
|
130
|
+
| 职责 | 连接管理、消息路由、鉴权钩子、静态资源(Desktop 前端) |
|
|
95
131
|
| 方法 | `connect`、`agent.chat`、`agent.cancel`、`subscribe_session`、`unsubscribe_session` 等 |
|
|
96
132
|
|
|
97
133
|
### Agent 核心
|
|
@@ -156,8 +192,29 @@
|
|
|
156
192
|
|
|
157
193
|
适用于:使用 **CLI**,或在自有环境中运行 **Gateway(Web)**。
|
|
158
194
|
|
|
195
|
+
### 前置环境准备
|
|
196
|
+
|
|
197
|
+
需先安装 **Node.js 20+**(Node >=20)。任选一种方式安装即可:
|
|
198
|
+
|
|
199
|
+
| 方式 | 说明 |
|
|
200
|
+
|------|------|
|
|
201
|
+
| **官网安装包** | 打开 [nodejs.org](https://nodejs.org/),下载 LTS 并安装;安装后终端执行 `node -v` 应显示 v20.x 或更高。 |
|
|
202
|
+
| **nvm(推荐)** | 多版本切换方便:`curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh \| bash`,重启终端后 `nvm install 20`、`nvm use 20`。 |
|
|
203
|
+
| **macOS (Homebrew)** | `brew install node@20`,或 `brew install nvm` 再用 nvm 安装 20。 |
|
|
204
|
+
| **Windows** | 使用 [nodejs.org](https://nodejs.org/) 安装包,或 `winget install OpenJS.NodeJS.LTS`。 |
|
|
205
|
+
| **Linux** | 使用发行版包管理器(如 `apt install nodejs`)或 [nvm](https://github.com/nvm-sh/nvm) 安装。 |
|
|
206
|
+
|
|
207
|
+
安装后请确认:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
node -v # 应为 v20.x 或 v22.x
|
|
211
|
+
npm -v # 能正常输出版本号
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 安装命令
|
|
215
|
+
|
|
159
216
|
```bash
|
|
160
|
-
# 全局安装(测试过node版本:20/22
|
|
217
|
+
# 全局安装(测试过 node 版本:20/22;24 太新,部分库需本地编译环境)
|
|
161
218
|
npm install -g @next-open-ai/openbot
|
|
162
219
|
```
|
|
163
220
|
|
|
@@ -196,6 +253,8 @@ npm link # 或 npm install -g . 本地全局安装
|
|
|
196
253
|
- 从 [Releases](https://github.com/next-open-ai/openbot/releases) 下载对应平台的安装包(macOS / Windows)。
|
|
197
254
|
- 安装后启动 OpenBot Desktop,按界面引导配置 API Key 与默认模型即可使用。
|
|
198
255
|
|
|
256
|
+
安装包由仓库通过 **Desktop 打包** 流程生成(见下方「三、开发 → 3.3 Desktop 开发 → Desktop 打包」)。
|
|
257
|
+
|
|
199
258
|
首次使用建议在设置中配置默认 Provider/模型,或通过 CLI 执行 `openbot login <provider> <apiKey> [model]` / `openbot config set-model <provider> <modelId>`(与桌面端共用 `~/.openbot/desktop/` 配置)。
|
|
200
259
|
|
|
201
260
|
---
|
|
@@ -268,6 +327,8 @@ openbot "总结一下当前有哪些技能"
|
|
|
268
327
|
openbot gateway --port 38080
|
|
269
328
|
```
|
|
270
329
|
|
|
330
|
+
若需网关开机/登录自启,可执行 `openbot service install`(支持 Linux / macOS / Windows);移除自启用 `openbot service uninstall`,停止当前网关用 `openbot service stop`。
|
|
331
|
+
|
|
271
332
|
客户端连接 `ws://localhost:38080`,使用 JSON-RPC 调用 `connect`、`agent.chat`、`agent.cancel` 等(详见下方「Gateway API 简述」)。
|
|
272
333
|
前端可自行实现或使用仓库内 Web 示例(若有)。
|
|
273
334
|
|
|
@@ -326,8 +387,9 @@ npm run build
|
|
|
326
387
|
## 3.1 CLI 开发
|
|
327
388
|
|
|
328
389
|
- 入口:`openbot` → bin → `dist/cli/cli.js`
|
|
329
|
-
- 技术:Commander(子命令 `gateway`、`login`、`config`)、TypeScript 5.7
|
|
390
|
+
- 技术:Commander(子命令 `gateway`、`login`、`config`、`service`)、TypeScript 5.7
|
|
330
391
|
- 配置与数据:`~/.openbot/agent`、`~/.openbot/desktop`(与桌面共用)
|
|
392
|
+
- Gateway 开机自启:`openbot service install` / `uninstall` / `stop`(见 `src/cli/service.ts`)
|
|
331
393
|
|
|
332
394
|
修改 CLI 后重新构建并本地安装:
|
|
333
395
|
|
|
@@ -341,7 +403,7 @@ openbot --help
|
|
|
341
403
|
|
|
342
404
|
## 3.2 Web 开发(Gateway + 前端)
|
|
343
405
|
|
|
344
|
-
- **Gateway**:`src/gateway/`,默认端口 38080,可 `-p`
|
|
406
|
+
- **Gateway**:`src/gateway/`,默认端口 38080,可 `-p` 指定;单进程内嵌 Nest,按 path 分流(`/server-api`、`/ws`、静态资源等);协议 JSON-RPC over WebSocket;职责包括连接管理、消息路由、鉴权、静态资源。
|
|
345
407
|
- **方法**:`connect`、`agent.chat`、`agent.cancel`、`subscribe_session`、`unsubscribe_session` 等。
|
|
346
408
|
|
|
347
409
|
本地启动网关:
|
|
@@ -357,7 +419,7 @@ openbot gateway --port 38080
|
|
|
357
419
|
|
|
358
420
|
## 3.3 Desktop 开发
|
|
359
421
|
|
|
360
|
-
- **后端**:NestJS(`src/server/`),前缀 `server-api`,默认端口 38081;Gateway
|
|
422
|
+
- **后端**:NestJS(`src/server/`),前缀 `server-api`,默认端口 38081;Gateway 内嵌时直接挂载该 Express,无独立子进程。
|
|
361
423
|
- **前端**:Electron 28 + Vue 3 + Pinia + Vite 5,位于 `apps/desktop/`。
|
|
362
424
|
|
|
363
425
|
```bash
|
|
@@ -371,6 +433,33 @@ npm run desktop:dev
|
|
|
371
433
|
npm run desktop:install
|
|
372
434
|
```
|
|
373
435
|
|
|
436
|
+
### Desktop 打包
|
|
437
|
+
|
|
438
|
+
从源码构建可安装的桌面安装包(DMG/NSIS/AppImage),供发布或本地安装使用。
|
|
439
|
+
|
|
440
|
+
**命令(在仓库根目录执行):**
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
npm run desktop:pack
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
该命令会依次执行:
|
|
447
|
+
|
|
448
|
+
1. **根目录构建**:`npm run build`,生成 `dist/`(含 Gateway、Server、Agent 等)。
|
|
449
|
+
2. **桌面构建**:`cd apps/desktop && npm run build`,其中包含:
|
|
450
|
+
- **build:gateway**:若需则再次构建根目录 `dist`;
|
|
451
|
+
- **build:copy-gateway**:将根目录 `dist` 复制到 `apps/desktop/gateway-dist`,写入 `package.json`(`type: "module"` + 生产依赖),并执行 `npm install --production`,得到带 `node_modules` 的 gateway 运行时;
|
|
452
|
+
- **build:renderer**:Vite 构建前端到 `renderer/dist`;
|
|
453
|
+
- **electron-builder**:打包为各平台安装包,并将 `gateway-dist` 作为 **extraResources** 拷贝到应用内 `Contents/Resources/dist`(含 Gateway 代码与依赖),无需用户安装 Node 即可运行。
|
|
454
|
+
|
|
455
|
+
**产出物:**
|
|
456
|
+
|
|
457
|
+
- **macOS**:`apps/desktop/dist/` 下生成 `.dmg`、`.zip`(如 `OpenBot Desktop-0.1.1-arm64.dmg`);
|
|
458
|
+
- **Windows**:nsis 安装程序;
|
|
459
|
+
- **Linux**:AppImage。
|
|
460
|
+
|
|
461
|
+
安装包安装后,Gateway 与前端均内嵌在应用内,用户无需单独安装 Node.js。
|
|
462
|
+
|
|
374
463
|
---
|
|
375
464
|
|
|
376
465
|
## 测试
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta charset="UTF-8" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
-
<title>OpenBot
|
|
8
|
+
<title>OpenBot</title>
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link
|
package/dist/cli/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
import { getOpenbotAgentDir } from "../core/agent/agent-dir.js";
|
|
7
7
|
import { run } from "../core/agent/run.js";
|
|
8
8
|
import { loadDesktopAgentConfig, getBoundAgentIdForCli, setProviderApiKey, setDefaultModel, getDesktopConfigList, syncDesktopConfigToModelsJson, ensureDesktopConfigInitialized, } from "../core/config/desktop-config.js";
|
|
9
|
+
import { writeGatewayPid, removeGatewayPidFile, serviceInstall, serviceUninstall, serviceStop, } from "./service.js";
|
|
9
10
|
const require = createRequire(import.meta.url);
|
|
10
11
|
const PKG = require("../../package.json");
|
|
11
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -107,17 +108,42 @@ program
|
|
|
107
108
|
console.error("Error: Invalid port number");
|
|
108
109
|
process.exit(1);
|
|
109
110
|
}
|
|
111
|
+
writeGatewayPid();
|
|
110
112
|
const { startGatewayServer } = await import("../gateway/index.js");
|
|
111
113
|
const { close } = await startGatewayServer(port);
|
|
112
|
-
// Handle graceful shutdown
|
|
113
114
|
const shutdown = async () => {
|
|
114
115
|
console.log("\nShutting down...");
|
|
116
|
+
removeGatewayPidFile();
|
|
115
117
|
await close();
|
|
116
118
|
process.exit(0);
|
|
117
119
|
};
|
|
118
120
|
process.on("SIGINT", shutdown);
|
|
119
121
|
process.on("SIGTERM", shutdown);
|
|
120
122
|
});
|
|
123
|
+
// Service: 开机自启 install / uninstall,以及 stop
|
|
124
|
+
const serviceCmd = program
|
|
125
|
+
.command("service")
|
|
126
|
+
.description("Gateway 开机/登录自启与停止(Linux cron、macOS LaunchAgent、Windows 计划任务)");
|
|
127
|
+
serviceCmd
|
|
128
|
+
.command("install")
|
|
129
|
+
.description("添加开机/登录自启:下次重启或登录后自动启动 gateway")
|
|
130
|
+
.action(() => {
|
|
131
|
+
const nodePath = process.execPath;
|
|
132
|
+
const cliPath = join(__dirname, "cli.js");
|
|
133
|
+
serviceInstall({ nodePath, cliPath });
|
|
134
|
+
});
|
|
135
|
+
serviceCmd
|
|
136
|
+
.command("uninstall")
|
|
137
|
+
.description("移除开机/登录自启")
|
|
138
|
+
.action(() => {
|
|
139
|
+
serviceUninstall();
|
|
140
|
+
});
|
|
141
|
+
serviceCmd
|
|
142
|
+
.command("stop")
|
|
143
|
+
.description("停止当前运行的 gateway 进程")
|
|
144
|
+
.action(() => {
|
|
145
|
+
serviceStop();
|
|
146
|
+
});
|
|
121
147
|
// Login command(写入桌面 config;可选 model,不传则取该 provider 第一个模型,补齐后可直接运行)
|
|
122
148
|
program
|
|
123
149
|
.command("login")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** 供 gateway 命令调用:写入当前进程 PID */
|
|
2
|
+
export declare function writeGatewayPid(): void;
|
|
3
|
+
/** 供 gateway 命令调用:关闭时删除 PID 文件 */
|
|
4
|
+
export declare function removeGatewayPidFile(): void;
|
|
5
|
+
/** 获取 PID 文件路径(供外部读取) */
|
|
6
|
+
export declare function getGatewayPidPath(): string;
|
|
7
|
+
export type ServiceOptions = {
|
|
8
|
+
nodePath: string;
|
|
9
|
+
cliPath: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function serviceInstall(opts: ServiceOptions): void;
|
|
12
|
+
export declare function serviceUninstall(): void;
|
|
13
|
+
export declare function serviceStop(): boolean;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openbot service install / uninstall / stop
|
|
3
|
+
* - install: 开机/登录自启(Linux cron @reboot, macOS LaunchAgent, Windows 计划任务)
|
|
4
|
+
* - uninstall: 移除自启
|
|
5
|
+
* - stop: 停止当前运行的 gateway 进程(依赖 gateway 写入的 PID 文件)
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { homedir, platform } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { execSync, spawnSync } from "node:child_process";
|
|
11
|
+
const OPENBOT_USER_DIR = process.env.OPENBOT_USER_DIR ?? join(homedir(), ".openbot");
|
|
12
|
+
const GATEWAY_PID_FILE = join(OPENBOT_USER_DIR, "gateway.pid");
|
|
13
|
+
const GATEWAY_PORT = 38080;
|
|
14
|
+
/** 供 gateway 命令调用:写入当前进程 PID */
|
|
15
|
+
export function writeGatewayPid() {
|
|
16
|
+
if (!existsSync(OPENBOT_USER_DIR))
|
|
17
|
+
mkdirSync(OPENBOT_USER_DIR, { recursive: true });
|
|
18
|
+
writeFileSync(GATEWAY_PID_FILE, String(process.pid), "utf-8");
|
|
19
|
+
}
|
|
20
|
+
/** 供 gateway 命令调用:关闭时删除 PID 文件 */
|
|
21
|
+
export function removeGatewayPidFile() {
|
|
22
|
+
try {
|
|
23
|
+
if (existsSync(GATEWAY_PID_FILE))
|
|
24
|
+
rmSync(GATEWAY_PID_FILE);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// ignore
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** 获取 PID 文件路径(供外部读取) */
|
|
31
|
+
export function getGatewayPidPath() {
|
|
32
|
+
return GATEWAY_PID_FILE;
|
|
33
|
+
}
|
|
34
|
+
function getBootCommand(nodePath, cliPath) {
|
|
35
|
+
const port = process.env.OPENBOT_GATEWAY_PORT ?? String(GATEWAY_PORT);
|
|
36
|
+
return `"${nodePath}" "${cliPath}" gateway -p ${port}`;
|
|
37
|
+
}
|
|
38
|
+
function detectPlatform() {
|
|
39
|
+
const p = platform();
|
|
40
|
+
if (p === "win32")
|
|
41
|
+
return "win32";
|
|
42
|
+
if (p === "darwin")
|
|
43
|
+
return "darwin";
|
|
44
|
+
return "linux";
|
|
45
|
+
}
|
|
46
|
+
/** Linux: cron @reboot */
|
|
47
|
+
function installLinux(nodePath, cliPath) {
|
|
48
|
+
const cronLine = `@reboot ${getBootCommand(nodePath, cliPath)}\n`;
|
|
49
|
+
const marker = "# openbot-gateway";
|
|
50
|
+
let current = "";
|
|
51
|
+
try {
|
|
52
|
+
current = execSync("crontab -l 2>/dev/null || true", { encoding: "utf-8" });
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// no crontab
|
|
56
|
+
}
|
|
57
|
+
if (current.includes(marker)) {
|
|
58
|
+
throw new Error("已存在 openbot gateway 的开机自启,请先执行 openbot service uninstall");
|
|
59
|
+
}
|
|
60
|
+
const newCrontab = current.trimEnd() + (current ? "\n" : "") + marker + "\n" + cronLine;
|
|
61
|
+
execSync("crontab -", { input: newCrontab, stdio: ["pipe", "inherit", "inherit"] });
|
|
62
|
+
console.log("[openbot] 已添加 cron @reboot,下次重启将自动启动 gateway");
|
|
63
|
+
}
|
|
64
|
+
function uninstallLinux() {
|
|
65
|
+
let current = "";
|
|
66
|
+
try {
|
|
67
|
+
current = execSync("crontab -l 2>/dev/null || true", { encoding: "utf-8" });
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
console.log("[openbot] 未找到 crontab 或已为空");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const marker = "# openbot-gateway";
|
|
74
|
+
const lines = current.split("\n");
|
|
75
|
+
const out = [];
|
|
76
|
+
let skipNext = false;
|
|
77
|
+
for (const line of lines) {
|
|
78
|
+
if (line.includes(marker)) {
|
|
79
|
+
skipNext = true;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (skipNext && line.trim().startsWith("@reboot") && line.includes("gateway")) {
|
|
83
|
+
skipNext = false;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
skipNext = false;
|
|
87
|
+
out.push(line);
|
|
88
|
+
}
|
|
89
|
+
const newCrontab = out.join("\n").replace(/\n\n+/g, "\n").trim();
|
|
90
|
+
if (newCrontab)
|
|
91
|
+
execSync("crontab -", { input: newCrontab, stdio: ["pipe", "inherit", "inherit"] });
|
|
92
|
+
else
|
|
93
|
+
execSync("crontab -r 2>/dev/null || true", { stdio: "inherit" });
|
|
94
|
+
console.log("[openbot] 已移除 cron @reboot");
|
|
95
|
+
}
|
|
96
|
+
function escapePlistString(s) {
|
|
97
|
+
return s
|
|
98
|
+
.replace(/&/g, "&")
|
|
99
|
+
.replace(/</g, "<")
|
|
100
|
+
.replace(/>/g, ">")
|
|
101
|
+
.replace(/"/g, """);
|
|
102
|
+
}
|
|
103
|
+
/** macOS: LaunchAgent (用户级,登录时运行) */
|
|
104
|
+
function installDarwin(nodePath, cliPath) {
|
|
105
|
+
const launchAgentsDir = join(homedir(), "Library", "LaunchAgents");
|
|
106
|
+
if (!existsSync(launchAgentsDir))
|
|
107
|
+
mkdirSync(launchAgentsDir, { recursive: true });
|
|
108
|
+
const plistPath = join(launchAgentsDir, "com.openbot.gateway.plist");
|
|
109
|
+
if (existsSync(plistPath)) {
|
|
110
|
+
throw new Error("已存在 openbot gateway 自启,请先执行 openbot service uninstall");
|
|
111
|
+
}
|
|
112
|
+
const port = process.env.OPENBOT_GATEWAY_PORT ?? String(GATEWAY_PORT);
|
|
113
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
114
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
115
|
+
<plist version="1.0">
|
|
116
|
+
<dict>
|
|
117
|
+
<key>Label</key>
|
|
118
|
+
<string>com.openbot.gateway</string>
|
|
119
|
+
<key>ProgramArguments</key>
|
|
120
|
+
<array>
|
|
121
|
+
<string>${escapePlistString(nodePath)}</string>
|
|
122
|
+
<string>${escapePlistString(cliPath)}</string>
|
|
123
|
+
<string>gateway</string>
|
|
124
|
+
<string>-p</string>
|
|
125
|
+
<string>${port}</string>
|
|
126
|
+
</array>
|
|
127
|
+
<key>RunAtLoad</key>
|
|
128
|
+
<true/>
|
|
129
|
+
<key>KeepAlive</key>
|
|
130
|
+
<false/>
|
|
131
|
+
<key>StandardOutPath</key>
|
|
132
|
+
<string>${escapePlistString(OPENBOT_USER_DIR)}/gateway.log</string>
|
|
133
|
+
<key>StandardErrorPath</key>
|
|
134
|
+
<string>${escapePlistString(OPENBOT_USER_DIR)}/gateway.err</string>
|
|
135
|
+
</dict>
|
|
136
|
+
</plist>
|
|
137
|
+
`;
|
|
138
|
+
writeFileSync(plistPath, plist, "utf-8");
|
|
139
|
+
execSync(`launchctl load "${plistPath}"`, { stdio: "inherit" });
|
|
140
|
+
console.log("[openbot] 已安装 LaunchAgent,下次登录将自动启动 gateway");
|
|
141
|
+
}
|
|
142
|
+
function uninstallDarwin() {
|
|
143
|
+
const plistPath = join(homedir(), "Library", "LaunchAgents", "com.openbot.gateway.plist");
|
|
144
|
+
if (existsSync(plistPath)) {
|
|
145
|
+
try {
|
|
146
|
+
execSync(`launchctl unload "${plistPath}"`, { stdio: "inherit" });
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// may already be unloaded
|
|
150
|
+
}
|
|
151
|
+
rmSync(plistPath);
|
|
152
|
+
console.log("[openbot] 已移除 LaunchAgent");
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
console.log("[openbot] 未找到自启配置");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** Windows: 任务计划(用户登录时运行) */
|
|
159
|
+
function installWin32(nodePath, cliPath) {
|
|
160
|
+
const taskName = "OpenBotGateway";
|
|
161
|
+
const r = spawnSync("schtasks", ["/query", "/tn", taskName], { encoding: "utf-8" });
|
|
162
|
+
if (r.status === 0) {
|
|
163
|
+
throw new Error("已存在 OpenBot Gateway 自启任务,请先执行 openbot service uninstall");
|
|
164
|
+
}
|
|
165
|
+
const port = process.env.OPENBOT_GATEWAY_PORT ?? String(GATEWAY_PORT);
|
|
166
|
+
const cmd = `"${nodePath}" "${cliPath}" gateway -p ${port}`;
|
|
167
|
+
const r2 = spawnSync("schtasks", [
|
|
168
|
+
"/create",
|
|
169
|
+
"/tn", taskName,
|
|
170
|
+
"/tr", cmd,
|
|
171
|
+
"/sc", "onlogon",
|
|
172
|
+
"/rl", "highest",
|
|
173
|
+
"/f",
|
|
174
|
+
], { encoding: "utf-8", stdio: "inherit", shell: true });
|
|
175
|
+
if (r2.status !== 0) {
|
|
176
|
+
throw new Error("创建计划任务失败,请以管理员身份运行或检查 schtasks");
|
|
177
|
+
}
|
|
178
|
+
console.log("[openbot] 已创建计划任务,下次登录将自动启动 gateway");
|
|
179
|
+
}
|
|
180
|
+
function uninstallWin32() {
|
|
181
|
+
const taskName = "OpenBotGateway";
|
|
182
|
+
const r = spawnSync("schtasks", ["/query", "/tn", taskName], { encoding: "utf-8" });
|
|
183
|
+
if (r.status !== 0) {
|
|
184
|
+
console.log("[openbot] 未找到自启任务");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
spawnSync("schtasks", ["/delete", "/tn", taskName, "/f"], { stdio: "inherit", shell: true });
|
|
188
|
+
console.log("[openbot] 已删除计划任务");
|
|
189
|
+
}
|
|
190
|
+
/** 停止 gateway:读 PID 文件发 SIGTERM */
|
|
191
|
+
function stopGateway() {
|
|
192
|
+
if (!existsSync(GATEWAY_PID_FILE)) {
|
|
193
|
+
console.log("[openbot] 未找到 gateway PID 文件,可能未以后台/服务方式运行");
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
const pidStr = readFileSync(GATEWAY_PID_FILE, "utf-8").trim();
|
|
197
|
+
const pid = parseInt(pidStr, 10);
|
|
198
|
+
if (Number.isNaN(pid)) {
|
|
199
|
+
console.log("[openbot] PID 文件内容无效");
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
if (platform() === "win32") {
|
|
204
|
+
spawnSync("taskkill", ["/pid", String(pid), "/f"], { stdio: "inherit", shell: true });
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
process.kill(pid, "SIGTERM");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
212
|
+
if (msg.includes("kill") || msg.includes("ESRCH")) {
|
|
213
|
+
console.log("[openbot] 进程已不存在");
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
throw e;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
removeGatewayPidFile();
|
|
220
|
+
console.log("[openbot] 已停止 gateway");
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
export function serviceInstall(opts) {
|
|
224
|
+
const p = detectPlatform();
|
|
225
|
+
if (p === "linux")
|
|
226
|
+
installLinux(opts.nodePath, opts.cliPath);
|
|
227
|
+
else if (p === "darwin")
|
|
228
|
+
installDarwin(opts.nodePath, opts.cliPath);
|
|
229
|
+
else
|
|
230
|
+
installWin32(opts.nodePath, opts.cliPath);
|
|
231
|
+
}
|
|
232
|
+
export function serviceUninstall() {
|
|
233
|
+
const p = detectPlatform();
|
|
234
|
+
if (p === "linux")
|
|
235
|
+
uninstallLinux();
|
|
236
|
+
else if (p === "darwin")
|
|
237
|
+
uninstallDarwin();
|
|
238
|
+
else
|
|
239
|
+
uninstallWin32();
|
|
240
|
+
}
|
|
241
|
+
export function serviceStop() {
|
|
242
|
+
return stopGateway();
|
|
243
|
+
}
|
package/dist/gateway/server.js
CHANGED
|
@@ -37,7 +37,8 @@ import { ensureDesktopConfigInitialized } from "../core/config/desktop-config.js
|
|
|
37
37
|
import { createNestAppEmbedded } from "../server/bootstrap.js";
|
|
38
38
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
39
39
|
const PACKAGE_ROOT = join(__dirname, "..", "..");
|
|
40
|
-
|
|
40
|
+
/** 内嵌到 Electron 时由主进程设置 OPENBOT_STATIC_DIR,指向打包后的 renderer/dist */
|
|
41
|
+
const STATIC_DIR = process.env.OPENBOT_STATIC_DIR || join(PACKAGE_ROOT, "apps", "desktop", "renderer", "dist");
|
|
41
42
|
const MIME_TYPES = {
|
|
42
43
|
".html": "text/html",
|
|
43
44
|
".js": "text/javascript",
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.8",
|
|
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",
|
|
@@ -50,11 +50,12 @@
|
|
|
50
50
|
"@types/sql.js": "^1.4.9",
|
|
51
51
|
"@types/ws": "^8.18.1",
|
|
52
52
|
"jest": "^29.7.0",
|
|
53
|
+
"rimraf": "^5.0.5",
|
|
53
54
|
"ts-jest": "^29.2.5",
|
|
54
55
|
"typescript": "^5.7.0"
|
|
55
56
|
},
|
|
56
57
|
"scripts": {
|
|
57
|
-
"clean": "
|
|
58
|
+
"clean": "rimraf dist",
|
|
58
59
|
"build": "npm run clean && tsc",
|
|
59
60
|
"build:web": "cd apps/desktop && npm run build:renderer",
|
|
60
61
|
"build:all": "npm run build && npm run build:web",
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
"dev": "npm run build && node dist/cli/cli.js",
|
|
65
66
|
"desktop:dev": "cd apps/desktop && npm run dev",
|
|
66
67
|
"desktop:build": "cd apps/desktop && npm run build",
|
|
68
|
+
"desktop:pack": "npm run build && cd apps/desktop && npm run build",
|
|
67
69
|
"desktop:install": "cd apps/desktop && npm install",
|
|
68
70
|
"test": "jest --config test/jest.config.cjs",
|
|
69
71
|
"test:memory": "node test/run-memory.mjs",
|