@workclaw/cli 1.0.2 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,54 +1,165 @@
1
- # WorkClaw CLI
2
-
3
- WorkClaw CLI 是一个用于初始化和配置 WorkClaw 插件的命令行工具。
4
-
5
- ## 安装
6
-
7
- ```bash
8
- npm install -g @workclaw/cli
9
- ```
10
-
11
- ## 使用
12
-
13
- ### 交互式初始化
14
-
15
- ```bash
16
- workclaw init
17
- ```
18
-
19
- ### 命令行参数
20
-
21
- | 参数 | 说明 |
22
- | ----------- | -------------- |
23
- | --scenario | 安装场景 |
24
- | --env | 环境 (test/prod) |
25
- | --phone | 手机号码 |
26
- | --user-pass | 用户密码 |
27
- | --debug | 开启调试日志 |
28
-
29
- ### 示例
30
-
31
- ```bash
32
- # 交互式安装
33
- workclaw init
34
-
35
- # 指定参数安装
36
- workclaw init --phone 13800138000 --user-pass your_password
37
-
38
- # 开启调试日志
39
- workclaw init --debug
40
- ```
41
-
42
- ## 开发
43
-
44
- ```bash
45
- # 构建
46
- npm run build
47
-
48
- # 本地测试
49
- node dist/index.js init --debug
50
- ```
51
-
52
- ## 许可证
53
-
54
- MIT License
1
+ # WorkClaw CLI
2
+
3
+ WorkClaw CLI 是一个用于初始化和配置 WorkClaw 插件的命令行工具。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ # 使用 npm 安装
9
+ npm install -g @workclaw/cli
10
+
11
+ # 使用 npx 直接运行(无需安装)
12
+ npx @workclaw/cli local
13
+ ```
14
+
15
+ ## 使用
16
+
17
+ ### 命令
18
+
19
+ CLI 提供两个命令:
20
+
21
+ | 命令 | 说明 |
22
+ |------|------|
23
+ | `workclaw local` | 本地账户安装(需要登录) |
24
+ | `workclaw box` | 盒子设备安装(无需登录) |
25
+
26
+ ### 交互式初始化
27
+
28
+ ```bash
29
+ # 本地安装(需要手机号密码)
30
+ workclaw local
31
+ # 或使用 npx
32
+ npx @workclaw/cli local
33
+
34
+ # 盒子安装(需要 AppKey 和 AppSecret)
35
+ workclaw box
36
+ # 或使用 npx
37
+ npx @workclaw/cli box
38
+ ```
39
+
40
+ ### 命令行参数
41
+
42
+ #### local 命令参数
43
+
44
+ | 参数 | 缩写 | 说明 |
45
+ | ---------------- | -- | -------------- |
46
+ | --scenario | -s | 安装场景 |
47
+ | --env | -e | 环境 (test/prod) |
48
+ | --phone | 无 | 手机号码 |
49
+ | --user-pass | 无 | 用户密码 |
50
+ | --plugin-version | 无 | 插件版本号 |
51
+ | --debug | 无 | 调试模式 |
52
+
53
+ #### box 命令参数
54
+
55
+ | 参数 | 缩写 | 说明 |
56
+ | ---------------- | -- | -------------- |
57
+ | --scenario | -s | 安装场景 |
58
+ | --env | -e | 环境 (test/prod) |
59
+ | --app-key | 无 | App Key |
60
+ | --app-secret | 无 | App Secret |
61
+ | --plugin-version | 无 | 插件版本号 |
62
+ | --debug | 无 | 调试模式 |
63
+
64
+ ### 安装场景
65
+
66
+ | 场景 | 说明 |
67
+ | ------------- | ------------ |
68
+ | windows-local | Windows 本地安装 |
69
+ | mac-local | macOS 本地安装 |
70
+ | linux-local | Linux 本地安装 |
71
+
72
+ ### 环境
73
+
74
+ | 环境 | 说明 |
75
+ | ---- | ---- |
76
+ | test | 测试环境 |
77
+ | prod | 正式环境 |
78
+
79
+ ## 示例
80
+
81
+ ### 本地安装
82
+
83
+ #### 交互式安装(推荐新手使用)
84
+
85
+ ```bash
86
+ # 全局安装后运行
87
+ workclaw local
88
+
89
+ # 或使用 npx 直接运行
90
+ npx @workclaw/cli local
91
+ ```
92
+
93
+ #### 仅指定手机号和密码
94
+
95
+ ```bash
96
+ workclaw local --phone 13800138000 --user-pass your_password
97
+ ```
98
+
99
+ #### 完整参数指定
100
+
101
+ ```bash
102
+ workclaw local --scenario windows-local --env test --phone 13800138000 --user-pass your_password
103
+ ```
104
+
105
+ #### 使用正式环境
106
+
107
+ ```bash
108
+ workclaw local --env prod --phone 13800138000 --user-pass your_password
109
+ ```
110
+
111
+ #### macOS 安装
112
+
113
+ ```bash
114
+ workclaw local --scenario mac-local --phone 13800138000 --user-pass your_password
115
+ ```
116
+
117
+ #### Linux 安装
118
+
119
+ ```bash
120
+ workclaw local --scenario linux-local --phone 13800138000 --user-pass your_password
121
+ ```
122
+
123
+ ### 盒子安装
124
+
125
+ 盒子安装适用于不需要用户登录的场景,直接使用 AppKey 和 AppSecret 进行安装:
126
+
127
+ ```bash
128
+ # 基本用法
129
+ workclaw box --app-key your_app_key --app-secret your_app_secret
130
+
131
+ # 指定环境
132
+ workclaw box --app-key your_app_key --app-secret your_app_secret --env prod
133
+
134
+ # 指定场景
135
+ workclaw box --app-key your_app_key --app-secret your_app_secret --scenario linux-local
136
+ ```
137
+
138
+ ### 指定插件版本
139
+
140
+ 可以使用 `--plugin-version` 参数安装指定版本的插件(默认安装最新版):
141
+
142
+ ```bash
143
+ # 安装指定版本
144
+ workclaw local --phone 13800138000 --user-pass your_password --plugin-version 1.0.0
145
+ workclaw box --app-key your_app_key --app-secret your_app_secret --plugin-version 1.0.0
146
+ ```
147
+
148
+ ### 调试模式
149
+
150
+ 当安装失败时,可以使用 `--debug` 参数查看详细日志:
151
+
152
+ ```bash
153
+ # 本地安装 + 调试模式
154
+ workclaw local --phone 13800138000 --user-pass your_password --debug
155
+
156
+ # 盒子安装 + 调试模式
157
+ workclaw box --app-key your_app_key --app-secret your_app_secret --debug
158
+
159
+ # 完整参数 + 调试模式
160
+ workclaw local --scenario windows-local --env test --phone 13800138000 --user-pass your_password --debug
161
+ ```
162
+
163
+ ## 许可证
164
+
165
+ MIT License
@@ -14,36 +14,31 @@ import ora from "ora";
14
14
  import * as tar from "tar";
15
15
  import axios from "axios";
16
16
  import "node:os";
17
- class BaseCommand {
18
- constructor(program2) {
19
- this.program = program2;
20
- this.options = [];
21
- }
22
- options = [];
23
- init() {
24
- const cmd = this.program.command(this.command);
25
- cmd.description(this.description);
26
- this.options.forEach(({ name, description }) => {
27
- cmd.option(name, description);
28
- });
29
- cmd.action(this.action.bind(this));
17
+ let isDebug = false;
18
+ function setDebug(debug) {
19
+ isDebug = debug;
20
+ }
21
+ function debugLog(...args) {
22
+ if (isDebug) {
23
+ console.log(chalk.gray(`[DEBUG]`), ...args);
30
24
  }
31
25
  }
32
- const ERROR_CODES = {
26
+ const ERROR_CODES$1 = {
33
27
  PHONE_REQUIRED: "PHONE_REQUIRED",
34
28
  USER_PASS_REQUIRED: "USER_PASS_REQUIRED",
35
29
  LOGIN_FAILED: "LOGIN_FAILED",
30
+ GET_BOUND_CONFIG_FAILED: "GET_BOUND_CONFIG_FAILED",
36
31
  NODE_VERSION_LOW: "NODE_VERSION_LOW",
37
32
  NPM_NOT_FOUND: "NPM_NOT_FOUND",
38
33
  NPM_INSTALL_FAILED: "NPM_INSTALL_FAILED"
39
34
  };
40
- class AppError extends Error {
35
+ let AppError$1 = class AppError extends Error {
41
36
  constructor(code, message) {
42
37
  super(message);
43
38
  this.code = code;
44
39
  this.name = "AppError";
45
40
  }
46
- }
41
+ };
47
42
  const CONFIG = {
48
43
  PLUGIN_NAME: "openclaw-workclaw",
49
44
  DEFAULT_BASE_URL: "https://workbrain.cn/backend-api",
@@ -112,7 +107,7 @@ async function login(phone, password, env) {
112
107
  if (data.code === 200 && data.data?.token) {
113
108
  return data.data.token;
114
109
  }
115
- throw new Error(data.message || "登录失败");
110
+ throw new AppError$1(ERROR_CODES$1.LOGIN_FAILED, data.message || "登录失败");
116
111
  }
117
112
  async function fetchBoundConfig(token, phone, env) {
118
113
  const config = getConfig(env);
@@ -136,11 +131,11 @@ async function fetchBoundConfig(token, phone, env) {
136
131
  modelApiBaseUrl: data.data.model_api_base_url
137
132
  };
138
133
  if (!boundConfig.appKey || !boundConfig.appSecret || !boundConfig.agentId) {
139
- throw new Error("获取绑定配置失败:缺少必要字段");
134
+ throw new AppError$1(ERROR_CODES$1.GET_BOUND_CONFIG_FAILED, "获取绑定配置失败:缺少必要字段");
140
135
  }
141
136
  return boundConfig;
142
137
  }
143
- throw new Error(data.message || data.msg || "获取绑定配置失败");
138
+ throw new AppError$1(ERROR_CODES$1.GET_BOUND_CONFIG_FAILED, data.message || data.msg || "获取绑定配置失败");
144
139
  }
145
140
  var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
146
141
  function int2char(n) {
@@ -4032,19 +4027,10 @@ function rsaEncrypt(txt) {
4032
4027
  const result = encrypt.encrypt(txt);
4033
4028
  return result || null;
4034
4029
  }
4035
- let isDebug = false;
4036
- function setDebug(debug) {
4037
- isDebug = debug;
4038
- }
4039
- function debugLog(...args) {
4040
- if (isDebug) {
4041
- console.log(chalk.gray(`[DEBUG]`), ...args);
4042
- }
4043
- }
4044
- function getHomeDir() {
4030
+ function getHomeDir$1() {
4045
4031
  return process$1.env.HOME || process$1.env.USERPROFILE || "";
4046
4032
  }
4047
- const PLUGIN_PACKAGE_NAME = "@workclaw/openclaw-workclaw";
4033
+ const PLUGIN_PACKAGE_NAME$1 = "@workclaw/openclaw-workclaw";
4048
4034
  class LocalInstaller {
4049
4035
  constructor(config) {
4050
4036
  this.config = config;
@@ -4052,13 +4038,19 @@ class LocalInstaller {
4052
4038
  }
4053
4039
  spinner;
4054
4040
  prefixText = "";
4041
+ getPackageName() {
4042
+ if (this.config.pluginVersion) {
4043
+ return `${PLUGIN_PACKAGE_NAME$1}@${this.config.pluginVersion}`;
4044
+ }
4045
+ return PLUGIN_PACKAGE_NAME$1;
4046
+ }
4055
4047
  validateConfig() {
4056
4048
  debugLog("[验证配置] 检查手机号码和密码...");
4057
4049
  if (!this.config.phone) {
4058
- throw new AppError(ERROR_CODES.PHONE_REQUIRED, "手机号码不能为空,请使用 --phone 参数");
4050
+ throw new AppError$1(ERROR_CODES$1.PHONE_REQUIRED, "手机号码不能为空,请使用 --phone 参数");
4059
4051
  }
4060
4052
  if (!this.config.userPass) {
4061
- throw new AppError(ERROR_CODES.USER_PASS_REQUIRED, "用户密码不能为空,请使用 --user-pass 参数");
4053
+ throw new AppError$1(ERROR_CODES$1.USER_PASS_REQUIRED, "用户密码不能为空,请使用 --user-pass 参数");
4062
4054
  }
4063
4055
  debugLog("[验证配置] 配置验证通过");
4064
4056
  }
@@ -4068,7 +4060,7 @@ class LocalInstaller {
4068
4060
  this.validateConfig();
4069
4061
  const env = this.config.env || "test";
4070
4062
  const config = getConfig(env);
4071
- const homeDir = getHomeDir();
4063
+ const homeDir = getHomeDir$1();
4072
4064
  const paths = {
4073
4065
  home: path.join(homeDir, config.DIRS.OPENCLAW),
4074
4066
  extensions: path.join(homeDir, config.DIRS.OPENCLAW, config.DIRS.EXTENSIONS),
@@ -4103,7 +4095,7 @@ class LocalInstaller {
4103
4095
  debugLog("[用户登录] RSA 加密密码...");
4104
4096
  const encryptedPassword = rsaEncrypt(this.config.userPass);
4105
4097
  if (!encryptedPassword) {
4106
- throw new AppError(ERROR_CODES.LOGIN_FAILED, "密码加密失败");
4098
+ throw new AppError$1(ERROR_CODES$1.LOGIN_FAILED, "密码加密失败");
4107
4099
  }
4108
4100
  debugLog("[用户登录] 密码加密成功");
4109
4101
  debugLog("[用户登录] 调用登录接口...");
@@ -4163,8 +4155,10 @@ class LocalInstaller {
4163
4155
  for (let i = 1; i <= maxRetries; i++) {
4164
4156
  debugLog(`[下载插件] 第 ${i} 次尝试下载 tarball...`);
4165
4157
  try {
4158
+ const packageName = this.getPackageName();
4159
+ debugLog(`[下载插件] 下载包名: ${packageName}`);
4166
4160
  const output = execSync(
4167
- `npm pack ${PLUGIN_PACKAGE_NAME} --registry=https://mirrors.tencent.com/npm/ --ignore-scripts`,
4161
+ `npm pack ${packageName} --registry=https://mirrors.tencent.com/npm/ --ignore-scripts`,
4168
4162
  { cwd: tempDir, encoding: "utf-8", timeout: 6e4 }
4169
4163
  );
4170
4164
  const filename = output.trim().split("\n").pop() || "";
@@ -4181,7 +4175,7 @@ class LocalInstaller {
4181
4175
  }
4182
4176
  }
4183
4177
  if (!tarballPath || !await fs.access(tarballPath).then(() => true).catch(() => false)) {
4184
- throw new AppError(ERROR_CODES.NPM_INSTALL_FAILED, `tarball 下载失败: ${lastError}`);
4178
+ throw new AppError$1(ERROR_CODES$1.NPM_INSTALL_FAILED, `tarball 下载失败: ${lastError}`);
4185
4179
  }
4186
4180
  this.spinner.prefixText = this.prefixText;
4187
4181
  this.spinner.text = `${chalk.cyan("下载插件")} ${chalk.dim("→")} ${chalk.yellow("解压 tarball")}`;
@@ -4196,12 +4190,12 @@ class LocalInstaller {
4196
4190
  });
4197
4191
  debugLog("[下载插件] tarball 解压成功");
4198
4192
  } catch (error) {
4199
- throw new AppError(ERROR_CODES.NPM_INSTALL_FAILED, `tarball 解压失败: ${error.message}`);
4193
+ throw new AppError$1(ERROR_CODES$1.NPM_INSTALL_FAILED, `tarball 解压失败: ${error.message}`);
4200
4194
  }
4201
4195
  const pluginPath = path.join(paths.target, "package.json");
4202
4196
  const pluginExists = await fs.access(pluginPath).then(() => true).catch(() => false);
4203
4197
  if (!pluginExists) {
4204
- throw new AppError(ERROR_CODES.NPM_INSTALL_FAILED, "插件解压后未找到 package.json");
4198
+ throw new AppError$1(ERROR_CODES$1.NPM_INSTALL_FAILED, "插件解压后未找到 package.json");
4205
4199
  }
4206
4200
  debugLog("[下载插件] 插件解压成功");
4207
4201
  this.prefixText += chalk.green(`✓ 插件下载完成
@@ -4360,7 +4354,7 @@ class LocalInstaller {
4360
4354
  installs: {
4361
4355
  [config.PLUGIN_NAME]: {
4362
4356
  source: "npm",
4363
- npm: PLUGIN_PACKAGE_NAME,
4357
+ npm: PLUGIN_PACKAGE_NAME$1,
4364
4358
  installPath: paths.target
4365
4359
  }
4366
4360
  },
@@ -4386,12 +4380,12 @@ class LocalInstaller {
4386
4380
  debugLog(`[更新配置] 配置文件写入成功: ${paths.config}`);
4387
4381
  }
4388
4382
  }
4389
- function checkEnv() {
4383
+ function checkEnv$1() {
4390
4384
  debugLog("[环境检查] 检测 Node.js 版本...");
4391
4385
  const nodeVersion = execSync("node --version", { stdio: "pipe" }).toString().trim();
4392
4386
  debugLog(`[环境检查] Node.js 版本: ${nodeVersion}`);
4393
4387
  if (!semver.gte(nodeVersion, "18.0.0")) {
4394
- throw new AppError(ERROR_CODES.NODE_VERSION_LOW, `Node.js 版本需要 >= 18.0.0,当前版本: ${nodeVersion}`);
4388
+ throw new AppError$1(ERROR_CODES$1.NODE_VERSION_LOW, `Node.js 版本需要 >= 18.0.0,当前版本: ${nodeVersion}`);
4395
4389
  }
4396
4390
  debugLog("[环境检查] Node.js 版本检查通过");
4397
4391
  debugLog("[环境检查] 检测 npm...");
@@ -4400,156 +4394,628 @@ function checkEnv() {
4400
4394
  debugLog(`[环境检查] npm 版本: ${npmVersion}`);
4401
4395
  debugLog("[环境检查] npm 检测通过");
4402
4396
  } catch {
4403
- throw new AppError(ERROR_CODES.NPM_NOT_FOUND, "未检测到 npm,请先安装 Node.js 和 npm");
4397
+ throw new AppError$1(ERROR_CODES$1.NPM_NOT_FOUND, "未检测到 npm,请先安装 Node.js 和 npm");
4404
4398
  }
4405
4399
  }
4406
- class InitCommand extends BaseCommand {
4407
- command = "init";
4408
- description = "初始化 WorkClaw 插件";
4409
- options = [
4410
- { name: "-s, --scenario <scenario>", description: "安装场景" },
4411
- { name: "-e, --env <env>", description: "环境 (test/prod)" },
4412
- { name: "--phone <phone>", description: "手机号码" },
4413
- { name: "--user-pass <userPass>", description: "用户密码" },
4414
- { name: "--debug", description: "开启调试日志" }
4415
- ];
4416
- constructor(program2) {
4417
- super(program2);
4418
- this.init();
4400
+ async function createLocalCommand(options) {
4401
+ setDebug(!!options.debug);
4402
+ debugLog("[初始化] 开始处理...");
4403
+ debugLog(`[初始化] 参数: scenario=${options.scenario}, env=${options.env}, phone=${options.phone}, debug=${options.debug}`);
4404
+ checkEnv$1();
4405
+ debugLog("[初始化] 环境检查通过");
4406
+ try {
4407
+ let scenario = options.scenario;
4408
+ let env = options.env;
4409
+ let phone = options.phone;
4410
+ let userPass = options.userPass;
4411
+ const questions = [];
4412
+ if (!scenario) {
4413
+ debugLog("[初始化] 需要选择安装场景");
4414
+ questions.push({
4415
+ type: "list",
4416
+ name: "scenario",
4417
+ message: `${nodeEmoji.get("desktop_computer")} 请选择安装场景:`,
4418
+ default: "windows-local",
4419
+ choices: [
4420
+ { name: `${chalk.green("Windows")} ${chalk.gray("本地安装")}`, value: "windows-local" },
4421
+ { name: `${chalk.green("macOS")} ${chalk.gray("本地安装")}`, value: "mac-local" },
4422
+ { name: `${chalk.green("Linux")} ${chalk.gray("本地安装")}`, value: "linux-local" }
4423
+ ]
4424
+ });
4425
+ } else {
4426
+ debugLog(`[初始化] 使用命令行参数: scenario=${scenario}`);
4427
+ }
4428
+ if (!env) {
4429
+ debugLog("[初始化] 需要选择环境");
4430
+ questions.push({
4431
+ type: "list",
4432
+ name: "env",
4433
+ message: `${nodeEmoji.get("globe_with_meridians")} 请选择环境:`,
4434
+ default: "test",
4435
+ choices: [
4436
+ { name: `${chalk.green("测试环境")} ${chalk.gray("(test)")}`, value: "test" },
4437
+ { name: `${chalk.red("正式环境")} ${chalk.gray("(prod)")}`, value: "prod" }
4438
+ ]
4439
+ });
4440
+ } else {
4441
+ debugLog(`[初始化] 使用命令行参数: env=${env}`);
4442
+ }
4443
+ if (!phone) {
4444
+ debugLog("[初始化] 需要输入手机号码");
4445
+ questions.push({
4446
+ type: "input",
4447
+ name: "phone",
4448
+ message: `${nodeEmoji.get("phone")} 请输入手机号码:`,
4449
+ validate: (value) => {
4450
+ if (!value || value.trim() === "") {
4451
+ return `${nodeEmoji.get("x")} 手机号码不能为空`;
4452
+ }
4453
+ if (!/^1[3-9]\d{9}$/.test(value)) {
4454
+ return `${nodeEmoji.get("warning")} 请输入正确的手机号码`;
4455
+ }
4456
+ return true;
4457
+ }
4458
+ });
4459
+ } else {
4460
+ debugLog("[初始化] 使用命令行参数: phone");
4461
+ }
4462
+ if (!userPass) {
4463
+ debugLog("[初始化] 需要输入用户密码");
4464
+ questions.push({
4465
+ type: "input",
4466
+ name: "userPass",
4467
+ message: `${nodeEmoji.get("key")} 请输入用户密码:`,
4468
+ validate: (value) => {
4469
+ if (!value || value.trim() === "") {
4470
+ return `${nodeEmoji.get("x")} 用户密码不能为空`;
4471
+ }
4472
+ return true;
4473
+ }
4474
+ });
4475
+ } else {
4476
+ debugLog("[初始化] 使用命令行参数: userPass");
4477
+ }
4478
+ if (questions.length > 0) {
4479
+ debugLog(`[初始化] 开始交互式问答,共 ${questions.length} 个问题`);
4480
+ const answers = await inquirer.prompt(questions);
4481
+ debugLog("[初始化] 交互式问答完成");
4482
+ scenario = scenario || answers.scenario;
4483
+ env = env || answers.env || "test";
4484
+ phone = phone || answers.phone;
4485
+ userPass = userPass || answers.userPass;
4486
+ }
4487
+ debugLog(`[初始化] 最终参数: scenario=${scenario}, env=${env}, phone=${phone}, pluginVersion=${options.pluginVersion}`);
4488
+ debugLog("[初始化] 创建 LocalInstaller 实例...");
4489
+ const installer = new LocalInstaller({
4490
+ phone,
4491
+ userPass,
4492
+ scenario,
4493
+ env,
4494
+ pluginVersion: options.pluginVersion
4495
+ });
4496
+ debugLog("[初始化] 开始安装...");
4497
+ await installer.install();
4498
+ debugLog("[初始化] 安装完成");
4499
+ console.log(installer.getPrefixText() + boxen(
4500
+ `${nodeEmoji.get("tada")} 插件初始化成功!`,
4501
+ {
4502
+ title: `${chalk.bold.green("初始化成功")}`,
4503
+ titleAlignment: "center",
4504
+ padding: { top: 1, bottom: 1, left: 5, right: 5 },
4505
+ margin: 1,
4506
+ borderStyle: "round",
4507
+ borderColor: "green",
4508
+ textAlignment: "center"
4509
+ }
4510
+ ));
4511
+ } catch (error) {
4512
+ debugLog(`[初始化] 发生错误: ${error.message}`);
4513
+ console.error(boxen(
4514
+ `${chalk.yellow(error.message)}`,
4515
+ {
4516
+ title: `${chalk.bold.red("初始化失败")}`,
4517
+ titleAlignment: "center",
4518
+ padding: { top: 1, bottom: 1, left: 5, right: 5 },
4519
+ margin: 1,
4520
+ borderStyle: "round",
4521
+ borderColor: "red",
4522
+ textAlignment: "center"
4523
+ }
4524
+ ));
4525
+ process$1.exit(1);
4419
4526
  }
4420
- async action(options) {
4421
- await this.handleInit(options);
4527
+ }
4528
+ function registerCommands$1(program2) {
4529
+ program2.command("local").description("本地账户安装(需要登录)").option("-s, --scenario <scenario>", "安装场景").option("-e, --env <env>", "环境 (test/prod)").option("--phone <phone>", "手机号码").option("--user-pass <userPass>", "用户密码").option("--plugin-version <plugin-version>", "插件版本号(默认最新版)").option("--debug", "开启调试日志").action(createLocalCommand);
4530
+ }
4531
+ const ERROR_CODES = {
4532
+ APP_KEY_REQUIRED: "APP_KEY_REQUIRED",
4533
+ APP_SECRET_REQUIRED: "APP_SECRET_REQUIRED",
4534
+ NODE_VERSION_LOW: "NODE_VERSION_LOW",
4535
+ NPM_NOT_FOUND: "NPM_NOT_FOUND",
4536
+ NPM_INSTALL_FAILED: "NPM_INSTALL_FAILED"
4537
+ };
4538
+ class AppError2 extends Error {
4539
+ constructor(code, message) {
4540
+ super(message);
4541
+ this.code = code;
4542
+ this.name = "AppError";
4543
+ }
4544
+ }
4545
+ function getHomeDir() {
4546
+ return process$1.env.HOME || process$1.env.USERPROFILE || "";
4547
+ }
4548
+ const PLUGIN_PACKAGE_NAME = "@workclaw/openclaw-workclaw";
4549
+ class BoxInstaller {
4550
+ constructor(config) {
4551
+ this.config = config;
4552
+ this.spinner = ora({ color: "cyan" }).start();
4553
+ }
4554
+ spinner;
4555
+ prefixText = "";
4556
+ getPackageName() {
4557
+ if (this.config.pluginVersion) {
4558
+ return `${PLUGIN_PACKAGE_NAME}@${this.config.pluginVersion}`;
4559
+ }
4560
+ return PLUGIN_PACKAGE_NAME;
4561
+ }
4562
+ validateConfig() {
4563
+ debugLog("[验证配置] 检查 appKey 和 appSecret...");
4564
+ if (!this.config.appKey) {
4565
+ throw new AppError2(ERROR_CODES.APP_KEY_REQUIRED, "AppKey 不能为空,请使用 --app-key 参数");
4566
+ }
4567
+ if (!this.config.appSecret) {
4568
+ throw new AppError2(ERROR_CODES.APP_SECRET_REQUIRED, "AppSecret 不能为空,请使用 --app-secret 参数");
4569
+ }
4570
+ debugLog("[验证配置] 配置验证通过");
4422
4571
  }
4423
- async handleInit(options) {
4424
- setDebug(!!options.debug);
4425
- debugLog("[初始化] 开始处理...");
4426
- debugLog(`[初始化] 参数: scenario=${options.scenario}, env=${options.env}, phone=${options.phone}, debug=${options.debug}`);
4427
- checkEnv();
4428
- debugLog("[初始化] 环境检查通过");
4572
+ async install() {
4429
4573
  try {
4430
- let scenario = options.scenario;
4431
- let env = options.env;
4432
- let phone = options.phone;
4433
- let userPass = options.userPass;
4434
- const questions = [];
4435
- if (!scenario) {
4436
- debugLog("[初始化] 需要选择安装场景");
4437
- questions.push({
4438
- type: "list",
4439
- name: "scenario",
4440
- message: `${nodeEmoji.get("desktop_computer")} 请选择安装场景:`,
4441
- default: "windows-local",
4442
- choices: [
4443
- { name: `${chalk.green("Windows")} ${chalk.gray("本地安装")}`, value: "windows-local" },
4444
- { name: `${chalk.green("macOS")} ${chalk.gray("本地安装")}`, value: "mac-local" },
4445
- { name: `${chalk.green("Linux")} ${chalk.gray("本地安装")}`, value: "linux-local" }
4446
- ]
4447
- });
4448
- } else {
4449
- debugLog(`[初始化] 使用命令行参数: scenario=${scenario}`);
4450
- }
4451
- if (!env) {
4452
- debugLog("[初始化] 需要选择环境");
4453
- questions.push({
4454
- type: "list",
4455
- name: "env",
4456
- message: `${nodeEmoji.get("globe_with_meridians")} 请选择环境:`,
4457
- default: "test",
4458
- choices: [
4459
- { name: `${chalk.green("测试环境")} ${chalk.gray("(test)")}`, value: "test" },
4460
- { name: `${chalk.red("正式环境")} ${chalk.gray("(prod)")}`, value: "prod" }
4461
- ]
4462
- });
4463
- } else {
4464
- debugLog(`[初始化] 使用命令行参数: env=${env}`);
4465
- }
4466
- if (!phone) {
4467
- debugLog("[初始化] 需要输入手机号码");
4468
- questions.push({
4469
- type: "input",
4470
- name: "phone",
4471
- message: `${nodeEmoji.get("phone")} 请输入手机号码:`,
4472
- validate: (value) => {
4473
- if (!value || value.trim() === "") {
4474
- return `${nodeEmoji.get("x")} 手机号码不能为空`;
4475
- }
4476
- if (!/^1[3-9]\d{9}$/.test(value)) {
4477
- return `${nodeEmoji.get("warning")} 请输入正确的手机号码`;
4478
- }
4479
- return true;
4574
+ debugLog("[安装开始]");
4575
+ this.validateConfig();
4576
+ const env = this.config.env || "test";
4577
+ const config = getConfig(env);
4578
+ const homeDir = getHomeDir();
4579
+ const paths = {
4580
+ home: path.join(homeDir, config.DIRS.OPENCLAW),
4581
+ extensions: path.join(homeDir, config.DIRS.OPENCLAW, config.DIRS.EXTENSIONS),
4582
+ target: path.join(homeDir, config.DIRS.OPENCLAW, config.DIRS.EXTENSIONS, config.PLUGIN_NAME),
4583
+ config: path.join(homeDir, config.DIRS.OPENCLAW, config.DIRS.CONFIG_FILE),
4584
+ workspace: path.join(homeDir, config.DIRS.OPENCLAW, config.DIRS.WORKSPACE),
4585
+ temp: path.join(homeDir, config.DIRS.OPENCLAW, config.DIRS.TEMP)
4586
+ };
4587
+ debugLog(`[路径配置] home=${paths.home}`);
4588
+ debugLog(`[路径配置] extensions=${paths.extensions}`);
4589
+ debugLog(`[路径配置] target=${paths.target}`);
4590
+ debugLog(`[路径配置] config=${paths.config}`);
4591
+ debugLog(`[路径配置] workspace=${paths.workspace}`);
4592
+ await this.doCleanOldFiles(paths.target);
4593
+ await this.doDownloadFromNpm(paths);
4594
+ await this.doUpdateConfig(paths, env);
4595
+ this.spinner.stop();
4596
+ } catch (error) {
4597
+ this.spinner.stop();
4598
+ throw error;
4599
+ }
4600
+ }
4601
+ getPrefixText() {
4602
+ return this.prefixText;
4603
+ }
4604
+ async doCleanOldFiles(targetPath) {
4605
+ this.spinner.prefixText = this.prefixText;
4606
+ this.spinner.text = `${chalk.cyan("清理旧版本")} ${chalk.dim("→")} ${chalk.yellow("检查目录...")}`;
4607
+ debugLog(`[清理旧版本] 检查目录: ${targetPath}`);
4608
+ try {
4609
+ await fs.access(targetPath);
4610
+ debugLog("[清理旧版本] 发现旧版本,开始清理...");
4611
+ this.spinner.prefixText = this.prefixText;
4612
+ this.spinner.text = `${chalk.cyan("清理旧版本")} ${chalk.dim("→")} ${chalk.yellow("正在清理...")}`;
4613
+ await fs.rm(targetPath, { recursive: true, force: true });
4614
+ this.prefixText += chalk.green(`✓ 旧版本清理完成
4615
+ `);
4616
+ debugLog("[清理旧版本] 清理完成");
4617
+ } catch {
4618
+ this.prefixText += chalk.green(`✓ 未发现旧版本
4619
+ `);
4620
+ debugLog("[清理旧版本] 未发现旧版本");
4621
+ }
4622
+ }
4623
+ async doDownloadFromNpm(paths) {
4624
+ this.spinner.prefixText = this.prefixText;
4625
+ this.spinner.text = `${chalk.cyan("下载插件")} ${chalk.dim("→")} ${chalk.yellow("准备目录")}`;
4626
+ debugLog("[下载插件] 创建扩展目录...");
4627
+ await fs.mkdir(paths.extensions, { recursive: true });
4628
+ debugLog(`[下载插件] 扩展目录: ${paths.extensions}`);
4629
+ const tempDir = path.join(paths.temp, `install-${Date.now()}`);
4630
+ debugLog(`[下载插件] 创建临时目录: ${tempDir}`);
4631
+ await fs.mkdir(tempDir, { recursive: true });
4632
+ this.spinner.prefixText = this.prefixText;
4633
+ this.spinner.text = `${chalk.cyan("下载插件")} ${chalk.dim("→")} ${chalk.yellow("下载 tarball")}`;
4634
+ debugLog("[下载插件] 执行 npm pack 下载源码包");
4635
+ debugLog(`[下载插件] 工作目录: ${tempDir}`);
4636
+ const tarballPath = await this.downloadTarball(tempDir);
4637
+ await this.extractTarball(tarballPath, paths, tempDir);
4638
+ }
4639
+ async downloadTarball(tempDir) {
4640
+ const maxRetries = 3;
4641
+ let lastError = "";
4642
+ let tarballPath = "";
4643
+ for (let i = 1; i <= maxRetries; i++) {
4644
+ debugLog(`[下载插件] 第 ${i} 次尝试下载 tarball...`);
4645
+ try {
4646
+ const packageName = this.getPackageName();
4647
+ debugLog(`[下载插件] 下载包名: ${packageName}`);
4648
+ const { execSync: execSync2 } = await import("node:child_process");
4649
+ const output = execSync2(
4650
+ `npm pack ${packageName} --registry=https://mirrors.tencent.com/npm/ --ignore-scripts`,
4651
+ { cwd: tempDir, encoding: "utf-8", timeout: 6e4 }
4652
+ );
4653
+ const filename = output.trim().split("\n").pop() || "";
4654
+ tarballPath = path.join(tempDir, filename);
4655
+ debugLog(`[下载插件] tarball 下载成功: ${tarballPath}`);
4656
+ break;
4657
+ } catch (error) {
4658
+ lastError = error.stderr?.toString() || error.message || "未知错误";
4659
+ debugLog(`[下载插件] 第 ${i} 次尝试失败: ${lastError}`);
4660
+ if (i < maxRetries) {
4661
+ debugLog("[下载插件] 等待 3 秒后重试...");
4662
+ await new Promise((resolve2) => setTimeout(resolve2, 3e3));
4663
+ }
4664
+ }
4665
+ }
4666
+ if (!tarballPath || !await fs.access(tarballPath).then(() => true).catch(() => false)) {
4667
+ throw new AppError2(ERROR_CODES.NPM_INSTALL_FAILED, `tarball 下载失败: ${lastError}`);
4668
+ }
4669
+ return tarballPath;
4670
+ }
4671
+ async extractTarball(tarballPath, paths, tempDir) {
4672
+ this.spinner.prefixText = this.prefixText;
4673
+ this.spinner.text = `${chalk.cyan("下载插件")} ${chalk.dim("→")} ${chalk.yellow("解压 tarball")}`;
4674
+ debugLog(`[下载插件] 创建插件目录: ${paths.target}`);
4675
+ await fs.mkdir(paths.target, { recursive: true });
4676
+ debugLog(`[下载插件] 解压 tarball 到: ${paths.target}`);
4677
+ try {
4678
+ await tar.extract({
4679
+ file: tarballPath,
4680
+ cwd: paths.target,
4681
+ strip: 1
4682
+ });
4683
+ debugLog("[下载插件] tarball 解压成功");
4684
+ } catch (error) {
4685
+ throw new AppError2(ERROR_CODES.NPM_INSTALL_FAILED, `tarball 解压失败: ${error.message}`);
4686
+ }
4687
+ const pluginPath = path.join(paths.target, "package.json");
4688
+ const pluginExists = await fs.access(pluginPath).then(() => true).catch(() => false);
4689
+ if (!pluginExists) {
4690
+ throw new AppError2(ERROR_CODES.NPM_INSTALL_FAILED, "插件解压后未找到 package.json");
4691
+ }
4692
+ debugLog("[下载插件] 插件解压成功");
4693
+ this.prefixText += chalk.green(`✓ 插件下载完成
4694
+ `);
4695
+ try {
4696
+ await fs.rm(tempDir, { recursive: true, force: true });
4697
+ debugLog("[下载插件] 清理临时目录完成");
4698
+ } catch {
4699
+ debugLog("[下载插件] 清理临时目录失败(忽略)");
4700
+ }
4701
+ }
4702
+ async doUpdateConfig(paths, env) {
4703
+ this.spinner.prefixText = this.prefixText;
4704
+ this.spinner.text = `${chalk.cyan("更新配置")} ${chalk.dim("→")} ${chalk.yellow("写入配置文件")}`;
4705
+ debugLog("[更新配置] 创建目录...");
4706
+ await fs.mkdir(paths.home, { recursive: true });
4707
+ await fs.mkdir(paths.workspace, { recursive: true });
4708
+ debugLog("[更新配置] 目录创建完成");
4709
+ debugLog("[更新配置] 读取原有配置...");
4710
+ let originalConfig = {};
4711
+ try {
4712
+ const content = await fs.readFile(paths.config, "utf-8");
4713
+ originalConfig = JSON.parse(content);
4714
+ debugLog("[更新配置] 读取原有配置成功");
4715
+ } catch {
4716
+ debugLog("[更新配置] 无原有配置");
4717
+ }
4718
+ const config = getConfig(env);
4719
+ const finalConfig = {
4720
+ ...originalConfig,
4721
+ diagnostics: {
4722
+ enabled: true,
4723
+ flags: ["*"]
4724
+ },
4725
+ browser: {
4726
+ enabled: false
4727
+ },
4728
+ models: {
4729
+ mode: "merge",
4730
+ providers: {
4731
+ "siliconflow-minimax": {
4732
+ baseUrl: config.MODEL_BASE_URL,
4733
+ apiKey: "",
4734
+ api: "openai-completions",
4735
+ authHeader: true,
4736
+ models: [{
4737
+ id: "Pro/MiniMaxAI/MiniMax-M2.5",
4738
+ name: "MiniMax-M2.5",
4739
+ reasoning: false,
4740
+ input: ["text"],
4741
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
4742
+ contextWindow: 2e5,
4743
+ maxTokens: 65536
4744
+ }]
4480
4745
  }
4481
- });
4482
- } else {
4483
- debugLog("[初始化] 使用命令行参数: phone");
4484
- }
4485
- if (!userPass) {
4486
- debugLog("[初始化] 需要输入用户密码");
4487
- questions.push({
4488
- type: "input",
4489
- name: "userPass",
4490
- message: `${nodeEmoji.get("key")} 请输入用户密码:`,
4491
- validate: (value) => {
4492
- if (!value || value.trim() === "") {
4493
- return `${nodeEmoji.get("x")} 用户密码不能为空`;
4746
+ }
4747
+ },
4748
+ agents: {
4749
+ defaults: {
4750
+ model: { primary: "siliconflow-minimax/Pro/MiniMaxAI/MiniMax-M2.5" },
4751
+ models: {
4752
+ "siliconflow-minimax/Pro/MiniMaxAI/MiniMax-M2.5": {
4753
+ alias: "Pro/MiniMaxAI/MiniMax-M2.5"
4494
4754
  }
4495
- return true;
4755
+ },
4756
+ workspace: paths.workspace,
4757
+ compaction: { mode: "safeguard" },
4758
+ verboseDefault: "full",
4759
+ maxConcurrent: 4,
4760
+ subagents: { maxConcurrent: 8 }
4761
+ },
4762
+ list: [{ id: "main", workspace: paths.workspace }]
4763
+ },
4764
+ tools: {
4765
+ deny: ["image", "web_search", "web_fetch"],
4766
+ media: { image: { enabled: false } },
4767
+ web: { search: { enabled: false }, fetch: { enabled: false } },
4768
+ elevated: { enabled: true, allowFrom: { webchat: ["*"] } },
4769
+ exec: { security: "full" }
4770
+ },
4771
+ bindings: [{
4772
+ agentId: "main",
4773
+ match: { channel: "openclaw-workclaw", accountId: "default" }
4774
+ }],
4775
+ messages: { ackReactionScope: "group-mentions" },
4776
+ commands: {
4777
+ native: "auto",
4778
+ nativeSkills: "auto",
4779
+ restart: true,
4780
+ ownerDisplay: "raw"
4781
+ },
4782
+ session: { dmScope: "per-account-channel-peer" },
4783
+ hooks: {
4784
+ internal: {
4785
+ enabled: true,
4786
+ entries: {
4787
+ "boot-md": { enabled: true },
4788
+ "bootstrap-extra-files": { enabled: true },
4789
+ "command-logger": { enabled: true },
4790
+ "session-memory": { enabled: true }
4496
4791
  }
4497
- });
4498
- } else {
4499
- debugLog("[初始化] 使用命令行参数: userPass");
4500
- }
4501
- if (questions.length > 0) {
4502
- debugLog(`[初始化] 开始交互式问答,共 ${questions.length} 个问题`);
4503
- const answers = await inquirer.prompt(questions);
4504
- debugLog("[初始化] 交互式问答完成");
4505
- scenario = scenario || answers.scenario;
4506
- env = env || answers.env || "test";
4507
- phone = phone || answers.phone;
4508
- userPass = userPass || answers.userPass;
4509
- }
4510
- debugLog(`[初始化] 最终参数: scenario=${scenario}, env=${env}, phone=${phone}`);
4511
- debugLog("[初始化] 创建 LocalInstaller 实例...");
4512
- const installer = new LocalInstaller({
4513
- phone,
4514
- userPass,
4515
- scenario,
4516
- env
4792
+ }
4793
+ },
4794
+ channels: {
4795
+ "openclaw-workclaw": {
4796
+ enabled: true,
4797
+ connectionMode: "websocket",
4798
+ wsConnectionStrategy: "per-appKey",
4799
+ appKey: this.config.appKey,
4800
+ appSecret: this.config.appSecret,
4801
+ baseUrl: this.config.baseUrl || config.DEFAULT_BASE_URL,
4802
+ websocketUrl: this.config.wsUrl || config.DEFAULT_WS_URL,
4803
+ allowInsecureTls: true,
4804
+ allowRawJsonPayload: true,
4805
+ accounts: {
4806
+ default: { enabled: true, agentId: "" }
4807
+ }
4808
+ }
4809
+ },
4810
+ gateway: {
4811
+ mode: "local",
4812
+ port: 18789,
4813
+ bind: "loopback",
4814
+ auth: { mode: "token", token: "" },
4815
+ tailscale: { mode: "off", resetOnExit: false },
4816
+ nodes: {
4817
+ denyCommands: [
4818
+ "camera.snap",
4819
+ "camera.clip",
4820
+ "screen.record",
4821
+ "contacts.add",
4822
+ "calendar.add",
4823
+ "reminders.add",
4824
+ "sms.send",
4825
+ "sms.search"
4826
+ ]
4827
+ },
4828
+ controlUi: {
4829
+ allowInsecureAuth: true,
4830
+ dangerouslyDisableDeviceAuth: true,
4831
+ dangerouslyAllowHostHeaderOriginFallback: true,
4832
+ allowedOrigins: ["http://localhost:18789", "http://127.0.0.1:18789"]
4833
+ }
4834
+ },
4835
+ skills: { load: { watch: true } },
4836
+ logging: {
4837
+ level: "info",
4838
+ consoleLevel: "info",
4839
+ consoleStyle: "pretty",
4840
+ redactSensitive: "off",
4841
+ file: "",
4842
+ redactPatterns: ["sk-.*"]
4843
+ },
4844
+ plugins: {
4845
+ allow: [config.PLUGIN_NAME],
4846
+ installs: {
4847
+ [config.PLUGIN_NAME]: {
4848
+ source: "npm",
4849
+ npm: "@workclaw/openclaw-workclaw",
4850
+ installPath: paths.target
4851
+ }
4852
+ },
4853
+ entries: {
4854
+ [config.PLUGIN_NAME]: { enabled: true }
4855
+ }
4856
+ },
4857
+ wizard: {
4858
+ lastRunAt: "",
4859
+ lastRunVersion: "",
4860
+ lastRunCommand: "",
4861
+ lastRunMode: ""
4862
+ },
4863
+ meta: {
4864
+ lastTouchedVersion: "",
4865
+ lastTouchedAt: ""
4866
+ }
4867
+ };
4868
+ debugLog("[更新配置] 写入配置文件...");
4869
+ await fs.writeFile(paths.config, JSON.stringify(finalConfig, null, 2), "utf-8");
4870
+ this.prefixText += chalk.green(`✓ 配置文件更新成功
4871
+ `);
4872
+ debugLog(`[更新配置] 配置文件写入成功: ${paths.config}`);
4873
+ }
4874
+ }
4875
+ function checkEnv() {
4876
+ debugLog("[环境检查] 检测 Node.js 版本...");
4877
+ const nodeVersion = execSync("node --version", { stdio: "pipe" }).toString().trim();
4878
+ debugLog(`[环境检查] Node.js 版本: ${nodeVersion}`);
4879
+ if (!semver.gte(nodeVersion, "18.0.0")) {
4880
+ throw new AppError2(ERROR_CODES.NODE_VERSION_LOW, `Node.js 版本需要 >= 18.0.0,当前版本: ${nodeVersion}`);
4881
+ }
4882
+ debugLog("[环境检查] Node.js 版本检查通过");
4883
+ debugLog("[环境检查] 检测 npm...");
4884
+ try {
4885
+ const npmVersion = execSync("npm --version", { stdio: "pipe" }).toString().trim();
4886
+ debugLog(`[环境检查] npm 版本: ${npmVersion}`);
4887
+ debugLog("[环境检查] npm 检测通过");
4888
+ } catch {
4889
+ throw new AppError2(ERROR_CODES.NPM_NOT_FOUND, "未检测到 npm,请先安装 Node.js 和 npm");
4890
+ }
4891
+ }
4892
+ async function createBoxCommand(options) {
4893
+ setDebug(!!options.debug);
4894
+ debugLog("[初始化] 开始处理...");
4895
+ debugLog(`[初始化] 参数: scenario=${options.scenario}, env=${options.env}, appKey=${options.appKey}, debug=${options.debug}`);
4896
+ checkEnv();
4897
+ debugLog("[初始化] 环境检查通过");
4898
+ try {
4899
+ let scenario = options.scenario;
4900
+ let env = options.env;
4901
+ let appKey = options.appKey;
4902
+ let appSecret = options.appSecret;
4903
+ const questions = [];
4904
+ if (!scenario) {
4905
+ debugLog("[初始化] 需要选择安装场景");
4906
+ questions.push({
4907
+ type: "list",
4908
+ name: "scenario",
4909
+ message: `${nodeEmoji.get("desktop_computer")} 请选择安装场景:`,
4910
+ default: "windows-local",
4911
+ choices: [
4912
+ { name: `${chalk.green("Windows")} ${chalk.gray("本地安装")}`, value: "windows-local" },
4913
+ { name: `${chalk.green("macOS")} ${chalk.gray("本地安装")}`, value: "mac-local" },
4914
+ { name: `${chalk.green("Linux")} ${chalk.gray("本地安装")}`, value: "linux-local" }
4915
+ ]
4916
+ });
4917
+ } else {
4918
+ debugLog(`[初始化] 使用命令行参数: scenario=${scenario}`);
4919
+ }
4920
+ if (!env) {
4921
+ debugLog("[初始化] 需要选择环境");
4922
+ questions.push({
4923
+ type: "list",
4924
+ name: "env",
4925
+ message: `${nodeEmoji.get("globe_with_meridians")} 请选择环境:`,
4926
+ default: "test",
4927
+ choices: [
4928
+ { name: `${chalk.green("测试环境")} ${chalk.gray("(test)")}`, value: "test" },
4929
+ { name: `${chalk.red("正式环境")} ${chalk.gray("(prod)")}`, value: "prod" }
4930
+ ]
4517
4931
  });
4518
- debugLog("[初始化] 开始安装...");
4519
- await installer.install();
4520
- debugLog("[初始化] 安装完成");
4521
- console.log(installer.getPrefixText() + boxen(
4522
- `${nodeEmoji.get("tada")} 插件初始化成功!`,
4523
- {
4524
- title: `${chalk.bold.green("初始化成功")}`,
4525
- titleAlignment: "center",
4526
- padding: { top: 1, bottom: 1, left: 5, right: 5 },
4527
- margin: 1,
4528
- borderStyle: "round",
4529
- borderColor: "green",
4530
- textAlignment: "center"
4932
+ } else {
4933
+ debugLog(`[初始化] 使用命令行参数: env=${env}`);
4934
+ }
4935
+ if (!appKey) {
4936
+ debugLog("[初始化] 需要输入 AppKey");
4937
+ questions.push({
4938
+ type: "input",
4939
+ name: "appKey",
4940
+ message: `${nodeEmoji.get("key")} 请输入 AppKey:`,
4941
+ validate: (value) => {
4942
+ if (!value || value.trim() === "") {
4943
+ return `${nodeEmoji.get("x")} AppKey 不能为空`;
4944
+ }
4945
+ return true;
4531
4946
  }
4532
- ));
4533
- } catch (error) {
4534
- debugLog(`[初始化] 发生错误: ${error.message}`);
4535
- console.error(boxen(
4536
- `${chalk.yellow(error.message)}`,
4537
- {
4538
- title: `${chalk.bold.red("初始化失败")}`,
4539
- titleAlignment: "center",
4540
- padding: { top: 1, bottom: 1, left: 5, right: 5 },
4541
- margin: 1,
4542
- borderStyle: "round",
4543
- borderColor: "red",
4544
- textAlignment: "center"
4947
+ });
4948
+ } else {
4949
+ debugLog("[初始化] 使用命令行参数: appKey");
4950
+ }
4951
+ if (!appSecret) {
4952
+ debugLog("[初始化] 需要输入 AppSecret");
4953
+ questions.push({
4954
+ type: "input",
4955
+ name: "appSecret",
4956
+ message: `${nodeEmoji.get("key")} 请输入 AppSecret:`,
4957
+ validate: (value) => {
4958
+ if (!value || value.trim() === "") {
4959
+ return `${nodeEmoji.get("x")} AppSecret 不能为空`;
4960
+ }
4961
+ return true;
4545
4962
  }
4546
- ));
4547
- process$1.exit(1);
4963
+ });
4964
+ } else {
4965
+ debugLog("[初始化] 使用命令行参数: appSecret");
4966
+ }
4967
+ if (questions.length > 0) {
4968
+ debugLog(`[初始化] 开始交互式问答,共 ${questions.length} 个问题`);
4969
+ const answers = await inquirer.prompt(questions);
4970
+ debugLog("[初始化] 交互式问答完成");
4971
+ scenario = scenario || answers.scenario;
4972
+ env = env || answers.env || "test";
4973
+ appKey = appKey || answers.appKey;
4974
+ appSecret = appSecret || answers.appSecret;
4548
4975
  }
4976
+ debugLog(`[初始化] 最终参数: scenario=${scenario}, env=${env}, pluginVersion=${options.pluginVersion}`);
4977
+ debugLog("[初始化] 创建 BoxInstaller 实例...");
4978
+ const installer = new BoxInstaller({
4979
+ appKey,
4980
+ appSecret,
4981
+ scenario,
4982
+ env,
4983
+ pluginVersion: options.pluginVersion
4984
+ });
4985
+ debugLog("[初始化] 开始安装...");
4986
+ await installer.install();
4987
+ debugLog("[初始化] 安装完成");
4988
+ console.log(installer.getPrefixText() + boxen(
4989
+ `${nodeEmoji.get("tada")} 插件初始化成功!`,
4990
+ {
4991
+ title: `${chalk.bold.green("初始化成功")}`,
4992
+ titleAlignment: "center",
4993
+ padding: { top: 1, bottom: 1, left: 5, right: 5 },
4994
+ margin: 1,
4995
+ borderStyle: "round",
4996
+ borderColor: "green",
4997
+ textAlignment: "center"
4998
+ }
4999
+ ));
5000
+ } catch (error) {
5001
+ debugLog(`[初始化] 发生错误: ${error.message}`);
5002
+ console.error(boxen(
5003
+ `${chalk.yellow(error.message)}`,
5004
+ {
5005
+ title: `${chalk.bold.red("初始化失败")}`,
5006
+ titleAlignment: "center",
5007
+ padding: { top: 1, bottom: 1, left: 5, right: 5 },
5008
+ margin: 1,
5009
+ borderStyle: "round",
5010
+ borderColor: "red",
5011
+ textAlignment: "center"
5012
+ }
5013
+ ));
5014
+ process$1.exit(1);
4549
5015
  }
4550
5016
  }
4551
- function createInitCommandInstance(program2) {
4552
- return new InitCommand(program2);
5017
+ function registerCommands(program2) {
5018
+ program2.command("box").description("盒子设备安装(无需登录)").option("-s, --scenario <scenario>", "安装场景").option("-e, --env <env>", "环境 (test/prod)").option("--app-key <appKey>", "App Key").option("--app-secret <appSecret>", "App Secret").option("--plugin-version <plugin-version>", "插件版本号(默认最新版)").option("--debug", "开启调试日志").action(createBoxCommand);
4553
5019
  }
4554
5020
  const __filename$1 = fileURLToPath(import.meta.url);
4555
5021
  const __dirname$1 = dirname(__filename$1);
@@ -4557,7 +5023,8 @@ const pkg = JSON.parse(readFileSync(resolve(__dirname$1, "../package.json"), "ut
4557
5023
  const program = new Command();
4558
5024
  function index() {
4559
5025
  program.name(Object.keys(pkg.bin)[0]).version(pkg.version).description(pkg.description);
4560
- createInitCommandInstance(program);
5026
+ registerCommands$1(program);
5027
+ registerCommands(program);
4561
5028
  program.parse(process$1.argv);
4562
5029
  }
4563
5030
  export {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import("./index-DdLG7OEj.js").then((cli) => {
2
+ import("./index-RP7oF6iQ.js").then((cli) => {
3
3
  cli.default();
4
4
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@workclaw/cli",
3
3
  "type": "module",
4
- "version": "1.0.2",
4
+ "version": "1.0.16",
5
5
  "description": "WorkClaw CLI 工具 - 用于初始化和配置 WorkClaw 插件",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -38,4 +38,4 @@
38
38
  "semver": "^7.7.1",
39
39
  "tar": "^7.4.0"
40
40
  }
41
- }
41
+ }