openclaw-agent-dashboard 1.0.4
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/.github/workflows/release.yml +56 -0
- package/README.md +302 -0
- package/docs/CHANGELOG_AGENT_MODIFICATIONS.md +132 -0
- package/docs/RELEASE-LATEST.md +189 -0
- package/docs/RELEASE-MODEL-CONFIG.md +95 -0
- package/docs/release-guide.md +259 -0
- package/docs/release-operations-manual.md +167 -0
- package/docs/specs/tr3-install-system.md +580 -0
- package/docs/windows-collaboration-model-paths-troubleshooting.md +0 -0
- package/frontend/index.html +12 -0
- package/frontend/package-lock.json +1240 -0
- package/frontend/package.json +19 -0
- package/frontend/src/App.vue +331 -0
- package/frontend/src/components/AgentCard.vue +796 -0
- package/frontend/src/components/AgentConfigPanel.vue +539 -0
- package/frontend/src/components/AgentDetailPanel.vue +738 -0
- package/frontend/src/components/ErrorAnalysisView.vue +546 -0
- package/frontend/src/components/ErrorCenterPanel.vue +844 -0
- package/frontend/src/components/PerformanceMonitor.vue +515 -0
- package/frontend/src/components/SettingsPanel.vue +236 -0
- package/frontend/src/components/TokenAnalysisPanel.vue +683 -0
- package/frontend/src/components/chain/ChainEdge.vue +85 -0
- package/frontend/src/components/chain/ChainNode.vue +166 -0
- package/frontend/src/components/chain/TaskChainView.vue +425 -0
- package/frontend/src/components/chain/index.ts +3 -0
- package/frontend/src/components/chain/types.ts +70 -0
- package/frontend/src/components/collaboration/CollaborationFlowSection.vue +1032 -0
- package/frontend/src/components/collaboration/CollaborationFlowWrapper.vue +113 -0
- package/frontend/src/components/performance/PerformancePanel.vue +119 -0
- package/frontend/src/components/performance/PerformanceSection.vue +1137 -0
- package/frontend/src/components/tasks/TaskStatusSection.vue +973 -0
- package/frontend/src/components/timeline/TimelineConnector.vue +31 -0
- package/frontend/src/components/timeline/TimelineRound.vue +135 -0
- package/frontend/src/components/timeline/TimelineStep.vue +691 -0
- package/frontend/src/components/timeline/TimelineToolLink.vue +109 -0
- package/frontend/src/components/timeline/TimelineView.vue +540 -0
- package/frontend/src/components/timeline/index.ts +5 -0
- package/frontend/src/components/timeline/types.ts +120 -0
- package/frontend/src/composables/index.ts +7 -0
- package/frontend/src/composables/useDebounce.ts +48 -0
- package/frontend/src/composables/useRealtime.ts +52 -0
- package/frontend/src/composables/useState.ts +52 -0
- package/frontend/src/composables/useThrottle.ts +46 -0
- package/frontend/src/composables/useVirtualScroll.ts +106 -0
- package/frontend/src/main.ts +4 -0
- package/frontend/src/managers/EventDispatcher.ts +127 -0
- package/frontend/src/managers/RealtimeDataManager.ts +293 -0
- package/frontend/src/managers/StateManager.ts +128 -0
- package/frontend/src/managers/index.ts +5 -0
- package/frontend/src/types/collaboration.ts +135 -0
- package/frontend/src/types/index.ts +20 -0
- package/frontend/src/types/performance.ts +105 -0
- package/frontend/src/types/task.ts +38 -0
- package/frontend/vite.config.ts +18 -0
- package/package.json +22 -0
- package/plugin/README.md +99 -0
- package/plugin/config.json.example +1 -0
- package/plugin/index.js +250 -0
- package/plugin/openclaw.plugin.json +17 -0
- package/plugin/package.json +21 -0
- package/scripts/build-plugin.js +67 -0
- package/scripts/bundle.sh +62 -0
- package/scripts/install-plugin.sh +162 -0
- package/scripts/install-python-deps.js +346 -0
- package/scripts/install-python-deps.sh +226 -0
- package/scripts/install.js +512 -0
- package/scripts/install.sh +367 -0
- package/scripts/lib/common.js +490 -0
- package/scripts/lib/common.sh +137 -0
- package/scripts/release-pack.sh +110 -0
- package/scripts/start.js +50 -0
- package/scripts/test_available_models.py +284 -0
- package/scripts/test_websocket_ping.py +44 -0
- package/src/backend/agents.py +73 -0
- package/src/backend/api/__init__.py +1 -0
- package/src/backend/api/agent_config_api.py +90 -0
- package/src/backend/api/agents.py +73 -0
- package/src/backend/api/agents_config.py +75 -0
- package/src/backend/api/chains.py +126 -0
- package/src/backend/api/collaboration.py +902 -0
- package/src/backend/api/debug_paths.py +39 -0
- package/src/backend/api/error_analysis.py +146 -0
- package/src/backend/api/errors.py +281 -0
- package/src/backend/api/performance.py +784 -0
- package/src/backend/api/subagents.py +770 -0
- package/src/backend/api/timeline.py +144 -0
- package/src/backend/api/websocket.py +251 -0
- package/src/backend/collaboration.py +405 -0
- package/src/backend/data/__init__.py +1 -0
- package/src/backend/data/agent_config_manager.py +270 -0
- package/src/backend/data/chain_reader.py +299 -0
- package/src/backend/data/config_reader.py +153 -0
- package/src/backend/data/error_analyzer.py +430 -0
- package/src/backend/data/session_reader.py +445 -0
- package/src/backend/data/subagent_reader.py +244 -0
- package/src/backend/data/task_history.py +118 -0
- package/src/backend/data/timeline_reader.py +981 -0
- package/src/backend/errors.py +63 -0
- package/src/backend/main.py +89 -0
- package/src/backend/mechanism_reader.py +131 -0
- package/src/backend/mechanisms.py +32 -0
- package/src/backend/performance.py +474 -0
- package/src/backend/requirements.txt +5 -0
- package/src/backend/session_reader.py +238 -0
- package/src/backend/status/__init__.py +1 -0
- package/src/backend/status/error_detector.py +122 -0
- package/src/backend/status/status_calculator.py +301 -0
- package/src/backend/status_calculator.py +121 -0
- package/src/backend/subagent_reader.py +229 -0
- package/src/backend/watchers/__init__.py +4 -0
- package/src/backend/watchers/file_watcher.py +159 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# OpenClaw Agent Dashboard 插件 - 安装/升级脚本
|
|
4
|
+
# 用法: npm run deploy(推荐)或 ./scripts/install-plugin.sh
|
|
5
|
+
#
|
|
6
|
+
# 选项:
|
|
7
|
+
# VERBOSE=1 显示详细输出(包括 npm/pip 的错误信息)
|
|
8
|
+
# DRY_RUN=1 仅预览,不执行实际安装
|
|
9
|
+
#
|
|
10
|
+
# 配置目录与 OpenClaw 一致:OPENCLAW_STATE_DIR > OPENCLAW_HOME > HOME
|
|
11
|
+
#
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
cd "$(dirname "$0")/.."
|
|
15
|
+
ROOT=$(pwd)
|
|
16
|
+
SCRIPT_DIR="$ROOT/scripts"
|
|
17
|
+
|
|
18
|
+
# 引入公共库
|
|
19
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
20
|
+
|
|
21
|
+
# 环境变量
|
|
22
|
+
VERBOSE="${VERBOSE:-0}"
|
|
23
|
+
DRY_RUN="${DRY_RUN:-0}"
|
|
24
|
+
|
|
25
|
+
# ============================================
|
|
26
|
+
# 本脚本特有函数
|
|
27
|
+
# ============================================
|
|
28
|
+
|
|
29
|
+
# 获取已安装版本
|
|
30
|
+
get_installed_version() {
|
|
31
|
+
local plugin_path="$1"
|
|
32
|
+
if [ -f "$plugin_path/openclaw.plugin.json" ]; then
|
|
33
|
+
parse_json_version "$plugin_path/openclaw.plugin.json"
|
|
34
|
+
else
|
|
35
|
+
echo ""
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# 检查命令是否存在
|
|
40
|
+
check_cmd() {
|
|
41
|
+
if ! command -v "$1" &>/dev/null; then
|
|
42
|
+
log_error "未找到 $1,请先安装: $2"
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# ============================================
|
|
48
|
+
# 主流程
|
|
49
|
+
# ============================================
|
|
50
|
+
|
|
51
|
+
OPENCLAW_CONFIG_DIR=$(resolve_openclaw_config_dir)
|
|
52
|
+
PLUGIN_PATH="${OPENCLAW_CONFIG_DIR}/extensions/openclaw-agent-dashboard"
|
|
53
|
+
NEW_VERSION=$(parse_json_version "$ROOT/plugin/openclaw.plugin.json")
|
|
54
|
+
OLD_VERSION=$(get_installed_version "$PLUGIN_PATH")
|
|
55
|
+
|
|
56
|
+
log_info "[安装] 配置目录: $OPENCLAW_CONFIG_DIR"
|
|
57
|
+
log_info "[安装] 插件路径: $PLUGIN_PATH"
|
|
58
|
+
echo ""
|
|
59
|
+
|
|
60
|
+
# 显示标题(区分安装/升级)
|
|
61
|
+
if [ -n "$OLD_VERSION" ] && [ -d "$PLUGIN_PATH" ]; then
|
|
62
|
+
log_info "=== OpenClaw Agent Dashboard 插件升级 ==="
|
|
63
|
+
echo ""
|
|
64
|
+
log_info " $OLD_VERSION → $NEW_VERSION"
|
|
65
|
+
else
|
|
66
|
+
log_info "=== OpenClaw Agent Dashboard 插件安装 ==="
|
|
67
|
+
echo ""
|
|
68
|
+
log_info " 版本: $NEW_VERSION"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# dry-run 模式:仅预览
|
|
72
|
+
if [ "$DRY_RUN" = "1" ]; then
|
|
73
|
+
echo ""
|
|
74
|
+
log_info "[DRY-RUN] 将执行以下操作:"
|
|
75
|
+
log_info " - 安装插件到: $PLUGIN_PATH"
|
|
76
|
+
log_info " - 安装 Python 依赖到 venv 或 --user"
|
|
77
|
+
log_ok "预览完成,未执行实际安装"
|
|
78
|
+
exit 0
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# 1. 检查前置条件
|
|
82
|
+
check_cmd node "https://nodejs.org"
|
|
83
|
+
check_cmd python3 "https://www.python.org"
|
|
84
|
+
check_cmd openclaw "npm install -g openclaw"
|
|
85
|
+
|
|
86
|
+
echo ""
|
|
87
|
+
log_ok "前置条件检查通过"
|
|
88
|
+
|
|
89
|
+
# 2. 构建前端(若通过 npm run deploy 调用,pack 已构建,跳过)
|
|
90
|
+
if [ -d "$ROOT/frontend/dist" ] && [ -n "$(ls -A "$ROOT/frontend/dist" 2>/dev/null)" ]; then
|
|
91
|
+
log_step "1/4 前端已构建,跳过"
|
|
92
|
+
else
|
|
93
|
+
log_step "1/4 构建前端..."
|
|
94
|
+
(cd frontend && run_silent npm install && npm run build)
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# 3. 打包插件(若通过 npm run deploy 调用,pack 已完成,跳过)
|
|
98
|
+
if [ -d "$ROOT/plugin/dashboard" ] && [ -f "$ROOT/plugin/dashboard/main.py" ]; then
|
|
99
|
+
log_step "2/4 插件已打包,跳过"
|
|
100
|
+
else
|
|
101
|
+
log_step "2/4 打包插件..."
|
|
102
|
+
node scripts/build-plugin.js
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# 4. 安装插件(升级时用 uninstall 清理配置+目录,避免 plugins.allow 引用已删目录导致校验失败)
|
|
106
|
+
PLUGIN_ID="openclaw-agent-dashboard"
|
|
107
|
+
if [ -d "$PLUGIN_PATH" ]; then
|
|
108
|
+
log_step "3/4 移除旧版本后安装..."
|
|
109
|
+
log_info " 执行: openclaw plugins uninstall $PLUGIN_ID"
|
|
110
|
+
if run_silent openclaw plugins uninstall "$PLUGIN_ID" --force; then
|
|
111
|
+
log_ok " 已卸载(配置记录)"
|
|
112
|
+
else
|
|
113
|
+
log_warn " uninstall 失败(可能未注册)"
|
|
114
|
+
fi
|
|
115
|
+
# uninstall 只删除配置记录,需要手动删除物理目录
|
|
116
|
+
rm -rf "$PLUGIN_PATH"
|
|
117
|
+
log_ok " 已删除旧目录"
|
|
118
|
+
else
|
|
119
|
+
log_step "3/4 安装插件..."
|
|
120
|
+
fi
|
|
121
|
+
log_info " 目标: $PLUGIN_PATH"
|
|
122
|
+
log_info " 执行: openclaw plugins install ./plugin"
|
|
123
|
+
if ! openclaw plugins install ./plugin; then
|
|
124
|
+
log_error "插件安装失败"
|
|
125
|
+
exit 1
|
|
126
|
+
fi
|
|
127
|
+
log_ok " 插件已安装"
|
|
128
|
+
|
|
129
|
+
# 5. 安装 Python 依赖
|
|
130
|
+
# 详见 docs/python-environment-compatibility.md
|
|
131
|
+
if [ -f "$PLUGIN_PATH/dashboard/requirements.txt" ]; then
|
|
132
|
+
if [ -n "$OLD_VERSION" ]; then
|
|
133
|
+
log_step "4/4 检查 Python 依赖..."
|
|
134
|
+
else
|
|
135
|
+
log_step "4/4 安装 Python 依赖..."
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# 调用独立的 Python 依赖安装脚本
|
|
139
|
+
DEPS_OPTS=""
|
|
140
|
+
[ "$VERBOSE" = "1" ] && DEPS_OPTS="--verbose"
|
|
141
|
+
|
|
142
|
+
if ! bash "$SCRIPT_DIR/install-python-deps.sh" "$PLUGIN_PATH" $DEPS_OPTS; then
|
|
143
|
+
exit 1
|
|
144
|
+
fi
|
|
145
|
+
else
|
|
146
|
+
log_warn "插件未正确安装(缺少 requirements.txt)"
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# 完成
|
|
150
|
+
echo ""
|
|
151
|
+
if [ -n "$OLD_VERSION" ]; then
|
|
152
|
+
log_ok "=== 升级完成 ($OLD_VERSION → $NEW_VERSION) ==="
|
|
153
|
+
else
|
|
154
|
+
log_ok "=== 安装完成 (v$NEW_VERSION) ==="
|
|
155
|
+
fi
|
|
156
|
+
echo ""
|
|
157
|
+
log_info "执行任意 openclaw 命令(如 openclaw tui)时,Dashboard 会自动启动。"
|
|
158
|
+
log_info "访问地址: http://localhost:38271"
|
|
159
|
+
echo ""
|
|
160
|
+
log_info "若端口被占用,可创建 ~/.openclaw-agent-dashboard/config.json 设置端口:"
|
|
161
|
+
log_info ' {"port": 38271}'
|
|
162
|
+
echo ""
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Python 依赖安装脚本(跨平台)
|
|
4
|
+
* 用法: node scripts/install-python-deps.js <plugin_dir> [--verbose]
|
|
5
|
+
*
|
|
6
|
+
* 策略:
|
|
7
|
+
* 1. venv(推荐,不受 PEP 668 影响)
|
|
8
|
+
* 2. pip --user(兜底)
|
|
9
|
+
*
|
|
10
|
+
* 选项:
|
|
11
|
+
* --verbose, -v 显示详细输出
|
|
12
|
+
* --venv-only 仅使用 venv,不回退 pip
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const {
|
|
18
|
+
logInfo,
|
|
19
|
+
logOk,
|
|
20
|
+
logWarn,
|
|
21
|
+
logError,
|
|
22
|
+
detectOS,
|
|
23
|
+
commandExists,
|
|
24
|
+
runCommand,
|
|
25
|
+
rmrf,
|
|
26
|
+
} = require('./lib/common');
|
|
27
|
+
|
|
28
|
+
// ============================================
|
|
29
|
+
// 参数解析
|
|
30
|
+
// ============================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 解析命令行参数
|
|
34
|
+
* @returns {{ pluginDir: string | null, verbose: boolean, venvOnly: boolean }}
|
|
35
|
+
*/
|
|
36
|
+
function parseArgs() {
|
|
37
|
+
const args = process.argv.slice(2);
|
|
38
|
+
let pluginDir = null;
|
|
39
|
+
let verbose = false;
|
|
40
|
+
let venvOnly = false;
|
|
41
|
+
|
|
42
|
+
for (const arg of args) {
|
|
43
|
+
if (arg === '--verbose' || arg === '-v') {
|
|
44
|
+
verbose = true;
|
|
45
|
+
} else if (arg === '--venv-only') {
|
|
46
|
+
venvOnly = true;
|
|
47
|
+
} else if (!arg.startsWith('-')) {
|
|
48
|
+
pluginDir = arg;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { pluginDir, verbose, venvOnly };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ============================================
|
|
56
|
+
// Python 环境检测
|
|
57
|
+
// ============================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 检查 venv 模块是否可用
|
|
61
|
+
* @returns {boolean}
|
|
62
|
+
*/
|
|
63
|
+
function checkVenvModule() {
|
|
64
|
+
const result = runCommand('python3', ['-c', 'import venv'], { silent: true });
|
|
65
|
+
return result.success;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 检查 pip 模块是否可用
|
|
70
|
+
* @returns {boolean}
|
|
71
|
+
*/
|
|
72
|
+
function checkPipModule() {
|
|
73
|
+
// 尝试 python3 -m pip
|
|
74
|
+
let result = runCommand('python3', ['-m', 'pip', '--version'], { silent: true });
|
|
75
|
+
if (result.success) return true;
|
|
76
|
+
|
|
77
|
+
// 尝试 pip3
|
|
78
|
+
if (commandExists('pip3')) return true;
|
|
79
|
+
|
|
80
|
+
// 尝试 pip
|
|
81
|
+
if (commandExists('pip')) return true;
|
|
82
|
+
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 获取 venv Python 路径
|
|
88
|
+
* @param {string} venvDir
|
|
89
|
+
* @returns {string | null}
|
|
90
|
+
*/
|
|
91
|
+
function getVenvPython(venvDir) {
|
|
92
|
+
// Unix: venv/bin/python
|
|
93
|
+
const unixPath = path.join(venvDir, 'bin', 'python');
|
|
94
|
+
if (fs.existsSync(unixPath)) return unixPath;
|
|
95
|
+
|
|
96
|
+
// Windows: venv\Scripts\python.exe
|
|
97
|
+
const winPath = path.join(venvDir, 'Scripts', 'python.exe');
|
|
98
|
+
if (fs.existsSync(winPath)) return winPath;
|
|
99
|
+
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ============================================
|
|
104
|
+
// Python 依赖安装
|
|
105
|
+
// ============================================
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 使用 venv 安装依赖
|
|
109
|
+
* @param {string} reqFile
|
|
110
|
+
* @param {string} venvDir
|
|
111
|
+
* @param {boolean} silent
|
|
112
|
+
* @returns {boolean}
|
|
113
|
+
*/
|
|
114
|
+
function installWithVenv(reqFile, venvDir, silent) {
|
|
115
|
+
logInfo(' 尝试: venv(推荐,不受 PEP 668 影响)');
|
|
116
|
+
|
|
117
|
+
// 清理旧 venv
|
|
118
|
+
if (fs.existsSync(venvDir)) {
|
|
119
|
+
rmrf(venvDir);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 创建 venv
|
|
123
|
+
logInfo(' 创建虚拟环境...');
|
|
124
|
+
const createResult = runCommand('python3', ['-m', 'venv', venvDir], { silent });
|
|
125
|
+
if (!createResult.success) {
|
|
126
|
+
logWarn(' venv 创建失败');
|
|
127
|
+
if (!silent) {
|
|
128
|
+
console.log(' 错误:', createResult.output);
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 获取 venv Python 路径
|
|
134
|
+
const venvPython = getVenvPython(venvDir);
|
|
135
|
+
if (!venvPython) {
|
|
136
|
+
logWarn(' 无法找到 venv Python');
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 升级 pip(静默,失败不影响)
|
|
141
|
+
runCommand(venvPython, ['-m', 'pip', 'install', '--upgrade', 'pip', '-q'], { silent: true });
|
|
142
|
+
|
|
143
|
+
// 安装依赖
|
|
144
|
+
logInfo(' 安装依赖...');
|
|
145
|
+
const installResult = runCommand(
|
|
146
|
+
venvPython,
|
|
147
|
+
['-m', 'pip', 'install', '-r', reqFile, '-q'],
|
|
148
|
+
{ silent, timeout: 180000 }
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
if (!installResult.success) {
|
|
152
|
+
logWarn(' venv 安装依赖失败');
|
|
153
|
+
if (!silent) {
|
|
154
|
+
console.log(' 错误:', installResult.output);
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 使用 pip --user 安装依赖
|
|
164
|
+
* @param {string} reqFile
|
|
165
|
+
* @param {boolean} silent
|
|
166
|
+
* @returns {{ success: boolean, method: string | null }}
|
|
167
|
+
*/
|
|
168
|
+
function installWithPipUser(reqFile, silent) {
|
|
169
|
+
logInfo(' 尝试: pip --user(PEP 668 兜底)');
|
|
170
|
+
|
|
171
|
+
const pipCommands = [
|
|
172
|
+
{ cmd: 'python3', args: ['-m', 'pip', 'install', '-r', reqFile, '-q', '--user'], name: 'python3 -m pip --user' },
|
|
173
|
+
{ cmd: 'python3', args: ['-m', 'pip', 'install', '-r', reqFile, '-q'], name: 'python3 -m pip' },
|
|
174
|
+
{ cmd: 'pip', args: ['install', '-r', reqFile, '-q', '--user'], name: 'pip --user' },
|
|
175
|
+
{ cmd: 'pip3', args: ['install', '-r', reqFile, '-q', '--user'], name: 'pip3 --user' },
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
for (const { cmd, args, name } of pipCommands) {
|
|
179
|
+
if (!commandExists(cmd)) continue;
|
|
180
|
+
|
|
181
|
+
const result = runCommand(cmd, args, { silent, timeout: 180000 });
|
|
182
|
+
if (result.success) {
|
|
183
|
+
return { success: true, method: name };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return { success: false, method: null };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 安装 Python 依赖
|
|
192
|
+
* @param {string} pluginDir
|
|
193
|
+
* @param {object} options
|
|
194
|
+
* @param {boolean} [options.verbose]
|
|
195
|
+
* @param {boolean} [options.venvOnly]
|
|
196
|
+
* @returns {boolean}
|
|
197
|
+
*/
|
|
198
|
+
function installPythonDeps(pluginDir, options = {}) {
|
|
199
|
+
const { verbose = false, venvOnly = false } = options;
|
|
200
|
+
const reqFile = path.join(pluginDir, 'dashboard', 'requirements.txt');
|
|
201
|
+
const venvDir = path.join(pluginDir, 'dashboard', '.venv');
|
|
202
|
+
|
|
203
|
+
// 检查 requirements.txt
|
|
204
|
+
if (!fs.existsSync(reqFile)) {
|
|
205
|
+
logWarn('未找到 requirements.txt');
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const silent = !verbose;
|
|
210
|
+
let success = false;
|
|
211
|
+
let method = null;
|
|
212
|
+
|
|
213
|
+
// 策略 1: venv(推荐)
|
|
214
|
+
if (checkVenvModule()) {
|
|
215
|
+
if (installWithVenv(reqFile, venvDir, silent)) {
|
|
216
|
+
success = true;
|
|
217
|
+
method = 'venv';
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
logWarn(' venv 模块不可用');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// 策略 2: pip --user 兜底
|
|
224
|
+
if (!success && !venvOnly) {
|
|
225
|
+
const result = installWithPipUser(reqFile, silent);
|
|
226
|
+
if (result.success) {
|
|
227
|
+
success = true;
|
|
228
|
+
method = result.method;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// 结果
|
|
233
|
+
if (success) {
|
|
234
|
+
logOk(`Python 依赖已就绪 (${method})`);
|
|
235
|
+
return true;
|
|
236
|
+
} else {
|
|
237
|
+
logError('Python 依赖安装失败');
|
|
238
|
+
printPythonDepsHelp(reqFile);
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ============================================
|
|
244
|
+
// 帮助信息
|
|
245
|
+
// ============================================
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* 打印 Python 依赖安装帮助
|
|
249
|
+
* @param {string} reqFile
|
|
250
|
+
*/
|
|
251
|
+
function printPythonDepsHelp(reqFile) {
|
|
252
|
+
const os = detectOS();
|
|
253
|
+
|
|
254
|
+
console.log('');
|
|
255
|
+
console.log('========================================');
|
|
256
|
+
console.log('请检查以下系统依赖是否已安装:');
|
|
257
|
+
console.log('========================================');
|
|
258
|
+
console.log('');
|
|
259
|
+
|
|
260
|
+
switch (os) {
|
|
261
|
+
case 'linux':
|
|
262
|
+
console.log('检测到 Linux 系统');
|
|
263
|
+
console.log('');
|
|
264
|
+
console.log('Debian/Ubuntu:');
|
|
265
|
+
console.log(' sudo apt update && sudo apt install python3 python3-pip python3-venv');
|
|
266
|
+
console.log('');
|
|
267
|
+
console.log('Fedora/CentOS/RHEL:');
|
|
268
|
+
console.log(' sudo dnf install python3 python3-pip');
|
|
269
|
+
console.log('');
|
|
270
|
+
break;
|
|
271
|
+
|
|
272
|
+
case 'macos':
|
|
273
|
+
console.log('检测到 macOS 系统');
|
|
274
|
+
console.log('');
|
|
275
|
+
console.log('使用 Homebrew:');
|
|
276
|
+
console.log(' brew install python3');
|
|
277
|
+
console.log('');
|
|
278
|
+
break;
|
|
279
|
+
|
|
280
|
+
case 'windows':
|
|
281
|
+
console.log('检测到 Windows 系统');
|
|
282
|
+
console.log('');
|
|
283
|
+
console.log('1. 从 https://www.python.org 下载安装 Python 3');
|
|
284
|
+
console.log('2. 安装时务必勾选 "Add Python to PATH"');
|
|
285
|
+
console.log('3. 安装完成后重新打开终端');
|
|
286
|
+
console.log('');
|
|
287
|
+
break;
|
|
288
|
+
|
|
289
|
+
default:
|
|
290
|
+
console.log('请确保已安装:');
|
|
291
|
+
console.log(' - Python 3');
|
|
292
|
+
console.log(' - pip (python3-pip)');
|
|
293
|
+
console.log(' - venv 模块 (python3-venv,Linux 通常需要单独安装)');
|
|
294
|
+
console.log('');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
console.log('========================================');
|
|
298
|
+
console.log('安装系统依赖后,重新执行:');
|
|
299
|
+
console.log('========================================');
|
|
300
|
+
console.log('');
|
|
301
|
+
console.log(' npm run deploy');
|
|
302
|
+
console.log('');
|
|
303
|
+
console.log('或手动安装依赖:');
|
|
304
|
+
console.log('');
|
|
305
|
+
console.log(` python3 -m pip install -r ${reqFile} --user`);
|
|
306
|
+
console.log('');
|
|
307
|
+
console.log('========================================');
|
|
308
|
+
console.log('调试模式:');
|
|
309
|
+
console.log('========================================');
|
|
310
|
+
console.log('');
|
|
311
|
+
console.log(' VERBOSE=1 npm run deploy');
|
|
312
|
+
console.log('');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ============================================
|
|
316
|
+
// 主函数
|
|
317
|
+
// ============================================
|
|
318
|
+
|
|
319
|
+
function main() {
|
|
320
|
+
const { pluginDir, verbose, venvOnly } = parseArgs();
|
|
321
|
+
|
|
322
|
+
// 检查参数
|
|
323
|
+
if (!pluginDir) {
|
|
324
|
+
console.log('用法: node scripts/install-python-deps.js <plugin_dir> [options]');
|
|
325
|
+
console.log('');
|
|
326
|
+
console.log('参数:');
|
|
327
|
+
console.log(' plugin_dir 插件安装目录 (必须)');
|
|
328
|
+
console.log('');
|
|
329
|
+
console.log('选项:');
|
|
330
|
+
console.log(' --verbose, -v 显示详细输出');
|
|
331
|
+
console.log(' --venv-only 仅使用 venv,不回退 pip');
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 检查目录
|
|
336
|
+
if (!fs.existsSync(pluginDir)) {
|
|
337
|
+
logError(`插件目录不存在: ${pluginDir}`);
|
|
338
|
+
process.exit(1);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 执行安装
|
|
342
|
+
const success = installPythonDeps(pluginDir, { verbose, venvOnly });
|
|
343
|
+
process.exit(success ? 0 : 1);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
main();
|