openclaw-agent-dashboard 1.0.7 → 1.0.9

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.
@@ -0,0 +1,209 @@
1
+ # Windows 安装问题排查指南
2
+
3
+ 本文档针对 `npx openclaw-agent-dashboard@1.0.8 --verbose` 在 Windows 下的安装问题进行说明和解决方案。
4
+
5
+ ---
6
+
7
+ ## 一、问题概览
8
+
9
+ 根据安装日志,主要有以下几类问题:
10
+
11
+ | 问题 | 严重程度 | 影响 |
12
+ |------|----------|------|
13
+ | Python 依赖安装失败 | 高 | Dashboard 后端无法启动 |
14
+ | 插件注册失败 | 中 | 需手动配置或重新安装 |
15
+ | Node.js DEP0190 警告 | 低 | 仅提示,不影响功能 |
16
+ | 安全模式警告 | 低 | 需配置 `plugins.allow` |
17
+
18
+ ---
19
+
20
+ ## 二、Python 依赖安装失败(核心问题)
21
+
22
+ ### 2.1 错误现象
23
+
24
+ ```
25
+ [Errno 11001] getaddrinfo failed
26
+ Failed to establish a new connection
27
+ ERROR: Could not find a version that satisfies the requirement fastapi==0.109.0
28
+ ERROR: No matching distribution found for fastapi==0.109.0
29
+ ```
30
+
31
+ ### 2.2 根本原因
32
+
33
+ `[Errno 11001] getaddrinfo failed` 是 **Windows 网络/DNS 解析错误**,表示 pip 无法连接到 PyPI (pypi.org) 下载包。
34
+
35
+ 常见原因:
36
+
37
+ 1. **企业网络限制**:公司防火墙或代理阻止访问 pypi.org
38
+ 2. **代理未配置**:使用代理上网但 pip 未设置代理
39
+ 3. **DNS 解析失败**:无法解析 pypi.org 域名
40
+ 4. **VPN 或网络策略**:限制外网访问
41
+
42
+ ### 2.3 解决方案
43
+
44
+ #### 方案 A:配置 pip 代理(如有 HTTP/HTTPS 代理)
45
+
46
+ ```powershell
47
+ # 临时设置(当前会话)
48
+ $env:HTTP_PROXY = "http://代理地址:端口"
49
+ $env:HTTPS_PROXY = "http://代理地址:端口"
50
+
51
+ # 或使用 pip 配置
52
+ python -m pip config set global.proxy "http://代理地址:端口"
53
+ ```
54
+
55
+ #### 方案 B:使用国内 PyPI 镜像
56
+
57
+ ```powershell
58
+ # 使用清华镜像
59
+ python -m pip install -r C:\Users\h00427263\.openclaw\extensions\openclaw-agent-dashboard\dashboard\requirements.txt --user -i https://pypi.tuna.tsinghua.edu.cn/simple
60
+
61
+ # 或阿里云镜像
62
+ python -m pip install -r C:\Users\h00427263\.openclaw\extensions\openclaw-agent-dashboard\dashboard\requirements.txt --user -i https://mirrors.aliyun.com/pypi/simple/
63
+ ```
64
+
65
+ #### 方案 C:离线安装(完全无外网时)
66
+
67
+ 1. 在有网络的机器上下载依赖:
68
+
69
+ ```powershell
70
+ pip download -r requirements.txt -d ./pip-packages
71
+ ```
72
+
73
+ 2. 将 `pip-packages` 目录和 `requirements.txt` 拷贝到目标机器
74
+ 3. 在目标机器执行:
75
+
76
+ ```powershell
77
+ pip install --no-index --find-links=./pip-packages -r requirements.txt --user
78
+ ```
79
+
80
+ #### 方案 D:跳过 Python 依赖安装
81
+
82
+ 若暂时无法解决网络问题,可先完成插件安装,后续再手动安装:
83
+
84
+ ```powershell
85
+ npx openclaw-agent-dashboard@1.0.8 --verbose --skip-python
86
+ ```
87
+
88
+ 然后等网络恢复后手动执行:
89
+
90
+ ```powershell
91
+ python -m pip install -r C:\Users\h00427263\.openclaw\extensions\openclaw-agent-dashboard\dashboard\requirements.txt --user
92
+ ```
93
+
94
+ > **注意**:Windows 下若 `python3` 不可用,请使用 `python` 命令。
95
+
96
+ ---
97
+
98
+ ## 三、插件注册失败
99
+
100
+ ### 3.1 错误现象
101
+
102
+ ```
103
+ plugin already exists: C:\Users\h00427263\.openclaw\extensions\openclaw-agent-dashboard (delete it first)
104
+ ⚠ 插件注册失败(Dashboard 可能需要在 openclaw 配置中手动添加)
105
+ ```
106
+
107
+ ### 3.2 原因说明
108
+
109
+ 安装脚本会先执行 `openclaw plugins uninstall`,再复制文件,最后执行 `openclaw plugins install`。
110
+ `uninstall` 可能只从配置中移除插件,**不会删除扩展目录**。当 `install` 发现目标目录已存在时,会报 "plugin already exists"。
111
+
112
+ ### 3.3 解决方案
113
+
114
+ #### 方案 A:安装前手动删除旧目录
115
+
116
+ ```powershell
117
+ # 删除旧插件目录
118
+ Remove-Item -Recurse -Force "C:\Users\h00427263\.openclaw\extensions\openclaw-agent-dashboard" -ErrorAction SilentlyContinue
119
+
120
+ # 重新安装
121
+ npx openclaw-agent-dashboard@1.0.8 --verbose
122
+ ```
123
+
124
+ #### 方案 B:手动配置 openclaw
125
+
126
+ 若插件文件已正确安装,可在 openclaw 配置中手动添加插件。配置文件通常位于:
127
+
128
+ - `C:\Users\h00427263\.openclaw\config.json` 或
129
+ - 环境变量 `OPENCLAW_STATE_DIR` 指向的目录
130
+
131
+ 在 `plugins.allow` 中添加 `openclaw-agent-dashboard`,例如:
132
+
133
+ ```json
134
+ {
135
+ "plugins": {
136
+ "allow": ["openclaw-agent-dashboard"]
137
+ }
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 四、其他警告说明
144
+
145
+ ### 4.1 Node.js DEP0190 警告
146
+
147
+ ```
148
+ (node:32244) [DEP0190] DeprecationWarning: Passing args to a child process with shell option true...
149
+ ```
150
+
151
+ - **含义**:在 `shell: true` 下传递参数时,Node.js 提示存在潜在安全风险。
152
+ - **影响**:当前仅为警告,不影响安装和运行。
153
+ - **处理**:可忽略,或等待后续版本改用更安全的调用方式。
154
+
155
+ ### 4.2 安全模式警告
156
+
157
+ ```
158
+ WARNING: Plugin "openclaw-agent-dashboard" contains dangerous code patterns: Shell command execution detected (child_process)
159
+ plugins.allow is empty; discovered non-bundled plugins may auto-load
160
+ ```
161
+
162
+ - **含义**:插件使用了 `child_process` 执行命令,被识别为“危险模式”。
163
+ - **影响**:插件可能被当作未受信任代码,需要显式允许。
164
+ - **处理**:在 openclaw 配置中设置 `plugins.allow: ["openclaw-agent-dashboard"]`。
165
+
166
+ ---
167
+
168
+ ## 五、推荐安装流程(Windows + 企业网络)
169
+
170
+ 1. **删除旧版本(若存在)**:
171
+
172
+ ```powershell
173
+ Remove-Item -Recurse -Force "$env:USERPROFILE\.openclaw\extensions\openclaw-agent-dashboard" -ErrorAction SilentlyContinue
174
+ ```
175
+
176
+ 2. **使用国内镜像安装**:
177
+
178
+ ```powershell
179
+ npx openclaw-agent-dashboard@1.0.8 --verbose --skip-python
180
+ ```
181
+
182
+ 3. **手动安装 Python 依赖(使用镜像)**:
183
+
184
+ ```powershell
185
+ python -m pip install -r "$env:USERPROFILE\.openclaw\extensions\openclaw-agent-dashboard\dashboard\requirements.txt" --user -i https://pypi.tuna.tsinghua.edu.cn/simple
186
+ ```
187
+
188
+ 4. **配置 openclaw 信任插件**(如仍有警告):
189
+
190
+ 编辑 `%USERPROFILE%\.openclaw\config.json`,添加:
191
+
192
+ ```json
193
+ {
194
+ "plugins": {
195
+ "allow": ["openclaw-agent-dashboard"]
196
+ }
197
+ }
198
+ ```
199
+
200
+ 5. **验证**:执行 `openclaw tui`,Dashboard 应自动启动,访问 http://localhost:38271。
201
+
202
+ ---
203
+
204
+ ## 六、后续改进建议(项目维护者)
205
+
206
+ 1. **安装脚本**:在 `openclaw plugins install` 前先 `rmrf(pluginPath)`,或改为传入 `extractedPluginDir` 让 openclaw 自行复制,避免 "plugin already exists"。
207
+ 2. **Python 安装**:支持通过环境变量或参数指定 pip 镜像(如 `PIP_INDEX_URL`)。
208
+ 3. **帮助信息**:在 Windows 下增加代理、镜像、离线安装的说明。
209
+ 4. **runCommand**:考虑使用 `execSync(cmd, args, { shell: false })` 或 `spawn` 传参,消除 DEP0190 警告。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-agent-dashboard",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "多 Agent 可视化看板 - 状态、任务、API、工作流、协作流程",
5
5
  "bin": {
6
6
  "openclaw-agent-dashboard": "scripts/install.js"
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-agent-dashboard",
3
3
  "name": "OpenClaw Agent Dashboard",
4
4
  "description": "多 Agent 可视化看板 - 状态、任务、API、工作流、协作流程",
5
- "version": "1.0.7",
5
+ "version": "1.0.9",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-agent-dashboard",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "多 Agent 可视化看板 - OpenClaw 插件",
5
5
  "main": "index.js",
6
6
  "openclaw": {
@@ -111,6 +111,23 @@ function getVenvPython(venvDir) {
111
111
  return null;
112
112
  }
113
113
 
114
+ // ============================================
115
+ // Pip 镜像
116
+ // ============================================
117
+
118
+ /**
119
+ * 获取 pip 镜像参数
120
+ * 支持环境变量 PIP_INDEX_URL 或 PIP_MIRROR
121
+ * @returns {string[]}
122
+ */
123
+ function getPipMirrorArgs() {
124
+ const mirror = process.env.PIP_INDEX_URL || process.env.PIP_MIRROR || '';
125
+ if (mirror) {
126
+ return ['-i', mirror, '--trusted-host', new URL(mirror).hostname];
127
+ }
128
+ return [];
129
+ }
130
+
114
131
  // ============================================
115
132
  // Python 依赖安装
116
133
  // ============================================
@@ -150,17 +167,31 @@ function installWithVenv(reqFile, venvDir, silent) {
150
167
  }
151
168
 
152
169
  // 升级 pip(静默,失败不影响)
153
- runCommand(venvPython, ['-m', 'pip', 'install', '--upgrade', 'pip', '-q'], { silent: true });
170
+ // 如果设了镜像,升级 pip 也用镜像
171
+ const pipMirrorArgs = getPipMirrorArgs();
172
+ runCommand(venvPython, ['-m', 'pip', 'install', '--upgrade', 'pip', '-q', ...pipMirrorArgs], { silent: true });
154
173
 
155
174
  // 安装依赖
156
175
  logInfo(' 安装依赖...');
157
176
  const installResult = runCommand(
158
177
  venvPython,
159
- ['-m', 'pip', 'install', '-r', reqFile, '-q'],
178
+ ['-m', 'pip', 'install', '-r', reqFile, '-q', ...pipMirrorArgs],
160
179
  { silent, timeout: 180000 }
161
180
  );
162
181
 
163
182
  if (!installResult.success) {
183
+ // 默认源失败,尝试清华镜像
184
+ if (!pipMirrorArgs.length) {
185
+ logWarn(' 默认源失败,尝试清华镜像...');
186
+ const tsinghuaArgs = ['-i', 'https://pypi.tuna.tsinghua.edu.cn/simple', '--trusted-host', 'pypi.tuna.tsinghua.edu.cn'];
187
+ runCommand(venvPython, ['-m', 'pip', 'install', '--upgrade', 'pip', '-q', ...tsinghuaArgs], { silent: true, timeout: 60000 });
188
+ const retryResult = runCommand(
189
+ venvPython,
190
+ ['-m', 'pip', 'install', '-r', reqFile, '-q', ...tsinghuaArgs],
191
+ { silent, timeout: 180000 }
192
+ );
193
+ if (retryResult.success) return true;
194
+ }
164
195
  logWarn(' venv 安装依赖失败');
165
196
  if (!silent) {
166
197
  console.log(' 错误:', installResult.output);
@@ -180,11 +211,12 @@ function installWithVenv(reqFile, venvDir, silent) {
180
211
  function installWithPipUser(reqFile, silent) {
181
212
  logInfo(' 尝试: pip --user(PEP 668 兜底)');
182
213
 
214
+ const pipMirrorArgs = getPipMirrorArgs();
215
+ const tsinghuaArgs = ['-i', 'https://pypi.tuna.tsinghua.edu.cn/simple', '--trusted-host', 'pypi.tuna.tsinghua.edu.cn'];
216
+
183
217
  const pipCommands = [
184
- { cmd: getPythonCmd(), args: ['-m', 'pip', 'install', '-r', reqFile, '-q', '--user'], name: 'pip --user' },
185
- { cmd: getPythonCmd(), args: ['-m', 'pip', 'install', '-r', reqFile, '-q'], name: 'pip' },
186
- { cmd: 'pip', args: ['install', '-r', reqFile, '-q', '--user'], name: 'pip --user' },
187
- { cmd: 'pip3', args: ['install', '-r', reqFile, '-q', '--user'], name: 'pip3 --user' },
218
+ { cmd: getPythonCmd(), args: ['-m', 'pip', 'install', '-r', reqFile, '-q', '--user', ...pipMirrorArgs], name: 'pip --user' },
219
+ { cmd: getPythonCmd(), args: ['-m', 'pip', 'install', '-r', reqFile, '-q', ...pipMirrorArgs], name: 'pip' },
188
220
  ];
189
221
 
190
222
  for (const { cmd, args, name } of pipCommands) {
@@ -196,6 +228,20 @@ function installWithPipUser(reqFile, silent) {
196
228
  }
197
229
  }
198
230
 
231
+ // 默认源失败,尝试清华镜像
232
+ if (!pipMirrorArgs.length) {
233
+ logWarn(' 默认源失败,尝试清华镜像...');
234
+ const retryCommands = [
235
+ { cmd: getPythonCmd(), args: ['-m', 'pip', 'install', '-r', reqFile, '-q', '--user', ...tsinghuaArgs], name: 'pip --user (tsinghua)' },
236
+ { cmd: getPythonCmd(), args: ['-m', 'pip', 'install', '-r', reqFile, '-q', ...tsinghuaArgs], name: 'pip (tsinghua)' },
237
+ ];
238
+ for (const { cmd, args, name } of retryCommands) {
239
+ if (!commandExists(cmd)) continue;
240
+ const result = runCommand(cmd, args, { silent, timeout: 180000 });
241
+ if (result.success) return { success: true, method: name };
242
+ }
243
+ }
244
+
199
245
  return { success: false, method: null };
200
246
  }
201
247
 
@@ -191,10 +191,24 @@ function shellEscape(arg) {
191
191
  */
192
192
  function runCommand(cmd, args = [], options = {}) {
193
193
  const { cwd, silent = true, timeout = 120000 } = options;
194
+ const isWin = process.platform === 'win32';
194
195
 
195
196
  try {
196
- // 构建命令字符串,对参数进行转义
197
- const cmdStr = [cmd, ...args.map(shellEscape)].join(' ');
197
+ // Windows 用双引号,Unix 用单引号
198
+ const esc = (arg) => {
199
+ if (!arg) return '""';
200
+ if (isWin) {
201
+ // Windows: 用双引号包裹含空格/特殊字符的路径
202
+ if (/[^a-zA-Z0-9_\-./:=@]/.test(arg)) {
203
+ return '"' + arg.replace(/"/g, '""') + '"';
204
+ }
205
+ return arg;
206
+ } else {
207
+ return shellEscape(arg);
208
+ }
209
+ };
210
+
211
+ const cmdStr = [cmd, ...args.map(esc)].join(' ');
198
212
 
199
213
  const result = execSync(
200
214
  cmdStr,
@@ -203,7 +217,7 @@ function runCommand(cmd, args = [], options = {}) {
203
217
  encoding: 'utf8',
204
218
  timeout,
205
219
  stdio: silent ? ['ignore', 'pipe', 'pipe'] : 'inherit',
206
- shell: process.platform === 'win32',
220
+ shell: true,
207
221
  }
208
222
  );
209
223
  return { success: true, code: 0, output: result || '' };