@workclaw/cli 1.0.322 → 1.0.326
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/dist/{index-ZySzMX4-.js → index-CtwdWK1B.js} +207 -120
- package/dist/index.js +1 -1
- package/dist/local/installer/installer.d.ts +5 -0
- package/dist/local/installer/installer.d.ts.map +1 -1
- package/dist/local/src/local.d.ts.map +1 -1
- package/dist/local/types/index.d.ts +1 -0
- package/dist/local/types/index.d.ts.map +1 -1
- package/dist/shared/utils/debug.d.ts +3 -4
- package/dist/shared/utils/debug.d.ts.map +1 -1
- package/dist/shared/utils/index.d.ts +2 -2
- package/dist/shared/utils/index.d.ts.map +1 -1
- package/dist/shared/utils/path.d.ts +6 -0
- package/dist/shared/utils/path.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -14,17 +14,11 @@ import fs from "node:fs/promises";
|
|
|
14
14
|
import ora from "ora";
|
|
15
15
|
import tar from "tar";
|
|
16
16
|
import axios from "axios";
|
|
17
|
-
import os from "node:os";
|
|
18
17
|
import crypto from "node:crypto";
|
|
18
|
+
import os from "node:os";
|
|
19
19
|
const debug = new Debug();
|
|
20
20
|
function setDebug(enabled) {
|
|
21
|
-
|
|
22
|
-
debug.enable();
|
|
23
|
-
} else {
|
|
24
|
-
debug.disable();
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function debugLog(...args) {
|
|
21
|
+
debug.enable(enabled);
|
|
28
22
|
}
|
|
29
23
|
const ERROR_CODES$1 = {
|
|
30
24
|
PHONE_REQUIRED: "PHONE_REQUIRED",
|
|
@@ -49,14 +43,18 @@ let AppError$1 = class AppError extends Error {
|
|
|
49
43
|
}
|
|
50
44
|
};
|
|
51
45
|
function checkEnv() {
|
|
46
|
+
debug.log("[环境检查] 检测 Node.js 版本...");
|
|
52
47
|
const nodeVersion = execSync("node --version", { stdio: "pipe" }).toString().trim();
|
|
48
|
+
debug.log(`[环境检查] Node.js 版本: ${nodeVersion}`);
|
|
53
49
|
if (!semver.gte(nodeVersion, "18.0.0")) {
|
|
54
50
|
throw new AppError$1(ERROR_CODES$1.NODE_VERSION_LOW, `Node.js 版本需要 >= 18.0.0,当前版本: ${nodeVersion}`);
|
|
55
51
|
}
|
|
52
|
+
debug.log("[环境检查] Node.js 版本检查通过");
|
|
53
|
+
debug.log("[环境检查] 检测 npm...");
|
|
56
54
|
try {
|
|
57
55
|
const npmVersion = execSync("npm --version", { stdio: "pipe" }).toString().trim();
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
debug.log(`[环境检查] npm 版本: ${npmVersion}`);
|
|
57
|
+
debug.log("[环境检查] npm 检测通过");
|
|
60
58
|
} catch {
|
|
61
59
|
throw new AppError$1(ERROR_CODES$1.NPM_NOT_FOUND, "未检测到 npm,请先安装 Node.js 和 npm");
|
|
62
60
|
}
|
|
@@ -99,12 +97,22 @@ const wsUrlSchema = z$1.string().url({
|
|
|
99
97
|
message: "请输入有效的 WebSocket URL"
|
|
100
98
|
}).optional();
|
|
101
99
|
z$1.boolean();
|
|
100
|
+
function cleanWindowsLongPath(inputPath) {
|
|
101
|
+
return inputPath.replace(/^[\\?]+/, "");
|
|
102
|
+
}
|
|
103
|
+
function normalizePath(inputPath) {
|
|
104
|
+
const cleaned = cleanWindowsLongPath(inputPath);
|
|
105
|
+
const normalized = path.normalize(cleaned);
|
|
106
|
+
return path.resolve(normalized);
|
|
107
|
+
}
|
|
102
108
|
function validateOpenclawPath(openclawPath) {
|
|
109
|
+
debug.log(`[路径验证] 开始验证路径: ${openclawPath}`);
|
|
103
110
|
const result = openclawPathSchema.safeParse(openclawPath);
|
|
104
111
|
if (!result.success) {
|
|
105
|
-
|
|
112
|
+
debug.log(`[路径验证] 失败: ${result.error.issues[0].message}`);
|
|
106
113
|
throw new Error(result.error.issues[0].message);
|
|
107
114
|
}
|
|
115
|
+
debug.log(`[路径验证] 通过: ${openclawPath}`);
|
|
108
116
|
}
|
|
109
117
|
const CONFIG = {
|
|
110
118
|
PLUGIN_NAME: "openclaw-workclaw",
|
|
@@ -226,6 +234,7 @@ class BoxInstaller {
|
|
|
226
234
|
spinner;
|
|
227
235
|
prefixText = "";
|
|
228
236
|
validateConfig() {
|
|
237
|
+
debug.log("[验证配置] 检查 appKey 和 appSecret...");
|
|
229
238
|
if (!this.config.appKey) {
|
|
230
239
|
throw new AppError2(ERROR_CODES.APP_KEY_REQUIRED, "AppKey 不能为空,请使用 --app-key 参数");
|
|
231
240
|
}
|
|
@@ -233,6 +242,7 @@ class BoxInstaller {
|
|
|
233
242
|
throw new AppError2(ERROR_CODES.APP_SECRET_REQUIRED, "AppSecret 不能为空,请使用 --app-secret 参数");
|
|
234
243
|
}
|
|
235
244
|
if (this.config.env === "custom") {
|
|
245
|
+
debug.log("[验证配置] 检查自定义环境 IP...");
|
|
236
246
|
if (!this.config.customIp) {
|
|
237
247
|
throw new AppError2(ERROR_CODES.INVALID_ARGUMENT, "自定义环境必须提供 --customIp 参数");
|
|
238
248
|
}
|
|
@@ -240,22 +250,24 @@ class BoxInstaller {
|
|
|
240
250
|
if (!result.success) {
|
|
241
251
|
throw new AppError2(ERROR_CODES.INVALID_ARGUMENT, result.error.issues[0].message);
|
|
242
252
|
}
|
|
253
|
+
debug.log("[验证配置] 自定义环境 IP 验证通过");
|
|
243
254
|
}
|
|
255
|
+
debug.log("[验证配置] 配置检查通过");
|
|
244
256
|
}
|
|
245
257
|
getPaths() {
|
|
246
258
|
const env = this.config.env || "test";
|
|
247
|
-
|
|
259
|
+
debug.log(`[getConfig] env=${env}, customIp=${this.config.customIp}`);
|
|
248
260
|
const config = getConfig(env, this.config.customIp);
|
|
249
261
|
let baseDir;
|
|
250
262
|
if (this.config.openclawPath) {
|
|
251
|
-
|
|
263
|
+
debug.log(`[路径验证] 验证自定义路径: ${this.config.openclawPath}`);
|
|
252
264
|
try {
|
|
253
265
|
validateOpenclawPath(this.config.openclawPath);
|
|
254
|
-
|
|
266
|
+
debug.log("[路径验证] 通过");
|
|
255
267
|
} catch (error) {
|
|
256
268
|
throw new AppError2(ERROR_CODES.INVALID_OPENCLAW_PATH, error.message);
|
|
257
269
|
}
|
|
258
|
-
baseDir = this.config.openclawPath;
|
|
270
|
+
baseDir = normalizePath(this.config.openclawPath);
|
|
259
271
|
} else {
|
|
260
272
|
const home = getHomeDir$1();
|
|
261
273
|
baseDir = path.join(home, config.DIRS.OPENCLAW);
|
|
@@ -277,6 +289,7 @@ class BoxInstaller {
|
|
|
277
289
|
updateSpinner(text) {
|
|
278
290
|
this.spinner.prefixText = this.prefixText;
|
|
279
291
|
this.spinner.text = chalk.cyan(text);
|
|
292
|
+
debug.log(`[Spinner] ${text}`);
|
|
280
293
|
}
|
|
281
294
|
getPrefixText() {
|
|
282
295
|
return this.prefixText;
|
|
@@ -289,7 +302,7 @@ class BoxInstaller {
|
|
|
289
302
|
}
|
|
290
303
|
async install() {
|
|
291
304
|
try {
|
|
292
|
-
|
|
305
|
+
debug.log("[盒子安装] 开始安装...");
|
|
293
306
|
this.validateConfig();
|
|
294
307
|
this.prefixText += chalk.green(` ✓ 配置验证完成
|
|
295
308
|
`);
|
|
@@ -299,25 +312,27 @@ class BoxInstaller {
|
|
|
299
312
|
await this.doDownloadFromNpm();
|
|
300
313
|
await this.doUpdateConfig(paths);
|
|
301
314
|
this.spinner.stop();
|
|
302
|
-
|
|
315
|
+
debug.log("[盒子安装] 安装完成");
|
|
303
316
|
} catch (error) {
|
|
304
317
|
this.spinner.stop();
|
|
305
|
-
|
|
318
|
+
debug.log(`[盒子安装] 安装失败: ${error.message}`);
|
|
306
319
|
throw error;
|
|
307
320
|
}
|
|
308
321
|
}
|
|
309
322
|
async doCleanOldFiles(paths) {
|
|
323
|
+
debug.log("[清理旧版本] 开始清理旧版本...");
|
|
310
324
|
this.prefixText += chalk.green(` ✓ 开始清理旧版本
|
|
311
325
|
`);
|
|
312
326
|
this.updateSpinner("正在清理旧版本...");
|
|
313
327
|
try {
|
|
314
328
|
await fs.access(paths.target);
|
|
315
|
-
|
|
329
|
+
debug.log("[清理旧版本] 删除旧版本目录...");
|
|
316
330
|
await fs.rm(paths.target, { recursive: true, force: true });
|
|
317
331
|
this.prefixText += chalk.green(` ✓ 旧版本清理成功
|
|
318
332
|
`);
|
|
319
|
-
|
|
333
|
+
debug.log("[清理旧版本] 旧版本清理成功");
|
|
320
334
|
} catch {
|
|
335
|
+
debug.log("[清理旧版本] 无旧版本需要清理");
|
|
321
336
|
this.prefixText += chalk.green(` ✓ 无旧版本需要清理
|
|
322
337
|
`);
|
|
323
338
|
}
|
|
@@ -325,27 +340,33 @@ class BoxInstaller {
|
|
|
325
340
|
await fs.access(paths.temp);
|
|
326
341
|
await fs.rm(paths.temp, { recursive: true, force: true });
|
|
327
342
|
} catch {
|
|
343
|
+
debug.log("[清理旧版本] 无临时目录需要清理");
|
|
328
344
|
}
|
|
329
345
|
}
|
|
330
346
|
async doDownloadFromNpm() {
|
|
331
347
|
const paths = this.getPaths();
|
|
332
348
|
this.spinner.prefixText = this.prefixText;
|
|
333
349
|
this.spinner.text = chalk.cyan("正在准备安装目录...");
|
|
350
|
+
debug.log("[下载插件] 创建扩展目录...");
|
|
334
351
|
await fs.mkdir(paths.extensions, { recursive: true });
|
|
335
|
-
|
|
352
|
+
debug.log(`[下载插件] 扩展目录: ${paths.extensions}`);
|
|
336
353
|
const tempDir = path.join(paths.temp, `install-${Date.now()}`);
|
|
354
|
+
debug.log(`[下载插件] 创建临时目录: ${tempDir}`);
|
|
337
355
|
await fs.mkdir(tempDir, { recursive: true });
|
|
338
356
|
this.spinner.prefixText = this.prefixText;
|
|
339
357
|
this.spinner.text = chalk.cyan("正在下载插件...");
|
|
358
|
+
debug.log("[下载插件] 执行 npm pack 下载源码包");
|
|
359
|
+
debug.log(`[下载插件] 工作目录: ${tempDir}`);
|
|
340
360
|
const maxRetries = 3;
|
|
341
361
|
let lastError = "";
|
|
342
362
|
let tarballPath = "";
|
|
343
363
|
for (let i = 1; i <= maxRetries; i++) {
|
|
344
364
|
this.spinner.prefixText = this.prefixText;
|
|
345
365
|
this.spinner.text = chalk.cyan(`正在下载 (${i}/${maxRetries})...`);
|
|
366
|
+
debug.log(`[下载插件] 第 ${i} 次尝试下载 tarball...`);
|
|
346
367
|
try {
|
|
347
368
|
const packageName = this.getPackageName();
|
|
348
|
-
|
|
369
|
+
debug.log(`[下载插件] 下载包名: ${packageName}`);
|
|
349
370
|
this.spinner.text = chalk.cyan("正在从 npm 获取插件...");
|
|
350
371
|
const output = await execAsync$1(
|
|
351
372
|
`npm pack ${packageName} --registry=https://mirrors.tencent.com/npm/ --ignore-scripts --quiet`,
|
|
@@ -354,12 +375,14 @@ class BoxInstaller {
|
|
|
354
375
|
const lines = output.trim().split("\n").filter((line) => line.trim());
|
|
355
376
|
const filename = lines[lines.length - 1] || "";
|
|
356
377
|
tarballPath = path.join(tempDir, filename);
|
|
357
|
-
|
|
378
|
+
debug.log(`[下载插件] tarball 下载成功: ${tarballPath}`);
|
|
358
379
|
break;
|
|
359
380
|
} catch (error) {
|
|
360
381
|
lastError = error.message || "未知错误";
|
|
382
|
+
debug.log(`[下载插件] 第 ${i} 次尝试失败: ${lastError}`);
|
|
361
383
|
if (i < maxRetries) {
|
|
362
384
|
this.spinner.text = chalk.cyan(`下载失败,3秒后重试...`);
|
|
385
|
+
debug.log("[下载插件] 等待 3 秒后重试...");
|
|
363
386
|
await new Promise((resolve2) => setTimeout(resolve2, 3e3));
|
|
364
387
|
}
|
|
365
388
|
}
|
|
@@ -369,16 +392,16 @@ class BoxInstaller {
|
|
|
369
392
|
}
|
|
370
393
|
this.spinner.prefixText = this.prefixText;
|
|
371
394
|
this.spinner.text = chalk.cyan("正在解压插件...");
|
|
372
|
-
|
|
395
|
+
debug.log(`[下载插件] 创建插件目录: ${paths.target}`);
|
|
373
396
|
await fs.mkdir(paths.target, { recursive: true });
|
|
374
|
-
|
|
397
|
+
debug.log(`[下载插件] 解压 tarball 到: ${paths.target}`);
|
|
375
398
|
try {
|
|
376
399
|
await tar.extract({
|
|
377
400
|
file: tarballPath,
|
|
378
401
|
cwd: paths.target,
|
|
379
402
|
strip: 1
|
|
380
403
|
});
|
|
381
|
-
|
|
404
|
+
debug.log("[下载插件] tarball 解压成功");
|
|
382
405
|
} catch (error) {
|
|
383
406
|
throw new AppError2(ERROR_CODES.NPM_INSTALL_FAILED, `tarball 解压失败: ${error.message}`);
|
|
384
407
|
}
|
|
@@ -387,27 +410,35 @@ class BoxInstaller {
|
|
|
387
410
|
if (!pluginExists) {
|
|
388
411
|
throw new AppError2(ERROR_CODES.NPM_INSTALL_FAILED, "插件解压后未找到 package.json");
|
|
389
412
|
}
|
|
413
|
+
debug.log("[下载插件] 插件解压成功");
|
|
390
414
|
this.spinner.prefixText = this.prefixText;
|
|
391
415
|
this.spinner.text = chalk.cyan("正在安装依赖...");
|
|
416
|
+
debug.log("[下载插件] 执行 npm install 安装生产环境依赖");
|
|
392
417
|
try {
|
|
393
418
|
await execAsync$1("npm install --omit=dev --ignore-scripts --quiet", {
|
|
394
419
|
cwd: paths.target,
|
|
395
420
|
timeout: 12e4
|
|
396
421
|
});
|
|
397
|
-
|
|
422
|
+
debug.log("[下载插件] npm install 执行成功");
|
|
398
423
|
} catch (error) {
|
|
399
424
|
const errorMsg = error.message || "";
|
|
400
|
-
if (errorMsg.includes("npm warn") || errorMsg.includes("deprecated"))
|
|
425
|
+
if (errorMsg.includes("npm warn") || errorMsg.includes("deprecated")) {
|
|
426
|
+
debug.log("[下载插件] npm install 有警告但可能成功,继续流程");
|
|
427
|
+
} else {
|
|
428
|
+
debug.log(`[下载插件] npm install 失败: ${errorMsg}`);
|
|
429
|
+
}
|
|
401
430
|
}
|
|
402
431
|
this.prefixText += chalk.green(` ✓ 插件安装完成
|
|
403
432
|
`);
|
|
404
433
|
try {
|
|
405
434
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
406
|
-
|
|
435
|
+
debug.log("[下载插件] 清理临时目录完成");
|
|
407
436
|
} catch {
|
|
437
|
+
debug.log("[下载插件] 清理临时目录失败(忽略)");
|
|
408
438
|
}
|
|
409
439
|
}
|
|
410
440
|
async doUpdateConfig(paths) {
|
|
441
|
+
debug.log("[更新配置] 开始更新配置...");
|
|
411
442
|
this.prefixText += chalk.green(` ✓ 开始更新配置
|
|
412
443
|
`);
|
|
413
444
|
this.updateSpinner("正在生成配置...");
|
|
@@ -415,8 +446,9 @@ class BoxInstaller {
|
|
|
415
446
|
try {
|
|
416
447
|
const content = await fs.readFile(paths.config, "utf-8");
|
|
417
448
|
originalConfig = JSON.parse(content);
|
|
418
|
-
|
|
449
|
+
debug.log("[更新配置] 读取原有配置成功");
|
|
419
450
|
} catch {
|
|
451
|
+
debug.log("[更新配置] 无原有配置");
|
|
420
452
|
}
|
|
421
453
|
const config = getConfig(this.config.env || "test", this.config.customIp);
|
|
422
454
|
const newConfig = {
|
|
@@ -655,16 +687,19 @@ class BoxInstaller {
|
|
|
655
687
|
};
|
|
656
688
|
const finalConfig = deepMerge$1(originalConfig, newConfig);
|
|
657
689
|
this.updateSpinner("正在写入配置...");
|
|
690
|
+
debug.log("[更新配置] 写入配置文件...");
|
|
658
691
|
await fs.writeFile(paths.config, JSON.stringify(finalConfig, null, 2), "utf-8");
|
|
659
692
|
this.prefixText += chalk.green(` ✓ 配置文件更新成功
|
|
660
693
|
`);
|
|
661
|
-
|
|
694
|
+
debug.log(`[更新配置] 配置文件写入成功: ${paths.config}`);
|
|
662
695
|
}
|
|
663
696
|
}
|
|
664
697
|
async function createBoxCommand(options) {
|
|
665
698
|
setDebug(!!options.debug);
|
|
666
|
-
|
|
699
|
+
debug.log("[盒子安装] 开始处理...");
|
|
700
|
+
debug.log(`[盒子安装] 参数: env=${options.env}, appKey=${options.appKey ? "***" : "未提供"}, customIp=${options.customIp}, debug=${options.debug}`);
|
|
667
701
|
checkEnv();
|
|
702
|
+
debug.log("[盒子安装] 环境检查通过");
|
|
668
703
|
try {
|
|
669
704
|
let env = options.env;
|
|
670
705
|
let appKey = options.appKey;
|
|
@@ -672,7 +707,7 @@ async function createBoxCommand(options) {
|
|
|
672
707
|
let customIp = options.customIp;
|
|
673
708
|
const questions = [];
|
|
674
709
|
if (!env) {
|
|
675
|
-
|
|
710
|
+
debug.log("[盒子安装] 需要选择环境");
|
|
676
711
|
questions.push({
|
|
677
712
|
type: "list",
|
|
678
713
|
name: "env",
|
|
@@ -685,10 +720,10 @@ async function createBoxCommand(options) {
|
|
|
685
720
|
]
|
|
686
721
|
});
|
|
687
722
|
} else {
|
|
688
|
-
|
|
723
|
+
debug.log(`[盒子安装] 使用命令行参数: env=${env}`);
|
|
689
724
|
}
|
|
690
725
|
if (!appKey) {
|
|
691
|
-
|
|
726
|
+
debug.log("[盒子安装] 需要输入 AppKey");
|
|
692
727
|
questions.push({
|
|
693
728
|
type: "input",
|
|
694
729
|
name: "appKey",
|
|
@@ -702,10 +737,10 @@ async function createBoxCommand(options) {
|
|
|
702
737
|
}
|
|
703
738
|
});
|
|
704
739
|
} else {
|
|
705
|
-
|
|
740
|
+
debug.log("[盒子安装] 使用命令行参数: appKey");
|
|
706
741
|
}
|
|
707
742
|
if (!appSecret) {
|
|
708
|
-
|
|
743
|
+
debug.log("[盒子安装] 需要输入 AppSecret");
|
|
709
744
|
questions.push({
|
|
710
745
|
type: "input",
|
|
711
746
|
name: "appSecret",
|
|
@@ -719,18 +754,18 @@ async function createBoxCommand(options) {
|
|
|
719
754
|
}
|
|
720
755
|
});
|
|
721
756
|
} else {
|
|
722
|
-
|
|
757
|
+
debug.log("[盒子安装] 使用命令行参数: appSecret");
|
|
723
758
|
}
|
|
724
759
|
if (questions.length > 0) {
|
|
725
|
-
|
|
760
|
+
debug.log(`[盒子安装] 开始交互式问答,共 ${questions.length} 个问题`);
|
|
726
761
|
const answers = await inquirer.prompt(questions);
|
|
727
|
-
|
|
762
|
+
debug.log("[盒子安装] 交互式问答完成");
|
|
728
763
|
env = env || answers.env || "test";
|
|
729
764
|
appKey = appKey || answers.appKey;
|
|
730
765
|
appSecret = appSecret || answers.appSecret;
|
|
731
766
|
}
|
|
732
767
|
if (env === "custom" && !customIp) {
|
|
733
|
-
|
|
768
|
+
debug.log("[盒子安装] 自定义环境需要输入后端 IP");
|
|
734
769
|
const answer = await inquirer.prompt([{
|
|
735
770
|
type: "input",
|
|
736
771
|
name: "customIp",
|
|
@@ -748,12 +783,12 @@ async function createBoxCommand(options) {
|
|
|
748
783
|
}
|
|
749
784
|
}]);
|
|
750
785
|
customIp = answer.customIp;
|
|
751
|
-
|
|
786
|
+
debug.log("[盒子安装] 获取自定义 customIp");
|
|
752
787
|
} else if (env === "custom" && customIp) {
|
|
753
|
-
|
|
788
|
+
debug.log(`[盒子安装] 使用命令行参数: customIp=${customIp}`);
|
|
754
789
|
}
|
|
755
|
-
|
|
756
|
-
|
|
790
|
+
debug.log(`[盒子安装] 最终参数: env=${env}, customIp=${customIp}, openclawPath=${options.openclawPath}`);
|
|
791
|
+
debug.log("[盒子安装] 创建 BoxInstaller 实例...");
|
|
757
792
|
const installer = new BoxInstaller({
|
|
758
793
|
appKey,
|
|
759
794
|
appSecret,
|
|
@@ -763,9 +798,9 @@ async function createBoxCommand(options) {
|
|
|
763
798
|
pluginVersion: options.pluginVersion,
|
|
764
799
|
openclawPath: options.openclawPath
|
|
765
800
|
});
|
|
766
|
-
|
|
801
|
+
debug.log("[盒子安装] 开始安装...");
|
|
767
802
|
await installer.install();
|
|
768
|
-
|
|
803
|
+
debug.log("[盒子安装] 安装完成");
|
|
769
804
|
console.log(installer.getPrefixText() + boxen(
|
|
770
805
|
`${chalk.green("✦")} 插件初始化成功!`,
|
|
771
806
|
{
|
|
@@ -779,7 +814,7 @@ async function createBoxCommand(options) {
|
|
|
779
814
|
}
|
|
780
815
|
));
|
|
781
816
|
} catch (error) {
|
|
782
|
-
|
|
817
|
+
debug.log(`[盒子安装] 发生错误: ${error.message}`);
|
|
783
818
|
console.error(boxen(
|
|
784
819
|
`${chalk.yellow(error.message)}`,
|
|
785
820
|
{
|
|
@@ -809,12 +844,13 @@ function createHttpClient(baseURL) {
|
|
|
809
844
|
}
|
|
810
845
|
async function httpPost(url, data, config) {
|
|
811
846
|
const client = createHttpClient();
|
|
812
|
-
|
|
813
|
-
|
|
847
|
+
debug.log(`[HTTP POST] 请求地址: ${url}`);
|
|
848
|
+
debug.log(`[HTTP POST] 请求参数: ${JSON.stringify(data)}`);
|
|
849
|
+
debug.log(`[HTTP POST] 请求头: ${JSON.stringify(config?.headers || {})}`);
|
|
814
850
|
try {
|
|
815
851
|
const response = await client.post(url, data, config);
|
|
816
|
-
|
|
817
|
-
|
|
852
|
+
debug.log(`[HTTP POST] 响应状态: ${response.status}`);
|
|
853
|
+
debug.log(`[HTTP POST] 响应内容: ${JSON.stringify(response.data)}`);
|
|
818
854
|
return {
|
|
819
855
|
status: response.status,
|
|
820
856
|
data: response.data
|
|
@@ -823,9 +859,9 @@ async function httpPost(url, data, config) {
|
|
|
823
859
|
const axiosError = error;
|
|
824
860
|
if (axiosError.response) {
|
|
825
861
|
const responseData = axiosError.response.data;
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
862
|
+
debug.log(`[HTTP POST] 响应状态: ${axiosError.response.status}`);
|
|
863
|
+
debug.log(`[HTTP POST] 响应状态文本: ${axiosError.response.statusText}`);
|
|
864
|
+
debug.log(`[HTTP POST] 响应数据: ${JSON.stringify(responseData)}`);
|
|
829
865
|
if (typeof responseData === "string") {
|
|
830
866
|
throw new AppError$1(ERROR_CODES$1.HTTP_ERROR, `请求失败 (${axiosError.response.status}): ${responseData}`);
|
|
831
867
|
} else if (responseData && typeof responseData === "object" && "message" in responseData) {
|
|
@@ -834,40 +870,48 @@ async function httpPost(url, data, config) {
|
|
|
834
870
|
throw new AppError$1(ERROR_CODES$1.HTTP_ERROR, `请求失败 (${axiosError.response.status}): ${axiosError.response.statusText}`);
|
|
835
871
|
}
|
|
836
872
|
} else if (axiosError.request) {
|
|
837
|
-
|
|
873
|
+
debug.log(`[HTTP POST] 未收到响应`);
|
|
874
|
+
debug.log(`[HTTP POST] 错误信息: ${axiosError.message}`);
|
|
838
875
|
throw new AppError$1(ERROR_CODES$1.NETWORK_ERROR, `网络错误: ${axiosError.message}`);
|
|
839
876
|
} else {
|
|
840
|
-
|
|
877
|
+
debug.log(`[HTTP POST] 请求配置错误`);
|
|
878
|
+
debug.log(`[HTTP POST] 错误信息: ${axiosError.message}`);
|
|
841
879
|
throw new AppError$1(ERROR_CODES$1.HTTP_ERROR, `请求配置错误: ${axiosError.message}`);
|
|
842
880
|
}
|
|
843
881
|
}
|
|
844
882
|
}
|
|
845
883
|
async function login(phone, password, config) {
|
|
846
884
|
const url = `${config.API.TUZAI_BASE_URL}/user/login/pass`;
|
|
885
|
+
debug.log("[登录] 开始登录...");
|
|
886
|
+
debug.log(`[登录] 请求地址: ${url}`);
|
|
887
|
+
debug.log(`[登录] 请求参数: phone=${phone}, password=***`);
|
|
847
888
|
try {
|
|
848
889
|
const response = await httpPost(url, {
|
|
849
890
|
phone,
|
|
850
891
|
userPass: password
|
|
851
892
|
});
|
|
852
893
|
const data = response.data;
|
|
853
|
-
|
|
894
|
+
debug.log(`[登录] 响应数据: ${JSON.stringify(data)}`);
|
|
854
895
|
if (data.code === 200 && data.data?.token) {
|
|
855
|
-
|
|
896
|
+
debug.log("[登录] 登录成功,获取到 token");
|
|
856
897
|
return data.data.token;
|
|
857
898
|
}
|
|
858
|
-
|
|
899
|
+
debug.log("[登录] 登录失败");
|
|
859
900
|
throw new AppError$1(ERROR_CODES$1.LOGIN_FAILED, data.message || "登录失败");
|
|
860
901
|
} catch (error) {
|
|
861
902
|
if (error instanceof AppError$1) {
|
|
862
903
|
throw error;
|
|
863
904
|
}
|
|
864
|
-
|
|
905
|
+
debug.log(`[登录] 发生错误: ${error.message}`);
|
|
865
906
|
throw new AppError$1(ERROR_CODES$1.LOGIN_FAILED, error.message);
|
|
866
907
|
}
|
|
867
908
|
}
|
|
868
909
|
async function fetchBoundConfig(token, phone, localCode, config) {
|
|
869
910
|
const url = `${config.API.TUZAI_BASE_URL}/work-bot/local/bound/init`;
|
|
870
|
-
|
|
911
|
+
debug.log("[获取绑定配置] 开始获取绑定配置...");
|
|
912
|
+
debug.log(`[获取绑定配置] 请求地址: ${url}`);
|
|
913
|
+
debug.log(`[获取绑定配置] 请求参数: phone=${phone}, localCode=${localCode}`);
|
|
914
|
+
debug.log(`[获取绑定配置] 请求头: Authorization=${token.substring(0, 20)}...`);
|
|
871
915
|
try {
|
|
872
916
|
const response = await httpPost(url, {
|
|
873
917
|
phone,
|
|
@@ -878,7 +922,7 @@ async function fetchBoundConfig(token, phone, localCode, config) {
|
|
|
878
922
|
}
|
|
879
923
|
});
|
|
880
924
|
const data = response.data;
|
|
881
|
-
|
|
925
|
+
debug.log(`[获取绑定配置] 响应数据: ${JSON.stringify(data)}`);
|
|
882
926
|
if (data.code === 200 && data.data) {
|
|
883
927
|
const boundConfig = {
|
|
884
928
|
appKey: data.data.app_key,
|
|
@@ -888,26 +932,26 @@ async function fetchBoundConfig(token, phone, localCode, config) {
|
|
|
888
932
|
modelApiKey: data.data.model_api_key,
|
|
889
933
|
modelApiBaseUrl: data.data.model_api_base_url
|
|
890
934
|
};
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
935
|
+
debug.log("[获取绑定配置] 获取绑定配置成功");
|
|
936
|
+
debug.log(`[获取绑定配置] appKey: ${boundConfig.appKey}`);
|
|
937
|
+
debug.log(`[获取绑定配置] appSecret: ${boundConfig.appSecret ? "***" : "undefined"}`);
|
|
938
|
+
debug.log(`[获取绑定配置] userId: ${boundConfig.userId}`);
|
|
939
|
+
debug.log(`[获取绑定配置] agentId: ${boundConfig.agentId}`);
|
|
940
|
+
debug.log(`[获取绑定配置] modelApiKey: ${boundConfig.modelApiKey || "undefined"}`);
|
|
941
|
+
debug.log(`[获取绑定配置] modelApiBaseUrl: ${boundConfig.modelApiBaseUrl || "undefined"}`);
|
|
898
942
|
if (!boundConfig.appKey || !boundConfig.appSecret || !boundConfig.agentId) {
|
|
899
|
-
|
|
943
|
+
debug.log("[获取绑定配置] 缺少必要字段");
|
|
900
944
|
throw new AppError$1(ERROR_CODES$1.GET_BOUND_CONFIG_FAILED, "获取绑定配置失败:缺少必要字段");
|
|
901
945
|
}
|
|
902
946
|
return boundConfig;
|
|
903
947
|
}
|
|
904
|
-
|
|
948
|
+
debug.log("[获取绑定配置] 获取绑定配置失败");
|
|
905
949
|
throw new AppError$1(ERROR_CODES$1.GET_BOUND_CONFIG_FAILED, data.message || data.msg || "获取绑定配置失败");
|
|
906
950
|
} catch (error) {
|
|
907
951
|
if (error instanceof AppError$1) {
|
|
908
952
|
throw error;
|
|
909
953
|
}
|
|
910
|
-
|
|
954
|
+
debug.log(`[获取绑定配置] 发生错误: ${error.message}`);
|
|
911
955
|
throw new AppError$1(ERROR_CODES$1.GET_BOUND_CONFIG_FAILED, error.message);
|
|
912
956
|
}
|
|
913
957
|
}
|
|
@@ -4893,6 +4937,7 @@ class LocalInstaller {
|
|
|
4893
4937
|
prefixText = "";
|
|
4894
4938
|
config;
|
|
4895
4939
|
envConfig;
|
|
4940
|
+
token = "";
|
|
4896
4941
|
/**
|
|
4897
4942
|
* 构造函数
|
|
4898
4943
|
*/
|
|
@@ -4906,29 +4951,31 @@ class LocalInstaller {
|
|
|
4906
4951
|
* 验证安装配置
|
|
4907
4952
|
*/
|
|
4908
4953
|
validateConfig() {
|
|
4954
|
+
debug.log("[验证配置] 检查配置...");
|
|
4909
4955
|
const result = LocalInstallerConfigSchema.safeParse(this.config);
|
|
4910
4956
|
if (!result.success) {
|
|
4911
|
-
|
|
4957
|
+
debug.log(`[验证配置] 验证失败: ${result.error.issues[0].message}`);
|
|
4912
4958
|
throw new AppError$1(ERROR_CODES$1.INVALID_ARGUMENT, result.error.issues[0].message);
|
|
4913
4959
|
}
|
|
4960
|
+
debug.log("[验证配置] 配置验证通过");
|
|
4914
4961
|
}
|
|
4915
4962
|
/**
|
|
4916
4963
|
* 执行安装流程
|
|
4917
4964
|
*/
|
|
4918
4965
|
async install() {
|
|
4919
4966
|
try {
|
|
4920
|
-
|
|
4967
|
+
debug.log("[安装开始]");
|
|
4921
4968
|
this.validateConfig();
|
|
4922
4969
|
let baseDir;
|
|
4923
4970
|
if (this.config.openclawPath) {
|
|
4924
|
-
|
|
4971
|
+
debug.log(`[路径验证] 验证自定义路径: ${this.config.openclawPath}`);
|
|
4925
4972
|
try {
|
|
4926
4973
|
validateOpenclawPath(this.config.openclawPath);
|
|
4927
|
-
|
|
4974
|
+
debug.log("[路径验证] 通过");
|
|
4928
4975
|
} catch (error) {
|
|
4929
4976
|
throw new AppError$1(ERROR_CODES$1.INVALID_OPENCLAW_PATH, error.message);
|
|
4930
4977
|
}
|
|
4931
|
-
baseDir = this.config.openclawPath;
|
|
4978
|
+
baseDir = normalizePath(this.config.openclawPath);
|
|
4932
4979
|
} else {
|
|
4933
4980
|
const homeDir = getHomeDir();
|
|
4934
4981
|
baseDir = path.join(homeDir, this.envConfig.DIRS.OPENCLAW);
|
|
@@ -4941,25 +4988,26 @@ class LocalInstaller {
|
|
|
4941
4988
|
workspace: path.join(baseDir, this.envConfig.DIRS.WORKSPACE),
|
|
4942
4989
|
temp: path.join(baseDir, this.envConfig.DIRS.TEMP)
|
|
4943
4990
|
};
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4991
|
+
debug.log(`[路径配置] home=${paths.home}`);
|
|
4992
|
+
debug.log(`[路径配置] extensions=${paths.extensions}`);
|
|
4993
|
+
debug.log(`[路径配置] target=${paths.target}`);
|
|
4994
|
+
debug.log(`[路径配置] config=${paths.config}`);
|
|
4995
|
+
debug.log(`[路径配置] workspace=${paths.workspace}`);
|
|
4949
4996
|
const machineId = generateMachineId();
|
|
4950
4997
|
const localCode = buildLocalCode(this.config.phone, machineId);
|
|
4951
|
-
|
|
4952
|
-
|
|
4998
|
+
debug.log(`[机器码] machineId=${machineId}`);
|
|
4999
|
+
debug.log(`[机器码] localCode=${localCode}`);
|
|
4953
5000
|
const token = await this.doLogin();
|
|
5001
|
+
this.token = token;
|
|
4954
5002
|
const boundConfig = await this.doFetchBoundConfig(token, localCode);
|
|
4955
5003
|
await this.doCleanOldFiles(paths.target);
|
|
4956
5004
|
await this.doDownloadFromNpm(paths);
|
|
4957
5005
|
await this.doUpdateConfig(paths, boundConfig);
|
|
4958
5006
|
try {
|
|
4959
5007
|
await fs.rm(paths.temp, { recursive: true, force: true });
|
|
4960
|
-
|
|
5008
|
+
debug.log("[安装完成] 清理根临时目录完成");
|
|
4961
5009
|
} catch {
|
|
4962
|
-
|
|
5010
|
+
debug.log("[安装完成] 清理根临时目录失败(忽略)");
|
|
4963
5011
|
}
|
|
4964
5012
|
this.spinner.stop();
|
|
4965
5013
|
} catch (error) {
|
|
@@ -4973,6 +5021,12 @@ class LocalInstaller {
|
|
|
4973
5021
|
getPrefixText() {
|
|
4974
5022
|
return this.prefixText;
|
|
4975
5023
|
}
|
|
5024
|
+
/**
|
|
5025
|
+
* 获取登录 token
|
|
5026
|
+
*/
|
|
5027
|
+
getToken() {
|
|
5028
|
+
return this.token;
|
|
5029
|
+
}
|
|
4976
5030
|
/**
|
|
4977
5031
|
* 获取完整的 npm 包名(包含版本号)
|
|
4978
5032
|
*/
|
|
@@ -4988,14 +5042,18 @@ class LocalInstaller {
|
|
|
4988
5042
|
async doLogin() {
|
|
4989
5043
|
this.spinner.prefixText = this.prefixText;
|
|
4990
5044
|
this.spinner.text = chalk.cyan("正在验证账号...");
|
|
4991
|
-
|
|
5045
|
+
debug.log(`[用户登录] 手机号: ${this.config.phone}`);
|
|
5046
|
+
debug.log("[用户登录] RSA 加密密码...");
|
|
4992
5047
|
const encryptedPassword = rsaEncrypt(this.config.userPass);
|
|
4993
5048
|
if (!encryptedPassword) {
|
|
4994
5049
|
throw new AppError$1(ERROR_CODES$1.LOGIN_FAILED, "密码加密失败");
|
|
4995
5050
|
}
|
|
5051
|
+
debug.log("[用户登录] 密码加密成功");
|
|
5052
|
+
debug.log("[用户登录] 调用登录接口...");
|
|
4996
5053
|
const token = await login(this.config.phone, encryptedPassword, this.envConfig);
|
|
4997
5054
|
this.prefixText += chalk.green(` ✓ 账号验证成功
|
|
4998
5055
|
`);
|
|
5056
|
+
debug.log("[用户登录] 登录成功");
|
|
4999
5057
|
return token;
|
|
5000
5058
|
}
|
|
5001
5059
|
/**
|
|
@@ -5004,10 +5062,13 @@ class LocalInstaller {
|
|
|
5004
5062
|
async doFetchBoundConfig(token, localCode) {
|
|
5005
5063
|
this.spinner.prefixText = this.prefixText;
|
|
5006
5064
|
this.spinner.text = chalk.cyan("正在获取绑定信息...");
|
|
5065
|
+
debug.log("[获取配置] 调用绑定配置接口...");
|
|
5066
|
+
debug.log(`[获取配置] localCode=${localCode}`);
|
|
5007
5067
|
const boundConfig = await fetchBoundConfig(token, this.config.phone, localCode, this.envConfig);
|
|
5008
|
-
|
|
5068
|
+
debug.log(`[获取配置] agentId=${boundConfig.agentId}, appKey=${boundConfig.appKey?.slice(0, 8)}...`);
|
|
5009
5069
|
this.prefixText += chalk.green(` ✓ 绑定信息获取成功
|
|
5010
5070
|
`);
|
|
5071
|
+
debug.log("[获取配置] 获取成功");
|
|
5011
5072
|
return boundConfig;
|
|
5012
5073
|
}
|
|
5013
5074
|
/**
|
|
@@ -5016,17 +5077,19 @@ class LocalInstaller {
|
|
|
5016
5077
|
async doCleanOldFiles(targetPath) {
|
|
5017
5078
|
this.spinner.prefixText = this.prefixText;
|
|
5018
5079
|
this.spinner.text = chalk.cyan("正在检查已安装版本...");
|
|
5080
|
+
debug.log(`[清理旧版本] 检查目录: ${targetPath}`);
|
|
5019
5081
|
try {
|
|
5020
5082
|
await fs.access(targetPath);
|
|
5021
5083
|
this.spinner.text = chalk.cyan("正在清理旧文件...");
|
|
5022
|
-
|
|
5084
|
+
debug.log("[清理旧版本] 发现旧版本,开始清理...");
|
|
5023
5085
|
await fs.rm(targetPath, { recursive: true, force: true });
|
|
5024
5086
|
this.prefixText += chalk.green(` ✓ 旧版本清理完成
|
|
5025
5087
|
`);
|
|
5026
|
-
|
|
5088
|
+
debug.log("[清理旧版本] 清理完成");
|
|
5027
5089
|
} catch {
|
|
5028
5090
|
this.prefixText += chalk.green(` ✓ 无旧版本需要清理
|
|
5029
5091
|
`);
|
|
5092
|
+
debug.log("[清理旧版本] 未发现旧版本");
|
|
5030
5093
|
}
|
|
5031
5094
|
}
|
|
5032
5095
|
/**
|
|
@@ -5035,21 +5098,26 @@ class LocalInstaller {
|
|
|
5035
5098
|
async doDownloadFromNpm(paths) {
|
|
5036
5099
|
this.spinner.prefixText = this.prefixText;
|
|
5037
5100
|
this.spinner.text = chalk.cyan("正在准备安装目录...");
|
|
5101
|
+
debug.log("[下载插件] 创建扩展目录...");
|
|
5038
5102
|
await fs.mkdir(paths.extensions, { recursive: true });
|
|
5039
|
-
|
|
5103
|
+
debug.log(`[下载插件] 扩展目录: ${paths.extensions}`);
|
|
5040
5104
|
const tempDir = path.join(paths.temp, `install-${Date.now()}`);
|
|
5105
|
+
debug.log(`[下载插件] 创建临时目录: ${tempDir}`);
|
|
5041
5106
|
await fs.mkdir(tempDir, { recursive: true });
|
|
5042
5107
|
this.spinner.prefixText = this.prefixText;
|
|
5043
5108
|
this.spinner.text = chalk.cyan("正在下载插件...");
|
|
5109
|
+
debug.log("[下载插件] 执行 npm pack 下载源码包");
|
|
5110
|
+
debug.log(`[下载插件] 工作目录: ${tempDir}`);
|
|
5044
5111
|
const maxRetries = 3;
|
|
5045
5112
|
let lastError = "";
|
|
5046
5113
|
let tarballPath = "";
|
|
5047
5114
|
for (let i = 1; i <= maxRetries; i++) {
|
|
5048
5115
|
this.spinner.prefixText = this.prefixText;
|
|
5049
5116
|
this.spinner.text = chalk.cyan(`正在下载 (${i}/${maxRetries})...`);
|
|
5117
|
+
debug.log(`[下载插件] 第 ${i} 次尝试下载 tarball...`);
|
|
5050
5118
|
try {
|
|
5051
5119
|
const packageName = this.getPackageName();
|
|
5052
|
-
|
|
5120
|
+
debug.log(`[下载插件] 下载包名: ${packageName}`);
|
|
5053
5121
|
this.spinner.text = chalk.cyan("正在从 npm 获取插件...");
|
|
5054
5122
|
const output = await execAsync(
|
|
5055
5123
|
`npm pack ${packageName} --registry=https://mirrors.tencent.com/npm/ --ignore-scripts --quiet`,
|
|
@@ -5058,12 +5126,14 @@ class LocalInstaller {
|
|
|
5058
5126
|
const lines = output.trim().split("\n").filter((line) => line.trim());
|
|
5059
5127
|
const filename = lines[lines.length - 1] || "";
|
|
5060
5128
|
tarballPath = path.join(tempDir, filename);
|
|
5061
|
-
|
|
5129
|
+
debug.log(`[下载插件] tarball 下载成功: ${tarballPath}`);
|
|
5062
5130
|
break;
|
|
5063
5131
|
} catch (error) {
|
|
5064
5132
|
lastError = error.message || "未知错误";
|
|
5133
|
+
debug.log(`[下载插件] 第 ${i} 次尝试失败: ${lastError}`);
|
|
5065
5134
|
if (i < maxRetries) {
|
|
5066
5135
|
this.spinner.text = chalk.cyan(`下载失败,3秒后重试...`);
|
|
5136
|
+
debug.log("[下载插件] 等待 3 秒后重试...");
|
|
5067
5137
|
await new Promise((resolve2) => setTimeout(resolve2, 3e3));
|
|
5068
5138
|
}
|
|
5069
5139
|
}
|
|
@@ -5073,16 +5143,16 @@ class LocalInstaller {
|
|
|
5073
5143
|
}
|
|
5074
5144
|
this.spinner.prefixText = this.prefixText;
|
|
5075
5145
|
this.spinner.text = chalk.cyan("正在解压插件...");
|
|
5076
|
-
|
|
5146
|
+
debug.log(`[下载插件] 创建插件目录: ${paths.target}`);
|
|
5077
5147
|
await fs.mkdir(paths.target, { recursive: true });
|
|
5078
|
-
|
|
5148
|
+
debug.log(`[下载插件] 解压 tarball 到: ${paths.target}`);
|
|
5079
5149
|
try {
|
|
5080
5150
|
await tar.extract({
|
|
5081
5151
|
file: tarballPath,
|
|
5082
5152
|
cwd: paths.target,
|
|
5083
5153
|
strip: 1
|
|
5084
5154
|
});
|
|
5085
|
-
|
|
5155
|
+
debug.log("[下载插件] tarball 解压成功");
|
|
5086
5156
|
} catch (error) {
|
|
5087
5157
|
throw new AppError$1(ERROR_CODES$1.NPM_INSTALL_FAILED, `tarball 解压失败: ${error.message}`);
|
|
5088
5158
|
}
|
|
@@ -5091,18 +5161,22 @@ class LocalInstaller {
|
|
|
5091
5161
|
if (!pluginExists) {
|
|
5092
5162
|
throw new AppError$1(ERROR_CODES$1.NPM_INSTALL_FAILED, "插件解压后未找到 package.json");
|
|
5093
5163
|
}
|
|
5164
|
+
debug.log("[下载插件] 插件解压成功");
|
|
5094
5165
|
this.spinner.prefixText = this.prefixText;
|
|
5095
5166
|
this.spinner.text = chalk.cyan("正在安装依赖...");
|
|
5167
|
+
debug.log("[下载插件] 执行 npm install 安装生产环境依赖");
|
|
5096
5168
|
try {
|
|
5097
5169
|
await execAsync("npm install --omit=dev --ignore-scripts --quiet", {
|
|
5098
5170
|
cwd: paths.target,
|
|
5099
5171
|
timeout: 12e4
|
|
5100
5172
|
});
|
|
5101
|
-
|
|
5173
|
+
debug.log("[下载插件] npm install 执行成功");
|
|
5102
5174
|
} catch (error) {
|
|
5103
5175
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
5104
|
-
if (errorMsg.includes("npm warn") || errorMsg.includes("deprecated"))
|
|
5105
|
-
|
|
5176
|
+
if (errorMsg.includes("npm warn") || errorMsg.includes("deprecated")) {
|
|
5177
|
+
debug.log("[下载插件] npm install 有警告但可能成功,继续流程");
|
|
5178
|
+
} else {
|
|
5179
|
+
debug.log(`[下载插件] npm install 失败: ${errorMsg}`);
|
|
5106
5180
|
throw new AppError$1(ERROR_CODES$1.NPM_INSTALL_FAILED, `依赖安装失败: ${errorMsg}`);
|
|
5107
5181
|
}
|
|
5108
5182
|
}
|
|
@@ -5110,8 +5184,9 @@ class LocalInstaller {
|
|
|
5110
5184
|
`);
|
|
5111
5185
|
try {
|
|
5112
5186
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
5113
|
-
|
|
5187
|
+
debug.log("[下载插件] 清理临时下载目录完成");
|
|
5114
5188
|
} catch {
|
|
5189
|
+
debug.log("[下载插件] 清理临时下载目录失败(忽略)");
|
|
5115
5190
|
}
|
|
5116
5191
|
}
|
|
5117
5192
|
/**
|
|
@@ -5120,14 +5195,18 @@ class LocalInstaller {
|
|
|
5120
5195
|
async doUpdateConfig(paths, boundConfig) {
|
|
5121
5196
|
this.spinner.prefixText = this.prefixText;
|
|
5122
5197
|
this.spinner.text = chalk.cyan("正在生成配置...");
|
|
5198
|
+
debug.log("[更新配置] 创建目录...");
|
|
5123
5199
|
await fs.mkdir(paths.home, { recursive: true });
|
|
5124
5200
|
await fs.mkdir(paths.workspace, { recursive: true });
|
|
5201
|
+
debug.log("[更新配置] 目录创建完成");
|
|
5202
|
+
debug.log("[更新配置] 读取原有配置...");
|
|
5125
5203
|
let originalConfig = {};
|
|
5126
5204
|
try {
|
|
5127
5205
|
const content = await fs.readFile(paths.config, "utf-8");
|
|
5128
5206
|
originalConfig = JSON.parse(content);
|
|
5129
|
-
|
|
5207
|
+
debug.log("[更新配置] 读取原有配置成功");
|
|
5130
5208
|
} catch {
|
|
5209
|
+
debug.log("[更新配置] 无原有配置");
|
|
5131
5210
|
}
|
|
5132
5211
|
const config = this.envConfig;
|
|
5133
5212
|
const newConfig = {
|
|
@@ -5296,16 +5375,19 @@ class LocalInstaller {
|
|
|
5296
5375
|
const finalConfig = deepMerge(originalConfig, newConfig);
|
|
5297
5376
|
this.spinner.prefixText = this.prefixText;
|
|
5298
5377
|
this.spinner.text = chalk.cyan("正在写入配置...");
|
|
5378
|
+
debug.log("[更新配置] 写入配置文件...");
|
|
5299
5379
|
await fs.writeFile(paths.config, JSON.stringify(finalConfig, null, 2), "utf-8");
|
|
5300
5380
|
this.prefixText += chalk.green(` ✓ 配置更新完成
|
|
5301
5381
|
`);
|
|
5302
|
-
|
|
5382
|
+
debug.log(`[更新配置] 配置文件写入成功: ${paths.config}`);
|
|
5303
5383
|
}
|
|
5304
5384
|
}
|
|
5305
5385
|
async function createLocalCommand(options) {
|
|
5306
5386
|
setDebug(!!options.debug);
|
|
5307
|
-
|
|
5387
|
+
debug.log("[初始化] 开始处理...");
|
|
5388
|
+
debug.log(`[初始化] 参数: env=${options.env}, phone=${options.phone}, customIp=${options.customIp}, debug=${options.debug}`);
|
|
5308
5389
|
checkEnv();
|
|
5390
|
+
debug.log("[初始化] 环境检查通过");
|
|
5309
5391
|
try {
|
|
5310
5392
|
let env = options.env;
|
|
5311
5393
|
let phone = options.phone;
|
|
@@ -5313,7 +5395,7 @@ async function createLocalCommand(options) {
|
|
|
5313
5395
|
let customIp = options.customIp;
|
|
5314
5396
|
const questions = [];
|
|
5315
5397
|
if (!env) {
|
|
5316
|
-
|
|
5398
|
+
debug.log("[初始化] 需要选择环境");
|
|
5317
5399
|
questions.push({
|
|
5318
5400
|
type: "list",
|
|
5319
5401
|
name: "env",
|
|
@@ -5326,10 +5408,10 @@ async function createLocalCommand(options) {
|
|
|
5326
5408
|
]
|
|
5327
5409
|
});
|
|
5328
5410
|
} else {
|
|
5329
|
-
|
|
5411
|
+
debug.log(`[初始化] 使用命令行参数: env=${env}`);
|
|
5330
5412
|
}
|
|
5331
5413
|
if (!phone) {
|
|
5332
|
-
|
|
5414
|
+
debug.log("[初始化] 需要输入手机号码");
|
|
5333
5415
|
questions.push({
|
|
5334
5416
|
type: "input",
|
|
5335
5417
|
name: "phone",
|
|
@@ -5346,10 +5428,10 @@ async function createLocalCommand(options) {
|
|
|
5346
5428
|
}
|
|
5347
5429
|
});
|
|
5348
5430
|
} else {
|
|
5349
|
-
|
|
5431
|
+
debug.log("[初始化] 使用命令行参数: phone");
|
|
5350
5432
|
}
|
|
5351
5433
|
if (!userPass) {
|
|
5352
|
-
|
|
5434
|
+
debug.log("[初始化] 需要输入用户密码");
|
|
5353
5435
|
questions.push({
|
|
5354
5436
|
type: "input",
|
|
5355
5437
|
name: "userPass",
|
|
@@ -5363,18 +5445,18 @@ async function createLocalCommand(options) {
|
|
|
5363
5445
|
}
|
|
5364
5446
|
});
|
|
5365
5447
|
} else {
|
|
5366
|
-
|
|
5448
|
+
debug.log("[初始化] 使用命令行参数: userPass");
|
|
5367
5449
|
}
|
|
5368
5450
|
if (questions.length > 0) {
|
|
5369
|
-
|
|
5451
|
+
debug.log(`[初始化] 开始交互式问答,共 ${questions.length} 个问题`);
|
|
5370
5452
|
const answers = await inquirer.prompt(questions);
|
|
5371
|
-
|
|
5453
|
+
debug.log("[初始化] 交互式问答完成");
|
|
5372
5454
|
env = env || answers.env || "test";
|
|
5373
5455
|
phone = phone || answers.phone;
|
|
5374
5456
|
userPass = userPass || answers.userPass;
|
|
5375
5457
|
}
|
|
5376
5458
|
if (env === "custom" && !customIp) {
|
|
5377
|
-
|
|
5459
|
+
debug.log("[初始化] 自定义环境需要输入后端 IP");
|
|
5378
5460
|
const answer = await inquirer.prompt([{
|
|
5379
5461
|
type: "input",
|
|
5380
5462
|
name: "customIp",
|
|
@@ -5392,12 +5474,12 @@ async function createLocalCommand(options) {
|
|
|
5392
5474
|
}
|
|
5393
5475
|
}]);
|
|
5394
5476
|
customIp = answer.customIp;
|
|
5395
|
-
|
|
5477
|
+
debug.log("[初始化] 获取自定义 customIp");
|
|
5396
5478
|
} else if (env === "custom" && customIp) {
|
|
5397
|
-
|
|
5479
|
+
debug.log(`[初始化] 使用命令行参数: customIp=${customIp}`);
|
|
5398
5480
|
}
|
|
5399
|
-
|
|
5400
|
-
|
|
5481
|
+
debug.log(`[初始化] 最终参数: env=${env}, phone=${phone}, customIp=${customIp}, pluginVersion=${options.pluginVersion}, openclawPath=${options.openclawPath}`);
|
|
5482
|
+
debug.log("[初始化] 创建 LocalInstaller 实例...");
|
|
5401
5483
|
const installer = new LocalInstaller({
|
|
5402
5484
|
phone,
|
|
5403
5485
|
userPass,
|
|
@@ -5407,9 +5489,9 @@ async function createLocalCommand(options) {
|
|
|
5407
5489
|
pluginVersion: options.pluginVersion,
|
|
5408
5490
|
openclawPath: options.openclawPath
|
|
5409
5491
|
});
|
|
5410
|
-
|
|
5492
|
+
debug.log("[初始化] 开始安装...");
|
|
5411
5493
|
await installer.install();
|
|
5412
|
-
|
|
5494
|
+
debug.log("[初始化] 安装完成");
|
|
5413
5495
|
console.log(installer.getPrefixText() + boxen(
|
|
5414
5496
|
`${chalk.green("✦")} 插件初始化成功!`,
|
|
5415
5497
|
{
|
|
@@ -5422,8 +5504,13 @@ async function createLocalCommand(options) {
|
|
|
5422
5504
|
textAlignment: "center"
|
|
5423
5505
|
}
|
|
5424
5506
|
));
|
|
5507
|
+
if (options.printToken) {
|
|
5508
|
+
const token = installer.getToken();
|
|
5509
|
+
console.log(`
|
|
5510
|
+
${chalk.cyan("Token: ")}${token}`);
|
|
5511
|
+
}
|
|
5425
5512
|
} catch (error) {
|
|
5426
|
-
|
|
5513
|
+
debug.log(`[初始化] 发生错误: ${error.message}`);
|
|
5427
5514
|
console.error(boxen(
|
|
5428
5515
|
`${chalk.yellow(error.message)}`,
|
|
5429
5516
|
{
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export declare class LocalInstaller {
|
|
|
8
8
|
private prefixText;
|
|
9
9
|
private readonly config;
|
|
10
10
|
private readonly envConfig;
|
|
11
|
+
private token;
|
|
11
12
|
/**
|
|
12
13
|
* 构造函数
|
|
13
14
|
*/
|
|
@@ -24,6 +25,10 @@ export declare class LocalInstaller {
|
|
|
24
25
|
* 获取前缀文本(用于显示安装进度)
|
|
25
26
|
*/
|
|
26
27
|
getPrefixText(): string;
|
|
28
|
+
/**
|
|
29
|
+
* 获取登录 token
|
|
30
|
+
*/
|
|
31
|
+
getToken(): string;
|
|
27
32
|
/**
|
|
28
33
|
* 获取完整的 npm 包名(包含版本号)
|
|
29
34
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../../src/local/installer/installer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA6B,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAgF/E;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;
|
|
1
|
+
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../../src/local/installer/installer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA6B,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAgF/E;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAC3C,OAAO,CAAC,KAAK,CAAa;IAE1B;;OAEG;gBACS,MAAM,EAAE,oBAAoB;IAOxC;;OAEG;IACH,cAAc,IAAI,IAAI;IAUtB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgE9B;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,OAAO,CAAC,cAAc;IAOtB;;OAEG;YACW,OAAO;IAoBrB;;OAEG;YACW,kBAAkB;IAchC;;OAEG;YACW,eAAe;IAmB7B;;OAEG;YACW,iBAAiB;IAoH/B;;OAEG;YACW,cAAc;CAmN7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../../src/local/src/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAQ5C;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../../src/local/src/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAQ5C;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA+J7E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/local/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAWvB,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/local/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAWvB,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;iBAmBtC,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAA;AAE7E;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;iBAO5B,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAE3D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Debug } from '@mingto/debug';
|
|
2
|
+
declare const debug: Debug;
|
|
1
3
|
/**
|
|
2
4
|
* 设置调试模式
|
|
3
5
|
*/
|
|
@@ -6,8 +8,5 @@ export declare function setDebug(enabled: boolean): void;
|
|
|
6
8
|
* 检查是否为调试模式
|
|
7
9
|
*/
|
|
8
10
|
export declare function isDebugMode(): boolean;
|
|
9
|
-
|
|
10
|
-
* 输出调试日志
|
|
11
|
-
*/
|
|
12
|
-
export declare function debugLog(...args: unknown[]): void;
|
|
11
|
+
export { debug };
|
|
13
12
|
//# sourceMappingURL=debug.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../../../src/shared/utils/debug.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../../../src/shared/utils/debug.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAErC,QAAA,MAAM,KAAK,OAAc,CAAA;AAEzB;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED,OAAO,EAAE,KAAK,EAAE,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { debug, isDebugMode, setDebug } from './debug';
|
|
2
2
|
export { checkEnv } from './env';
|
|
3
3
|
export { createLogger, logger } from './logger';
|
|
4
4
|
export type { Logger } from './logger';
|
|
5
|
-
export { validateOpenclawPath } from './path';
|
|
5
|
+
export { normalizePath, validateOpenclawPath } from './path';
|
|
6
6
|
export { absolutePathSchema, appKeySchema, appSecretSchema, booleanSchema, EnvironmentSchema, ipv4Schema, isValid, openclawPathSchema, phoneSchema, pluginVersionSchema, userPassSchema, validate, validateWithMessage, windowsPathSchema, withMessage, wsUrlSchema, } from './validate';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAC/C,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAA;AAC5D,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,OAAO,EACP,kBAAkB,EAClB,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,QAAQ,EACR,mBAAmB,EACnB,iBAAiB,EACjB,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../src/shared/utils/path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../src/shared/utils/path.ts"],"names":[],"mappings":"AAcA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAU/D"}
|
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.326",
|
|
5
5
|
"description": "WorkClaw CLI 工具 - 用于初始化和配置 WorkClaw 插件",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"build": "vite build"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@mingto/debug": "^1.0.
|
|
29
|
+
"@mingto/debug": "^1.0.27",
|
|
30
30
|
"@types/semver": "^7.7.0",
|
|
31
31
|
"@types/tar": "^6.1.13",
|
|
32
32
|
"axios": "^1.7.9",
|
|
@@ -40,4 +40,4 @@
|
|
|
40
40
|
"tar": "^6.2.1",
|
|
41
41
|
"zod": "^4.3.6"
|
|
42
42
|
}
|
|
43
|
-
}
|
|
43
|
+
}
|