codewave-openclaw-installer 2.1.9 → 2.1.11
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/bin/check.mjs +1 -1
- package/bin/install-lib.mjs +155 -16
- package/bin/install.mjs +192 -32
- package/package.json +2 -1
package/bin/check.mjs
CHANGED
|
@@ -433,7 +433,7 @@ async function main() {
|
|
|
433
433
|
|
|
434
434
|
// CLI 工具
|
|
435
435
|
checks['razel-py-cli'] = await checkCli('razel-py-cli', {
|
|
436
|
-
installCmd: '
|
|
436
|
+
installCmd: 'pipx install razel-py-cli && razel-py-cli init',
|
|
437
437
|
});
|
|
438
438
|
checks['razel-cli'] = await checkCli('razel-cli', {
|
|
439
439
|
installCmd: 'npm install -g @lcap-delivery/razel-cli@latest && razel-cli init',
|
package/bin/install-lib.mjs
CHANGED
|
@@ -198,6 +198,131 @@ export function classifyPythonInstallFailure(stderr = '') {
|
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
export function getCoreBootstrapPlan(platform = process.platform) {
|
|
202
|
+
if (platform === 'win32') {
|
|
203
|
+
const wingetArgs = (packageId) => [
|
|
204
|
+
'install',
|
|
205
|
+
'-e',
|
|
206
|
+
'--id',
|
|
207
|
+
packageId,
|
|
208
|
+
'--accept-package-agreements',
|
|
209
|
+
'--accept-source-agreements',
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
return [
|
|
213
|
+
{
|
|
214
|
+
key: 'nodejs',
|
|
215
|
+
optional: false,
|
|
216
|
+
checkCommands: ['node', 'npm'],
|
|
217
|
+
resultKeys: ['node', 'npm'],
|
|
218
|
+
install: { cmd: 'winget', args: wingetArgs('OpenJS.NodeJS.LTS') },
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
key: 'python3',
|
|
222
|
+
optional: false,
|
|
223
|
+
checkCommands: ['python3', 'python', 'py'],
|
|
224
|
+
resultKeys: ['python3', 'pip'],
|
|
225
|
+
install: { cmd: 'winget', args: wingetArgs('Python.Python.3.13') },
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
key: 'jq',
|
|
229
|
+
optional: false,
|
|
230
|
+
checkCommands: ['jq'],
|
|
231
|
+
resultKeys: ['jq'],
|
|
232
|
+
install: { cmd: 'winget', args: wingetArgs('jqlang.jq') },
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
key: 'pandoc',
|
|
236
|
+
optional: true,
|
|
237
|
+
checkCommands: ['pandoc'],
|
|
238
|
+
resultKeys: ['pandoc'],
|
|
239
|
+
install: { cmd: 'winget', args: wingetArgs('JohnMacFarlane.Pandoc') },
|
|
240
|
+
},
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (platform === 'darwin') {
|
|
245
|
+
const brewArgs = (formula) => ['install', formula];
|
|
246
|
+
return [
|
|
247
|
+
{
|
|
248
|
+
key: 'nodejs',
|
|
249
|
+
optional: false,
|
|
250
|
+
checkCommands: ['node', 'npm'],
|
|
251
|
+
resultKeys: ['node', 'npm'],
|
|
252
|
+
install: { cmd: 'brew', args: brewArgs('node') },
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
key: 'python3',
|
|
256
|
+
optional: false,
|
|
257
|
+
checkCommands: ['python3'],
|
|
258
|
+
resultKeys: ['python3', 'pip'],
|
|
259
|
+
install: { cmd: 'brew', args: brewArgs('python') },
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
key: 'pkg-config',
|
|
263
|
+
optional: false,
|
|
264
|
+
checkCommands: ['pkg-config'],
|
|
265
|
+
resultKeys: ['pkg-config'],
|
|
266
|
+
install: { cmd: 'brew', args: brewArgs('pkg-config') },
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
key: 'cairo',
|
|
270
|
+
optional: false,
|
|
271
|
+
checkCommands: ['pkg-config'],
|
|
272
|
+
verifyArgs: ['--exists', 'cairo'],
|
|
273
|
+
resultKeys: ['cairo'],
|
|
274
|
+
install: { cmd: 'brew', args: brewArgs('cairo') },
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
key: 'jq',
|
|
278
|
+
optional: false,
|
|
279
|
+
checkCommands: ['jq'],
|
|
280
|
+
resultKeys: ['jq'],
|
|
281
|
+
install: { cmd: 'brew', args: brewArgs('jq') },
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
key: 'pandoc',
|
|
285
|
+
optional: true,
|
|
286
|
+
checkCommands: ['pandoc'],
|
|
287
|
+
resultKeys: ['pandoc'],
|
|
288
|
+
install: { cmd: 'brew', args: brewArgs('pandoc') },
|
|
289
|
+
},
|
|
290
|
+
];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function getPipxBootstrapPlan({
|
|
297
|
+
platform = process.platform,
|
|
298
|
+
hasBrew = false,
|
|
299
|
+
pythonCommand = 'python3',
|
|
300
|
+
pythonLauncher = null,
|
|
301
|
+
} = {}) {
|
|
302
|
+
if (platform === 'win32') {
|
|
303
|
+
const launcher = pythonLauncher || pythonCommand || 'py';
|
|
304
|
+
return {
|
|
305
|
+
install: { cmd: launcher, args: ['-m', 'pip', 'install', '--user', 'pipx'] },
|
|
306
|
+
ensurePath: { cmd: launcher, args: ['-m', 'pipx', 'ensurepath'] },
|
|
307
|
+
invoke: { cmd: launcher, args: ['-m', 'pipx'] },
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (platform === 'darwin' && hasBrew) {
|
|
312
|
+
return {
|
|
313
|
+
install: { cmd: 'brew', args: ['install', 'pipx'] },
|
|
314
|
+
ensurePath: { cmd: 'pipx', args: ['ensurepath'] },
|
|
315
|
+
invoke: { cmd: 'pipx', args: [] },
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
install: { cmd: pythonCommand, args: ['-m', 'pip', 'install', '--user', 'pipx'] },
|
|
321
|
+
ensurePath: { cmd: pythonCommand, args: ['-m', 'pipx', 'ensurepath'] },
|
|
322
|
+
invoke: { cmd: pythonCommand, args: ['-m', 'pipx'] },
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
201
326
|
function joinForPlatform(platform, root, candidate) {
|
|
202
327
|
if (platform === 'win32') {
|
|
203
328
|
return path.win32.join(root, candidate);
|
|
@@ -288,6 +413,10 @@ export function buildRazelConfigBootstrap({
|
|
|
288
413
|
razelConfig = {},
|
|
289
414
|
env = process.env,
|
|
290
415
|
hasOpenclawLlm = false,
|
|
416
|
+
availableCommands = {
|
|
417
|
+
'razel-cli': true,
|
|
418
|
+
'razel-py-cli': true,
|
|
419
|
+
},
|
|
291
420
|
} = {}) {
|
|
292
421
|
const writes = [];
|
|
293
422
|
const missing = [];
|
|
@@ -298,6 +427,7 @@ export function buildRazelConfigBootstrap({
|
|
|
298
427
|
}
|
|
299
428
|
writes.push({ cli, key, value });
|
|
300
429
|
};
|
|
430
|
+
const hasCommand = (cli) => availableCommands[cli] !== false;
|
|
301
431
|
|
|
302
432
|
queueWrite('razel-cli', 'popo_app_id', env.POPO_APP_ID);
|
|
303
433
|
queueWrite('razel-cli', 'popo_app_secret', env.POPO_APP_SECRET);
|
|
@@ -311,11 +441,14 @@ export function buildRazelConfigBootstrap({
|
|
|
311
441
|
if (!hasPopoAfterBootstrap) {
|
|
312
442
|
missing.push({
|
|
313
443
|
group: 'popo',
|
|
314
|
-
title: 'PoPo 认证缺失',
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
444
|
+
title: hasCommand('razel-cli') ? 'PoPo 认证缺失' : 'PoPo 认证缺失(当前未安装 razel-cli)',
|
|
445
|
+
interactive: hasCommand('razel-cli'),
|
|
446
|
+
commands: hasCommand('razel-cli')
|
|
447
|
+
? [
|
|
448
|
+
'razel-cli config set popo_app_id <your-app-id>',
|
|
449
|
+
'razel-cli config set popo_app_secret <your-app-secret>',
|
|
450
|
+
]
|
|
451
|
+
: [],
|
|
319
452
|
});
|
|
320
453
|
}
|
|
321
454
|
|
|
@@ -329,11 +462,14 @@ export function buildRazelConfigBootstrap({
|
|
|
329
462
|
if (!hasLlmAfterBootstrap) {
|
|
330
463
|
missing.push({
|
|
331
464
|
group: 'llm',
|
|
332
|
-
title: 'LLM 配置缺失',
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
465
|
+
title: hasCommand('razel-py-cli') ? 'LLM 配置缺失' : 'LLM 配置缺失(当前未安装 razel-py-cli)',
|
|
466
|
+
interactive: hasCommand('razel-py-cli'),
|
|
467
|
+
commands: hasCommand('razel-py-cli')
|
|
468
|
+
? [
|
|
469
|
+
'razel-py-cli config set llm_base_url <your-llm-base-url>',
|
|
470
|
+
'razel-py-cli config set llm_api_key <your-llm-api-key>',
|
|
471
|
+
]
|
|
472
|
+
: [],
|
|
337
473
|
});
|
|
338
474
|
}
|
|
339
475
|
|
|
@@ -350,12 +486,15 @@ export function buildRazelConfigBootstrap({
|
|
|
350
486
|
if (!hasAsrAfterBootstrap) {
|
|
351
487
|
missing.push({
|
|
352
488
|
group: 'asr',
|
|
353
|
-
title: 'ASR Provider 缺失',
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
489
|
+
title: hasCommand('razel-py-cli') ? 'ASR Provider 缺失' : 'ASR Provider 缺失(当前未安装 razel-py-cli)',
|
|
490
|
+
interactive: hasCommand('razel-py-cli'),
|
|
491
|
+
commands: hasCommand('razel-py-cli')
|
|
492
|
+
? [
|
|
493
|
+
'razel-py-cli config set dashscope_api_key <your-dashscope-key>',
|
|
494
|
+
'razel-py-cli config set aigw_base_url <your-aigw-base-url>',
|
|
495
|
+
'razel-py-cli config set aigw_api_key <your-aigw-api-key>',
|
|
496
|
+
]
|
|
497
|
+
: [],
|
|
359
498
|
});
|
|
360
499
|
}
|
|
361
500
|
|
package/bin/install.mjs
CHANGED
|
@@ -16,7 +16,9 @@ import {
|
|
|
16
16
|
createInstallContext,
|
|
17
17
|
detectDesktopAppInstallMatches,
|
|
18
18
|
evaluateChannelDetection,
|
|
19
|
+
getCoreBootstrapPlan,
|
|
19
20
|
getDesktopAppCandidates,
|
|
21
|
+
getPipxBootstrapPlan,
|
|
20
22
|
resolvePromptDefault,
|
|
21
23
|
shouldDryRun,
|
|
22
24
|
} from './install-lib.mjs';
|
|
@@ -34,10 +36,25 @@ const dim = (s) => `\x1b[97m${s}\x1b[0m`;
|
|
|
34
36
|
|
|
35
37
|
const isWin = process.platform === 'win32';
|
|
36
38
|
const NPM = 'npm';
|
|
37
|
-
const PIP = isWin ? 'pip' : 'pip3';
|
|
38
39
|
const DRY_RUN = shouldDryRun(process.argv);
|
|
39
40
|
const OPENCLAW_CONFIG_PATH = join(homedir(), '.openclaw', 'openclaw.json');
|
|
40
41
|
|
|
42
|
+
function prependPathOnce(candidate) {
|
|
43
|
+
if (!candidate) return;
|
|
44
|
+
const delimiter = isWin ? ';' : ':';
|
|
45
|
+
const segments = (process.env.PATH || '').split(delimiter).filter(Boolean);
|
|
46
|
+
if (segments.includes(candidate)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
process.env.PATH = [candidate, ...segments].join(delimiter);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function refreshLocalToolPaths() {
|
|
53
|
+
prependPathOnce(isWin
|
|
54
|
+
? path.win32.join(homedir(), '.local', 'bin')
|
|
55
|
+
: path.posix.join(homedir(), '.local', 'bin'));
|
|
56
|
+
}
|
|
57
|
+
|
|
41
58
|
function resolveExecutable(cmd) {
|
|
42
59
|
if (!isWin) {
|
|
43
60
|
return cmd;
|
|
@@ -344,24 +361,155 @@ async function ensureCommand(command, installArgs, resultKey, context, initArgs
|
|
|
344
361
|
contextResult(context, resultKey, 'ok');
|
|
345
362
|
}
|
|
346
363
|
|
|
347
|
-
async function
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
364
|
+
async function commandExistsAny(commands = []) {
|
|
365
|
+
for (const command of commands) {
|
|
366
|
+
if (await commandExists(command)) {
|
|
367
|
+
return command;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async function installCoreDependency(spec, context) {
|
|
374
|
+
const verifySpec = async () => {
|
|
375
|
+
if (spec.verifyArgs?.length) {
|
|
376
|
+
if (!(await commandExists(spec.checkCommands[0]))) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
return (await run(spec.checkCommands[0], spec.verifyArgs, { stdio: 'ignore' })) === 0;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const present = [];
|
|
383
|
+
for (const command of spec.checkCommands) {
|
|
384
|
+
if (await commandExists(command)) {
|
|
385
|
+
present.push(command);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return spec.key === 'python3' ? present.length > 0 : present.length === spec.checkCommands.length;
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const hasDependency = await verifySpec();
|
|
392
|
+
if (hasDependency) {
|
|
393
|
+
for (const resultKey of spec.resultKeys) {
|
|
394
|
+
contextResult(context, resultKey, 'reused');
|
|
395
|
+
}
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (!(await commandExists(spec.install.cmd))) {
|
|
400
|
+
const status = spec.optional ? `optional missing (${spec.install.cmd} missing)` : `blocked (${spec.install.cmd} missing)`;
|
|
401
|
+
console.log(yellow(` ⚠️ 缺少 ${spec.install.cmd},无法自动安装 ${spec.key}`));
|
|
402
|
+
for (const resultKey of spec.resultKeys) {
|
|
403
|
+
contextResult(context, resultKey, status);
|
|
404
|
+
}
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
console.log(dim(` 正在安装 ${spec.key}...`));
|
|
409
|
+
const installCode = await run(spec.install.cmd, spec.install.args);
|
|
410
|
+
if (installCode !== 0) {
|
|
411
|
+
const status = spec.optional ? 'optional install failed' : 'failed';
|
|
412
|
+
console.log(yellow(` ⚠️ ${spec.key} 自动安装失败`));
|
|
413
|
+
for (const resultKey of spec.resultKeys) {
|
|
414
|
+
contextResult(context, resultKey, status);
|
|
415
|
+
}
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const installOk = await verifySpec();
|
|
420
|
+
const status = installOk ? 'ok' : (spec.optional ? 'optional verify failed' : 'installed (verify failed)');
|
|
421
|
+
if (installOk) {
|
|
422
|
+
console.log(green(` ✅ ${spec.key} 安装成功`));
|
|
423
|
+
} else {
|
|
424
|
+
console.log(yellow(` ⚠️ ${spec.key} 安装后未通过命令校验`));
|
|
360
425
|
}
|
|
426
|
+
for (const resultKey of spec.resultKeys) {
|
|
427
|
+
contextResult(context, resultKey, status);
|
|
428
|
+
}
|
|
429
|
+
return installOk;
|
|
430
|
+
}
|
|
361
431
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
432
|
+
async function resolvePythonRuntime() {
|
|
433
|
+
const python313 = await commandExists('python3.13');
|
|
434
|
+
if (!isWin && python313) {
|
|
435
|
+
return { pythonCommand: 'python3.13', pythonLauncher: null, pipCommand: 'pip3' };
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const python3 = await commandExists('python3');
|
|
439
|
+
const python = await commandExists('python');
|
|
440
|
+
const py = await commandExists('py');
|
|
441
|
+
const pip3 = await commandExists('pip3');
|
|
442
|
+
const pip = await commandExists('pip');
|
|
443
|
+
|
|
444
|
+
if (isWin) {
|
|
445
|
+
return {
|
|
446
|
+
pythonCommand: python || python3 ? (python ? 'python' : 'python3') : (py ? 'py' : null),
|
|
447
|
+
pythonLauncher: py ? 'py' : null,
|
|
448
|
+
pipCommand: pip ? 'pip' : (pip3 ? 'pip3' : null),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
pythonCommand: python3 ? 'python3' : (python ? 'python' : null),
|
|
454
|
+
pythonLauncher: null,
|
|
455
|
+
pipCommand: pip3 ? 'pip3' : (pip ? 'pip' : null),
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async function ensurePipx(context) {
|
|
460
|
+
if (await commandExists('pipx')) {
|
|
461
|
+
contextResult(context, 'pipx', 'reused');
|
|
462
|
+
return {
|
|
463
|
+
available: true,
|
|
464
|
+
invoke: { cmd: 'pipx', args: [] },
|
|
465
|
+
pythonCommand: (await resolvePythonRuntime()).pythonCommand,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const runtime = await resolvePythonRuntime();
|
|
470
|
+
if (!runtime.pythonCommand && !runtime.pythonLauncher) {
|
|
471
|
+
console.log(yellow(' ⚠️ 缺少 Python 运行时,无法安装 pipx'));
|
|
472
|
+
contextResult(context, 'pipx', 'blocked (missing python3)');
|
|
473
|
+
return { available: false, invoke: null, pythonCommand: null };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const pipxPlan = getPipxBootstrapPlan({
|
|
477
|
+
platform: process.platform,
|
|
478
|
+
hasBrew: await commandExists('brew'),
|
|
479
|
+
pythonCommand: runtime.pythonCommand || 'python3',
|
|
480
|
+
pythonLauncher: runtime.pythonLauncher,
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
if (!(await commandExists(pipxPlan.install.cmd))) {
|
|
484
|
+
console.log(yellow(` ⚠️ 缺少 ${pipxPlan.install.cmd},无法自动安装 pipx`));
|
|
485
|
+
contextResult(context, 'pipx', `blocked (${pipxPlan.install.cmd} missing)`);
|
|
486
|
+
return { available: false, invoke: null, pythonCommand: runtime.pythonCommand };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
console.log(dim(' 正在安装 pipx...'));
|
|
490
|
+
const installCode = await run(pipxPlan.install.cmd, pipxPlan.install.args);
|
|
491
|
+
if (installCode !== 0) {
|
|
492
|
+
if (runtime.pipCommand) {
|
|
493
|
+
const classification = classifyPythonInstallFailure('externally-managed-environment');
|
|
494
|
+
console.log(dim(` 提示: ${classification.message}`));
|
|
495
|
+
}
|
|
496
|
+
contextResult(context, 'pipx', 'failed');
|
|
497
|
+
return { available: false, invoke: null, pythonCommand: runtime.pythonCommand };
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
await run(pipxPlan.ensurePath.cmd, pipxPlan.ensurePath.args);
|
|
501
|
+
refreshLocalToolPaths();
|
|
502
|
+
const pipxAvailable = await commandExists('pipx');
|
|
503
|
+
contextResult(context, 'pipx', pipxAvailable ? 'ok' : 'ok (path refresh may be needed)');
|
|
504
|
+
return {
|
|
505
|
+
available: true,
|
|
506
|
+
invoke: pipxAvailable ? { cmd: 'pipx', args: [] } : pipxPlan.invoke,
|
|
507
|
+
pythonCommand: runtime.pythonCommand,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async function installPythonCli(context) {
|
|
512
|
+
const pipxState = await ensurePipx(context);
|
|
365
513
|
|
|
366
514
|
if (await commandExists('razel-py-cli')) {
|
|
367
515
|
console.log(dim(' ↺ 已检测到 razel-py-cli,跳过安装'));
|
|
@@ -369,33 +517,32 @@ async function installPythonCli(context) {
|
|
|
369
517
|
return;
|
|
370
518
|
}
|
|
371
519
|
|
|
372
|
-
if (!
|
|
373
|
-
console.log(yellow(' ⚠️ 未检测到 pipx
|
|
374
|
-
if (hasPip) {
|
|
375
|
-
const classification = classifyPythonInstallFailure('externally-managed-environment');
|
|
376
|
-
console.log(dim(` 提示: ${classification.message}`));
|
|
377
|
-
}
|
|
520
|
+
if (!pipxState.available || !pipxState.invoke) {
|
|
521
|
+
console.log(yellow(' ⚠️ 未检测到 pipx,无法继续安装 razel-py-cli'));
|
|
378
522
|
contextResult(context, 'razel-py-cli', 'blocked (pipx missing)');
|
|
379
523
|
return;
|
|
380
524
|
}
|
|
381
525
|
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
384
|
-
|
|
385
|
-
} else {
|
|
386
|
-
console.log(dim(' 未检测到 python3.13,pipx 将使用默认 python3 安装 razel-py-cli'));
|
|
526
|
+
const pipxArgs = [...pipxState.invoke.args, 'install'];
|
|
527
|
+
if (pipxState.pythonCommand) {
|
|
528
|
+
pipxArgs.push('--python', pipxState.pythonCommand);
|
|
387
529
|
}
|
|
530
|
+
pipxArgs.push('razel-py-cli');
|
|
388
531
|
|
|
389
|
-
const pipxCode = await run(
|
|
532
|
+
const pipxCode = await run(pipxState.invoke.cmd, pipxArgs);
|
|
390
533
|
if (pipxCode !== 0) {
|
|
391
|
-
console.log(yellow(` ⚠️ razel-py-cli 通过 pipx
|
|
534
|
+
console.log(yellow(` ⚠️ razel-py-cli 通过 pipx 安装失败${pipxState.pythonCommand ? ` (python=${pipxState.pythonCommand})` : ''}`));
|
|
392
535
|
contextResult(context, 'razel-py-cli', 'failed (pipx)');
|
|
393
536
|
return;
|
|
394
537
|
}
|
|
395
538
|
|
|
396
539
|
console.log(green(' ✅ razel-py-cli 通过 pipx 安装成功'));
|
|
540
|
+
refreshLocalToolPaths();
|
|
397
541
|
console.log(dim(' 正在初始化...'));
|
|
398
|
-
|
|
542
|
+
let initCode = await run('razel-py-cli', ['init']);
|
|
543
|
+
if (initCode !== 0 && pipxState.invoke) {
|
|
544
|
+
initCode = await run(pipxState.invoke.cmd, [...pipxState.invoke.args, 'run', 'razel-py-cli', 'init']);
|
|
545
|
+
}
|
|
399
546
|
contextResult(context, 'razel-py-cli', initCode === 0 ? 'ok' : 'installed via pipx (init failed)');
|
|
400
547
|
}
|
|
401
548
|
|
|
@@ -426,6 +573,10 @@ function printConfigGuidance(missing) {
|
|
|
426
573
|
console.log(yellow(' 仍有配置项缺失,可直接使用以下命令继续补齐:'));
|
|
427
574
|
for (const item of missing) {
|
|
428
575
|
console.log(dim(` • ${item.title}`));
|
|
576
|
+
if (item.commands.length === 0) {
|
|
577
|
+
console.log(dim(' 当前缺少对应 CLI,暂时无法在配置向导中写入;请先完成核心依赖安装。'));
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
429
580
|
for (const command of item.commands) {
|
|
430
581
|
console.log(cyan(` ${command}`));
|
|
431
582
|
}
|
|
@@ -477,7 +628,7 @@ async function collectInteractiveConfigWrites(missing, options = {}) {
|
|
|
477
628
|
};
|
|
478
629
|
|
|
479
630
|
const writes = [];
|
|
480
|
-
const hasGroup = (group) => missing.some((item) => item.group === group);
|
|
631
|
+
const hasGroup = (group) => missing.some((item) => item.group === group && item.interactive !== false);
|
|
481
632
|
|
|
482
633
|
console.log(dim(' 检测到关键配置缺失,可现在交互配置(支持跳过)'));
|
|
483
634
|
const shouldConfigure = await askYesNo(' 现在进入配置向导吗?[y/N] ', true);
|
|
@@ -828,6 +979,10 @@ async function runPreflight(context) {
|
|
|
828
979
|
}
|
|
829
980
|
|
|
830
981
|
async function runCoreInstall(context) {
|
|
982
|
+
const corePlan = getCoreBootstrapPlan(process.platform);
|
|
983
|
+
for (const spec of corePlan) {
|
|
984
|
+
await installCoreDependency(spec, context);
|
|
985
|
+
}
|
|
831
986
|
await installPythonCli(context);
|
|
832
987
|
await ensureCommand('razel-cli', [NPM, 'install', '-g', '@lcap-delivery/razel-cli@latest'], 'razel-cli', context, ['init']);
|
|
833
988
|
}
|
|
@@ -836,10 +991,15 @@ async function runConfigBootstrap(context) {
|
|
|
836
991
|
const razelConfigPath = join(homedir(), '.razel', 'config.json');
|
|
837
992
|
const razelConfig = readJson(razelConfigPath) || {};
|
|
838
993
|
const openclawConfig = readJson(join(homedir(), '.openclaw', 'openclaw.json'));
|
|
994
|
+
const availableCommands = {
|
|
995
|
+
'razel-cli': await commandExists('razel-cli'),
|
|
996
|
+
'razel-py-cli': await commandExists('razel-py-cli'),
|
|
997
|
+
};
|
|
839
998
|
const plan = buildRazelConfigBootstrap({
|
|
840
999
|
razelConfig,
|
|
841
1000
|
env: process.env,
|
|
842
1001
|
hasOpenclawLlm: Boolean(openclawConfig?.providers || openclawConfig?.model || openclawConfig?.models),
|
|
1002
|
+
availableCommands,
|
|
843
1003
|
});
|
|
844
1004
|
|
|
845
1005
|
if (plan.writes.length === 0 && plan.missing.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codewave-openclaw-installer",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.11",
|
|
4
4
|
"description": "网易智企 CodeWave OpenClaw 扩展:Skills + CLI 依赖一键安装",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
8
|
+
"codewave-openclaw-installer": "bin/install.mjs",
|
|
8
9
|
"codewave-install": "bin/install.mjs",
|
|
9
10
|
"codewave-check": "bin/check.mjs"
|
|
10
11
|
},
|