huaweicloud-devkit 0.1.26-dev.0 → 1.0.1
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 +70 -104
- package/README.zh-CN.md +138 -0
- package/package.json +3 -2
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +43 -43
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +42 -42
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +43 -43
- package/plugins/huaweicloud-core/.mcp.json +3 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +44 -0
- package/plugins/huaweicloud-core/safety/policy.json +15 -1
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +21 -0
- package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +22 -2
- package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +17 -0
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +2 -2
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +51 -1
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +23 -3
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +2 -2
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +21 -3
- package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +40 -0
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +48 -5
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +134 -0
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +5 -2
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +7 -6
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +3 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +1 -1
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +87 -0
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +95 -0
- package/plugins/huaweicloud-core/src/auth/service.mjs +63 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +11 -0
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +87 -0
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +153 -0
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +105 -0
- package/plugins/huaweicloud-core/src/setup-cli.mjs +733 -74
- package/plugins/huaweicloud-core/src/tools.mjs +164 -3
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +427 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +132 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +227 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +202 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +158 -0
- package/plugins/huaweicloud-core/src/ws-exec/index.js +19 -0
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +338 -0
|
@@ -6,6 +6,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
import { homedir, platform } from 'node:os';
|
|
7
7
|
import { createInterface } from 'node:readline';
|
|
8
8
|
import { spawnSync } from 'node:child_process';
|
|
9
|
+
import { getAuthStatus, syncAuth } from './auth/service.mjs';
|
|
10
|
+
import { globalCredentialsPath, readGlobalCredentials, writeGlobalCredentials, writeObsConfig } from './auth/credentials.mjs';
|
|
9
11
|
|
|
10
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
13
|
const PLUGIN_ROOT = resolve(__dirname, '..');
|
|
@@ -19,7 +21,7 @@ try {
|
|
|
19
21
|
const BANNER = `
|
|
20
22
|
╔══════════════════════════════════════════════╗
|
|
21
23
|
║ HuaweiCloud DevKit v${pkgVersion}${' '.repeat(Math.max(0, 22 - String(pkgVersion).length))}║
|
|
22
|
-
║ https://github.com/huaweicloud
|
|
24
|
+
║ https://github.com/huaweicloud ║
|
|
23
25
|
╚══════════════════════════════════════════════╝
|
|
24
26
|
`;
|
|
25
27
|
|
|
@@ -40,7 +42,7 @@ function opencodeConfigFile() {
|
|
|
40
42
|
function codexDesktopSkillsDir() { return join(homedir(), '.agents', 'skills'); }
|
|
41
43
|
function codexDesktopCommandsDir() { return join(homedir(), '.agents', 'commands'); }
|
|
42
44
|
function codexDesktopPluginsDir() { return join(homedir(), '.agents', 'huaweicloud-plugins'); }
|
|
43
|
-
function
|
|
45
|
+
function codexConfigToml() { return join(homedir(), '.codex', 'config.toml'); }
|
|
44
46
|
|
|
45
47
|
function codeartsSkillsDir() { return join(homedir(), '.codeartsdoer', 'skills'); }
|
|
46
48
|
function codeartsMcpSettingsDir() { return join(homedir(), '.codeartsdoer', 'mcp'); }
|
|
@@ -50,6 +52,10 @@ function codeartsProjectSkillsDir() { return join(codeartsProjectDir(), 'skills'
|
|
|
50
52
|
function codeartsProjectMcpSettingsFile() { return join(codeartsProjectDir(), 'mcp', 'mcp_settings.json'); }
|
|
51
53
|
function codeartsPluginsDir() { return join(homedir(), '.codeartsdoer', 'huaweicloud-plugins'); }
|
|
52
54
|
|
|
55
|
+
function workbuddySkillsDir() { return join(homedir(), '.workbuddy', 'skills'); }
|
|
56
|
+
function workbuddyMcpConfigFile() { return join(homedir(), '.workbuddy', 'mcp.json'); }
|
|
57
|
+
function workbuddyPluginsDir() { return join(homedir(), '.workbuddy', 'huaweicloud-plugins'); }
|
|
58
|
+
|
|
53
59
|
// Detect CodeArts sandbox mode (bash_mode in permission config).
|
|
54
60
|
function detectCodeartsSandbox() {
|
|
55
61
|
try {
|
|
@@ -110,19 +116,34 @@ function copyDir(src, dest) {
|
|
|
110
116
|
|
|
111
117
|
function removeIfExists(p) {
|
|
112
118
|
if (existsSync(p)) {
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
try {
|
|
120
|
+
rmSync(p, { recursive: true, force: true });
|
|
121
|
+
return true;
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.log(` \x1b[33m[WARN]\x1b[0m Could not remove ${p}: ${e.message}`);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
115
126
|
}
|
|
116
127
|
return false;
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
function updateOpenCodeConfig(pluginDir) {
|
|
120
131
|
const configPath = opencodeConfigFile();
|
|
132
|
+
const mcpPath = join(pluginDir, 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
121
133
|
let config = {};
|
|
122
134
|
if (existsSync(configPath)) {
|
|
123
|
-
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {
|
|
135
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {
|
|
136
|
+
console.log(` \x1b[33m[WARN]\x1b[0m Could not parse ${configPath} (jsonc comments?). Skipping MCP config write; ensure "mcp.huaweicloud-devkit" points to ${mcpPath}.`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const existing = config.mcp?.['huaweicloud-devkit'];
|
|
140
|
+
if (existing && existing.type === 'local'
|
|
141
|
+
&& Array.isArray(existing.command) && existing.command[0] === 'node'
|
|
142
|
+
&& existing.command[1] === mcpPath) {
|
|
143
|
+
console.log(` OpenCode MCP config unchanged: ${configPath}`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
124
146
|
}
|
|
125
|
-
const mcpPath = join(pluginDir, 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
126
147
|
config.mcp = config.mcp || {};
|
|
127
148
|
config.mcp['huaweicloud-devkit'] = {
|
|
128
149
|
type: 'local',
|
|
@@ -221,6 +242,14 @@ function uninstallCodex() {
|
|
|
221
242
|
shell: true, windowsHide: true, stdio: 'pipe',
|
|
222
243
|
});
|
|
223
244
|
console.log(` ${r.stdout ? r.stdout.toString().trim() : r.stderr.toString().trim()}`);
|
|
245
|
+
|
|
246
|
+
for (const name of new Set([marketplaceName, 'HuaweiCloud-Devkit'])) {
|
|
247
|
+
console.log(` Removing Codex marketplace: ${name}`);
|
|
248
|
+
const r2 = spawnSync(`codex plugin marketplace remove "${name}"`, [], {
|
|
249
|
+
shell: true, windowsHide: true, stdio: 'pipe',
|
|
250
|
+
});
|
|
251
|
+
console.log(` ${r2.stdout ? r2.stdout.toString().trim() : r2.stderr.toString().trim()}`);
|
|
252
|
+
}
|
|
224
253
|
}
|
|
225
254
|
|
|
226
255
|
function codexStatus() {
|
|
@@ -279,6 +308,101 @@ function uninstallOpenCode() {
|
|
|
279
308
|
removeOpenCodeConfig();
|
|
280
309
|
}
|
|
281
310
|
|
|
311
|
+
// Remove huawei* entries in targetDir that no longer exist in sourceDir (stale files from an older version).
|
|
312
|
+
function pruneStale(targetDir, sourceDir) {
|
|
313
|
+
if (!existsSync(targetDir) || !existsSync(sourceDir)) return 0;
|
|
314
|
+
const sourceNames = new Set(readdirSync(sourceDir));
|
|
315
|
+
let removed = 0;
|
|
316
|
+
for (const entry of readdirSync(targetDir, { withFileTypes: true })) {
|
|
317
|
+
if (!entry.name.startsWith('huawei')) continue;
|
|
318
|
+
if (!sourceNames.has(entry.name)) {
|
|
319
|
+
removeIfExists(join(targetDir, entry.name));
|
|
320
|
+
removed++;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return removed;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
327
|
+
async function updateOpenCode() {
|
|
328
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
329
|
+
const commandsSrc = join(PACKAGE_ROOT, 'integrations', 'opencode', 'commands');
|
|
330
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
331
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
332
|
+
const pluginDest = opencodePluginsDir();
|
|
333
|
+
|
|
334
|
+
copyDir(skillsSrc, opencodeSkillsDir());
|
|
335
|
+
const staleSkills = pruneStale(opencodeSkillsDir(), skillsSrc);
|
|
336
|
+
console.log(` Skills updated -> ${opencodeSkillsDir()}${staleSkills > 0 ? ` (removed ${staleSkills} stale)` : ''}`);
|
|
337
|
+
copyDir(commandsSrc, opencodeCommandsDir());
|
|
338
|
+
const staleCommands = pruneStale(opencodeCommandsDir(), commandsSrc);
|
|
339
|
+
console.log(` Commands updated -> ${opencodeCommandsDir()}${staleCommands > 0 ? ` (removed ${staleCommands} stale)` : ''}`);
|
|
340
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
341
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
342
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
343
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
344
|
+
updateOpenCodeConfig(pluginDest);
|
|
345
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
346
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function codexMcpServerPath() {
|
|
350
|
+
return join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function codexConfigSectionText(mcpPath) {
|
|
354
|
+
return [
|
|
355
|
+
'[mcp_servers.huaweicloud-devkit]',
|
|
356
|
+
'command = "node"',
|
|
357
|
+
`args = ["${mcpPath}"]`,
|
|
358
|
+
'',
|
|
359
|
+
'[mcp_servers.huaweicloud-devkit.env]',
|
|
360
|
+
'HUAWEICLOUD_AGENT_TOOLKIT_MODE = "local"',
|
|
361
|
+
'',
|
|
362
|
+
].join('\n');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Returns true when the config file was written, false when it was already correct.
|
|
366
|
+
function ensureCodexConfigSection(mcpPath) {
|
|
367
|
+
const configPath = codexConfigToml();
|
|
368
|
+
let existing = '';
|
|
369
|
+
if (existsSync(configPath)) {
|
|
370
|
+
try { existing = readFileSync(configPath, 'utf8'); } catch {}
|
|
371
|
+
if (existing.includes('[mcp_servers.huaweicloud-devkit]')) {
|
|
372
|
+
if (existing.includes(`args = ["${mcpPath}"]`)) {
|
|
373
|
+
console.log(` Config unchanged: ${configPath}`);
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
removeCodexConfigSection();
|
|
377
|
+
existing = '';
|
|
378
|
+
if (existsSync(configPath)) {
|
|
379
|
+
try { existing = readFileSync(configPath, 'utf8'); } catch {}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
384
|
+
if (existing && !existing.endsWith('\n')) existing += '\n';
|
|
385
|
+
writeFileSync(configPath, existing + codexConfigSectionText(mcpPath));
|
|
386
|
+
console.log(` Config updated: ${configPath}`);
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function removeCodexConfigSection() {
|
|
391
|
+
const configPath = codexConfigToml();
|
|
392
|
+
if (!existsSync(configPath)) return;
|
|
393
|
+
const lines = readFileSync(configPath, 'utf8').split(/\r?\n/);
|
|
394
|
+
const out = [];
|
|
395
|
+
let skip = false;
|
|
396
|
+
for (const line of lines) {
|
|
397
|
+
if (/^\[mcp_servers\.huaweicloud-devkit(\]|\.)/.test(line)) { skip = true; continue; }
|
|
398
|
+
if (skip && line.startsWith('[')) skip = false;
|
|
399
|
+
if (!skip) out.push(line);
|
|
400
|
+
}
|
|
401
|
+
while (out.length > 0 && out[out.length - 1].trim() === '') out.pop();
|
|
402
|
+
writeFileSync(configPath, out.join('\n') + (out.length > 0 ? '\n' : ''));
|
|
403
|
+
console.log(' Config cleaned');
|
|
404
|
+
}
|
|
405
|
+
|
|
282
406
|
async function installCodexDesktop() {
|
|
283
407
|
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
284
408
|
const commandsSrc = join(PACKAGE_ROOT, 'integrations', 'opencode', 'commands');
|
|
@@ -296,7 +420,7 @@ async function installCodexDesktop() {
|
|
|
296
420
|
console.log(` Safety Policy -> ${join(codexDesktopPluginsDir(), 'safety')}`);
|
|
297
421
|
|
|
298
422
|
// Generate .mcp.json with absolute paths for Codex Desktop MCP server discovery
|
|
299
|
-
const mcpServerAbsPath =
|
|
423
|
+
const mcpServerAbsPath = codexMcpServerPath();
|
|
300
424
|
const mcpConfig = {
|
|
301
425
|
mcpServers: {
|
|
302
426
|
'huaweicloud-devkit': {
|
|
@@ -316,20 +440,50 @@ async function installCodexDesktop() {
|
|
|
316
440
|
console.log(` Plugin Manifest -> ${join(codexDesktopPluginsDir(), '.codex-plugin')}`);
|
|
317
441
|
}
|
|
318
442
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
443
|
+
ensureCodexConfigSection(mcpServerAbsPath);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
447
|
+
async function updateCodexDesktop() {
|
|
448
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
449
|
+
const commandsSrc = join(PACKAGE_ROOT, 'integrations', 'opencode', 'commands');
|
|
450
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
451
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
452
|
+
const pluginDest = codexDesktopPluginsDir();
|
|
453
|
+
|
|
454
|
+
copyDir(skillsSrc, codexDesktopSkillsDir());
|
|
455
|
+
const staleSkills = pruneStale(codexDesktopSkillsDir(), skillsSrc);
|
|
456
|
+
console.log(` Skills updated -> ${codexDesktopSkillsDir()}${staleSkills > 0 ? ` (removed ${staleSkills} stale)` : ''}`);
|
|
457
|
+
copyDir(commandsSrc, codexDesktopCommandsDir());
|
|
458
|
+
const staleCommands = pruneStale(codexDesktopCommandsDir(), commandsSrc);
|
|
459
|
+
console.log(` Commands updated -> ${codexDesktopCommandsDir()}${staleCommands > 0 ? ` (removed ${staleCommands} stale)` : ''}`);
|
|
460
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
461
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
462
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
463
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
464
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
465
|
+
|
|
466
|
+
const mcpServerAbsPath = codexMcpServerPath();
|
|
467
|
+
const mcpConfig = {
|
|
468
|
+
mcpServers: {
|
|
469
|
+
'huaweicloud-devkit': {
|
|
470
|
+
command: 'node',
|
|
471
|
+
args: [mcpServerAbsPath],
|
|
472
|
+
env: { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' },
|
|
473
|
+
},
|
|
474
|
+
},
|
|
330
475
|
};
|
|
331
|
-
writeFileSync(
|
|
332
|
-
console.log(` Config updated
|
|
476
|
+
writeFileSync(join(pluginDest, '.mcp.json'), JSON.stringify(mcpConfig, null, 2));
|
|
477
|
+
console.log(` MCP Config updated -> ${join(pluginDest, '.mcp.json')}`);
|
|
478
|
+
|
|
479
|
+
const codexPluginSrc = join(PLUGIN_ROOT, '.codex-plugin');
|
|
480
|
+
if (existsSync(codexPluginSrc)) {
|
|
481
|
+
copyDir(codexPluginSrc, join(pluginDest, '.codex-plugin'));
|
|
482
|
+
console.log(` Plugin Manifest updated -> ${join(pluginDest, '.codex-plugin')}`);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
ensureCodexConfigSection(mcpServerAbsPath);
|
|
486
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
333
487
|
}
|
|
334
488
|
|
|
335
489
|
function uninstallCodexDesktop() {
|
|
@@ -360,29 +514,28 @@ function uninstallCodexDesktop() {
|
|
|
360
514
|
if (removeIfExists(codexDesktopPluginsDir())) {
|
|
361
515
|
console.log(' Removed MCP server and safety policy');
|
|
362
516
|
}
|
|
363
|
-
|
|
364
|
-
if (existsSync(configPath)) {
|
|
365
|
-
let config = {};
|
|
366
|
-
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
|
|
367
|
-
if (config.mcp?.['huaweicloud-devkit']) {
|
|
368
|
-
delete config.mcp['huaweicloud-devkit'];
|
|
369
|
-
if (Object.keys(config.mcp).length === 0) delete config.mcp;
|
|
370
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
371
|
-
console.log(' Config cleaned');
|
|
372
|
-
}
|
|
373
|
-
}
|
|
517
|
+
removeCodexConfigSection();
|
|
374
518
|
}
|
|
375
519
|
|
|
376
520
|
function registerCodeartsMcp(configPath) {
|
|
377
521
|
const mcpPath = join(codeartsPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
522
|
+
const env = { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' };
|
|
523
|
+
const hcloudBin = findHcloudBin();
|
|
524
|
+
if (hcloudBin) env.HCLOUD_BIN = hcloudBin.replace(/\\/g, '/');
|
|
378
525
|
let config = {};
|
|
379
526
|
if (existsSync(configPath)) {
|
|
380
|
-
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {
|
|
527
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {
|
|
528
|
+
console.log(` \x1b[33m[WARN]\x1b[0m Could not parse ${configPath}. Skipping MCP config write; ensure "mcpServers.huaweicloud-devkit" points to ${mcpPath}.`);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const existing = config.mcpServers?.['huaweicloud-devkit'];
|
|
532
|
+
if (existing && existing.command === 'node'
|
|
533
|
+
&& Array.isArray(existing.args) && existing.args[0] === mcpPath) {
|
|
534
|
+
console.log(` MCP config unchanged: ${configPath}`);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
381
537
|
}
|
|
382
538
|
config.mcpServers = config.mcpServers || {};
|
|
383
|
-
const env = { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' };
|
|
384
|
-
const hcloudBin = findHcloudBin();
|
|
385
|
-
if (hcloudBin) env.HCLOUD_BIN = hcloudBin.replace(/\\/g, '/');
|
|
386
539
|
config.mcpServers['huaweicloud-devkit'] = {
|
|
387
540
|
command: 'node',
|
|
388
541
|
args: [mcpPath],
|
|
@@ -414,6 +567,28 @@ async function installCodeArts() {
|
|
|
414
567
|
registerCodeartsMcp(codeartsProjectMcpSettingsFile());
|
|
415
568
|
}
|
|
416
569
|
|
|
570
|
+
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
571
|
+
async function updateCodeArts() {
|
|
572
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
573
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
574
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
575
|
+
const pluginDest = codeartsPluginsDir();
|
|
576
|
+
|
|
577
|
+
for (const dir of [codeartsSkillsDir(), codeartsProjectSkillsDir()]) {
|
|
578
|
+
copyDir(skillsSrc, dir);
|
|
579
|
+
const stale = pruneStale(dir, skillsSrc);
|
|
580
|
+
console.log(` Skills updated -> ${dir}${stale > 0 ? ` (removed ${stale} stale)` : ''}`);
|
|
581
|
+
}
|
|
582
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
583
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
584
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
585
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
586
|
+
registerCodeartsMcp(codeartsMcpSettingsFile());
|
|
587
|
+
registerCodeartsMcp(codeartsProjectMcpSettingsFile());
|
|
588
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
589
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
590
|
+
}
|
|
591
|
+
|
|
417
592
|
function uninstallCodeArts() {
|
|
418
593
|
let removed = 0;
|
|
419
594
|
for (const skillsDir of [codeartsSkillsDir(), codeartsProjectSkillsDir()]) {
|
|
@@ -463,6 +638,126 @@ function codeartsStatus() {
|
|
|
463
638
|
}
|
|
464
639
|
}
|
|
465
640
|
|
|
641
|
+
// Returns true when the config file was written, false when it was already correct.
|
|
642
|
+
function ensureWorkbuddyMcpConfig() {
|
|
643
|
+
const configPath = workbuddyMcpConfigFile();
|
|
644
|
+
const mcpPath = join(workbuddyPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
645
|
+
const env = { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' };
|
|
646
|
+
const hcloudBin = findHcloudBin();
|
|
647
|
+
if (hcloudBin) env.HCLOUD_BIN = hcloudBin.replace(/\\/g, '/');
|
|
648
|
+
let config = {};
|
|
649
|
+
if (existsSync(configPath)) {
|
|
650
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {
|
|
651
|
+
console.log(` \x1b[33m[WARN]\x1b[0m Could not parse ${configPath}. Skipping MCP config write; ensure "mcpServers.huaweicloud-devkit" points to ${mcpPath}.`);
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
const existing = config.mcpServers?.['huaweicloud-devkit'];
|
|
655
|
+
if (existing && existing.command === 'node'
|
|
656
|
+
&& Array.isArray(existing.args) && existing.args[0] === mcpPath) {
|
|
657
|
+
console.log(` MCP config unchanged: ${configPath}`);
|
|
658
|
+
return false;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
config.mcpServers = config.mcpServers || {};
|
|
662
|
+
config.mcpServers['huaweicloud-devkit'] = {
|
|
663
|
+
command: 'node',
|
|
664
|
+
args: [mcpPath],
|
|
665
|
+
env,
|
|
666
|
+
};
|
|
667
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
668
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
669
|
+
console.log(` MCP config updated: ${configPath}`);
|
|
670
|
+
return true;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
async function installWorkBuddy() {
|
|
674
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
675
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
676
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
677
|
+
const pluginDest = workbuddyPluginsDir();
|
|
678
|
+
|
|
679
|
+
copyDir(skillsSrc, workbuddySkillsDir());
|
|
680
|
+
console.log(` Skills -> ${workbuddySkillsDir()}`);
|
|
681
|
+
|
|
682
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
683
|
+
console.log(` MCP Server -> ${join(pluginDest, 'src')}`);
|
|
684
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
685
|
+
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
686
|
+
|
|
687
|
+
ensureWorkbuddyMcpConfig();
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
691
|
+
async function updateWorkBuddy() {
|
|
692
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
693
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
694
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
695
|
+
const pluginDest = workbuddyPluginsDir();
|
|
696
|
+
|
|
697
|
+
copyDir(skillsSrc, workbuddySkillsDir());
|
|
698
|
+
const stale = pruneStale(workbuddySkillsDir(), skillsSrc);
|
|
699
|
+
console.log(` Skills updated -> ${workbuddySkillsDir()}${stale > 0 ? ` (removed ${stale} stale)` : ''}`);
|
|
700
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
701
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
702
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
703
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
704
|
+
ensureWorkbuddyMcpConfig();
|
|
705
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
706
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function uninstallWorkBuddy() {
|
|
710
|
+
const skillsDir = workbuddySkillsDir();
|
|
711
|
+
let removed = 0;
|
|
712
|
+
if (existsSync(skillsDir)) {
|
|
713
|
+
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
714
|
+
if (entry.name.startsWith('huawei')) {
|
|
715
|
+
removeIfExists(join(skillsDir, entry.name));
|
|
716
|
+
removed++;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (removed > 0) console.log(` Removed ${removed} skills`);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
if (removeIfExists(workbuddyPluginsDir())) {
|
|
723
|
+
console.log(' Removed MCP server and safety policy');
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const configPath = workbuddyMcpConfigFile();
|
|
727
|
+
if (existsSync(configPath)) {
|
|
728
|
+
let config = {};
|
|
729
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
|
|
730
|
+
if (config.mcpServers?.['huaweicloud-devkit']) {
|
|
731
|
+
delete config.mcpServers['huaweicloud-devkit'];
|
|
732
|
+
if (Object.keys(config.mcpServers).length === 0) delete config.mcpServers;
|
|
733
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
734
|
+
console.log(` MCP config cleaned: ${configPath}`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function workbuddyStatus() {
|
|
740
|
+
const pluginDir = workbuddyPluginsDir();
|
|
741
|
+
const skillsDir = workbuddySkillsDir();
|
|
742
|
+
console.log(` MCP Server: ${existsSync(join(pluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
|
|
743
|
+
console.log(` Safety Policy: ${existsSync(join(pluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
|
|
744
|
+
let skillCount = 0;
|
|
745
|
+
if (existsSync(skillsDir)) {
|
|
746
|
+
skillCount = readdirSync(skillsDir, { withFileTypes: true })
|
|
747
|
+
.filter((d) => d.isDirectory() && d.name.startsWith('huawei')).length;
|
|
748
|
+
}
|
|
749
|
+
console.log(` Skills: ${skillCount > 0 ? `\x1b[32m${skillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`);
|
|
750
|
+
const configPath = workbuddyMcpConfigFile();
|
|
751
|
+
if (existsSync(configPath)) {
|
|
752
|
+
try {
|
|
753
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
754
|
+
console.log(` MCP config: ${config.mcpServers?.['huaweicloud-devkit'] ? '\x1b[32mConfigured\x1b[0m' : '\x1b[31mNot configured\x1b[0m'}`);
|
|
755
|
+
} catch {
|
|
756
|
+
console.log(` MCP config: \x1b[31mInvalid\x1b[0m`);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
466
761
|
function opencodeStatus() {
|
|
467
762
|
const pluginDir = opencodePluginsDir();
|
|
468
763
|
const skillsDir = opencodeSkillsDir();
|
|
@@ -492,6 +787,7 @@ function parseTarget() {
|
|
|
492
787
|
if (val === 'codex') return 'codex';
|
|
493
788
|
if (val === 'codex-desktop') return 'codex-desktop';
|
|
494
789
|
if (val === 'codearts') return 'codearts';
|
|
790
|
+
if (val === 'workbuddy') return 'workbuddy';
|
|
495
791
|
if (val === 'all') return 'all';
|
|
496
792
|
return 'opencode';
|
|
497
793
|
}
|
|
@@ -514,6 +810,10 @@ async function cmdInstall() {
|
|
|
514
810
|
console.log('\n[CodeArts]');
|
|
515
811
|
await installCodeArts();
|
|
516
812
|
}
|
|
813
|
+
if (target === 'workbuddy' || target === 'all') {
|
|
814
|
+
console.log('\n[WorkBuddy]');
|
|
815
|
+
await installWorkBuddy();
|
|
816
|
+
}
|
|
517
817
|
if (target === 'codex' || target === 'all') {
|
|
518
818
|
console.log('\n[Codex]');
|
|
519
819
|
if (!hasCodexCLI()) {
|
|
@@ -539,7 +839,9 @@ async function cmdInstall() {
|
|
|
539
839
|
} console.log(`\n\x1b[32mInstallation complete!\x1b[0m`);
|
|
540
840
|
const appName = target === 'codearts' ? 'CodeArts'
|
|
541
841
|
: target === 'codex-desktop' ? 'Codex Desktop'
|
|
542
|
-
: target === 'codex' ? 'Codex'
|
|
842
|
+
: target === 'codex' ? 'Codex'
|
|
843
|
+
: target === 'workbuddy' ? 'WorkBuddy'
|
|
844
|
+
: 'OpenCode';
|
|
543
845
|
const pad = ' '.repeat(24 - appName.length);
|
|
544
846
|
console.log(`\n\x1b[1m\x1b[33m╔══════════════════════════════════════════════════════╗`);
|
|
545
847
|
console.log(`\x1b[1m\x1b[33m║ MCP 工具在重启 ${appName} 会话后才生效${pad}║`);
|
|
@@ -555,10 +857,17 @@ async function cmdInstall() {
|
|
|
555
857
|
console.log(`\nKooCLI (hcloud) detected.`);
|
|
556
858
|
}
|
|
557
859
|
|
|
558
|
-
console.log(`\
|
|
860
|
+
console.log(`\n\x1b[1m下一步:\x1b[0m`);
|
|
861
|
+
console.log(` 1. 配置统一凭据:npx huaweicloud-devkit auth init`);
|
|
862
|
+
console.log(` 2. 重启 ${appName} 会话(MCP 工具重启后生效)`);
|
|
863
|
+
console.log(` 3. 运行自检:npx huaweicloud-devkit doctor`);
|
|
559
864
|
|
|
560
865
|
// Write install marker for doctor to detect
|
|
561
|
-
const markerDir = target === 'codearts' ? codeartsPluginsDir()
|
|
866
|
+
const markerDir = target === 'codearts' ? codeartsPluginsDir()
|
|
867
|
+
: target === 'workbuddy' ? workbuddyPluginsDir()
|
|
868
|
+
: target === 'codex-desktop' ? codexDesktopPluginsDir()
|
|
869
|
+
: opencodePluginsDir();
|
|
870
|
+
mkdirSync(markerDir, { recursive: true });
|
|
562
871
|
writeFileSync(join(markerDir, '.installed'), new Date().toISOString());
|
|
563
872
|
if (target === 'opencode' || target === 'all') {
|
|
564
873
|
console.log('Or describe your Huawei Cloud task in OpenCode');
|
|
@@ -569,6 +878,9 @@ async function cmdInstall() {
|
|
|
569
878
|
if (target === 'codex' || target === 'all') {
|
|
570
879
|
console.log('Or mention @huaweicloud-core in Codex');
|
|
571
880
|
}
|
|
881
|
+
if (target === 'workbuddy' || target === 'all') {
|
|
882
|
+
console.log('Or describe your Huawei Cloud task in WorkBuddy');
|
|
883
|
+
}
|
|
572
884
|
}
|
|
573
885
|
|
|
574
886
|
async function cmdUninstall() {
|
|
@@ -584,15 +896,34 @@ async function cmdUninstall() {
|
|
|
584
896
|
console.log('\n[CodeArts]');
|
|
585
897
|
uninstallCodeArts();
|
|
586
898
|
}
|
|
587
|
-
if (target === '
|
|
899
|
+
if (target === 'workbuddy' || target === 'all') {
|
|
900
|
+
console.log('\n[WorkBuddy]');
|
|
901
|
+
uninstallWorkBuddy();
|
|
902
|
+
}
|
|
903
|
+
if (target === 'codex-desktop' || target === 'codex' || target === 'all') {
|
|
588
904
|
console.log('\n[Codex]');
|
|
589
905
|
uninstallCodexDesktop();
|
|
590
|
-
if (
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
906
|
+
if (target === 'codex' || target === 'all') {
|
|
907
|
+
if (!hasCodexCLI()) {
|
|
908
|
+
console.log(' \x1b[33mCodex CLI not found. Run "npm uninstall -g codex" to fully remove.\x1b[0m');
|
|
909
|
+
} else {
|
|
910
|
+
uninstallCodex();
|
|
911
|
+
}
|
|
594
912
|
}
|
|
595
913
|
}
|
|
914
|
+
if (target === 'all') {
|
|
915
|
+
const vaultPath = globalCredentialsPath();
|
|
916
|
+
if (removeIfExists(vaultPath)) {
|
|
917
|
+
console.log(' Removed credential vault');
|
|
918
|
+
}
|
|
919
|
+
const vaultDir = dirname(vaultPath);
|
|
920
|
+
try {
|
|
921
|
+
if (existsSync(vaultDir) && readdirSync(vaultDir).length === 0) {
|
|
922
|
+
rmSync(vaultDir, { recursive: true, force: true });
|
|
923
|
+
console.log(` Removed empty directory: ${vaultDir}`);
|
|
924
|
+
}
|
|
925
|
+
} catch {}
|
|
926
|
+
}
|
|
596
927
|
console.log(`\n\x1b[32mUninstall complete.\x1b[0m`);
|
|
597
928
|
}
|
|
598
929
|
|
|
@@ -609,6 +940,10 @@ async function cmdStatus() {
|
|
|
609
940
|
console.log('\n[CodeArts]');
|
|
610
941
|
codeartsStatus();
|
|
611
942
|
}
|
|
943
|
+
if (target === 'workbuddy' || target === 'all') {
|
|
944
|
+
console.log('\n[WorkBuddy]');
|
|
945
|
+
workbuddyStatus();
|
|
946
|
+
}
|
|
612
947
|
if (target === 'codex' || target === 'all') {
|
|
613
948
|
console.log('\n[Codex]');
|
|
614
949
|
if (!hasCodexCLI()) {
|
|
@@ -636,13 +971,16 @@ async function cmdDoctor() {
|
|
|
636
971
|
// Node.js
|
|
637
972
|
check('Node.js >= 20', process.versions.node.split('.')[0] >= 20, 'Run: nvm install 20 && nvm use 20');
|
|
638
973
|
|
|
639
|
-
// MCP server — check OpenCode
|
|
974
|
+
// MCP server — check OpenCode, Codex Desktop, CodeArts, and WorkBuddy paths
|
|
640
975
|
const opencodePluginDir = opencodePluginsDir();
|
|
641
976
|
const codexPluginDir = codexDesktopPluginsDir();
|
|
977
|
+
const workbuddyPluginDir = workbuddyPluginsDir();
|
|
642
978
|
const mcpOk = existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs'))
|
|
643
|
-
|| existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs'))
|
|
979
|
+
|| existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs'))
|
|
980
|
+
|| existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs'));
|
|
644
981
|
const mcpTarget = existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs')) ? 'OpenCode'
|
|
645
|
-
: existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs')) ? 'Codex Desktop'
|
|
982
|
+
: existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs')) ? 'Codex Desktop'
|
|
983
|
+
: existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs')) ? 'WorkBuddy' : '';
|
|
646
984
|
check('MCP server installed', mcpOk, 'Run: npx huaweicloud-devkit install');
|
|
647
985
|
|
|
648
986
|
if (mcpOk) {
|
|
@@ -650,10 +988,11 @@ async function cmdDoctor() {
|
|
|
650
988
|
}
|
|
651
989
|
|
|
652
990
|
const safetyOk = existsSync(join(opencodePluginDir, 'safety', 'policy.json'))
|
|
653
|
-
|| existsSync(join(codexPluginDir, 'safety', 'policy.json'))
|
|
991
|
+
|| existsSync(join(codexPluginDir, 'safety', 'policy.json'))
|
|
992
|
+
|| existsSync(join(workbuddyPluginDir, 'safety', 'policy.json'));
|
|
654
993
|
check('Safety policy installed', safetyOk, 'Run: npx huaweicloud-devkit install');
|
|
655
994
|
|
|
656
|
-
// MCP config — check OpenCode
|
|
995
|
+
// MCP config — check OpenCode, Codex Desktop, and WorkBuddy
|
|
657
996
|
let mcpConfigured = false;
|
|
658
997
|
let mcpCfgTarget = '';
|
|
659
998
|
const opencodeCfg = opencodeConfigFile();
|
|
@@ -663,11 +1002,18 @@ async function cmdDoctor() {
|
|
|
663
1002
|
if (cfg.mcp && cfg.mcp['huaweicloud-devkit']) { mcpConfigured = true; mcpCfgTarget = 'OpenCode'; }
|
|
664
1003
|
} catch {}
|
|
665
1004
|
}
|
|
666
|
-
const codexCfg =
|
|
1005
|
+
const codexCfg = codexConfigToml();
|
|
667
1006
|
if (!mcpConfigured && existsSync(codexCfg)) {
|
|
668
1007
|
try {
|
|
669
|
-
const cfg =
|
|
670
|
-
if (cfg.
|
|
1008
|
+
const cfg = readFileSync(codexCfg, 'utf8');
|
|
1009
|
+
if (cfg.includes('[mcp_servers.huaweicloud-devkit]')) { mcpConfigured = true; mcpCfgTarget = 'Codex Desktop'; }
|
|
1010
|
+
} catch {}
|
|
1011
|
+
}
|
|
1012
|
+
const workbuddyCfg = workbuddyMcpConfigFile();
|
|
1013
|
+
if (!mcpConfigured && existsSync(workbuddyCfg)) {
|
|
1014
|
+
try {
|
|
1015
|
+
const cfg = JSON.parse(readFileSync(workbuddyCfg, 'utf8'));
|
|
1016
|
+
if (cfg.mcpServers && cfg.mcpServers['huaweicloud-devkit']) { mcpConfigured = true; mcpCfgTarget = 'WorkBuddy'; }
|
|
671
1017
|
} catch {}
|
|
672
1018
|
}
|
|
673
1019
|
check('MCP configured', mcpConfigured, mcpCfgTarget ? `Found in ${mcpCfgTarget} config` : 'Run: npx huaweicloud-devkit install');
|
|
@@ -695,40 +1041,56 @@ async function cmdDoctor() {
|
|
|
695
1041
|
// Check auth
|
|
696
1042
|
const authCheck = spawnSync(`"${hcloudBin}" configure list`, [], { shell: true, windowsHide: true, stdio: 'pipe', timeout: 5000 });
|
|
697
1043
|
const hasAuth = authCheck.status === 0 && /access.?key/i.test(authCheck.stdout.toString());
|
|
698
|
-
check('hcloud credentials configured', hasAuth, 'Run:
|
|
1044
|
+
check('hcloud credentials configured', hasAuth, 'Run: npx huaweicloud-devkit auth init');
|
|
699
1045
|
}
|
|
700
1046
|
|
|
701
1047
|
// Skills
|
|
702
|
-
const skillsOptions = [opencodeSkillsDir(), codexDesktopSkillsDir(), codeartsSkillsDir()];
|
|
703
|
-
let skillCount = 0, skillsDir = '';
|
|
1048
|
+
const skillsOptions = [opencodeSkillsDir(), codexDesktopSkillsDir(), codeartsSkillsDir(), workbuddySkillsDir()];
|
|
1049
|
+
let skillCount = 0, skillsDir = '', missingSkills = [];
|
|
704
1050
|
for (const dir of skillsOptions) {
|
|
705
1051
|
if (!existsSync(dir)) continue;
|
|
706
|
-
const
|
|
707
|
-
.filter((d) => d.isDirectory() && d.name.startsWith('huawei'))
|
|
1052
|
+
const entries = readdirSync(dir, { withFileTypes: true })
|
|
1053
|
+
.filter((d) => d.isDirectory() && d.name.startsWith('huawei'));
|
|
1054
|
+
const count = entries.length;
|
|
708
1055
|
if (count > skillCount) { skillCount = count; skillsDir = dir; }
|
|
1056
|
+
for (const d of entries) {
|
|
1057
|
+
if (!existsSync(join(dir, d.name, 'SKILL.md'))) missingSkills.push(d.name);
|
|
1058
|
+
}
|
|
709
1059
|
}
|
|
710
1060
|
const skillsOk = skillCount >= 6;
|
|
711
|
-
check(`Skills installed (${skillCount})`, skillsOk, 'Run: npx huaweicloud-devkit
|
|
1061
|
+
check(`Skills installed (${skillCount})`, skillsOk, 'Run: npx huaweicloud-devkit install');
|
|
1062
|
+
if (missingSkills.length > 0) {
|
|
1063
|
+
console.log(` \x1b[33m[WARN]\x1b[0m ${missingSkills.length} skill(s) missing SKILL.md: ${missingSkills.join(', ')} — Run: npx huaweicloud-devkit install`);
|
|
1064
|
+
warn++;
|
|
1065
|
+
}
|
|
712
1066
|
|
|
713
1067
|
console.log(`\nResults: ${pass} pass, ${warn} warn, ${fail} fail`);
|
|
714
1068
|
|
|
715
1069
|
if (mcpConfigured && !hcloudOk) {
|
|
716
|
-
console.log('\n\x1b[33mMCP is configured but hcloud is not installed. Install hcloud then restart
|
|
1070
|
+
console.log('\n\x1b[33mMCP is configured but hcloud is not installed. Install hcloud then restart your session.\x1b[0m');
|
|
717
1071
|
}
|
|
718
1072
|
if (fail > 0) {
|
|
719
|
-
console.log('\x1b[33mFix failures above, then restart your
|
|
1073
|
+
console.log('\x1b[33mFix failures above, then restart your session.\x1b[0m');
|
|
720
1074
|
}
|
|
721
1075
|
if (fail === 0 && mcpConfigured) {
|
|
722
|
-
console.log('\n\x1b[32mAll checks passed.\x1b[0m Restart
|
|
1076
|
+
console.log('\n\x1b[32mAll checks passed.\x1b[0m Restart your session, then describe your Huawei Cloud task');
|
|
723
1077
|
}
|
|
724
1078
|
|
|
725
|
-
// Detect "installed but not restarted"
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
1079
|
+
// Detect "installed but not restarted" — check all supported agents
|
|
1080
|
+
const installedMarkers = [
|
|
1081
|
+
{ path: join(opencodePluginsDir(), '.installed'), name: 'OpenCode' },
|
|
1082
|
+
{ path: join(codexDesktopPluginsDir(), '.installed'), name: 'Codex Desktop' },
|
|
1083
|
+
{ path: join(workbuddyPluginsDir(), '.installed'), name: 'WorkBuddy' },
|
|
1084
|
+
{ path: join(codeartsPluginsDir(), '.installed'), name: 'CodeArts' },
|
|
1085
|
+
];
|
|
1086
|
+
for (const marker of installedMarkers) {
|
|
1087
|
+
if (existsSync(marker.path)) {
|
|
1088
|
+
console.log(`\n\x1b[1m\x1b[31m╔══════════════════════════════════════════╗`);
|
|
1089
|
+
console.log(`\x1b[1m\x1b[31m║ 请重启 ${marker.name}!MCP 工具尚未激活 ║`);
|
|
1090
|
+
console.log(`\x1b[1m\x1b[31m║ 关闭当前会话 → 重新打开即可 ║`);
|
|
1091
|
+
console.log(`\x1b[1m\x1b[31m╚══════════════════════════════════════════╝\x1b[0m`);
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
732
1094
|
}
|
|
733
1095
|
}
|
|
734
1096
|
|
|
@@ -736,11 +1098,105 @@ async function cmdUpdate() {
|
|
|
736
1098
|
console.log(BANNER);
|
|
737
1099
|
const target = parseTarget();
|
|
738
1100
|
|
|
739
|
-
if (target === 'opencode'
|
|
740
|
-
if (!existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs'))
|
|
1101
|
+
if (target === 'opencode') {
|
|
1102
|
+
if (!existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1103
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
console.log('[OpenCode]');
|
|
1107
|
+
await updateOpenCode();
|
|
1108
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1109
|
+
console.log(`\x1b[33mMCP 工具在重启 OpenCode 会话后才生效。\x1b[0m`);
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
if (target === 'codex-desktop') {
|
|
1114
|
+
if (!existsSync(join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1115
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
console.log('[Codex Desktop]');
|
|
1119
|
+
await updateCodexDesktop();
|
|
1120
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1121
|
+
console.log(`\x1b[33mMCP 工具在重启 Codex Desktop 会话后才生效。\x1b[0m`);
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
if (target === 'codex') {
|
|
1126
|
+
if (!hasCodexCLI()) {
|
|
1127
|
+
console.log(` \x1b[31mCodex CLI not found.\x1b[0m`);
|
|
1128
|
+
if (process.platform === 'win32') {
|
|
1129
|
+
console.log(` \x1b[33mTip: use --target codex-desktop for Codex Desktop on Windows\x1b[0m`);
|
|
1130
|
+
}
|
|
1131
|
+
console.log(` \x1b[31mInstall Codex CLI: https://github.com/openai/codex-cli\x1b[0m`);
|
|
1132
|
+
process.exitCode = 1;
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
console.log('[Codex]');
|
|
1136
|
+
installCodex();
|
|
1137
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1138
|
+
console.log(`\x1b[33mRestart the Codex session for changes to take effect.\x1b[0m`);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
if (target === 'codearts') {
|
|
1143
|
+
if (!existsSync(join(codeartsPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1144
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
console.log('[CodeArts]');
|
|
1148
|
+
await updateCodeArts();
|
|
1149
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1150
|
+
console.log(`\x1b[33mMCP 工具在重启 CodeArts 会话后才生效。\x1b[0m`);
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
if (target === 'workbuddy') {
|
|
1155
|
+
if (!existsSync(join(workbuddyPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1156
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
console.log('[WorkBuddy]');
|
|
1160
|
+
await updateWorkBuddy();
|
|
1161
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1162
|
+
console.log(`\x1b[33mMCP 工具在重启 WorkBuddy 会话后才生效。\x1b[0m`);
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
if (target === 'all') {
|
|
1167
|
+
let updatedAny = false;
|
|
1168
|
+
if (existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1169
|
+
console.log('[OpenCode]');
|
|
1170
|
+
await updateOpenCode();
|
|
1171
|
+
updatedAny = true;
|
|
1172
|
+
}
|
|
1173
|
+
if (existsSync(join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1174
|
+
console.log('\n[Codex Desktop]');
|
|
1175
|
+
await updateCodexDesktop();
|
|
1176
|
+
updatedAny = true;
|
|
1177
|
+
}
|
|
1178
|
+
if (existsSync(join(codeartsPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1179
|
+
console.log('\n[CodeArts]');
|
|
1180
|
+
await updateCodeArts();
|
|
1181
|
+
updatedAny = true;
|
|
1182
|
+
}
|
|
1183
|
+
if (existsSync(join(workbuddyPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1184
|
+
console.log('\n[WorkBuddy]');
|
|
1185
|
+
await updateWorkBuddy();
|
|
1186
|
+
updatedAny = true;
|
|
1187
|
+
}
|
|
1188
|
+
if (codexStatus()) {
|
|
1189
|
+
console.log('\n[Codex]');
|
|
1190
|
+
installCodex();
|
|
1191
|
+
updatedAny = true;
|
|
1192
|
+
}
|
|
1193
|
+
if (!updatedAny) {
|
|
741
1194
|
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
742
1195
|
return;
|
|
743
1196
|
}
|
|
1197
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1198
|
+
console.log(`\x1b[33mMCP 工具在重启各 agent 会话后才生效。\x1b[0m`);
|
|
1199
|
+
return;
|
|
744
1200
|
}
|
|
745
1201
|
|
|
746
1202
|
await cmdUninstall();
|
|
@@ -807,9 +1263,24 @@ async function cmdInstallHcloud() {
|
|
|
807
1263
|
// Clean up zip
|
|
808
1264
|
rmSync(zipPath, { force: true });
|
|
809
1265
|
|
|
810
|
-
// Add to PATH
|
|
1266
|
+
// Add to user PATH (append + dedupe within the User scope only; never copy
|
|
1267
|
+
// session/system entries into the user PATH, and never use setx PATH which
|
|
1268
|
+
// overwrites the whole variable and truncates at 1024 chars).
|
|
811
1269
|
console.log(' Adding to user PATH...');
|
|
812
|
-
|
|
1270
|
+
const pathPs = [
|
|
1271
|
+
'$ErrorActionPreference = "Stop"',
|
|
1272
|
+
`$target = '${installDir.replace(/'/g, "''")}'`,
|
|
1273
|
+
'$cur = [Environment]::GetEnvironmentVariable("Path", "User")',
|
|
1274
|
+
'if (-not $cur) { $cur = "" }',
|
|
1275
|
+
'$parts = @($cur -split ";" | Where-Object { $_ -ne "" })',
|
|
1276
|
+
'if ($parts -notcontains $target) {',
|
|
1277
|
+
' [Environment]::SetEnvironmentVariable("Path", (@($parts) + $target) -join ";", "User")',
|
|
1278
|
+
' Write-Output " Added to user PATH (deduped): $target"',
|
|
1279
|
+
'} else {',
|
|
1280
|
+
' Write-Output " Already in user PATH: $target"',
|
|
1281
|
+
'}',
|
|
1282
|
+
].join('; ');
|
|
1283
|
+
spawnSync('powershell', ['-NoProfile', '-Command', pathPs], { stdio: 'inherit', windowsHide: true, timeout: 30000 });
|
|
813
1284
|
|
|
814
1285
|
console.log(`\n\x1b[32mInstall complete.\x1b[0m`);
|
|
815
1286
|
console.log(` Verify: ${join(installDir, 'hcloud.exe')} version`);
|
|
@@ -868,11 +1339,191 @@ async function cmdInstallHcloud() {
|
|
|
868
1339
|
|
|
869
1340
|
console.log('\nAfter install, set HCLOUD_BIN if hcloud is not on PATH.');
|
|
870
1341
|
console.log('\n\x1b[1m\x1b[33m=== Configure credentials SAFELY ===\x1b[0m');
|
|
871
|
-
console.log('
|
|
1342
|
+
console.log(' Unified credentials (recommended): npx huaweicloud-devkit auth init');
|
|
1343
|
+
console.log(' KooCLI only (alternative): hcloud configure init');
|
|
872
1344
|
console.log(' NEVER: hcloud configure set --cli-access-key=xxx (AK/SK in shell history!)');
|
|
873
1345
|
console.log('\nThen run: npx huaweicloud-devkit doctor');
|
|
874
1346
|
}
|
|
875
1347
|
|
|
1348
|
+
function readLineQuestion(prompt) {
|
|
1349
|
+
return new Promise((resolve) => {
|
|
1350
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1351
|
+
rl.question(prompt, (answer) => {
|
|
1352
|
+
rl.close();
|
|
1353
|
+
resolve(answer.trim());
|
|
1354
|
+
});
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
async function readSecret(prompt) {
|
|
1359
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
1360
|
+
throw new Error(
|
|
1361
|
+
`Cannot read "${prompt.trim()}" securely in a non-interactive session. Set HW_ACCESS_KEY/HW_SECRET_KEY environment variables instead, or run "npx huaweicloud-devkit auth init" in a real terminal.`
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
process.stdout.write(prompt);
|
|
1366
|
+
const wasRaw = process.stdin.isRaw;
|
|
1367
|
+
process.stdin.setRawMode(true);
|
|
1368
|
+
process.stdin.resume();
|
|
1369
|
+
|
|
1370
|
+
return new Promise((resolve) => {
|
|
1371
|
+
let value = '';
|
|
1372
|
+
const onData = (chunk) => {
|
|
1373
|
+
for (const ch of chunk.toString('utf8')) {
|
|
1374
|
+
if (ch === '\r' || ch === '\n') {
|
|
1375
|
+
cleanup();
|
|
1376
|
+
resolve(value.trim());
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
if (ch === '\u0003') {
|
|
1380
|
+
cleanup();
|
|
1381
|
+
process.exit(130);
|
|
1382
|
+
}
|
|
1383
|
+
if (ch === '\b' || ch === '\u007f') {
|
|
1384
|
+
value = value.slice(0, -1);
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
value += ch;
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
const cleanup = () => {
|
|
1391
|
+
process.stdin.setRawMode(wasRaw);
|
|
1392
|
+
process.stdin.pause();
|
|
1393
|
+
process.stdin.off('data', onData);
|
|
1394
|
+
process.stdout.write('\n');
|
|
1395
|
+
};
|
|
1396
|
+
process.stdin.on('data', onData);
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
function configureHcloud(credentials) {
|
|
1401
|
+
const hcloudBin = findHcloudBin() || (process.env.HCLOUD_BIN || 'hcloud');
|
|
1402
|
+
const args = [
|
|
1403
|
+
'configure',
|
|
1404
|
+
'set',
|
|
1405
|
+
`--cli-access-key=${credentials.ak}`,
|
|
1406
|
+
`--cli-secret-key=${credentials.sk}`,
|
|
1407
|
+
`--cli-region=${credentials.region || ''}`,
|
|
1408
|
+
];
|
|
1409
|
+
const r = spawnSync(hcloudBin, args, {
|
|
1410
|
+
shell: false,
|
|
1411
|
+
windowsHide: true,
|
|
1412
|
+
stdio: 'pipe',
|
|
1413
|
+
timeout: 30000,
|
|
1414
|
+
});
|
|
1415
|
+
return {
|
|
1416
|
+
ok: r.status === 0,
|
|
1417
|
+
code: r.status,
|
|
1418
|
+
error: String(r.stderr || '').trim().slice(0, 240),
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
function printAuthAgents(agents = {}) {
|
|
1423
|
+
for (const [agent, info] of Object.entries(agents)) {
|
|
1424
|
+
console.log(` ${agent}: ${info.configured ? '[OK]' : '[MISSING]'}`);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
function printAuthStatus(status) {
|
|
1429
|
+
console.log(`Credentials vault: ${status.credentialsConfigured ? 'configured' : 'missing'} (${status.credentialsPath})`);
|
|
1430
|
+
console.log(`OBS config: ${status.obsConfigured ? 'configured' : 'missing'} (${status.obsConfigPath})`);
|
|
1431
|
+
console.log(`KooCLI: ${status.kooCliInstalled ? 'installed' : 'missing'}`);
|
|
1432
|
+
console.log('Agent MCP registration:');
|
|
1433
|
+
printAuthAgents(status.agents);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
async function cmdAuthInit() {
|
|
1437
|
+
console.log(BANNER);
|
|
1438
|
+
console.log('HuaweiCloud DevKit Unified Authentication Setup\n');
|
|
1439
|
+
console.log('\x1b[1m获取 AK/SK(如果还没有):\x1b[0m');
|
|
1440
|
+
console.log(' 1. 打开华为云"访问密钥"页签:');
|
|
1441
|
+
console.log(' https://console.huaweicloud.com/iam/?region=cn-north-4#/mine/accessKey');
|
|
1442
|
+
console.log(' 2. 点击"新增访问密钥",完成身份验证');
|
|
1443
|
+
console.log(' 3. 下载凭证文件(内含 AK 和 SK)。');
|
|
1444
|
+
console.log(' 注意:SK 只在创建密钥时显示一次,请妥善保存该文件。\n');
|
|
1445
|
+
|
|
1446
|
+
let ak = process.env.HW_ACCESS_KEY || '';
|
|
1447
|
+
let sk = process.env.HW_SECRET_KEY || '';
|
|
1448
|
+
let securityToken = process.env.HW_SECURITY_TOKEN || '';
|
|
1449
|
+
let region = process.env.HW_REGION || process.env.HUAWEICLOUD_REGION || '';
|
|
1450
|
+
|
|
1451
|
+
const interactive = process.stdin.isTTY && process.stdout.isTTY;
|
|
1452
|
+
if (!interactive && (!ak || !sk)) {
|
|
1453
|
+
console.error('\x1b[31mNon-interactive session detected. Provide credentials via environment variables instead:\x1b[0m');
|
|
1454
|
+
console.error(' HW_ACCESS_KEY, HW_SECRET_KEY');
|
|
1455
|
+
console.error(' (Or run "npx huaweicloud-devkit auth init" in a real terminal.)');
|
|
1456
|
+
process.exitCode = 1;
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
if (!ak) ak = await readLineQuestion('Access Key ID (AK): ');
|
|
1461
|
+
if (!sk) sk = await readSecret('Secret Access Key (SK): ');
|
|
1462
|
+
if (interactive && !securityToken) securityToken = await readLineQuestion('Security Token (optional, press Enter to skip): ');
|
|
1463
|
+
if (!region) region = 'cn-north-4';
|
|
1464
|
+
|
|
1465
|
+
if (!ak || !sk) {
|
|
1466
|
+
console.error('\nAK and SK are required.');
|
|
1467
|
+
process.exitCode = 1;
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
|
|
1472
|
+
const vaultPath = writeGlobalCredentials({ ak, sk, securityToken, region });
|
|
1473
|
+
|
|
1474
|
+
try {
|
|
1475
|
+
writeObsConfig({ ak, sk, securityToken, region });
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
console.log(`OBS config sync failed: ${error.message}`);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
if (findHcloudBin()) {
|
|
1481
|
+
const result = configureHcloud({ ak, sk, region });
|
|
1482
|
+
if (!result.ok) console.log(`KooCLI update failed: ${result.error || result.code}`);
|
|
1483
|
+
} else {
|
|
1484
|
+
console.log('KooCLI not found. Run "npx huaweicloud-devkit install-hcloud" and then "auth sync".');
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
console.log('\nCredentials synchronized.');
|
|
1488
|
+
console.log('\nNext steps:');
|
|
1489
|
+
console.log(' npx huaweicloud-devkit install --target all');
|
|
1490
|
+
console.log(' Restart your agent sessions.');
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
async function cmdAuthSync() {
|
|
1494
|
+
const target = parseTarget();
|
|
1495
|
+
console.log(BANNER);
|
|
1496
|
+
console.log('Synchronizing Huawei Cloud authentication...\n');
|
|
1497
|
+
|
|
1498
|
+
const credentials = readGlobalCredentials();
|
|
1499
|
+
if (!credentials?.ak || !credentials?.sk) {
|
|
1500
|
+
console.error('No global credentials found. Run "npx huaweicloud-devkit auth init" first.');
|
|
1501
|
+
process.exitCode = 1;
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
const result = syncAuth(target);
|
|
1506
|
+
if (result.ok) {
|
|
1507
|
+
console.log('Credentials synchronized.');
|
|
1508
|
+
} else {
|
|
1509
|
+
console.error(result.error);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
async function cmdAuthStatus() {
|
|
1514
|
+
const target = parseTarget();
|
|
1515
|
+
console.log(BANNER);
|
|
1516
|
+
console.log('HuaweiCloud DevKit Authentication Status\n');
|
|
1517
|
+
printAuthStatus(getAuthStatus(target));
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
async function cmdAuth() {
|
|
1521
|
+
const sub = (process.argv[3] || 'status').toLowerCase();
|
|
1522
|
+
if (sub === 'init' || sub === 'setup') return cmdAuthInit();
|
|
1523
|
+
if (sub === 'sync' || sub === 'refresh') return cmdAuthSync();
|
|
1524
|
+
return cmdAuthStatus();
|
|
1525
|
+
}
|
|
1526
|
+
|
|
876
1527
|
async function main() {
|
|
877
1528
|
const cmd = process.argv[2] || 'help';
|
|
878
1529
|
|
|
@@ -903,12 +1554,15 @@ async function main() {
|
|
|
903
1554
|
case 'install-hcloud':
|
|
904
1555
|
await cmdInstallHcloud();
|
|
905
1556
|
break;
|
|
1557
|
+
case 'auth':
|
|
1558
|
+
await cmdAuth();
|
|
1559
|
+
break;
|
|
906
1560
|
case 'help':
|
|
907
1561
|
case '--help':
|
|
908
1562
|
case '-h':
|
|
909
1563
|
default:
|
|
910
1564
|
console.log(BANNER);
|
|
911
|
-
console.log('Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|all>]\n');
|
|
1565
|
+
console.log('Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|workbuddy|all>]\n');
|
|
912
1566
|
console.log('Commands:');
|
|
913
1567
|
console.log(' install Install skills, MCP server, safety policy');
|
|
914
1568
|
console.log(' uninstall Remove installed files');
|
|
@@ -917,14 +1571,19 @@ async function main() {
|
|
|
917
1571
|
console.log(' status Show installation status');
|
|
918
1572
|
console.log(' doctor Self-check: hcloud, MCP, skills, auth');
|
|
919
1573
|
console.log(' install-hcloud Show KooCLI install commands for your OS');
|
|
1574
|
+
console.log(' auth Manage unified auth: init | sync | status');
|
|
920
1575
|
console.log(' help Show this help');
|
|
921
1576
|
console.log('\nOptions:');
|
|
922
|
-
console.log(' --target Target agent: opencode (default), codex, codearts, all');
|
|
1577
|
+
console.log(' --target Target agent: opencode (default), codex, codearts, workbuddy, all');
|
|
923
1578
|
console.log('\nExamples:');
|
|
924
1579
|
console.log(' npx huaweicloud-devkit install');
|
|
925
1580
|
console.log(' npx huaweicloud-devkit install --target codex');
|
|
926
1581
|
console.log(' npx huaweicloud-devkit install --target codearts');
|
|
1582
|
+
console.log(' npx huaweicloud-devkit install --target workbuddy');
|
|
927
1583
|
console.log(' npx huaweicloud-devkit install --target all');
|
|
1584
|
+
console.log(' npx huaweicloud-devkit auth init');
|
|
1585
|
+
console.log(' npx huaweicloud-devkit auth sync --target all');
|
|
1586
|
+
console.log(' npx huaweicloud-devkit auth status --target all');
|
|
928
1587
|
break;
|
|
929
1588
|
}
|
|
930
1589
|
}
|