tuyoo-devflow 0.1.0
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/CHANGELOG.md +44 -0
- package/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +21 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/agent/doctor.d.ts +2 -0
- package/dist/commands/agent/doctor.js +70 -0
- package/dist/commands/agent/doctor.js.map +1 -0
- package/dist/commands/agent/index.d.ts +2 -0
- package/dist/commands/agent/index.js +8 -0
- package/dist/commands/agent/index.js.map +1 -0
- package/dist/commands/agent/install.d.ts +2 -0
- package/dist/commands/agent/install.js +300 -0
- package/dist/commands/agent/install.js.map +1 -0
- package/dist/commands/agent/shared.d.ts +11 -0
- package/dist/commands/agent/shared.js +33 -0
- package/dist/commands/agent/shared.js.map +1 -0
- package/dist/commands/agent/start.d.ts +3 -0
- package/dist/commands/agent/start.js +85 -0
- package/dist/commands/agent/start.js.map +1 -0
- package/dist/constants/versionPolicy.d.ts +12 -0
- package/dist/constants/versionPolicy.js +13 -0
- package/dist/constants/versionPolicy.js.map +1 -0
- package/dist/constants/whitelist.d.ts +3 -0
- package/dist/constants/whitelist.js +43 -0
- package/dist/constants/whitelist.js.map +1 -0
- package/dist/core/generator/linuxSystemd.d.ts +15 -0
- package/dist/core/generator/linuxSystemd.js +64 -0
- package/dist/core/generator/linuxSystemd.js.map +1 -0
- package/dist/core/generator/macLaunchd.d.ts +15 -0
- package/dist/core/generator/macLaunchd.js +104 -0
- package/dist/core/generator/macLaunchd.js.map +1 -0
- package/dist/core/generator/macScript.d.ts +2 -0
- package/dist/core/generator/macScript.js +20 -0
- package/dist/core/generator/macScript.js.map +1 -0
- package/dist/core/generator/winBat.d.ts +2 -0
- package/dist/core/generator/winBat.js +20 -0
- package/dist/core/generator/winBat.js.map +1 -0
- package/dist/core/jenkins/directLaunch.d.ts +2 -0
- package/dist/core/jenkins/directLaunch.js +22 -0
- package/dist/core/jenkins/directLaunch.js.map +1 -0
- package/dist/core/jenkins/downloadAgentJar.d.ts +1 -0
- package/dist/core/jenkins/downloadAgentJar.js +13 -0
- package/dist/core/jenkins/downloadAgentJar.js.map +1 -0
- package/dist/core/jenkins/launchArgs.d.ts +2 -0
- package/dist/core/jenkins/launchArgs.js +22 -0
- package/dist/core/jenkins/launchArgs.js.map +1 -0
- package/dist/core/jenkins/parseAgentCommand.d.ts +7 -0
- package/dist/core/jenkins/parseAgentCommand.js +108 -0
- package/dist/core/jenkins/parseAgentCommand.js.map +1 -0
- package/dist/core/log/redact.d.ts +2 -0
- package/dist/core/log/redact.js +26 -0
- package/dist/core/log/redact.js.map +1 -0
- package/dist/core/pipeline/setupPytuyooPipeline.d.ts +30 -0
- package/dist/core/pipeline/setupPytuyooPipeline.js +254 -0
- package/dist/core/pipeline/setupPytuyooPipeline.js.map +1 -0
- package/dist/core/platform.d.ts +5 -0
- package/dist/core/platform.js +27 -0
- package/dist/core/platform.js.map +1 -0
- package/dist/core/precheck/autoInstall.d.ts +9 -0
- package/dist/core/precheck/autoInstall.js +249 -0
- package/dist/core/precheck/autoInstall.js.map +1 -0
- package/dist/core/precheck/linuxGuard.d.ts +2 -0
- package/dist/core/precheck/linuxGuard.js +48 -0
- package/dist/core/precheck/linuxGuard.js.map +1 -0
- package/dist/core/precheck/network.d.ts +2 -0
- package/dist/core/precheck/network.js +42 -0
- package/dist/core/precheck/network.js.map +1 -0
- package/dist/core/precheck/systemInfo.d.ts +2 -0
- package/dist/core/precheck/systemInfo.js +37 -0
- package/dist/core/precheck/systemInfo.js.map +1 -0
- package/dist/core/precheck/tools.d.ts +12 -0
- package/dist/core/precheck/tools.js +135 -0
- package/dist/core/precheck/tools.js.map +1 -0
- package/dist/core/security/secretStore.d.ts +5 -0
- package/dist/core/security/secretStore.js +310 -0
- package/dist/core/security/secretStore.js.map +1 -0
- package/dist/core/shell/commandRunner.d.ts +17 -0
- package/dist/core/shell/commandRunner.js +94 -0
- package/dist/core/shell/commandRunner.js.map +1 -0
- package/dist/core/store/installCommandCache.d.ts +9 -0
- package/dist/core/store/installCommandCache.js +66 -0
- package/dist/core/store/installCommandCache.js.map +1 -0
- package/dist/core/store/profileStore.d.ts +4 -0
- package/dist/core/store/profileStore.js +43 -0
- package/dist/core/store/profileStore.js.map +1 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/CheckReport.d.ts +5 -0
- package/dist/ui/CheckReport.js +24 -0
- package/dist/ui/CheckReport.js.map +1 -0
- package/dist/ui/renderOnce.d.ts +2 -0
- package/dist/ui/renderOnce.js +9 -0
- package/dist/ui/renderOnce.js.map +1 -0
- package/package.json +63 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Jenkins 白名单检查更新为按协议检查端口:
|
|
12
|
+
- `http://` 仅检查 `80`
|
|
13
|
+
- `https://` 仅检查 `443`
|
|
14
|
+
- 始终检查 `50000`
|
|
15
|
+
- 工具策略更新:Java 版本要求从 11+ 提升为 OpenJDK 21+。
|
|
16
|
+
- Java 安装实现调整为 OpenJDK 发行版(macOS / Windows / Linux)。
|
|
17
|
+
- Java 检查逻辑收紧:仅接受 OpenJDK 发行版,非 OpenJDK 即使版本满足也判定失败。
|
|
18
|
+
- 增加 Git / Java / Miniconda 缺失时的自动安装引导与执行流程。
|
|
19
|
+
- `doctor` 命令优化为无参数交互流程:先展示系统信息与检查结果,再按勾选项安装缺失工具。
|
|
20
|
+
- 自动安装流程增加详细实时日志与心跳提示,便于判断安装进度和卡点。
|
|
21
|
+
- `install` 在 conda 环境准备后新增 `conda activate` 可用性检测,并支持一键执行 `conda init`。
|
|
22
|
+
- `install` 检测到 `pytuyoo-pipeline` conda 环境已存在时,默认跳过仓库 clone/依赖安装,并提供“继续执行完整同步”的交互确认。
|
|
23
|
+
- `install` 新增 Agent 启动命令本地临时缓存;二次执行默认复用并可选择修改。
|
|
24
|
+
- 生成策略改为直接写入完整 `java -jar agent.jar ...` 到 `start_jenkins_agent.sh/.bat`,不再依赖 `profile.json`。
|
|
25
|
+
- Linux `install` 安装 systemd 后改为 `systemctl enable --now`,安装完成即启动。
|
|
26
|
+
- macOS 安装策略升级为生成 `LaunchAgent plist` 并使用 `launchctl`(`gui/<uid>` + `Aqua`)管理,以支持 GUI 会话权限。
|
|
27
|
+
- GitLab Token 改为仅用于本次 clone,不再持久化保存;clone 后自动重置 remote 为无凭据地址。
|
|
28
|
+
- Jenkins secret 存储策略简化为明文写入启动脚本(不再依赖 keyring)。
|
|
29
|
+
- 项目命名升级为 `Tuyoo DevFlow`:npm 包名改为 `tuyoo-devflow`,CLI 命令改为 `tdf`。
|
|
30
|
+
|
|
31
|
+
## [0.1.0] - 2026-04-10
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- Initialize `tuyoo-devflow` (Tuyoo DevFlow CLI) with TypeScript + React + Ink CLI architecture.
|
|
36
|
+
- Add `agent doctor` for platform/network/tooling pre-checks.
|
|
37
|
+
- Add `agent install` for parsing agent commands, preparing conda env, and generating startup artifacts.
|
|
38
|
+
- Add cross-platform startup generators:
|
|
39
|
+
- Linux systemd service + startup script
|
|
40
|
+
- macOS shell startup script
|
|
41
|
+
- Windows bat startup script
|
|
42
|
+
- Add secure secret management with keychain/keyring via `keytar`.
|
|
43
|
+
- Add test coverage for command parsing, whitelist targets, and generators.
|
|
44
|
+
- Add release-ready packaging and CI/CD workflows.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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,150 @@
|
|
|
1
|
+
# Tuyoo DevFlow CLI
|
|
2
|
+
|
|
3
|
+
`Tuyoo DevFlow` 是面向 TuyooGame 研发流程的 CLI 工具集,一期聚焦 Jenkins Agent 安装主流程。
|
|
4
|
+
|
|
5
|
+
## 功能范围(一期)
|
|
6
|
+
|
|
7
|
+
- 跨平台支持:`macOS`、`Windows`、`Linux`
|
|
8
|
+
- 前置检查:系统约束、白名单连通性、工具与版本
|
|
9
|
+
- Jenkins 端口检查策略:
|
|
10
|
+
- Jenkins URL 为 `http://` 时仅检查 `80`
|
|
11
|
+
- Jenkins URL 为 `https://` 时仅检查 `443`
|
|
12
|
+
- 始终检查 Jenkins `50000`(JNLP)
|
|
13
|
+
- 依赖准备:`Miniconda` + `pytuyoo-pipeline` 虚拟环境
|
|
14
|
+
- Agent 产物:
|
|
15
|
+
- Linux 生成 `systemd` 服务(优先自动安装并立即启动)
|
|
16
|
+
- macOS 生成 `LaunchAgent plist` 并通过 `launchctl` 管理(GUI/Aqua 会话)
|
|
17
|
+
- Windows 生成 `start_jenkins_agent.bat`
|
|
18
|
+
- 安全策略:
|
|
19
|
+
- Jenkins `secret` 明文写入启动脚本(按简化流程)
|
|
20
|
+
- GitLab `token` 仅用于本次 clone,完成后不持久化保存
|
|
21
|
+
- clone 完成后会重置 git remote,清理凭据 URL
|
|
22
|
+
- 不在日志明文落盘
|
|
23
|
+
|
|
24
|
+
## 环境要求
|
|
25
|
+
|
|
26
|
+
- Node.js `>= 20`
|
|
27
|
+
- Java `>= 21`(OpenJDK 21+)
|
|
28
|
+
- Git `>= 2.30`
|
|
29
|
+
- Miniconda(`conda` 命令可用)
|
|
30
|
+
- Linux 额外要求:
|
|
31
|
+
- 系统必须是 `rocky9*`
|
|
32
|
+
- 必须存在 `tywork` 用户
|
|
33
|
+
- 建议 `root` 或 `tywork` 身份执行
|
|
34
|
+
|
|
35
|
+
## 安装与开发
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install
|
|
39
|
+
npm run build
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
本地调试:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm run dev -- --help
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 命令说明
|
|
49
|
+
|
|
50
|
+
检查模式(只读):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
tdf agent doctor
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
进入后会自动进入交互式页面:
|
|
57
|
+
|
|
58
|
+
- 先展示当前系统信息
|
|
59
|
+
- 再执行 doctor 检查并输出结果
|
|
60
|
+
- 若存在缺失工具,提供可勾选的自动安装项
|
|
61
|
+
|
|
62
|
+
安装模式:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
tdf agent install
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
当检测到 Git/Java/Miniconda 缺失或版本不满足时,`install` 会引导自动安装:
|
|
69
|
+
|
|
70
|
+
- Git:安装最新版本
|
|
71
|
+
- Java:安装 OpenJDK 21+(若检测到非 OpenJDK 发行版也会判定失败并引导替换)
|
|
72
|
+
- Miniconda:macOS 可选 `Homebrew` 或 `curl + bash`,Linux 默认 `curl + bash`
|
|
73
|
+
- 自动安装时会实时输出子进程日志,并每 15 秒输出“仍在安装中”状态提示
|
|
74
|
+
- conda 环境准备完成后会自动检测 `conda activate` 能力,若未初始化会醒目提示并自动执行 `conda init`
|
|
75
|
+
|
|
76
|
+
安装时会提示输入:
|
|
77
|
+
|
|
78
|
+
1. `curl -sO .../jnlpJars/agent.jar`
|
|
79
|
+
2. `java -jar agent.jar ...`
|
|
80
|
+
3. GitLab 用户名 / Token(仅在需要重新 clone/安装依赖时询问;Token 仅用于本次 clone,不持久化保存)
|
|
81
|
+
|
|
82
|
+
命令缓存机制:
|
|
83
|
+
|
|
84
|
+
- 首次输入的两行 Agent 命令会保存到本地临时文件
|
|
85
|
+
- 再次执行 `install` 时会询问是否修改(默认不修改,直接复用继续)
|
|
86
|
+
|
|
87
|
+
启动 Agent:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Linux
|
|
91
|
+
bash <workDir>/start_jenkins_agent.sh
|
|
92
|
+
|
|
93
|
+
# macOS(推荐)
|
|
94
|
+
launchctl print gui/$(id -u)/<service-name>
|
|
95
|
+
|
|
96
|
+
# Windows
|
|
97
|
+
<workDir>\\start_jenkins_agent.bat
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## 生成产物
|
|
101
|
+
|
|
102
|
+
- Linux:
|
|
103
|
+
- `${workDir}/start_jenkins_agent.sh`
|
|
104
|
+
- 若具备权限,安装 `/etc/systemd/system/<service>.service` 并执行 `systemctl enable --now`
|
|
105
|
+
- 若无权限,回落写到 `${workDir}/<service>.service` 并提示手动安装 + 启动
|
|
106
|
+
- macOS:
|
|
107
|
+
- `${workDir}/start_jenkins_agent.sh`
|
|
108
|
+
- `~/Library/LaunchAgents/<service>.plist`
|
|
109
|
+
- 自动执行 `launchctl bootout/bootstrap/kickstart`,注册到 `gui/<uid>`(Aqua)
|
|
110
|
+
- 若自动安装失败,回落写到 `${workDir}/<service>.plist` 并提示手动安装命令
|
|
111
|
+
- Windows:`${workDir}/start_jenkins_agent.bat`
|
|
112
|
+
- 启动脚本直接包含完整 `java -jar agent.jar ...` 命令(含 Jenkins secret)
|
|
113
|
+
|
|
114
|
+
## 测试
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm test
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
发布前自检:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npm run verify
|
|
124
|
+
npm run pack:dry
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
已覆盖:
|
|
128
|
+
|
|
129
|
+
- Jenkins 启动命令解析(`-url` / `-jnlpUrl`)
|
|
130
|
+
- 脚本与 systemd/launchd 生成
|
|
131
|
+
- 白名单目标构造
|
|
132
|
+
|
|
133
|
+
## 常见问题
|
|
134
|
+
|
|
135
|
+
- Linux 无法自动安装 systemd:按提示执行 `sudo cp ...` 与 `systemctl daemon-reload`。
|
|
136
|
+
- macOS 首次运行可能触发系统权限弹窗(如辅助功能/屏幕录制等),请在图形会话中按提示授权。
|
|
137
|
+
- 白名单检查失败:优先确认 DNS、代理、防火墙与内网访问策略。
|
|
138
|
+
|
|
139
|
+
## CI / 发布
|
|
140
|
+
|
|
141
|
+
- GitHub Actions
|
|
142
|
+
- CI:`.github/workflows/ci.yml`
|
|
143
|
+
- 发布:`.github/workflows/publish.yml`(tag `v*` 或手动触发)
|
|
144
|
+
- Secret:`NPM_TOKEN`
|
|
145
|
+
- GitLab CI
|
|
146
|
+
- CI + 发布:`.gitlab-ci.yml`
|
|
147
|
+
- 规则:tag 命中 `vX.Y.Z` 时执行 `npm publish`
|
|
148
|
+
- Variable:`NPM_TOKEN`
|
|
149
|
+
|
|
150
|
+
详细发布步骤见 `RELEASING.md`。
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { registerAgentCommand } from "./commands/agent/index.js";
|
|
5
|
+
import { safeErrorMessage } from "./core/log/redact.js";
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const pkg = require("../package.json");
|
|
8
|
+
async function bootstrap() {
|
|
9
|
+
const program = new Command();
|
|
10
|
+
program
|
|
11
|
+
.name("tdf")
|
|
12
|
+
.description("Tuyoo DevFlow CLI 工具集")
|
|
13
|
+
.version(pkg.version, "-V, --version", "输出版本号");
|
|
14
|
+
registerAgentCommand(program);
|
|
15
|
+
await program.parseAsync(process.argv);
|
|
16
|
+
}
|
|
17
|
+
bootstrap().catch((error) => {
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
console.error(`命令执行失败: ${safeErrorMessage(error)}`);
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;AAE9D,KAAK,UAAU,SAAS;IACtB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,KAAK,CAAC;SACX,WAAW,CAAC,uBAAuB,CAAC;SACpC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,CAAC,CAAC;IAElD,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAE9B,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC1B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACrB,OAAO,CAAC,KAAK,CAAC,WAAW,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { input } from "@inquirer/prompts";
|
|
3
|
+
import { getCurrentUsername, getSupportedPlatform } from "../../core/platform.js";
|
|
4
|
+
import { safeErrorMessage } from "../../core/log/redact.js";
|
|
5
|
+
import { guideAndInstallMissingTools } from "../../core/precheck/autoInstall.js";
|
|
6
|
+
import { checkRequiredTools } from "../../core/precheck/tools.js";
|
|
7
|
+
import { collectSystemInfoResults } from "../../core/precheck/systemInfo.js";
|
|
8
|
+
import { CheckReport } from "../../ui/CheckReport.js";
|
|
9
|
+
import { renderOnce } from "../../ui/renderOnce.js";
|
|
10
|
+
import { getFailedToolChecks, hasLinuxOrNetworkFailure, printCheckSummary, runDoctorChecks, } from "./shared.js";
|
|
11
|
+
export function registerDoctorCommand(agentCommand) {
|
|
12
|
+
agentCommand
|
|
13
|
+
.command("doctor")
|
|
14
|
+
.description("进入交互式 doctor 页面并执行检查")
|
|
15
|
+
.action(async () => {
|
|
16
|
+
try {
|
|
17
|
+
const platform = getSupportedPlatform();
|
|
18
|
+
const username = getCurrentUsername();
|
|
19
|
+
const systemInfo = collectSystemInfoResults(platform, username);
|
|
20
|
+
await renderOnce(_jsx(CheckReport, { title: "\u5F53\u524D\u7CFB\u7EDF\u4FE1\u606F", results: systemInfo }));
|
|
21
|
+
const jenkinsUrl = await input({
|
|
22
|
+
message: "请输入 Jenkins URL(例如 http://jenkins.example.com/)",
|
|
23
|
+
validate(value) {
|
|
24
|
+
try {
|
|
25
|
+
new URL(value);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return "URL 格式不正确";
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
let checks = await runDoctorChecks(platform, jenkinsUrl);
|
|
34
|
+
await printCheckSummary(checks);
|
|
35
|
+
if (hasLinuxOrNetworkFailure(checks)) {
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
console.error("系统/网络前置检查未通过,请修复后重试。");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const failedToolChecks = getFailedToolChecks(checks);
|
|
41
|
+
if (failedToolChecks.length > 0) {
|
|
42
|
+
const installResult = await guideAndInstallMissingTools(platform, failedToolChecks);
|
|
43
|
+
if (installResult.selected.length === 0) {
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
console.error("你未选择任何工具进行修复,doctor 未通过。");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const toolChecksAfterInstall = await checkRequiredTools(platform);
|
|
49
|
+
await renderOnce(_jsx(CheckReport, { title: "\u5DE5\u5177\u590D\u68C0\u7ED3\u679C", results: toolChecksAfterInstall }));
|
|
50
|
+
if (toolChecksAfterInstall.some((item) => item.status === "fail")) {
|
|
51
|
+
process.exitCode = 1;
|
|
52
|
+
console.error("工具自动安装后仍有失败项,请手动修复。");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
checks = await runDoctorChecks(platform, jenkinsUrl);
|
|
56
|
+
}
|
|
57
|
+
if (hasLinuxOrNetworkFailure(checks)) {
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
console.error("复检发现系统/网络检查失败,请修复后重试。");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
console.log("前置检查全部通过。");
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
console.error(`doctor 执行失败: ${safeErrorMessage(error)}`);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/commands/agent/doctor.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { registerDoctorCommand } from "./doctor.js";
|
|
2
|
+
import { registerInstallCommand } from "./install.js";
|
|
3
|
+
export function registerAgentCommand(program) {
|
|
4
|
+
const agentCommand = program.command("agent").description("Jenkins Agent 安装与运维命令");
|
|
5
|
+
registerDoctorCommand(agentCommand);
|
|
6
|
+
registerInstallCommand(agentCommand);
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/agent/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAC;IACnF,qBAAqB,CAAC,YAAY,CAAC,CAAC;IACpC,sBAAsB,CAAC,YAAY,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { confirm, input, password } from "@inquirer/prompts";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { VERSION_POLICY } from "../../constants/versionPolicy.js";
|
|
6
|
+
import { downloadAgentJar } from "../../core/jenkins/downloadAgentJar.js";
|
|
7
|
+
import { buildJavaArgsFromParsed } from "../../core/jenkins/directLaunch.js";
|
|
8
|
+
import { parseAgentCommand } from "../../core/jenkins/parseAgentCommand.js";
|
|
9
|
+
import { safeErrorMessage } from "../../core/log/redact.js";
|
|
10
|
+
import { getCurrentUsername, getDefaultPipelineRepoDir, getSupportedPlatform } from "../../core/platform.js";
|
|
11
|
+
import { checkCondaEnvExists, inspectCondaActivateForShell, runCondaInitForShell, setupPytuyooPipelineEnv, } from "../../core/pipeline/setupPytuyooPipeline.js";
|
|
12
|
+
import { guideAndInstallMissingTools } from "../../core/precheck/autoInstall.js";
|
|
13
|
+
import { checkRequiredTools } from "../../core/precheck/tools.js";
|
|
14
|
+
import { buildLinuxSystemdUnit, tryInstallSystemdService, writeLinuxStartupScript, } from "../../core/generator/linuxSystemd.js";
|
|
15
|
+
import { resolveMacGuiUid, tryInstallMacLaunchAgent, } from "../../core/generator/macLaunchd.js";
|
|
16
|
+
import { writeMacStartupScript } from "../../core/generator/macScript.js";
|
|
17
|
+
import { writeWindowsStartupBat } from "../../core/generator/winBat.js";
|
|
18
|
+
import { loadInstallCommandCache, saveInstallCommandCache, shouldPersistInstallCommandCache, } from "../../core/store/installCommandCache.js";
|
|
19
|
+
import { renderOnce } from "../../ui/renderOnce.js";
|
|
20
|
+
import { CheckReport } from "../../ui/CheckReport.js";
|
|
21
|
+
import { getFailedToolChecks, hasLinuxOrNetworkFailure, printCheckSummary, runDoctorChecks, } from "./shared.js";
|
|
22
|
+
function printAgentCommandInputGuide() {
|
|
23
|
+
console.log("");
|
|
24
|
+
console.log("步骤 1/5: 请输入 Jenkins Agent 启动命令");
|
|
25
|
+
console.log("需要两行命令(可从 Jenkins 节点页面复制):");
|
|
26
|
+
console.log(" [第1行] curl 下载命令(macOS/Linux):");
|
|
27
|
+
console.log(" curl -sO http://<jenkins-host>/jnlpJars/agent.jar");
|
|
28
|
+
console.log(" [第1行] curl 下载命令(Windows):");
|
|
29
|
+
console.log(" curl.exe -sO http://<jenkins-host>/jnlpJars/agent.jar");
|
|
30
|
+
console.log(" [第2行] java 启动命令(支持 -url 或 -jnlpUrl):");
|
|
31
|
+
console.log(' java -jar agent.jar -url http://<jenkins-host>/ -secret <secret> -name "<node>" -workDir "<workDir>"');
|
|
32
|
+
console.log("提示: 命令中的 secret 将明文写入启动脚本。");
|
|
33
|
+
console.log("");
|
|
34
|
+
}
|
|
35
|
+
function formatCacheTime(isoTime) {
|
|
36
|
+
if (!isoTime) {
|
|
37
|
+
return "未知时间";
|
|
38
|
+
}
|
|
39
|
+
const date = new Date(isoTime);
|
|
40
|
+
if (Number.isNaN(date.getTime())) {
|
|
41
|
+
return isoTime;
|
|
42
|
+
}
|
|
43
|
+
return date.toLocaleString();
|
|
44
|
+
}
|
|
45
|
+
function maskSecretInJavaCommand(command) {
|
|
46
|
+
return command.replace(/(-secret\s+)(?:"[^"]+"|'[^']+'|\S+)/gi, "$1***");
|
|
47
|
+
}
|
|
48
|
+
function sanitizeName(value) {
|
|
49
|
+
return value
|
|
50
|
+
.toLowerCase()
|
|
51
|
+
.replace(/[^a-z0-9_-]+/g, "-")
|
|
52
|
+
.replace(/-+/g, "-")
|
|
53
|
+
.replace(/^-|-$/g, "");
|
|
54
|
+
}
|
|
55
|
+
function defaultServiceName(nodeName) {
|
|
56
|
+
const base = sanitizeName(nodeName ?? "agent");
|
|
57
|
+
return `jenkins-agent-${base || "default"}`;
|
|
58
|
+
}
|
|
59
|
+
export function registerInstallCommand(agentCommand) {
|
|
60
|
+
agentCommand
|
|
61
|
+
.command("install")
|
|
62
|
+
.description("安装 Jenkins Agent 并生成启动脚本/服务")
|
|
63
|
+
.option("--curl-line <line>", "curl 下载命令行")
|
|
64
|
+
.option("--java-line <line>", "java 启动命令行")
|
|
65
|
+
.option("--gitlab-user <user>", "GitLab 用户名")
|
|
66
|
+
.option("--gitlab-token <token>", "GitLab Token(不建议命令行明文传入)")
|
|
67
|
+
.action(async (options) => {
|
|
68
|
+
try {
|
|
69
|
+
const platform = getSupportedPlatform();
|
|
70
|
+
const currentUser = getCurrentUsername();
|
|
71
|
+
if (platform === "linux" && currentUser !== "tywork" && currentUser !== "root") {
|
|
72
|
+
throw new Error("Linux 请使用 tywork 或 root 身份执行安装。");
|
|
73
|
+
}
|
|
74
|
+
const cachedCommands = await loadInstallCommandCache();
|
|
75
|
+
let curlLine = options.curlLine;
|
|
76
|
+
let javaLine = options.javaLine;
|
|
77
|
+
if (cachedCommands && (!curlLine || !javaLine)) {
|
|
78
|
+
console.log(`检测到本地临时缓存的 Agent 命令(更新时间: ${formatCacheTime(cachedCommands.updatedAt)})。`);
|
|
79
|
+
console.log("缓存内容如下(敏感信息已脱敏):");
|
|
80
|
+
console.log(` [第1行] ${cachedCommands.curlLine}`);
|
|
81
|
+
console.log(` [第2行] ${maskSecretInJavaCommand(cachedCommands.javaLine)}`);
|
|
82
|
+
const shouldModifyCachedCommand = await confirm({
|
|
83
|
+
message: "是否需要修改命令?(默认不修改,直接继续下一步)",
|
|
84
|
+
default: false,
|
|
85
|
+
});
|
|
86
|
+
if (!shouldModifyCachedCommand) {
|
|
87
|
+
curlLine = curlLine ?? cachedCommands.curlLine;
|
|
88
|
+
javaLine = javaLine ?? cachedCommands.javaLine;
|
|
89
|
+
console.log("已使用本地缓存命令,继续执行。");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!curlLine || !javaLine) {
|
|
93
|
+
printAgentCommandInputGuide();
|
|
94
|
+
}
|
|
95
|
+
curlLine =
|
|
96
|
+
curlLine ??
|
|
97
|
+
(await input({
|
|
98
|
+
message: "请输入第1行:curl 下载 agent.jar 命令",
|
|
99
|
+
default: cachedCommands?.curlLine,
|
|
100
|
+
validate(value) {
|
|
101
|
+
if (!value.includes("curl")) {
|
|
102
|
+
return "命令需包含 curl 或 curl.exe";
|
|
103
|
+
}
|
|
104
|
+
if (!value.includes("agent.jar")) {
|
|
105
|
+
return "命令需包含 agent.jar 下载地址";
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
},
|
|
109
|
+
}));
|
|
110
|
+
javaLine =
|
|
111
|
+
javaLine ??
|
|
112
|
+
(await input({
|
|
113
|
+
message: "请输入第2行:java 启动 Agent 命令(需包含 -secret 与 -workDir)",
|
|
114
|
+
default: cachedCommands?.javaLine,
|
|
115
|
+
validate(value) {
|
|
116
|
+
if (!value.includes("java")) {
|
|
117
|
+
return "命令需包含 java";
|
|
118
|
+
}
|
|
119
|
+
if (!value.includes("-secret")) {
|
|
120
|
+
return "命令需包含 -secret 参数";
|
|
121
|
+
}
|
|
122
|
+
if (!value.includes("-workDir")) {
|
|
123
|
+
return "命令需包含 -workDir 参数";
|
|
124
|
+
}
|
|
125
|
+
if (!value.includes("-url") && !value.includes("-jnlpUrl")) {
|
|
126
|
+
return "命令需包含 -url 或 -jnlpUrl 参数";
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
},
|
|
130
|
+
}));
|
|
131
|
+
console.log("步骤 2/5: 执行系统、网络与工具检查...");
|
|
132
|
+
const parsed = parseAgentCommand({ curlLine, javaLine });
|
|
133
|
+
const javaArgs = buildJavaArgsFromParsed(parsed);
|
|
134
|
+
const checks = await runDoctorChecks(platform, parsed.jenkinsBaseUrl);
|
|
135
|
+
await printCheckSummary(checks);
|
|
136
|
+
if (hasLinuxOrNetworkFailure(checks)) {
|
|
137
|
+
throw new Error("系统/网络前置检查未通过,安装中止。");
|
|
138
|
+
}
|
|
139
|
+
const failedToolChecks = getFailedToolChecks(checks);
|
|
140
|
+
if (failedToolChecks.length > 0) {
|
|
141
|
+
await guideAndInstallMissingTools(platform, failedToolChecks);
|
|
142
|
+
const toolChecksAfterInstall = await checkRequiredTools(platform);
|
|
143
|
+
await renderOnce(_jsx(CheckReport, { title: "\u5DE5\u5177\u590D\u68C0\u7ED3\u679C", results: toolChecksAfterInstall }));
|
|
144
|
+
const stillFail = toolChecksAfterInstall.some((item) => item.status === "fail");
|
|
145
|
+
if (stillFail) {
|
|
146
|
+
throw new Error("工具自动安装后仍有检查项失败,请手动处理后重试。");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (shouldPersistInstallCommandCache(curlLine, javaLine)) {
|
|
150
|
+
await saveInstallCommandCache(curlLine, javaLine);
|
|
151
|
+
}
|
|
152
|
+
console.log("步骤 3/5: 准备 GitLab 凭证与 conda 流水线环境...");
|
|
153
|
+
const repoDir = getDefaultPipelineRepoDir(platform);
|
|
154
|
+
const hasPipelineEnv = await checkCondaEnvExists({
|
|
155
|
+
platform,
|
|
156
|
+
envName: VERSION_POLICY.conda.envName,
|
|
157
|
+
});
|
|
158
|
+
let shouldSetupPipelineRepo = true;
|
|
159
|
+
if (hasPipelineEnv) {
|
|
160
|
+
console.log(`[步骤 3/5] 检测到 conda 环境 ${VERSION_POLICY.conda.envName} 已存在,默认无需重复 clone pytuyoo-pipeline。`);
|
|
161
|
+
shouldSetupPipelineRepo = await confirm({
|
|
162
|
+
message: "如需重新 clone 仓库并执行 poetry install,请选择继续执行",
|
|
163
|
+
default: false,
|
|
164
|
+
});
|
|
165
|
+
if (!shouldSetupPipelineRepo) {
|
|
166
|
+
console.log("[步骤 3/5] 已按默认策略跳过 pytuyoo-pipeline 仓库与依赖安装。");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (shouldSetupPipelineRepo) {
|
|
170
|
+
const gitlabUser = options.gitlabUser ??
|
|
171
|
+
(await input({
|
|
172
|
+
message: "请输入 GitLab 用户名",
|
|
173
|
+
default: currentUser,
|
|
174
|
+
validate(value) {
|
|
175
|
+
return value.trim().length > 0 ? true : "用户名不能为空";
|
|
176
|
+
},
|
|
177
|
+
}));
|
|
178
|
+
const gitlabToken = options.gitlabToken ??
|
|
179
|
+
(await password({
|
|
180
|
+
message: "请输入 GitLab Token(仅用于本次 clone,完成后不会持久化保存)",
|
|
181
|
+
mask: "*",
|
|
182
|
+
validate(value) {
|
|
183
|
+
return value.trim().length > 0 ? true : "Token 不能为空";
|
|
184
|
+
},
|
|
185
|
+
}));
|
|
186
|
+
await setupPytuyooPipelineEnv({
|
|
187
|
+
platform,
|
|
188
|
+
repoDir,
|
|
189
|
+
gitlabUsername: gitlabUser,
|
|
190
|
+
gitlabToken,
|
|
191
|
+
logger(message) {
|
|
192
|
+
console.log(`[步骤 3/5] ${message}`);
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
const condaActivateStatus = await inspectCondaActivateForShell({
|
|
197
|
+
platform,
|
|
198
|
+
asUser: platform === "linux" ? "tywork" : undefined,
|
|
199
|
+
envName: VERSION_POLICY.conda.envName,
|
|
200
|
+
});
|
|
201
|
+
if (condaActivateStatus.supported) {
|
|
202
|
+
console.log(`[步骤 3/5] ${condaActivateStatus.detail}`);
|
|
203
|
+
if (condaActivateStatus.needsInit && condaActivateStatus.shellName) {
|
|
204
|
+
console.log("[步骤 3/5] ========================================");
|
|
205
|
+
console.log(`[步骤 3/5] 注意: 检测到手动 conda activate 可能失败,将自动执行 conda init ${condaActivateStatus.shellName}`);
|
|
206
|
+
console.log("[步骤 3/5] ========================================");
|
|
207
|
+
try {
|
|
208
|
+
const afterInit = await runCondaInitForShell({
|
|
209
|
+
platform,
|
|
210
|
+
asUser: platform === "linux" ? "tywork" : undefined,
|
|
211
|
+
logger(message) {
|
|
212
|
+
console.log(`[步骤 3/5] ${message}`);
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
console.log(`[步骤 3/5] ${afterInit.detail}`);
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
console.warn(`[步骤 3/5] conda init 自动执行失败: ${safeErrorMessage(error)}`);
|
|
219
|
+
if (condaActivateStatus.manualInitCommand) {
|
|
220
|
+
console.warn(`[步骤 3/5] 请手动执行: ${condaActivateStatus.manualInitCommand},并重开终端后使用 conda activate ${VERSION_POLICY.conda.envName}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
console.log("步骤 4/5: 生成 Agent 配置并下载 agent.jar...");
|
|
226
|
+
await fs.ensureDir(parsed.workDir);
|
|
227
|
+
const startupScriptPath = platform === "windows"
|
|
228
|
+
? path.join(parsed.workDir, "start_jenkins_agent.bat")
|
|
229
|
+
: path.join(parsed.workDir, "start_jenkins_agent.sh");
|
|
230
|
+
const serviceName = platform === "windows" ? "" : defaultServiceName(parsed.nodeName);
|
|
231
|
+
const jarPath = path.join(parsed.workDir, "agent.jar");
|
|
232
|
+
await downloadAgentJar(parsed.jarDownloadUrl, jarPath);
|
|
233
|
+
let summaryLines = [];
|
|
234
|
+
if (platform === "linux") {
|
|
235
|
+
await writeLinuxStartupScript(startupScriptPath, parsed.workDir, javaArgs);
|
|
236
|
+
const installed = await tryInstallSystemdService({
|
|
237
|
+
serviceName,
|
|
238
|
+
serviceUser: "tywork",
|
|
239
|
+
workDir: parsed.workDir,
|
|
240
|
+
startupScriptPath,
|
|
241
|
+
agentLabel: parsed.nodeName,
|
|
242
|
+
});
|
|
243
|
+
summaryLines = [
|
|
244
|
+
`Conda 环境: ${VERSION_POLICY.conda.envName}`,
|
|
245
|
+
`启动脚本(含明文 secret): ${startupScriptPath}`,
|
|
246
|
+
`服务文件: ${installed.unitPath}`,
|
|
247
|
+
installed.hint,
|
|
248
|
+
];
|
|
249
|
+
if (!installed.installed) {
|
|
250
|
+
const fallbackContent = buildLinuxSystemdUnit({
|
|
251
|
+
serviceName,
|
|
252
|
+
serviceUser: "tywork",
|
|
253
|
+
workDir: parsed.workDir,
|
|
254
|
+
startupScriptPath,
|
|
255
|
+
agentLabel: parsed.nodeName,
|
|
256
|
+
});
|
|
257
|
+
await fs.writeFile(path.join(parsed.workDir, "systemd.preview.txt"), fallbackContent, "utf8");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (platform === "macos") {
|
|
261
|
+
await writeMacStartupScript(startupScriptPath, parsed.workDir, javaArgs);
|
|
262
|
+
const guiUid = await resolveMacGuiUid();
|
|
263
|
+
const installed = await tryInstallMacLaunchAgent({
|
|
264
|
+
serviceName,
|
|
265
|
+
guiUid,
|
|
266
|
+
workDir: parsed.workDir,
|
|
267
|
+
startupScriptPath,
|
|
268
|
+
});
|
|
269
|
+
summaryLines = [
|
|
270
|
+
`Conda 环境: ${VERSION_POLICY.conda.envName}`,
|
|
271
|
+
`启动脚本(含明文 secret): ${startupScriptPath}`,
|
|
272
|
+
`launchd 配置: ${installed.plistPath}`,
|
|
273
|
+
`服务标识: ${installed.serviceTarget}`,
|
|
274
|
+
"会话类型: GUI (Aqua),可触发图形权限请求",
|
|
275
|
+
installed.hint,
|
|
276
|
+
];
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
await writeWindowsStartupBat(startupScriptPath, parsed.workDir, javaArgs);
|
|
280
|
+
summaryLines = [
|
|
281
|
+
`Conda 环境: ${VERSION_POLICY.conda.envName}`,
|
|
282
|
+
`启动脚本(含明文 secret): ${startupScriptPath}`,
|
|
283
|
+
`手动启动: ${startupScriptPath}`,
|
|
284
|
+
];
|
|
285
|
+
}
|
|
286
|
+
console.log("步骤 5/5: 输出安装结果...");
|
|
287
|
+
await renderOnce(_jsx(CheckReport, { title: "\u5B89\u88C5\u5B8C\u6210", results: summaryLines.map((line, index) => ({
|
|
288
|
+
name: `步骤 ${index + 1}`,
|
|
289
|
+
status: "pass",
|
|
290
|
+
detail: line,
|
|
291
|
+
})) }));
|
|
292
|
+
console.log("安装流程已完成。");
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
process.exitCode = 1;
|
|
296
|
+
console.error(`install 执行失败: ${safeErrorMessage(error)}`);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=install.js.map
|