@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 +165 -54
- package/dist/{index-DdLG7OEj.js → index-RP7oF6iQ.js} +645 -178
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,54 +1,165 @@
|
|
|
1
|
-
# WorkClaw CLI
|
|
2
|
-
|
|
3
|
-
WorkClaw CLI 是一个用于初始化和配置 WorkClaw 插件的命令行工具。
|
|
4
|
-
|
|
5
|
-
## 安装
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
|
22
|
-
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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
|
|
134
|
+
throw new AppError$1(ERROR_CODES$1.GET_BOUND_CONFIG_FAILED, "获取绑定配置失败:缺少必要字段");
|
|
140
135
|
}
|
|
141
136
|
return boundConfig;
|
|
142
137
|
}
|
|
143
|
-
throw new
|
|
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
|
-
|
|
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 ${
|
|
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
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
options
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
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
|
-
|
|
4421
|
-
|
|
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
|
|
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
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
const
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
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
|
-
}
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
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
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
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
|
-
}
|
|
4534
|
-
debugLog(
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
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
|
-
|
|
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
|
|
4552
|
-
|
|
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
|
-
|
|
5026
|
+
registerCommands$1(program);
|
|
5027
|
+
registerCommands(program);
|
|
4561
5028
|
program.parse(process$1.argv);
|
|
4562
5029
|
}
|
|
4563
5030
|
export {
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workclaw/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
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
|
+
}
|