huaweicloud-devkit 1.1.2-next.2 → 1.1.2-next.4

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.
Files changed (38) hide show
  1. package/README.md +22 -0
  2. package/README.zh-CN.md +22 -0
  3. package/integrations/hermes/skills-snapshot.json +175 -0
  4. package/package.json +3 -1
  5. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
  6. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
  7. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
  8. package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +1 -1
  9. package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
  10. package/plugins/huaweicloud-core/hooks/hooks.json +2 -2
  11. package/plugins/huaweicloud-core/hooks/huaweicloud-safety.mjs +56 -0
  12. package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
  13. package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +1 -1
  14. package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +6 -7
  15. package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +7 -6
  16. package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +1 -1
  17. package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +1 -1
  18. package/plugins/huaweicloud-core/skills/huawei-ecs/references/evs.md +3 -1
  19. package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +10 -10
  20. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +2 -2
  21. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/deploy-workflow.md +1 -1
  22. package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +15 -9
  23. package/plugins/huaweicloud-core/skills/huawei-iac/SKILL.md +1 -1
  24. package/plugins/huaweicloud-core/skills/huawei-iac/references/resource-catalog.md +1 -1
  25. package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +3 -3
  26. package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +15 -15
  27. package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +1 -1
  28. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +18 -16
  29. package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +1 -1
  30. package/plugins/huaweicloud-core/src/auth/credentials.mjs +6 -2
  31. package/plugins/huaweicloud-core/src/auth/reconcile.mjs +30 -6
  32. package/plugins/huaweicloud-core/src/auth/service.mjs +16 -22
  33. package/plugins/huaweicloud-core/src/hcloud-cli.mjs +16 -10
  34. package/plugins/huaweicloud-core/src/hcloud-probe.mjs +154 -0
  35. package/plugins/huaweicloud-core/src/koocli-version.mjs +47 -0
  36. package/plugins/huaweicloud-core/src/safety-policy.mjs +17 -5
  37. package/plugins/huaweicloud-core/src/setup-cli.mjs +196 -134
  38. package/plugins/huaweicloud-core/src/tools.mjs +16 -17
@@ -18,7 +18,7 @@ import { createRequire } from 'node:module';
18
18
 
19
19
  import { getAuthStatus, syncAuth } from './auth/service.mjs';
20
20
  import { SUPPORTED_AGENT_TARGETS } from './auth/agent-registration.mjs';
21
- import { resolveManagedProfile } from './auth/reconcile.mjs';
21
+ import { fingerprint, resolveManagedProfile } from './auth/reconcile.mjs';
22
22
  import { redactSecrets } from './safety-policy.mjs';
23
23
  import {
24
24
  globalCredentialsPath,
@@ -36,6 +36,8 @@ import {
36
36
  } from './proxy/proxy-config.mjs';
37
37
  import { removeKooCli, removeObsConfig } from './sandbox/uninstall-cleanup.mjs';
38
38
  import { queryDistTagsSync, determineTarget, semverCompare } from './update-check.mjs';
39
+ import { getKooCliVersion, compareVersion, kooCliDownloadBase, KOO_CLI_BASE } from './koocli-version.mjs';
40
+ import { findHcloudBin, hcloudProbeNextStep, probeHcloud } from './hcloud-probe.mjs';
39
41
 
40
42
  const { DatabaseSync } = createRequire(import.meta.url)('node:sqlite');
41
43
 
@@ -413,23 +415,6 @@ function detectCodeartsSandbox() {
413
415
  }
414
416
  }
415
417
 
416
- // Locate an existing hcloud executable (HCLOUD_BIN, ~/hcloud on Windows, ~/.local/bin elsewhere).
417
- function findHcloudBin() {
418
- if (process.env.HCLOUD_BIN && existsSync(process.env.HCLOUD_BIN)) return process.env.HCLOUD_BIN;
419
- const isWin = platform() === 'win32';
420
- const candidates = isWin
421
- ? [join(homedir(), 'hcloud', 'hcloud.exe')]
422
- : [
423
- join(homedir(), '.local', 'bin', 'hcloud'),
424
- join(homedir(), 'hcloud', 'hcloud'),
425
- join(homedir(), 'hcloud', 'hcloud.exe'),
426
- ];
427
- for (const c of candidates) {
428
- if (existsSync(c)) return c;
429
- }
430
- return null;
431
- }
432
-
433
418
  function printSandboxWarning(reason) {
434
419
  console.log(`\n\x1b[1m\x1b[31m⚠ 检测到码道沙箱模式 (bash_mode: sandbox)\x1b[0m`);
435
420
  console.log(`\x1b[31m ${reason}\x1b[0m`);
@@ -623,27 +608,25 @@ function hasCodexCLI() {
623
608
  return false;
624
609
  }
625
610
 
626
- function checkHcloud() {
627
- const bin = findHcloudBin() || process.env.HCLOUD_BIN || 'hcloud';
628
- if (!existsSync(bin)) return false;
629
- try {
630
- if (statSync(bin).size < 1024) return false;
631
- } catch {
632
- return false;
633
- }
611
+ function getMarketplaceName() {
612
+ const marketplacePath = join(PACKAGE_ROOT, '.agents', 'plugins', 'marketplace.json');
634
613
  try {
635
- const r = spawnSync(`"${bin}" version`, [], { shell: true, windowsHide: true, stdio: 'pipe', timeout: 5000 });
636
- const out = (r.stdout ? r.stdout.toString() : '') + (r.stderr ? r.stderr.toString() : '');
637
- return r.status === 0 && /KooCLI|Current.*version|当前KooCLI/i.test(out);
638
- } catch {
639
- return false;
640
- }
614
+ const manifest = JSON.parse(readFileSync(marketplacePath, 'utf8'));
615
+ if (manifest.name) return manifest.name;
616
+ } catch {}
617
+ return 'huaweicloud-devkit';
641
618
  }
642
619
 
643
- function getMarketplaceName() {
620
+ function getCodexPluginName() {
644
621
  const marketplacePath = join(PACKAGE_ROOT, '.agents', 'plugins', 'marketplace.json');
645
622
  try {
646
- const manifest = JSON.parse(readFileSync(marketplacePath, 'utf8'));
623
+ const marketplace = JSON.parse(readFileSync(marketplacePath, 'utf8'));
624
+ const pluginName = marketplace.plugins?.[0]?.name;
625
+ if (pluginName) return pluginName;
626
+ } catch {}
627
+ const codexManifestPath = join(PLUGIN_ROOT, '.codex-plugin', 'plugin.json');
628
+ try {
629
+ const manifest = JSON.parse(readFileSync(codexManifestPath, 'utf8'));
647
630
  if (manifest.name) return manifest.name;
648
631
  } catch {}
649
632
  return 'huaweicloud-devkit';
@@ -651,7 +634,7 @@ function getMarketplaceName() {
651
634
 
652
635
  function installCodex() {
653
636
  const marketplaceRoot = PACKAGE_ROOT;
654
- const pluginName = 'huaweicloud-core';
637
+ const pluginName = getCodexPluginName();
655
638
  const marketplaceName = getMarketplaceName();
656
639
 
657
640
  console.log(` Registering Codex marketplace: ${marketplaceRoot}`);
@@ -667,6 +650,10 @@ function installCodex() {
667
650
  console.log(` \x1b[33mUse: npx huaweicloud-devkit install --target codex-desktop\x1b[0m`);
668
651
  return false;
669
652
  }
653
+ if (r1.status !== 0) {
654
+ console.log(` \x1b[31mCodex marketplace registration failed.\x1b[0m`);
655
+ return false;
656
+ }
670
657
 
671
658
  console.log(` Installing plugin: ${pluginName}@${marketplaceName}`);
672
659
  const r2 = spawnSync(`codex plugin add "${pluginName}@${marketplaceName}"`, [], {
@@ -681,20 +668,26 @@ function installCodex() {
681
668
  console.log(` \x1b[33mUse: npx huaweicloud-devkit install --target codex-desktop\x1b[0m`);
682
669
  return false;
683
670
  }
671
+ if (r2.status !== 0) {
672
+ console.log(` \x1b[31mCodex plugin installation failed.\x1b[0m`);
673
+ return false;
674
+ }
684
675
 
685
676
  return true;
686
677
  }
687
678
 
688
679
  function uninstallCodex() {
689
- const pluginName = 'huaweicloud-core';
680
+ const pluginName = getCodexPluginName();
690
681
  const marketplaceName = getMarketplaceName();
691
- console.log(` Removing Codex plugin: ${pluginName}@${marketplaceName}`);
692
- const r = spawnSync(`codex plugin remove "${pluginName}@${marketplaceName}"`, [], {
693
- shell: true,
694
- windowsHide: true,
695
- stdio: 'pipe',
696
- });
697
- console.log(` ${r.stdout ? r.stdout.toString().trim() : r.stderr.toString().trim()}`);
682
+ for (const name of new Set([pluginName, 'huaweicloud-core'])) {
683
+ console.log(` Removing Codex plugin: ${name}@${marketplaceName}`);
684
+ const r = spawnSync(`codex plugin remove "${name}@${marketplaceName}"`, [], {
685
+ shell: true,
686
+ windowsHide: true,
687
+ stdio: 'pipe',
688
+ });
689
+ console.log(` ${r.stdout ? r.stdout.toString().trim() : r.stderr.toString().trim()}`);
690
+ }
698
691
 
699
692
  for (const name of new Set([marketplaceName, 'HuaweiCloud-Devkit'])) {
700
693
  console.log(` Removing Codex marketplace: ${name}`);
@@ -710,7 +703,7 @@ function uninstallCodex() {
710
703
  function codexStatus() {
711
704
  const r = spawnSync('codex plugin list', [], { shell: true, windowsHide: true, stdio: 'pipe' });
712
705
  const out = r.stdout ? r.stdout.toString() : '';
713
- return out.includes('huaweicloud-core');
706
+ return out.includes(getCodexPluginName()) || out.includes('huaweicloud-core');
714
707
  }
715
708
 
716
709
  async function installOpenCode() {
@@ -3067,75 +3060,116 @@ function checkForUpdate() {
3067
3060
  }
3068
3061
  }
3069
3062
 
3063
+ function installMarkerDirForTarget(target) {
3064
+ if (target === 'codex') return null;
3065
+ if (target === 'dsh') return dshPluginsDir();
3066
+ if (target === 'codearts') return codeartsPluginsDir();
3067
+ if (target === 'codearts-work') return codeartsWorkPluginsDir();
3068
+ if (target === 'workbuddy') return workbuddyPluginsDir();
3069
+ if (target === 'officeace') return officeacePluginsDir();
3070
+ if (target === 'hermes') return hermesPluginsDir();
3071
+ if (target === 'openclaw') return openclawPluginsDir();
3072
+ if (target === 'atomcode') return atomcodePluginsDir();
3073
+ if (target === 'codex-desktop') return codexDesktopPluginsDir();
3074
+ if (target === 'opencode') return opencodePluginsDir();
3075
+ return null;
3076
+ }
3077
+
3078
+ function writeInstallMarker(target) {
3079
+ const markerDir = installMarkerDirForTarget(target);
3080
+ if (!markerDir) return;
3081
+ mkdirSync(markerDir, { recursive: true });
3082
+ writeFileSync(join(markerDir, '.installed'), new Date().toISOString());
3083
+ }
3084
+
3070
3085
  async function cmdInstall() {
3071
3086
  const target = parseTarget();
3072
3087
  console.log(BANNER);
3073
3088
  console.log(`Installing HuaweiCloud DevKit${target !== 'opencode' ? ` for ${target}` : ''}...\n`);
3074
3089
  checkNode();
3075
3090
  checkForUpdate();
3091
+ const installFailures = [];
3092
+
3093
+ async function runInstallStep(stepTarget, title, fn) {
3094
+ console.log(title);
3095
+ try {
3096
+ const result = await fn();
3097
+ if (result === false) {
3098
+ installFailures.push(stepTarget);
3099
+ return false;
3100
+ }
3101
+ writeInstallMarker(stepTarget);
3102
+ return true;
3103
+ } catch (error) {
3104
+ installFailures.push(stepTarget);
3105
+ console.log(` \x1b[31m${stepTarget} install failed: ${error.message}\x1b[0m`);
3106
+ return false;
3107
+ }
3108
+ }
3076
3109
 
3077
3110
  if (target === 'opencode' || target === 'all') {
3078
- console.log('[OpenCode]');
3079
- await installOpenCode();
3111
+ await runInstallStep('opencode', '[OpenCode]', installOpenCode);
3080
3112
  }
3081
3113
  if (target === 'codex-desktop' || target === 'all') {
3082
- console.log('\n[Codex Desktop]');
3083
- await installCodexDesktop();
3114
+ await runInstallStep('codex-desktop', '\n[Codex Desktop]', installCodexDesktop);
3084
3115
  }
3085
3116
  if (target === 'codearts' || target === 'all') {
3086
- console.log('\n[CodeArts]');
3087
- await installCodeArts();
3117
+ await runInstallStep('codearts', '\n[CodeArts]', installCodeArts);
3088
3118
  }
3089
3119
  if (target === 'codearts-work' || target === 'all') {
3090
- console.log('\n[CodeArts Work]');
3091
- await installCodeArtsWork();
3120
+ await runInstallStep('codearts-work', '\n[CodeArts Work]', installCodeArtsWork);
3092
3121
  }
3093
3122
  if (target === 'workbuddy' || target === 'all') {
3094
- console.log('\n[WorkBuddy]');
3095
- await installWorkBuddy();
3123
+ await runInstallStep('workbuddy', '\n[WorkBuddy]', installWorkBuddy);
3096
3124
  }
3097
3125
  if (target === 'dsh' || target === 'all') {
3098
- console.log('\n[DSH]');
3099
- await installDsh();
3126
+ await runInstallStep('dsh', '\n[DSH]', installDsh);
3100
3127
  }
3101
3128
  if (target === 'officeace' || target === 'all') {
3102
- console.log('\n[OfficeAce]');
3103
- await installOfficeAce();
3129
+ await runInstallStep('officeace', '\n[OfficeAce]', installOfficeAce);
3104
3130
  }
3105
3131
  if (target === 'hermes' || target === 'all') {
3106
- console.log('\n[Hermes Agent]');
3107
- await installHermes();
3132
+ await runInstallStep('hermes', '\n[Hermes Agent]', installHermes);
3108
3133
  }
3109
3134
  if (target === 'openclaw' || target === 'all') {
3110
- console.log('\n[OpenClaw]');
3111
- await installOpenClaw();
3135
+ await runInstallStep('openclaw', '\n[OpenClaw]', installOpenClaw);
3112
3136
  }
3113
3137
  if (target === 'atomcode' || target === 'all') {
3114
- console.log('\n[AtomCode]');
3115
- await installAtomCode();
3138
+ await runInstallStep('atomcode', '\n[AtomCode]', installAtomCode);
3116
3139
  }
3117
3140
  if (target === 'codex' || target === 'all') {
3118
- console.log('\n[Codex]');
3119
- if (!hasCodexCLI()) {
3120
- if (target === 'codex') {
3121
- console.log(` \x1b[31mCodex CLI not found.\x1b[0m`);
3141
+ await runInstallStep('codex', '\n[Codex]', () => {
3142
+ if (!hasCodexCLI()) {
3143
+ if (target === 'codex') {
3144
+ console.log(` \x1b[31mCodex CLI not found.\x1b[0m`);
3145
+ if (process.platform === 'win32') {
3146
+ console.log(` \x1b[33mTip: Codex Desktop on Windows installs codex.exe under WindowsApps,\x1b[0m`);
3147
+ console.log(` \x1b[33m which may fail with "Access is denied". Try instead:\x1b[0m`);
3148
+ console.log(` \x1b[33m npx huaweicloud-devkit install --target codex-desktop\x1b[0m`);
3149
+ }
3150
+ console.log(` \x1b[31mOr install Codex CLI: https://github.com/openai/codex-cli\x1b[0m`);
3151
+ return false;
3152
+ }
3153
+ console.log(` \x1b[33mCodex CLI not found. Skipping Codex.\x1b[0m`);
3122
3154
  if (process.platform === 'win32') {
3123
- console.log(` \x1b[33mTip: Codex Desktop on Windows installs codex.exe under WindowsApps,\x1b[0m`);
3124
- console.log(` \x1b[33m which may fail with "Access is denied". Try instead:\x1b[0m`);
3125
- console.log(` \x1b[33m npx huaweicloud-devkit install --target codex-desktop\x1b[0m`);
3155
+ console.log(' \x1b[33mTip: try --target codex-desktop for Codex Desktop on Windows\x1b[0m');
3156
+ } else {
3157
+ console.log(' Install Codex CLI to enable: npx huaweicloud-devkit install --target codex');
3126
3158
  }
3127
- console.log(` \x1b[31mOr install Codex CLI: https://github.com/openai/codex-cli\x1b[0m`);
3128
- process.exit(1);
3129
- }
3130
- console.log(` \x1b[33mCodex CLI not found. Skipping Codex.\x1b[0m`);
3131
- if (process.platform === 'win32') {
3132
- console.log(' \x1b[33mTip: try --target codex-desktop for Codex Desktop on Windows\x1b[0m');
3133
- } else {
3134
- console.log(' Install Codex CLI to enable: npx huaweicloud-devkit install --target codex');
3159
+ return true;
3135
3160
  }
3136
- } else {
3137
- installCodex();
3161
+ return installCodex();
3162
+ });
3163
+ }
3164
+
3165
+ if (installFailures.length > 0) {
3166
+ console.log(`\n\x1b[31mInstallation failed for: ${installFailures.join(', ')}\x1b[0m`);
3167
+ if (target === 'all') {
3168
+ console.log(
3169
+ '\x1b[33mSome targets may have been installed before the failure. Re-run status/doctor per target.\x1b[0m',
3170
+ );
3138
3171
  }
3172
+ process.exit(1);
3139
3173
  }
3140
3174
 
3141
3175
  console.log(`\n\x1b[32mInstallation complete!\x1b[0m`);
@@ -3190,10 +3224,15 @@ async function cmdInstall() {
3190
3224
  console.log(`\x1b[1m\x1b[33m╚══════════════════════════════════════════════════════╝\x1b[0m`);
3191
3225
  }
3192
3226
 
3193
- const hcloudOk = checkHcloud();
3194
- if (!hcloudOk) {
3195
- console.log(`\n\x1b[33mKooCLI (hcloud) is not installed.`);
3196
- console.log(` Run: npx huaweicloud-devkit install-hcloud\x1b[0m`);
3227
+ const hcloudProbe = probeHcloud();
3228
+ if (!hcloudProbe.ok) {
3229
+ if (hcloudProbe.status === 'sandbox_home_failure') {
3230
+ console.log(`\n\x1b[33mKooCLI (hcloud) detected, but cannot run in this sandbox.`);
3231
+ console.log(` ${hcloudProbeNextStep(hcloudProbe)}\x1b[0m`);
3232
+ } else {
3233
+ console.log(`\n\x1b[33mKooCLI (hcloud) is not ready.`);
3234
+ console.log(` ${hcloudProbeNextStep(hcloudProbe)}\x1b[0m`);
3235
+ }
3197
3236
  } else {
3198
3237
  console.log(`\nKooCLI (hcloud) detected.`);
3199
3238
  }
@@ -3212,27 +3251,6 @@ async function cmdInstall() {
3212
3251
  }
3213
3252
  console.log(` 4. 运行自检:npx huaweicloud-devkit doctor`);
3214
3253
 
3215
- // Write install marker for doctor to detect
3216
- const markerDir =
3217
- target === 'dsh'
3218
- ? dshPluginsDir()
3219
- : target === 'codearts'
3220
- ? codeartsPluginsDir()
3221
- : target === 'codearts-work'
3222
- ? codeartsWorkPluginsDir()
3223
- : target === 'workbuddy'
3224
- ? workbuddyPluginsDir()
3225
- : target === 'officeace'
3226
- ? officeacePluginsDir()
3227
- : target === 'openclaw'
3228
- ? codexDesktopPluginsDir()
3229
- : target === 'atomcode'
3230
- ? atomcodePluginsDir()
3231
- : target === 'codex-desktop'
3232
- ? codexDesktopPluginsDir()
3233
- : opencodePluginsDir();
3234
- mkdirSync(markerDir, { recursive: true });
3235
- writeFileSync(join(markerDir, '.installed'), new Date().toISOString());
3236
3254
  if (target === 'opencode' || target === 'all') {
3237
3255
  console.log('Or describe your Huawei Cloud task in OpenCode');
3238
3256
  }
@@ -3243,7 +3261,7 @@ async function cmdInstall() {
3243
3261
  console.log('Or describe your Huawei Cloud task in CodeArts Work');
3244
3262
  }
3245
3263
  if (target === 'codex' || target === 'all') {
3246
- console.log('Or mention @huaweicloud-core in Codex');
3264
+ console.log('Or mention @huaweicloud-devkit in Codex');
3247
3265
  }
3248
3266
  if (target === 'workbuddy' || target === 'all') {
3249
3267
  console.log('Or describe your Huawei Cloud task in WorkBuddy');
@@ -3546,6 +3564,21 @@ async function cmdDoctor() {
3546
3564
  existsSync(join(atomcodePluginDir, 'safety', 'policy.json'));
3547
3565
  check('Safety policy installed', safetyOk, 'Run: npx huaweicloud-devkit install');
3548
3566
 
3567
+ const hookConfigPath = join(PLUGIN_ROOT, 'hooks', 'hooks.json');
3568
+ const hookNodePath = join(PLUGIN_ROOT, 'hooks', 'huaweicloud-safety.mjs');
3569
+ let hookConfigUsesNode = false;
3570
+ let hookConfigUsesPython = false;
3571
+ try {
3572
+ const hookConfigText = readFileSync(hookConfigPath, 'utf8');
3573
+ hookConfigUsesNode = hookConfigText.includes('huaweicloud-safety.mjs') && /\bnode\b/.test(hookConfigText);
3574
+ hookConfigUsesPython = /\bpython3?\b/.test(hookConfigText);
3575
+ } catch {}
3576
+ check(
3577
+ 'Safety hook runtime (Node)',
3578
+ existsSync(hookNodePath) && hookConfigUsesNode && !hookConfigUsesPython,
3579
+ 'hooks/hooks.json should invoke node hooks/huaweicloud-safety.mjs, not python3',
3580
+ );
3581
+
3549
3582
  // MCP config — check OpenCode, Codex Desktop, CodeArts, WorkBuddy, and DSH
3550
3583
  let mcpConfigured = false;
3551
3584
  let mcpCfgTarget = '';
@@ -3657,28 +3690,47 @@ async function cmdDoctor() {
3657
3690
  }
3658
3691
 
3659
3692
  // hcloud CLI
3660
- const hcloudBin = findHcloudBin() || process.env.HCLOUD_BIN || 'hcloud';
3661
- const hcloudCheck = spawnSync(`"${hcloudBin}" version`, [], {
3662
- shell: true,
3663
- windowsHide: true,
3664
- stdio: 'pipe',
3665
- timeout: 5000,
3666
- });
3667
- const hcloudOut = (hcloudCheck.stdout || '').toString() + (hcloudCheck.stderr || '').toString();
3668
- const hcloudOk = hcloudCheck.status === 0 && /KooCLI|Current.*version|当前KooCLI/i.test(hcloudOut);
3669
- check('hcloud CLI installed', hcloudOk, 'Run: npx huaweicloud-devkit install-hcloud');
3693
+ const hcloudProbe = probeHcloud();
3694
+ const hcloudBin = hcloudProbe.executable;
3695
+ const hcloudOk = hcloudProbe.ok;
3696
+ check('hcloud CLI installed', hcloudProbe.installed, hcloudProbeNextStep(hcloudProbe));
3697
+ if (hcloudProbe.status === 'sandbox_home_failure') {
3698
+ warn++;
3699
+ console.log(` \x1b[33m[WARN]\x1b[0m hcloud cannot resolve the Windows user home in this agent sandbox.`);
3700
+ console.log(` ${hcloudProbeNextStep(hcloudProbe)}`);
3701
+ } else if (hcloudProbe.status === 'privacy_pending') {
3702
+ warn++;
3703
+ console.log(` \x1b[33m[WARN]\x1b[0m KooCLI privacy agreement is pending.`);
3704
+ console.log(` ${hcloudProbeNextStep(hcloudProbe)}`);
3705
+ }
3706
+
3707
+ const kooCliTarget = getKooCliVersion();
3708
+ if (hcloudOk && kooCliTarget) {
3709
+ const installed = hcloudProbe.installedVersion;
3710
+ if (installed && compareVersion(installed, kooCliTarget) !== 0) {
3711
+ warn++;
3712
+ console.log(
3713
+ ` \x1b[33m[WARN]\x1b[0m KooCLI version mismatch: installed ${installed}, this plugin is paired with ${kooCliTarget}.`,
3714
+ );
3715
+ console.log(
3716
+ ` Reinstall the pinned version: curl -LO "${kooCliDownloadBase()}/huaweicloud-cli-linux-amd64.tar.gz" (or your platform's package), or run \`npx huaweicloud-devkit install-hcloud\`.`,
3717
+ );
3718
+ }
3719
+ }
3670
3720
 
3671
3721
  // CodeArts sandbox mode warning
3672
3722
  const sandboxMode = detectCodeartsSandbox();
3673
3723
  if (sandboxMode === 'sandbox') {
3674
- console.log(` \x1b[33m[WARN]\x1b[0m CodeArts sandbox mode active (bash_mode: sandbox)`);
3675
- console.log(` KooCLI may fail to write config in ~/.hcloud/ and hang on the privacy agreement.`);
3676
- console.log(` Fix: disable sandbox (Settings → Permissions) or use a terminal outside CodeArts.`);
3724
+ console.log(` \x1b[33m[WARN]\x1b[0m 检测到码道沙箱模式 (bash_mode: sandbox)`);
3725
+ console.log(` KooCLI 可能无法写入 ~/.hcloud/ 配置目录,并卡在隐私协议确认。`);
3726
+ console.log(
3727
+ ` 修复:在码道设置中关闭沙箱模式后重试 (设置 → 对话流 → 智能体 终端命令运行模式 → 自动运行),或改用码道外的终端。`,
3728
+ );
3677
3729
  warn++;
3678
3730
  }
3679
3731
 
3680
3732
  if (hcloudOk) {
3681
- const ver = (hcloudCheck.stdout.toString().match(/(\d+\.\d+\.\d+)/) || [])[1] || 'unknown';
3733
+ const ver = hcloudProbe.installedVersion || 'unknown';
3682
3734
  console.log(` Version: ${ver}`);
3683
3735
 
3684
3736
  // Check auth
@@ -4016,7 +4068,9 @@ async function cmdInstallHcloud() {
4016
4068
 
4017
4069
  const os = platform();
4018
4070
  const arch = process.arch;
4019
- const baseUrl = 'https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest';
4071
+ const baseUrl = kooCliDownloadBase();
4072
+ const kooCliVersion = getKooCliVersion();
4073
+ const latestUrl = `${KOO_CLI_BASE}/latest`;
4020
4074
  const installDir = os === 'win32' ? join(homedir(), 'hcloud') : join(homedir(), '.local', 'bin');
4021
4075
 
4022
4076
  if (os === 'win32') {
@@ -4113,28 +4167,30 @@ async function cmdInstallHcloud() {
4113
4167
  }
4114
4168
  }
4115
4169
  } else if (os === 'linux') {
4116
- console.log('[Linux] One-liner install:');
4117
- console.log(
4118
- ' curl -sSL https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y',
4119
- );
4120
- console.log(`\nOr manual: ${arch === 'arm64' ? 'ARM64' : 'AMD64'}`);
4170
+ console.log('[Linux] Auto-install (pinned to KooCLI <version>):'.replace('<version>', kooCliVersion || ''));
4121
4171
  const pkg = arch === 'arm64' ? 'linux-arm64' : 'linux-amd64';
4122
4172
  console.log(` curl -LO "${baseUrl}/huaweicloud-cli-${pkg}.tar.gz"`);
4123
4173
  console.log(` tar -zxvf huaweicloud-cli-${pkg}.tar.gz`);
4124
4174
  console.log(` mv hcloud ~/.local/bin/`);
4125
4175
  console.log(` hcloud version`);
4126
- console.log(`\nFull guide: https://support.huaweicloud.com/qs-hcli/hcli_02_003_02.html`);
4127
- } else if (os === 'darwin') {
4128
- console.log('[macOS] One-liner install:');
4176
+ console.log(`\n Or one-liner (installs LATEST, may differ from pinned ${kooCliVersion || ''}):`);
4177
+ console.log(` curl -sSL ${latestUrl}/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y`);
4129
4178
  console.log(
4130
- ' curl -sSL https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y',
4179
+ ` \x1b[33m⚠️ The one-liner installs the latest KooCLI. If \`hcloud version\` != ${kooCliVersion}, doctor/check_cli will warn — use the pinned download above instead.\x1b[0m`,
4131
4180
  );
4132
- console.log(`\nOr manual: ${arch === 'arm64' ? 'ARM64 (Apple Silicon)' : 'AMD64 (Intel)'}`);
4181
+ console.log(`\nFull guide: https://support.huaweicloud.com/qs-hcli/hcli_02_003_02.html`);
4182
+ } else if (os === 'darwin') {
4183
+ console.log('[macOS] Auto-install (pinned to KooCLI <version>):'.replace('<version>', kooCliVersion || ''));
4133
4184
  const pkg = arch === 'arm64' ? 'mac-arm64' : 'mac-amd64';
4134
4185
  console.log(` curl -LO "${baseUrl}/huaweicloud-cli-${pkg}.tar.gz"`);
4135
4186
  console.log(` tar -zxvf huaweicloud-cli-${pkg}.tar.gz`);
4136
4187
  console.log(` mv hcloud /usr/local/bin/`);
4137
4188
  console.log(` hcloud version`);
4189
+ console.log(`\n Or one-liner (installs LATEST, may differ from pinned ${kooCliVersion || ''}):`);
4190
+ console.log(` curl -sSL ${latestUrl}/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y`);
4191
+ console.log(
4192
+ ` \x1b[33m⚠️ The one-liner installs the latest KooCLI. If \`hcloud version\` != ${kooCliVersion}, doctor/check_cli will warn — use the pinned download above instead.\x1b[0m`,
4193
+ );
4138
4194
  console.log(`\nFull guide: https://support.huaweicloud.com/qs-hcli/hcli_02_003_03.html`);
4139
4195
  }
4140
4196
 
@@ -4346,12 +4402,18 @@ async function cmdAuthReconcile() {
4346
4402
  console.log(`OBS sync failed: ${error.message}`);
4347
4403
  }
4348
4404
  const profile = resolveManagedProfile();
4405
+ let hcloudSynced = false;
4349
4406
  if (profile) {
4350
4407
  const r = runHcloudConfigure(profile, credentials.ak, credentials.sk, credentials.region);
4408
+ hcloudSynced = r.ok;
4351
4409
  console.log(` KooCLI ${r.ok ? 'synced' : 'sync failed'}: profile=${profile} ${r.error || ''}`);
4352
4410
  }
4353
- writeLastSync();
4354
- console.log('Done. .last_sync refreshed.');
4411
+ if (hcloudSynced) {
4412
+ writeLastSync({ kooCliProfile: profile, s1Fingerprint: fingerprint(credentials.ak, credentials.sk) });
4413
+ console.log('Done. .last_sync refreshed.');
4414
+ } else {
4415
+ console.log('Done. .last_sync was not refreshed because KooCLI sync did not complete.');
4416
+ }
4355
4417
  }
4356
4418
 
4357
4419
  async function cmdAuthSync() {
@@ -57,6 +57,7 @@ import {
57
57
  resolveSkipFilePath,
58
58
  upgradePackage,
59
59
  } from './update-check.mjs';
60
+ import { hcloudProbeNextStep, probeHcloud } from './hcloud-probe.mjs';
60
61
 
61
62
  const __dirname = dirname(fileURLToPath(import.meta.url));
62
63
  const SKILLS_ROOT_DEV = join(__dirname, '..', 'skills');
@@ -174,7 +175,8 @@ const SKILLS_ROOT = resolveSkillsRoot();
174
175
  export const TOOL_DEFINITIONS = [
175
176
  {
176
177
  name: 'huaweicloud_check_cli',
177
- description: 'Check whether Huawei Cloud KooCLI hcloud is installed. Returns redacted output.',
178
+ description:
179
+ 'Check whether Huawei Cloud KooCLI hcloud is installed and whether its version matches the plugin paired version. Returns redacted output.',
178
180
  inputSchema: {
179
181
  type: 'object',
180
182
  properties: {},
@@ -982,7 +984,9 @@ function persistCredentials(ak, sk, securityToken, region) {
982
984
  } else {
983
985
  hcloud = runHcloudConfigure(profile, ak, sk, region);
984
986
  }
985
- writeLastSync();
987
+ if (hcloud.ok) {
988
+ writeLastSync({ kooCliProfile: profile, s1Fingerprint: fingerprint(ak, sk) });
989
+ }
986
990
  return {
987
991
  status: 'ok',
988
992
  scope: 'persist',
@@ -1380,22 +1384,17 @@ function hookResult(result) {
1380
1384
  }
1381
1385
 
1382
1386
  export async function runVersionCheck(options = {}) {
1383
- const result = await runHcloud(['version'], {
1384
- ...options,
1385
- maxRetries: options.maxRetries ?? 0,
1386
- });
1387
- const errorText = result.error || result.stderr || '';
1388
- const isSpawnError = /ENOENT|SPAWN_ERROR/i.test(errorText) || result.code === 'SPAWN_ERROR';
1387
+ const result = probeHcloud(options);
1389
1388
  return {
1390
- installed: result.ok,
1391
- authenticated: result.ok && !/配置文件中不存在配置项|USE_ERROR.*配置/i.test(result.stdout || ''),
1392
- errorCode: isSpawnError ? 'HCLOUD_NOT_FOUND' : undefined,
1393
- output: result.ok ? result.stdout : errorText,
1394
- nextStep: result.ok
1395
- ? 'Use huaweicloud_show_profile_redacted to inspect the active KooCLI profile safely.'
1396
- : isSpawnError
1397
- ? 'hcloud executable not found. Set HCLOUD_BIN to the full hcloud path, or install KooCLI: npx huaweicloud-devkit install-hcloud. Then restart the agent.'
1398
- : 'Install Huawei Cloud KooCLI: npx huaweicloud-devkit install-hcloud. Configure credentials outside the agent conversation.',
1389
+ installed: result.installed,
1390
+ authenticated: result.ok && !/配置文件中不存在配置项|USE_ERROR.*配置/i.test(result.output || ''),
1391
+ errorCode: result.errorCode,
1392
+ status: result.status,
1393
+ output: result.output,
1394
+ kooCliVersion: result.requiredVersion || undefined,
1395
+ installedVersion: result.installedVersion || undefined,
1396
+ versionMismatch: Boolean(result.versionMismatch),
1397
+ nextStep: hcloudProbeNextStep(result),
1399
1398
  authHint:
1400
1399
  'If hcloud is installed but commands fail with "配置文件中不存在配置项", run `npx huaweicloud-devkit auth init` outside agent chat to configure credentials.',
1401
1400
  };