polymind_last1 0.0.0-development

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2026] [DevStation]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,308 @@
1
+ # PolyMind
2
+
3
+ 🌐 语言选择 | [English](README_EN.md) | **简体中文**
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![npm version](https://img.shields.io/npm/v/polymind.svg)](https://www.npmjs.com/package/polymind)
6
+
7
+ 原生集成 agentd 服务的自托管 AI Agent 交互平台。PolyMind 将 AI 对话、Agent 工作流编排与多模型管理融为一体,提供开箱即用的完整解决方案,让你无需复杂配置即可搭建专属的 AI Agent 运行环境。
8
+
9
+ ## 目录
10
+
11
+ - [项目介绍](#项目介绍)
12
+ - [功能特性](#功能特性)
13
+ - [技术架构](#技术架构)
14
+ - [安装指南](#安装指南)
15
+ - [快速开始](#快速开始)
16
+ - [配置说明](#配置说明)
17
+ - [部署流程](#部署流程)
18
+ - [本地开发](#本地开发)
19
+ - [许可证](#许可证)
20
+ - [支持与反馈](#支持与反馈)
21
+
22
+ ### 功能特性
23
+
24
+ - 🧩 **原生 agentd 集成** — 通过 agentd 服务管理 Agent 的完整生命周期(创建、沙箱运行、暂停/恢复),支持 OpenCode、OpenClaw、Claude Code 等多种 Adapter
25
+ - 💬 **智能对话** — 持久化多会话管理,支持 Markdown 渲染、代码高亮、数学公式(KaTeX)、流程图(Mermaid)等富文本展示
26
+ - 🛠️ **技能市场** — 内置技能仓库管理,支持从市场安装、上传自定义技能包,为 Agent 注入专业能力
27
+ - 🔧 **多模型管理** — 统一配置和切换不同 AI 模型提供商,灵活适配业务需求
28
+ - 📌 **分面板工作区** — 可调整大小的分面板布局,同时查看对话与工具面板,适配多种使用场景
29
+ - 🔐 **安全认证** — 基于 Token 的身份认证机制,保障 API 接口访问安全
30
+ - 🚀 **开箱即用** — 作为独立 npm 包分发,一条命令完成安装和启动
31
+
32
+ ### 技术架构
33
+
34
+ | 层级 | 技术栈 |
35
+ |------|--------|
36
+ | 前端框架 | Next.js 16 + React 19 |
37
+ | 语言 | TypeScript 5.7 |
38
+ | UI 组件 | shadcn/ui + Radix UI |
39
+ | 样式方案 | Tailwind CSS 4 |
40
+ | 状态管理 | Zustand 5 |
41
+ | 图表可视化 | Recharts |
42
+
43
+ ### 安装指南
44
+
45
+ #### 方式一:一键脚本安装(推荐非开发人员使用)
46
+
47
+ 项目提供了 `install.sh` 脚本,自动完成环境检测、依赖安装和服务启动,适合非开发人员快速部署。
48
+
49
+ **前置条件:**
50
+
51
+ | 依赖 | 说明 | 安装方式 |
52
+ |------|------|----------|
53
+ | Node.js | v22+ | [nodejs.org](https://nodejs.org/) |
54
+ | pnpm | 包管理器 | `npm install -g pnpm` |
55
+ | Python 3 | 后端运行时 | [python.org](https://www.python.org/downloads/) |
56
+ | pip | Python 包管理器 | 随 Python 一同安装 |
57
+ | OpenClaw | Agent 适配器 | `pnpm add -g openclaw@2026.5.7` |
58
+
59
+ **安装步骤:**
60
+
61
+ 1. 克隆仓库:
62
+
63
+ ```bash
64
+ git clone https://atomgit.com/openeuler/polymind.git
65
+ cd polymind
66
+ ```
67
+
68
+ 2. 运行安装脚本:
69
+
70
+ ```bash
71
+ bash install.sh
72
+ ```
73
+
74
+ 脚本将自动执行以下操作:
75
+ - **环境检测** — 检查 Node.js、pnpm、Python、pip、OpenClaw 是否已安装
76
+ - **镜像源配置** — 检测国内网络环境,提示使用华为云镜像加速
77
+ - **网络配置** — 交互式配置允许访问的 IP 地址(支持本地和远程访问)
78
+ - **依赖安装** — 通过 pnpm 安装 `polymind` 前端包,通过 pip 安装 `witty-service` 后端包
79
+ - **服务启动** — 自动检测可用端口,启动前后端服务
80
+
81
+ 3. 安装完成后,脚本会输出访问地址和进程 PID:
82
+
83
+ ```
84
+ ============================================
85
+ PolyMind 启动成功!
86
+ ============================================
87
+
88
+ 前端: http://localhost:3000
89
+ 后端: http://127.0.0.1:8000
90
+
91
+ 停止服务: kill <BACKEND_PID> <FRONTEND_PID>
92
+ 修改配置: ~/.polymind/.env
93
+ ```
94
+
95
+ #### 方式二:npm 包安装
96
+
97
+ 如果已自行部署 agentd 后端服务,可仅安装前端:
98
+
99
+ ```bash
100
+ npm install -g polymind
101
+ # 或
102
+ pnpm add -g polymind
103
+ # 或
104
+ yarn global add polymind
105
+ ```
106
+
107
+ ### 快速开始
108
+
109
+ #### 通过 start.sh 启动
110
+
111
+ 完成安装后,使用 `start.sh` 脚本启动服务(无需重新安装依赖):
112
+
113
+ ```bash
114
+ bash start.sh
115
+ ```
116
+
117
+ 脚本会自动:
118
+ - 检查 `polymind` 和 `witty-service` 是否已安装
119
+ - 加载 `~/.polymind/.env` 中的持久化配置
120
+ - 检测可用端口并启动前后端服务
121
+
122
+ #### 通过 CLI 启动
123
+
124
+ 如果已全局安装 polymind,可直接使用命令行:
125
+
126
+ ```bash
127
+ # 默认端口启动
128
+ polymind
129
+
130
+ # 指定端口
131
+ polymind --port 8080
132
+
133
+ # 指定绑定地址
134
+ polymind --host 0.0.0.0 --port 8080
135
+ ```
136
+
137
+ #### 环境变量启动
138
+
139
+ 支持通过环境变量控制启动参数,适合脚本化部署:
140
+
141
+ ```bash
142
+ BACKEND_HOST=192.168.1.100 BACKEND_PORT=8000 FRONTEND_PORT=3000 polymind
143
+ ```
144
+
145
+ ## 配置说明
146
+
147
+ PolyMind 使用 `~/.polymind/.env` 作为全局配置文件,首次运行时自动生成。修改配置后重启服务即可生效。
148
+
149
+ ### 核心配置项
150
+
151
+ | 配置项 | 说明 | 默认值 |
152
+ |--------|------|--------|
153
+ | `NEXT_PUBLIC_AGENTD_API_URL` | agentd 后端 API 地址 | `http://127.0.0.1:8000` |
154
+ | `NEXT_PUBLIC_WS_URL` | WebSocket 连接地址 | `ws://127.0.0.1:8000/ws` |
155
+ | `NEXT_PUBLIC_API_TIMEOUT` | API 请求超时时间(毫秒) | `30000` |
156
+ | `NEXT_PUBLIC_AUTH_TOKEN` | API 访问认证 Token | `dev-token` |
157
+ | `NEXT_PUBLIC_APP_NAME` | 应用名称 | `PolyMind` |
158
+ | `NEXT_PUBLIC_DEBUG` | 调试模式 | `false` |
159
+
160
+ ### 连接与重试配置
161
+
162
+ | 配置项 | 说明 | 默认值 |
163
+ |--------|------|--------|
164
+ | `NEXT_PUBLIC_MAX_RECONNECT_ATTEMPTS` | WebSocket 最大重连次数 | `5` |
165
+ | `NEXT_PUBLIC_RECONNECT_INTERVAL` | 重连间隔(毫秒) | `3000` |
166
+
167
+ ### 网络与访问控制
168
+
169
+ | 配置项 | 说明 | 默认值 |
170
+ |--------|------|--------|
171
+ | `BACKEND_HOST` | 后端服务主机地址 | `127.0.0.1` |
172
+ | `BACKEND_PORT` | 后端服务端口 | `8000` |
173
+ | `FRONTEND_PORT` | 前端服务端口 | `3000` |
174
+ | `ALLOWED_ORIGINS` | 允许访问的来源(逗号分隔) | `127.0.0.1,localhost` |
175
+
176
+ > **提示:** 如需从外部机器访问 PolyMind,请将 `ALLOWED_ORIGINS` 添加服务器 IP,并将 `BACKEND_HOST` 设为服务器的局域网 IP 地址。
177
+
178
+ ## 部署流程
179
+
180
+ ### 测试环境
181
+
182
+ 适用于集成测试和功能验证:
183
+
184
+ ```bash
185
+ # 构建生产包
186
+ pnpm run build
187
+
188
+ # 使用构建产物启动
189
+ pnpm run start
190
+ ```
191
+
192
+ ### 生产环境
193
+
194
+ **方式一:npm 全局安装(推荐)**
195
+
196
+ ```bash
197
+ # 全局安装
198
+ pnpm add -g polymind
199
+
200
+ # 启动服务
201
+ polymind --host 0.0.0.0 --port 3000
202
+ ```
203
+
204
+ **方式二:从源码构建**
205
+
206
+ ```bash
207
+ # 克隆并构建
208
+ git clone https://atomgit.com/openeuler/polymind.git
209
+ cd polymind
210
+ pnpm install
211
+ pnpm run build
212
+
213
+ # 启动
214
+ pnpm run start
215
+ ```
216
+
217
+ ### 生产环境注意事项
218
+
219
+ - **认证 Token** — 务必修改 `NEXT_PUBLIC_AUTH_TOKEN` 默认值,使用强随机字符串
220
+ - **网络隔离** — 生产环境建议将 `ALLOWED_ORIGINS` 限制为可信 IP,避免未授权访问
221
+ - **反向代理** — 推荐在 PolyMind 前部署 Nginx 等反向代理,配置 HTTPS 证书
222
+ - **进程管理** — 建议使用 systemd 或 PM2 管理服务进程,实现自动重启
223
+
224
+ ## 本地开发
225
+
226
+ ### 环境搭建
227
+
228
+ ```bash
229
+ # 1. 克隆仓库
230
+ git clone https://atomgit.com/openeuler/polymind.git
231
+ cd polymind
232
+
233
+ # 2. 安装依赖
234
+ pnpm install
235
+
236
+ # 3. 根据需要修改 .env 中的配置
237
+
238
+ # 4. 启动开发服务器
239
+ pnpm run dev
240
+ ```
241
+
242
+ ### 常用开发命令
243
+
244
+ | 命令 | 说明 |
245
+ |------|------|
246
+ | `pnpm run dev` | 启动开发服务器(热重载) |
247
+ | `pnpm run build` | 构建生产包 |
248
+ | `pnpm run start` | 使用构建产物启动服务 |
249
+ | `pnpm run lint` | 运行 ESLint 代码检查 |
250
+ | `pnpm run test` | 运行 Jest 测试 |
251
+
252
+ ### 项目结构
253
+
254
+ ```
255
+ polymind/
256
+ ├── app/ # Next.js App Router 页面
257
+ │ ├── config/ # 应用配置管理
258
+ │ ├── layout.tsx # 根布局
259
+ │ └── page.tsx # 主页面
260
+ ├── bin/ # CLI 入口(start.js)
261
+ ├── components/ # React 组件
262
+ │ ├── chat/ # 对话相关组件
263
+ │ ├── settings/ # 设置页面组件
264
+ │ ├── tool-panel/ # 工具面板(Agent、CVE、Backport)
265
+ │ └── ui/ # shadcn/ui 基础组件
266
+ ├── docs/ # 设计文档与 API 规范
267
+ ├── hooks/ # 自定义 React Hooks
268
+ ├── lib/ # 工具函数与类型定义
269
+ ├── services/ # API 服务层
270
+ │ ├── agent-service.ts # Agent 管理
271
+ │ ├── session-service.ts # 会话管理
272
+ │ ├── message-service.ts # 消息服务
273
+ │ ├── skill-service.ts # 技能管理
274
+ │ ├── model-service.ts # 模型管理
275
+ │ ├── cve-service.ts # CVE 漏洞服务
276
+ │ └── patchflow-agent-service.ts # Patchflow Agent
277
+ ├── scripts/ # 构建/安装辅助脚本
278
+ ├── install.sh # 一键安装脚本
279
+ ├── start.sh # 一键启动脚本
280
+ └── packaging/ # 打包发布脚本
281
+ ```
282
+
283
+ ### 代码贡献流程
284
+
285
+ 1. Fork 本仓库
286
+ 2. 创建功能分支:`git checkout -b feature/your-feature`
287
+ 3. 提交更改:`git commit -m 'Add your feature'`
288
+ 4. 推送分支:`git push origin feature/your-feature`
289
+ 5. 提交 Pull Request
290
+
291
+ ### 开发规范
292
+
293
+ - **代码风格** — 遵循 ESLint 配置,提交前运行 `pnpm run lint` 检查
294
+ - **提交规范** — 使用语义化提交信息(如 `feat:`、`fix:`、`docs:`、`refactor:`)
295
+ - **类型安全** — 充分利用 TypeScript 类型系统,避免使用 `any`
296
+ - **组件规范** — UI 组件基于 shadcn/ui 规范,保持与现有组件风格一致
297
+ - **测试覆盖** — 新增功能需编写对应的单元测试(Jest)
298
+
299
+
300
+ ## 许可证
301
+
302
+ 本项目基于 [MIT 许可证](LICENSE) 开源。
303
+
304
+ ## 支持与反馈
305
+
306
+ - **问题反馈** — 请在 [AtomGit Issues](https://atomgit.com/openeuler/polymind/issues) 提交
307
+ - **功能建议** — 欢迎通过 Issue 或 Pull Request 参与
308
+ - **项目主页** — [https://atomgit.com/openeuler/polymind](https://atomgit.com/openeuler/polymind)
package/bin/start.js ADDED
@@ -0,0 +1,317 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'child_process';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
+ import os from 'os';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ const rootDir = path.join(__dirname, '..');
11
+
12
+ // 查找 Next.js standalone 目录(兼容嵌套和扁平两种结构)
13
+ function findStandaloneDir() {
14
+ const base = path.join(rootDir, '.next', 'standalone');
15
+
16
+ // 情况1: server.js 直接在 standalone/ 下
17
+ if (fs.existsSync(path.join(base, 'server.js'))) {
18
+ return base;
19
+ }
20
+
21
+ // 情况2: server.js 在 standalone/<name>/ 子目录下(Next.js 按包名嵌套)
22
+ try {
23
+ const entries = fs.readdirSync(base, { withFileTypes: true });
24
+ for (const entry of entries) {
25
+ if (entry.isDirectory()) {
26
+ const candidate = path.join(base, entry.name);
27
+ if (fs.existsSync(path.join(candidate, 'server.js'))) {
28
+ return candidate;
29
+ }
30
+ }
31
+ }
32
+ } catch (e) {
33
+ // 目录不存在等情况,回退到默认路径
34
+ }
35
+
36
+ // 回退:返回默认路径,让后续报错给出清晰的错误信息
37
+ return base;
38
+ }
39
+
40
+ const standaloneDir = findStandaloneDir();
41
+
42
+ // 处理命令行参数
43
+ const args = process.argv.slice(2);
44
+ const portIndex = args.indexOf('--port');
45
+ const port = portIndex !== -1 ? args[portIndex + 1] : '3000';
46
+ const hostIndex = args.indexOf('--host');
47
+ const host = hostIndex !== -1 ? args[hostIndex + 1] : '0.0.0.0';
48
+
49
+ // ==================== 运行时配置加载 ====================
50
+ const homedir = os.homedir();
51
+ const polymindDir = path.join(homedir, '.polymind');
52
+ const envPath = path.join(polymindDir, '.env');
53
+ const publicEnv = {};
54
+
55
+ // 确保配置目录存在
56
+ fs.mkdirSync(polymindDir, { recursive: true });
57
+
58
+ // 获取后端端口(从环境变量或默认值)
59
+ const backendPort = process.env.BACKEND_PORT || '8000';
60
+ const backendHost = process.env.BACKEND_HOST || '127.0.0.1';
61
+
62
+ // 动态替换构建后代码中的硬编码后端地址
63
+ function replaceBackendUrlInBuild() {
64
+ if (backendPort === '8000' && backendHost === '127.0.0.1') {
65
+ // 默认配置,无需替换
66
+ return;
67
+ }
68
+
69
+ const targetDir = path.join(standaloneDir, '.next');
70
+ if (!fs.existsSync(targetDir)) {
71
+ return;
72
+ }
73
+
74
+ const newApiUrl = `http://${backendHost}:${backendPort}`;
75
+ const newWsUrl = `ws://${backendHost}:${backendPort}`;
76
+
77
+ console.log(`🔄 动态替换构建代码中的后端地址 -> ${newApiUrl}`);
78
+
79
+ // 使用正则匹配任意端口号(解决上次替换后本次无法匹配旧端口的问题)
80
+ const apiRegex = /http:\/\/127\.0\.0\.1:\d+/g;
81
+ const wsRegex = /ws:\/\/127\.0\.0\.1:\d+/g;
82
+
83
+ // 递归查找所有 .js 文件
84
+ const files = [];
85
+ function walk(dir) {
86
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
87
+ for (const entry of entries) {
88
+ if (entry.isDirectory()) {
89
+ walk(path.join(dir, entry.name));
90
+ } else if (entry.name.endsWith('.js')) {
91
+ files.push(path.join(dir, entry.name));
92
+ }
93
+ }
94
+ }
95
+
96
+ try {
97
+ walk(targetDir);
98
+ } catch (e) {
99
+ return;
100
+ }
101
+
102
+ for (const file of files) {
103
+ try {
104
+ let content = fs.readFileSync(file, 'utf-8');
105
+ const oldContent = content;
106
+ content = content.replace(apiRegex, newApiUrl);
107
+ content = content.replace(wsRegex, newWsUrl);
108
+ if (content !== oldContent) {
109
+ fs.writeFileSync(file, content);
110
+ console.log(` ✓ ${path.relative(rootDir, file)}`);
111
+ }
112
+ } catch (e) {
113
+ // 忽略无法读取/写入的文件
114
+ }
115
+ }
116
+ }
117
+
118
+ // 动态替换 server.js 中的 allowedDevOrigins
119
+ function replaceAllowedOrigins() {
120
+ // 1. 优先从环境变量获取,其次从 .env 配置文件读取
121
+ let allowedOriginsEnv = process.env.ALLOWED_ORIGINS;
122
+
123
+ if (!allowedOriginsEnv || allowedOriginsEnv.trim() === '') {
124
+ // 环境变量未设置,尝试从 .env 文件读取(支持持久化配置)
125
+ if (fs.existsSync(envPath)) {
126
+ try {
127
+ const envContent = fs.readFileSync(envPath, 'utf-8');
128
+ const match = envContent.match(/^ALLOWED_ORIGINS=(.+)$/m);
129
+ if (match && match[1].trim() !== '') {
130
+ allowedOriginsEnv = match[1].trim();
131
+ }
132
+ } catch (e) {
133
+ // 读取失败,忽略
134
+ }
135
+ }
136
+ }
137
+
138
+ if (!allowedOriginsEnv || allowedOriginsEnv.trim() === '') {
139
+ // 未配置,使用默认值
140
+ return;
141
+ }
142
+
143
+ const serverPath = path.join(standaloneDir, 'server.js');
144
+ if (!fs.existsSync(serverPath)) {
145
+ return;
146
+ }
147
+
148
+ // 将逗号分隔的字符串转换为 JSON 数组格式
149
+ const originsArray = allowedOriginsEnv
150
+ .split(',')
151
+ .map(o => o.trim())
152
+ .filter(o => o !== '');
153
+
154
+ if (originsArray.length === 0) {
155
+ console.warn('⚠️ ALLOWED_ORIGINS 格式无效,使用默认值');
156
+ return;
157
+ }
158
+
159
+ const newAllowedOrigins = JSON.stringify(originsArray);
160
+
161
+ try {
162
+ let content = fs.readFileSync(serverPath, 'utf-8');
163
+ const oldContent = content;
164
+ // 匹配 "allowedDevOrigins":[...] 格式并替换(支持 IPv6、端口号等)
165
+ content = content.replace(
166
+ /"allowedDevOrigins"\s*:\s*\[[^\]]*\]/g,
167
+ `"allowedDevOrigins":${newAllowedOrigins}`
168
+ );
169
+
170
+ if (content !== oldContent) {
171
+ fs.writeFileSync(serverPath, content);
172
+ console.log(`🔄 动态配置允许的访问来源: ${newAllowedOrigins}`);
173
+ }
174
+ } catch (e) {
175
+ console.warn(`⚠️ 无法修改 allowedDevOrigins: ${e.message}`);
176
+ }
177
+ }
178
+
179
+ // 执行替换
180
+ replaceBackendUrlInBuild();
181
+ replaceAllowedOrigins();
182
+
183
+ // 默认配置内容(动态插入后端端口)
184
+ const defaultEnvContent = `# PolyMind 全局配置文件
185
+ # 修改后重启服务即可生效
186
+ # ==============================================
187
+ # 后端API地址
188
+ NEXT_PUBLIC_AGENTD_API_URL=http://${backendHost}:${backendPort}
189
+ # WebSocket地址
190
+ NEXT_PUBLIC_WS_URL=ws://${backendHost}:${backendPort}/ws
191
+ # API请求超时时间(毫秒)
192
+ NEXT_PUBLIC_API_TIMEOUT=30000
193
+ # 最大重连次数
194
+ NEXT_PUBLIC_MAX_RECONNECT_ATTEMPTS=5
195
+ # 重连间隔(毫秒)
196
+ NEXT_PUBLIC_RECONNECT_INTERVAL=3000
197
+ # 应用名称
198
+ NEXT_PUBLIC_APP_NAME=PolyMind
199
+ # 应用版本
200
+ NEXT_PUBLIC_APP_VERSION=1.0.0
201
+ # 后端服务地址(默认 127.0.0.1,远程访问时设为虚拟机 IP)
202
+ BACKEND_HOST=127.0.0.1
203
+ # 允许访问的来源(逗号分隔,修改后重启生效)
204
+ ALLOWED_ORIGINS=127.0.0.1,localhost
205
+ # 调试模式
206
+ NEXT_PUBLIC_DEBUG=false
207
+ `;
208
+
209
+ // 检查是否需要动态更新后端端口配置
210
+ let envContent = '';
211
+ if (fs.existsSync(envPath)) {
212
+ envContent = fs.readFileSync(envPath, 'utf-8');
213
+
214
+ // 如果环境变量指定了后端端口,强制更新配置文件
215
+ if (process.env.BACKEND_PORT) {
216
+ const newApiUrl = `NEXT_PUBLIC_AGENTD_API_URL=http://${backendHost}:${backendPort}`;
217
+ const newWsUrl = `NEXT_PUBLIC_WS_URL=ws://${backendHost}:${backendPort}/ws`;
218
+
219
+ // 使用正则替换现有的配置
220
+ envContent = envContent.replace(/NEXT_PUBLIC_AGENTD_API_URL=.*/, newApiUrl);
221
+ envContent = envContent.replace(/NEXT_PUBLIC_WS_URL=.*/, newWsUrl);
222
+
223
+ // 写入更新后的配置文件
224
+ fs.writeFileSync(envPath, envContent);
225
+ console.log(`🔄 动态更新后端地址: ${newApiUrl}`);
226
+ console.log(`🔄 动态更新WebSocket地址: ${newWsUrl}`);
227
+ }
228
+ } else {
229
+ // 配置文件不存在,使用默认配置(已包含动态端口)
230
+ envContent = defaultEnvContent;
231
+ fs.writeFileSync(envPath, envContent);
232
+ console.log(`📝 已生成默认配置文件: ${envPath}`);
233
+ }
234
+
235
+ // 读取全局配置文件
236
+ console.log(`📝 加载全局配置: ${envPath}`);
237
+ console.log(`💡 修改配置后重启服务即可生效`);
238
+
239
+ // 解析.env文件内容
240
+ envContent.split('\n').forEach(line => {
241
+ line = line.trim();
242
+ if (!line || line.startsWith('#')) return;
243
+ const [key, ...valueParts] = line.split('=');
244
+ const configKey = key.trim();
245
+ const configValue = valueParts.join('=').trim().replace(/^["']|["']$/g, '');
246
+
247
+ // 注入到环境变量
248
+ process.env[configKey] = configValue;
249
+
250
+ // 收集NEXT_PUBLIC_开头的配置,需要暴露给前端
251
+ if (configKey.startsWith('NEXT_PUBLIC_')) {
252
+ publicEnv[configKey] = configValue;
253
+ }
254
+ });
255
+
256
+ // 生成动态配置文件,注入到前端
257
+ const publicDir = path.join(standaloneDir, 'public');
258
+ const envJsPath = path.join(publicDir, 'env.js');
259
+
260
+ // 兼容原有代码,直接挂载到window.process.env
261
+ const envJsContent = `
262
+ // PolyMind 运行时动态配置,自动生成请勿修改
263
+ window.process = window.process || {};
264
+ window.process.env = Object.assign(window.process.env || {}, ${JSON.stringify(publicEnv)});
265
+ `;
266
+
267
+ // 写入配置文件
268
+ fs.mkdirSync(publicDir, { recursive: true });
269
+ fs.writeFileSync(envJsPath, envJsContent);
270
+ console.log(`✅ 共加载 ${Object.keys(publicEnv).length} 项自定义配置`);
271
+ // ==================== 配置加载完成 ====================
272
+
273
+ // 生成访问地址显示
274
+ let displayHost = host;
275
+ if (host === '0.0.0.0' || host === '::') {
276
+ displayHost = 'localhost';
277
+ // 提示可以通过服务器IP访问
278
+ console.log(`🚀 启动 PolyMind Web 服务`);
279
+ console.log(`📌 绑定地址: ${host}`);
280
+ console.log(`📌 服务端口: ${port}`);
281
+ console.log(`📂 服务根目录: ${rootDir}`);
282
+ console.log(`🌐 本地访问: http://${displayHost}:${port}`);
283
+ console.log(`🌐 局域网访问: http://<服务器IP>:${port}`);
284
+ } else {
285
+ console.log(`🚀 启动 PolyMind Web 服务`);
286
+ console.log(`📌 绑定地址: ${host}`);
287
+ console.log(`📌 服务端口: ${port}`);
288
+ console.log(`📂 服务根目录: ${rootDir}`);
289
+ console.log(`🌐 访问地址: http://${host}:${port}`);
290
+ }
291
+
292
+ // 启动Next.js独立服务,直接传递参数确保生效
293
+ const serverPath = path.join(standaloneDir, 'server.js');
294
+ const serverArgs = [serverPath, '--host', host, '--port', port];
295
+
296
+ // 打印执行命令方便排查
297
+ console.log(`🔧 启动命令: node ${serverArgs.join(' ')}`);
298
+
299
+ const server = spawn('node', serverArgs, {
300
+ cwd: standaloneDir,
301
+ env: {
302
+ ...process.env,
303
+ PORT: port,
304
+ HOST: host,
305
+ HOSTNAME: host,
306
+ },
307
+ stdio: 'inherit'
308
+ });
309
+
310
+ server.on('close', (code) => {
311
+ process.exit(code);
312
+ });
313
+
314
+ server.on('error', (err) => {
315
+ console.error('❌ 启动失败:', err.message);
316
+ process.exit(1);
317
+ });
package/next.config.js ADDED
@@ -0,0 +1,5 @@
1
+ // next.config.js
2
+ export default {
3
+ output: 'standalone',
4
+ allowedDevOrigins: ['127.0.0.1'],
5
+ };
package/package.json ADDED
@@ -0,0 +1,150 @@
1
+ {
2
+ "name": "polymind_last1",
3
+ "version": "0.0.0-development",
4
+ "description": "Self-hosted AI agent interaction platform with native agentd service integration.",
5
+ "keywords": [
6
+ "ai",
7
+ "agent",
8
+ "ai-chat",
9
+ "agent-platform",
10
+ "self-hosted",
11
+ "agentd",
12
+ "open-source"
13
+ ],
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://atomgit.com/openeuler/polymind.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://atomgit.com/openeuler/polymind/issues"
21
+ },
22
+ "homepage": "https://atomgit.com/openeuler/polymind#readme",
23
+ "type": "module",
24
+ "bin": {
25
+ "polymind_last1": "./bin/start.js"
26
+ },
27
+ "files": [
28
+ ".next/standalone/**/*",
29
+ "bin/**/*",
30
+ "scripts/postinstall.js",
31
+ "next.config.js",
32
+ "next.config.mjs",
33
+ "package.json"
34
+ ],
35
+ "scripts": {
36
+ "dev": "bash scripts/load-env.sh next dev -H 0.0.0.0",
37
+ "build": "next build && node scripts/copy-static.js",
38
+ "start": "bash scripts/load-env.sh next start",
39
+ "lint": "eslint .",
40
+ "lint:fix": "eslint . --fix",
41
+ "format": "prettier . --write",
42
+ "format:check": "prettier . --check",
43
+ "test": "jest",
44
+ "postinstall": "node scripts/postinstall.js",
45
+ "prepare": "husky",
46
+ "release": "semantic-release",
47
+ "release:dry-run": "semantic-release --dry-run"
48
+ },
49
+ "lint-staged": {
50
+ "*.{js,jsx,ts,tsx}": [
51
+ "eslint --fix",
52
+ "prettier --write"
53
+ ],
54
+ "*.{json,css,scss,less,html}": [
55
+ "prettier --write"
56
+ ]
57
+ },
58
+ "publishConfig": {
59
+ "access": "public",
60
+ "registry": "https://registry.npmjs.org/"
61
+ },
62
+ "dependencies": {
63
+ "@radix-ui/react-accordion": "^1.2.1",
64
+ "@radix-ui/react-alert-dialog": "^1.1.15",
65
+ "@radix-ui/react-aspect-ratio": "^1.1.8",
66
+ "@radix-ui/react-avatar": "^1.1.11",
67
+ "@radix-ui/react-checkbox": "^1.3.3",
68
+ "@radix-ui/react-collapsible": "^1.1.12",
69
+ "@radix-ui/react-context-menu": "^2.2.2",
70
+ "@radix-ui/react-dialog": "^1.1.15",
71
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
72
+ "@radix-ui/react-hover-card": "^1.1.9",
73
+ "@radix-ui/react-label": "^2.1.8",
74
+ "@radix-ui/react-menubar": "^1.1.11",
75
+ "@radix-ui/react-navigation-menu": "^1.2.1",
76
+ "@radix-ui/react-popover": "^1.1.15",
77
+ "@radix-ui/react-progress": "^1.1.8",
78
+ "@radix-ui/react-radio-group": "^1.3.8",
79
+ "@radix-ui/react-scroll-area": "^1.2.10",
80
+ "@radix-ui/react-select": "^2.2.6",
81
+ "@radix-ui/react-separator": "^1.1.8",
82
+ "@radix-ui/react-slider": "^1.3.6",
83
+ "@radix-ui/react-slot": "^1.2.4",
84
+ "@radix-ui/react-switch": "^1.2.6",
85
+ "@radix-ui/react-tabs": "^1.1.13",
86
+ "@radix-ui/react-toast": "^1.2.15",
87
+ "@radix-ui/react-toggle": "^1.1.6",
88
+ "@radix-ui/react-toggle-group": "^1.1.0",
89
+ "@radix-ui/react-tooltip": "^1.2.8",
90
+ "@vercel/analytics": "1.6.1",
91
+ "autoprefixer": "^10.4.20",
92
+ "class-variance-authority": "^0.7.1",
93
+ "clsx": "^2.1.1",
94
+ "cmdk": "1.1.1",
95
+ "date-fns": "4.1.0",
96
+ "embla-carousel-react": "8.5.1",
97
+ "input-otp": "1.4.1",
98
+ "katex": "^0.16.45",
99
+ "lucide-react": "^0.564.0",
100
+ "mermaid": "^11.14.0",
101
+ "next": "16.2.0",
102
+ "next-themes": "^0.4.6",
103
+ "react": "19.2.4",
104
+ "react-day-picker": "^8.7.1",
105
+ "react-dom": "19.2.4",
106
+ "react-hook-form": "7.53.0",
107
+ "react-markdown": "^10.1.0",
108
+ "react-resizable-panels": "^2.1.7",
109
+ "react-syntax-highlighter": "^16.1.1",
110
+ "recharts": "2.13.3",
111
+ "rehype-katex": "^7.0.1",
112
+ "rehype-raw": "^7.0.0",
113
+ "remark-gfm": "^4.0.1",
114
+ "remark-math": "^6.0.0",
115
+ "sonner": "^1.7.1",
116
+ "tailwind-merge": "^3.3.1",
117
+ "vaul": "0.9.6",
118
+ "zustand": "^5.0.0"
119
+ },
120
+ "devDependencies": {
121
+ "@commitlint/cli": "^21.0.2",
122
+ "@commitlint/config-conventional": "^21.0.2",
123
+ "@semantic-release/changelog": "^6.0.3",
124
+ "@semantic-release/commit-analyzer": "^13.0.1",
125
+ "@semantic-release/github": "^12.0.8",
126
+ "@semantic-release/npm": "^13.1.5",
127
+ "@semantic-release/release-notes-generator": "^14.1.1",
128
+ "@tailwindcss/postcss": "^4.2.0",
129
+ "@types/jest": "^30.0.0",
130
+ "@types/katex": "^0.16.8",
131
+ "@types/node": "^22",
132
+ "@types/react": "19.2.14",
133
+ "@types/react-dom": "19.2.3",
134
+ "@types/react-syntax-highlighter": "^15.5.13",
135
+ "conventional-changelog-conventionalcommits": "^9.3.1",
136
+ "eslint": "^9.39.4",
137
+ "eslint-config-next": "^16.2.7",
138
+ "eslint-config-prettier": "^10.1.8",
139
+ "husky": "^9.1.7",
140
+ "jest": "^30.3.0",
141
+ "lint-staged": "^17.0.7",
142
+ "postcss": "^8.5",
143
+ "prettier": "^3.8.3",
144
+ "semantic-release": "^25.0.3",
145
+ "tailwindcss": "^4.2.0",
146
+ "ts-jest": "^29.4.9",
147
+ "tw-animate-css": "1.3.3",
148
+ "typescript": "5.7.3"
149
+ }
150
+ }
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
+
6
+ // 获取用户主目录
7
+ const homedir = os.homedir();
8
+ const polymindDir = path.join(homedir, '.polymind');
9
+ const envPath = path.join(polymindDir, '.env');
10
+
11
+ // 创建配置目录
12
+ fs.mkdirSync(polymindDir, { recursive: true });
13
+
14
+ // 默认配置内容
15
+ const defaultEnvContent = `# PolyMind 全局配置文件
16
+ # 修改后重启服务即可生效
17
+ # ==============================================
18
+ # 后端API地址
19
+ NEXT_PUBLIC_AGENTD_API_URL=http://127.0.0.1:8000
20
+ # WebSocket地址
21
+ NEXT_PUBLIC_WS_URL=ws://127.0.0.1:8000/ws
22
+ # API请求超时时间(毫秒)
23
+ NEXT_PUBLIC_API_TIMEOUT=30000
24
+ # 最大重连次数
25
+ NEXT_PUBLIC_MAX_RECONNECT_ATTEMPTS=5
26
+ # 重连间隔(毫秒)
27
+ NEXT_PUBLIC_RECONNECT_INTERVAL=3000
28
+ # 应用名称
29
+ NEXT_PUBLIC_APP_NAME=PolyMind
30
+ # 应用版本
31
+ NEXT_PUBLIC_APP_VERSION=1.0.0
32
+ # 调试模式
33
+ NEXT_PUBLIC_DEBUG=false
34
+ `;
35
+
36
+ // 如果配置文件不存在则写入默认配置,存在则不覆盖
37
+ if (!fs.existsSync(envPath)) {
38
+ fs.writeFileSync(envPath, defaultEnvContent);
39
+ console.log(`✅ PolyMind 全局配置目录已创建: ${polymindDir}`);
40
+ console.log(`📝 默认配置文件已生成: ${envPath}`);
41
+ } else {
42
+ console.log(`✅ PolyMind 配置文件已存在: ${envPath},跳过初始化`);
43
+ }