huaweicloud-devkit 1.0.3-dev.0 → 1.1.0-next.0

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 (96) hide show
  1. package/.agents/plugins/marketplace.json +18 -18
  2. package/README.md +114 -41
  3. package/README.zh-CN.md +114 -41
  4. package/bin/setup.cjs +7 -4
  5. package/integrations/opencode/opencode.json +1 -4
  6. package/package.json +63 -42
  7. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -17
  8. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +4 -17
  9. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -17
  10. package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
  11. package/plugins/huaweicloud-core/.mcp.json +2 -5
  12. package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -18
  13. package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +32 -9
  14. package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
  15. package/plugins/huaweicloud-core/safety/policy.json +75 -99
  16. package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +41 -13
  17. package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +34 -34
  18. package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +18 -18
  19. package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +19 -19
  20. package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +34 -32
  21. package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +30 -30
  22. package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +20 -20
  23. package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +31 -31
  24. package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +21 -20
  25. package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +39 -30
  26. package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +8 -1
  27. package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +6 -0
  28. package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +39 -36
  29. package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +15 -4
  30. package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +11 -9
  31. package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +13 -13
  32. package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +40 -39
  33. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +12 -10
  34. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +21 -20
  35. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +19 -19
  36. package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +20 -20
  37. package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +27 -23
  38. package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +27 -27
  39. package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +65 -39
  40. package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +9 -9
  41. package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +33 -25
  42. package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +51 -44
  43. package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +19 -19
  44. package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +12 -7
  45. package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +13 -12
  46. package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +4 -4
  47. package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +63 -58
  48. package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +128 -42
  49. package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +26 -26
  50. package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +39 -39
  51. package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +13 -12
  52. package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +20 -19
  53. package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +15 -15
  54. package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +18 -18
  55. package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +13 -13
  56. package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +3 -3
  57. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +25 -2
  58. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +3 -3
  59. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +27 -1
  60. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +3 -3
  61. package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +65 -47
  62. package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +3 -3
  63. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +6 -6
  64. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +15 -1
  65. package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +0 -1
  66. package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +3 -3
  67. package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +33 -33
  68. package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +3 -3
  69. package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +134 -2
  70. package/plugins/huaweicloud-core/src/auth/credentials.mjs +77 -4
  71. package/plugins/huaweicloud-core/src/auth/service.mjs +2 -1
  72. package/plugins/huaweicloud-core/src/data/icons-manifest.v1.json +3517 -0
  73. package/plugins/huaweicloud-core/src/hcloud-cli.mjs +27 -8
  74. package/plugins/huaweicloud-core/src/icon-library.mjs +176 -0
  75. package/plugins/huaweicloud-core/src/mcp-server.mjs +31 -3
  76. package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +73 -0
  77. package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +94 -0
  78. package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +4 -1
  79. package/plugins/huaweicloud-core/src/safety-policy.mjs +123 -68
  80. package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +14 -10
  81. package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +35 -27
  82. package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
  83. package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +526 -8
  84. package/plugins/huaweicloud-core/src/search-market.mjs +55 -16
  85. package/plugins/huaweicloud-core/src/setup-cli.mjs +1942 -206
  86. package/plugins/huaweicloud-core/src/tools.mjs +678 -131
  87. package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +72 -38
  88. package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +10 -5
  89. package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +13 -16
  90. package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +12 -16
  91. package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +46 -31
  92. package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +268 -0
  93. package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
  94. package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +11 -5
  95. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +0 -190
  96. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +0 -124
@@ -1,15 +1,31 @@
1
- import { planHcloudCommand, runHcloud } from './hcloud-cli.mjs';
2
- import { classifyTextCommand, redactSecrets } from './safety-policy.mjs';
3
- import { evaluateArtifacts, evaluateCommandRisk, evaluateDeployPlan } from './risk-rule-engine.mjs';
4
- import { readFileSync, readdirSync, existsSync, writeFileSync, mkdirSync } from 'node:fs';
1
+ import { readFileSync, readdirSync, existsSync, writeFileSync } from 'node:fs';
5
2
  import { join, dirname } from 'node:path';
6
3
  import { fileURLToPath } from 'node:url';
7
4
  import { homedir } from 'node:os';
5
+ import { spawnSync } from 'node:child_process';
6
+
7
+ import { evaluateArtifacts, evaluateCommandRisk, evaluateDeployPlan } from './risk-rule-engine.mjs';
8
+ import { classifyTextCommand, redactSecrets } from './safety-policy.mjs';
9
+ import { planHcloudCommand, runHcloud } from './hcloud-cli.mjs';
8
10
  import { searchMarketplace } from './search-market.mjs';
9
- import { execWithSession, closeSession, DEFAULT_WORKSPACE_ID } from './sandbox/session-manager.mjs';
11
+ import { getServiceIcon } from './icon-library.mjs';
12
+ import {
13
+ execWithSession,
14
+ execOneShot,
15
+ closeSession,
16
+ uploadFileWithSession,
17
+ uploadProjectWithSession,
18
+ getCurrentWorkspaceId,
19
+ setWorkspaceId,
20
+ } from './sandbox/session-manager.mjs';
10
21
  import { hdkitCheckUser, hdkitSignAgreement, hdkitConnect, hdkitCredentials } from './sandbox/hdkitservice-api.mjs';
11
22
  import { getAuthStatus, syncAuth } from './auth/service.mjs';
12
- import { readGlobalCredentials, writeObsConfig as writeObsConfigFile } from './auth/credentials.mjs';
23
+ import {
24
+ readGlobalCredentials,
25
+ writeObsConfig as writeObsConfigFile,
26
+ setRuntimeCredentials,
27
+ clearRuntimeCredentials,
28
+ } from './auth/credentials.mjs';
13
29
 
14
30
  const __dirname = dirname(fileURLToPath(import.meta.url));
15
31
  const SKILLS_ROOT_DEV = join(__dirname, '..', 'skills');
@@ -25,12 +41,86 @@ function workbuddySkillsDir() {
25
41
  const home = homedir();
26
42
  return join(home, '.workbuddy', 'skills');
27
43
  }
44
+ function dshSkillsDir() {
45
+ const home = process.env.DSH_HOME || join(homedir(), '.dsh');
46
+ return join(home, 'skills');
47
+ }
48
+ function readOfficeaceRegistryInstallDir() {
49
+ if (process.platform !== 'win32') return null;
50
+ try {
51
+ const r = spawnSync('reg', ['query', 'HKCU\\SOFTWARE\\OfficeAce\\OfficeAce', '/v', 'InstallDir'], {
52
+ encoding: 'utf8',
53
+ windowsHide: true,
54
+ timeout: 5000,
55
+ });
56
+ if (r.status === 0) {
57
+ const m = r.stdout.match(/InstallDir\s+REG_SZ\s+(.+)/);
58
+ if (m) return m[1].trim();
59
+ }
60
+ } catch {}
61
+ return null;
62
+ }
63
+
64
+ function officeaceSkillsRoot() {
65
+ const configRoot = process.env.OFFICE_CLAW_CONFIG_ROOT;
66
+ if (configRoot && existsSync(join(configRoot, 'capabilities.json'))) return join(configRoot, 'skills');
67
+ const regDir = readOfficeaceRegistryInstallDir();
68
+ if (regDir) {
69
+ const dir = join(regDir, '.office-claw', 'skills');
70
+ if (existsSync(dir)) return dir;
71
+ }
72
+ if (process.platform === 'win32') {
73
+ const bases = [process.env.ProgramFiles, 'C:\\Program Files', 'D:\\Program Files'];
74
+ if (process.env.LOCALAPPDATA) bases.push(join(process.env.LOCALAPPDATA, 'Programs'));
75
+ for (const base of bases) {
76
+ if (!base) continue;
77
+ const dir = join(base, 'OfficeAce', '.office-claw', 'skills');
78
+ if (existsSync(dir)) return dir;
79
+ }
80
+ }
81
+ return null;
82
+ }
83
+
84
+ function hermesSkillsDir() {
85
+ if (process.env.HERMES_HOME) return join(process.env.HERMES_HOME, 'skills');
86
+ // Hermes on Windows stores under LOCALAPPDATA, not ~/.hermes
87
+ if (process.platform === 'win32' && process.env.LOCALAPPDATA) {
88
+ return join(process.env.LOCALAPPDATA, 'hermes', 'skills');
89
+ }
90
+ const home = homedir();
91
+ return join(home, '.hermes', 'skills');
92
+ }
93
+
94
+ export function listSkillDirs(root) {
95
+ if (!existsSync(root)) return [];
96
+ try {
97
+ return readdirSync(root, { withFileTypes: true })
98
+ .filter((d) => (d.isDirectory() || d.isSymbolicLink()) && existsSync(join(root, d.name, 'SKILL.md')))
99
+ .map((d) => d.name);
100
+ } catch {
101
+ return [];
102
+ }
103
+ }
104
+
105
+ export function findSkillsRoot(candidates) {
106
+ for (const dir of candidates) {
107
+ if (listSkillDirs(dir).length > 0) return dir;
108
+ }
109
+ return null;
110
+ }
111
+
28
112
  function resolveSkillsRoot() {
29
- if (existsSync(SKILLS_ROOT_DEV)) return SKILLS_ROOT_DEV;
30
- if (existsSync(codeartsSkillsDir())) return codeartsSkillsDir();
31
- if (existsSync(opencodeSkillsDir())) return opencodeSkillsDir();
32
- if (existsSync(workbuddySkillsDir())) return workbuddySkillsDir();
33
- return SKILLS_ROOT_DEV;
113
+ return (
114
+ findSkillsRoot([
115
+ SKILLS_ROOT_DEV,
116
+ dshSkillsDir(),
117
+ codeartsSkillsDir(),
118
+ opencodeSkillsDir(),
119
+ workbuddySkillsDir(),
120
+ officeaceSkillsRoot(),
121
+ hermesSkillsDir(),
122
+ ]) || SKILLS_ROOT_DEV
123
+ );
34
124
  }
35
125
  const SKILLS_ROOT = resolveSkillsRoot();
36
126
 
@@ -90,7 +180,8 @@ export const TOOL_DEFINITIONS = [
90
180
  },
91
181
  {
92
182
  name: 'huaweicloud_list_operations',
93
- description: 'List KooCLI operations for a Huawei Cloud service by running local/read-only hcloud <Service> --help.',
183
+ description:
184
+ 'List KooCLI operations for a Huawei Cloud service by running local/read-only hcloud <Service> --help.',
94
185
  inputSchema: {
95
186
  type: 'object',
96
187
  required: ['service'],
@@ -108,7 +199,8 @@ export const TOOL_DEFINITIONS = [
108
199
  },
109
200
  {
110
201
  name: 'huaweicloud_run_approved_command',
111
- description: 'Run a write-capable hcloud command only after the exact command has been shown and explicitly approved by the user.',
202
+ description:
203
+ 'Run a write-capable hcloud command only after the exact command has been shown and explicitly approved by the user.',
112
204
  inputSchema: {
113
205
  type: 'object',
114
206
  required: ['args', 'approvedCommand', 'approvedByUser'],
@@ -188,7 +280,8 @@ export const TOOL_DEFINITIONS = [
188
280
  },
189
281
  {
190
282
  name: 'huaweicloud_hook_check_deploy_plan',
191
- description: 'Check a structured or textual deployment plan for Huawei Cloud sandbox, exposure, IAM, and cost risks.',
283
+ description:
284
+ 'Check a structured or textual deployment plan for Huawei Cloud sandbox, exposure, IAM, and cost risks.',
192
285
  inputSchema: {
193
286
  type: 'object',
194
287
  required: ['plan'],
@@ -227,19 +320,24 @@ export const TOOL_DEFINITIONS = [
227
320
  },
228
321
  {
229
322
  name: 'huaweicloud_search_docs',
230
- description: 'Search across Huawei Cloud SKILL.md files and local documentation. Returns top 10 relevant results with source, name, snippet, and relevance score. Use when the agent needs to discover which skill covers a topic, or when uncertain about API parameters, quotas, or limitations.',
323
+ description:
324
+ 'Search across Huawei Cloud SKILL.md files and local documentation. Returns top 10 relevant results with source, name, snippet, and relevance score. Use when the agent needs to discover which skill covers a topic, or when uncertain about API parameters, quotas, or limitations.',
231
325
  inputSchema: {
232
326
  type: 'object',
233
327
  required: ['query'],
234
328
  properties: {
235
329
  query: { type: 'string', description: 'Search query across skill descriptions and documentation.' },
236
- topic: { type: 'string', description: 'Optional filter: all | ecs | obs | vpc | iam | rds | cce | modelarts | dew. Defaults to all.' },
330
+ topic: {
331
+ type: 'string',
332
+ description: 'Optional filter: all | ecs | obs | vpc | iam | rds | cce | modelarts | dew. Defaults to all.',
333
+ },
237
334
  },
238
335
  },
239
336
  },
240
337
  {
241
338
  name: 'huaweicloud_retrieve_skill',
242
- description: 'Retrieve a full SKILL.md by skill name. Returns the complete skill content plus list of reference files. Use when the agent has identified which skill to load and needs the full procedure.',
339
+ description:
340
+ 'Retrieve a full SKILL.md by skill name. Returns the complete skill content plus list of reference files. Use when the agent has identified which skill to load and needs the full procedure.',
243
341
  inputSchema: {
244
342
  type: 'object',
245
343
  required: ['name'],
@@ -250,7 +348,8 @@ export const TOOL_DEFINITIONS = [
250
348
  },
251
349
  {
252
350
  name: 'huaweicloud_list_regions',
253
- description: 'List available Huawei Cloud regions. Returns region IDs, display names, and endpoints. Use when the agent needs to discover available regions before creating resources.',
351
+ description:
352
+ 'List available Huawei Cloud regions. Returns region IDs, display names, and endpoints. Use when the agent needs to discover available regions before creating resources.',
254
353
  inputSchema: {
255
354
  type: 'object',
256
355
  properties: {},
@@ -258,30 +357,58 @@ export const TOOL_DEFINITIONS = [
258
357
  },
259
358
  {
260
359
  name: 'huaweicloud_get_regional_availability',
261
- description: 'Check if a specific Huawei Cloud service is available in a target region. Use before creating resources to prevent failures from regional unavailability.',
360
+ description:
361
+ 'Check if a specific Huawei Cloud service is available in a target region. Use before creating resources to prevent failures from regional unavailability.',
262
362
  inputSchema: {
263
363
  type: 'object',
264
364
  required: ['service', 'region'],
265
365
  properties: {
266
- service: { type: 'string', description: 'Service name: ecs, obs, rds, gaussdb, cce, modelarts, functiongraph, etc.' },
366
+ service: {
367
+ type: 'string',
368
+ description: 'Service name: ecs, obs, rds, gaussdb, cce, modelarts, functiongraph, etc.',
369
+ },
267
370
  region: { type: 'string', description: 'Region ID: cn-south-1, cn-north-4, ap-southeast-3, etc.' },
268
371
  },
269
372
  },
270
373
  },
271
374
  {
272
375
  name: 'huaweicloud_search_marketplace',
273
- description: 'Search the Huawei Cloud agent skill marketplace for available skills. Returns scored results with names, categories, and descriptions. Use when built-in skills are insufficient or the user asks what skills exist.',
376
+ description:
377
+ 'Search the Huawei Cloud agent skill marketplace for available skills. Returns scored results with names, categories, and descriptions. Use when built-in skills are insufficient or the user asks what skills exist.',
274
378
  inputSchema: {
275
379
  type: 'object',
276
380
  properties: {
277
381
  query: { type: 'string', description: 'Search query across skill name, description, triggers, and service.' },
278
- category: { type: 'string', description: 'Optional category filter: computing, storage, network, security, devtools, monitoring, etc.' },
382
+ category: {
383
+ type: 'string',
384
+ description: 'Optional category filter: computing, storage, network, security, devtools, monitoring, etc.',
385
+ },
386
+ },
387
+ },
388
+ },
389
+ {
390
+ name: 'huaweicloud_get_service_icon',
391
+ description:
392
+ 'Find the official Huawei Cloud service logo from the Huawei Cloud Icons library (open.huaweicloud.com/openplatform/icons.html). Returns top 5 matches with CDN logo URLs, local paths, category, aliases, and product page links. Provide service (e.g. ecs, obs, modelarts, 对象存储) or category (e.g. 计算, 存储, 人工智能) to browse. Use when generating PPT, architecture diagrams (draw.io), or frontend pages that need official Huawei Cloud service logos.',
393
+ inputSchema: {
394
+ type: 'object',
395
+ properties: {
396
+ service: {
397
+ type: 'string',
398
+ description:
399
+ 'Service name, alias, or Chinese name, e.g. ecs, obs, modelarts, 对象存储, 虚拟私有云. Omit to browse by category only.',
400
+ },
401
+ category: {
402
+ type: 'string',
403
+ description: 'Optional category filter, e.g. 计算, 存储, 网络, 人工智能, 数据库, 安全, 企业应用.',
404
+ },
279
405
  },
280
406
  },
281
407
  },
282
408
  {
283
409
  name: 'huaweicloud_setup_obs_config',
284
- description: 'Synchronize KooCLI credentials to OBS config (~/.obsutilconfig). KooCLI and OBS use separate credential stores — hcloud commands work fine but OBS commands fail with "Please set ak, sk" unless this sync is done. Run this once to enable OBS operations; re-run after changing hcloud credentials.',
410
+ description:
411
+ 'Synchronize KooCLI credentials to OBS config (~/.obsutilconfig). KooCLI and OBS use separate credential stores — hcloud commands work fine but OBS commands fail with "Please set ak, sk" unless this sync is done. Run this once to enable OBS operations; re-run after changing hcloud credentials.',
285
412
  inputSchema: {
286
413
  type: 'object',
287
414
  properties: {
@@ -291,35 +418,83 @@ export const TOOL_DEFINITIONS = [
291
418
  },
292
419
  {
293
420
  name: 'huaweicloud_auth_status',
294
- description: 'Check unified Huawei Cloud authentication status across the global credential vault, OBS, KooCLI, and all supported agent MCP registrations. Returns only redacted/status information, never credentials.',
421
+ description:
422
+ 'Check unified Huawei Cloud authentication status across the global credential vault, OBS, KooCLI, and all supported agent MCP registrations. Returns only redacted/status information, never credentials.',
295
423
  inputSchema: {
296
424
  type: 'object',
297
425
  properties: {
298
- target: { type: 'string', description: 'Agent target to check: opencode, codex, codex-desktop, codearts, workbuddy, or all (default).' },
426
+ target: {
427
+ type: 'string',
428
+ description:
429
+ 'Agent target to check: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, hermes, openclaw, atomcode, or all (default).',
430
+ },
299
431
  },
300
432
  },
301
433
  },
302
434
  {
303
435
  name: 'huaweicloud_auth_sync',
304
- description: 'Synchronize credentials from the global Huawei Cloud credential vault to OBS and report agent registration status. Does not write secrets into any agent config.',
436
+ description:
437
+ 'Synchronize credentials from the global Huawei Cloud credential vault to OBS and report agent registration status. Does not write secrets into any agent config.',
438
+ inputSchema: {
439
+ type: 'object',
440
+ properties: {
441
+ target: {
442
+ type: 'string',
443
+ description:
444
+ 'Agent target to report after sync: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, hermes, or all (default).',
445
+ },
446
+ },
447
+ },
448
+ },
449
+ {
450
+ name: 'huaweicloud_auth_init',
451
+ description:
452
+ 'Set or clear runtime Huawei Cloud credentials (AK/SK) for this MCP session. Runtime credentials take highest priority over environment variables and config files for all subsequent API calls. Use when switching accounts within the same Agent session — call with AK/SK to switch, or with clear=true to fall back to env/file credentials.',
305
453
  inputSchema: {
306
454
  type: 'object',
307
455
  properties: {
308
- target: { type: 'string', description: 'Agent target to report after sync: opencode, codex, codex-desktop, codearts, workbuddy, or all (default).' },
456
+ ak: { type: 'string', description: 'Huawei Cloud Access Key (required unless clear=true)' },
457
+ sk: { type: 'string', description: 'Huawei Cloud Secret Key (required unless clear=true)' },
458
+ region: { type: 'string', description: 'Default region (optional)' },
459
+ clear: { type: 'boolean', description: 'Set to true to clear runtime credentials and revert to env/file' },
309
460
  },
310
461
  },
311
462
  },
312
463
  {
313
464
  name: 'huaweicloud_sandbox_exec_with_session',
314
- description: 'Execute a command on a workspace terminal with session reuse (state persists across calls). Shell state (cd, env vars, aliases) carries over between calls.',
465
+ description:
466
+ 'Execute a command on a workspace terminal with session reuse (state persists across calls). Shell state (cd, env vars, aliases) carries over between calls. Use for interactive work and command sequences that need shared state. NOT for long-running commands (>30s) — prefer exec_one_shot for those.',
315
467
  inputSchema: {
316
468
  type: 'object',
317
469
  required: ['command'],
318
470
  properties: {
319
471
  command: { type: 'string', description: 'The shell command to execute on the remote workspace' },
320
- workspace_id: { type: 'string', description: 'The workspace ID' },
472
+ workspace_id: {
473
+ type: 'string',
474
+ description:
475
+ 'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
476
+ },
477
+ username: { type: 'string', description: 'Login username for the remote terminal (default: root)' },
478
+ timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 120000)' },
479
+ },
480
+ },
481
+ },
482
+ {
483
+ name: 'huaweicloud_sandbox_exec_one_shot',
484
+ description:
485
+ 'Execute a command on a workspace terminal with a fresh connection per call (no session state carries over). Each invocation opens a new WebSocket connection, executes one command, then disconnects. Use for long-running build/deploy/install commands (>30s) that do not need shell state persistence between calls. More stable than session-based execution for heavy workloads.',
486
+ inputSchema: {
487
+ type: 'object',
488
+ required: ['command'],
489
+ properties: {
490
+ command: { type: 'string', description: 'The shell command to execute on the remote workspace' },
491
+ workspace_id: {
492
+ type: 'string',
493
+ description:
494
+ 'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
495
+ },
321
496
  username: { type: 'string', description: 'Login username for the remote terminal (default: root)' },
322
- timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 30000)' },
497
+ timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 120000)' },
323
498
  },
324
499
  },
325
500
  },
@@ -329,14 +504,72 @@ export const TOOL_DEFINITIONS = [
329
504
  inputSchema: {
330
505
  type: 'object',
331
506
  properties: {
332
- workspace_id: { type: 'string', description: 'The workspace ID' },
507
+ workspace_id: {
508
+ type: 'string',
509
+ description:
510
+ 'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
511
+ },
333
512
  username: { type: 'string', description: 'Login username (default: root)' },
334
513
  },
335
514
  },
336
515
  },
516
+ {
517
+ name: 'huaweicloud_sandbox_upload_file',
518
+ description:
519
+ 'Upload a local file into the sandbox workspace. Base64-encodes the file, writes it in small chunks through the terminal session (the exec channel is fragile for large single commands), then decodes and verifies the md5 checksum. Use this instead of embedding large file content directly in a command.',
520
+ inputSchema: {
521
+ type: 'object',
522
+ required: ['local_path', 'remote_path'],
523
+ properties: {
524
+ local_path: { type: 'string', description: 'Absolute path to the local file to upload.' },
525
+ remote_path: { type: 'string', description: 'Target path in the sandbox, e.g. /workspace/<repo>/index.html.' },
526
+ workspace_id: {
527
+ type: 'string',
528
+ description:
529
+ 'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
530
+ },
531
+ username: { type: 'string', description: 'Login username (default: root)' },
532
+ timeout_ms: { type: 'number', description: 'Per-command execution timeout in milliseconds (default: 120000)' },
533
+ },
534
+ },
535
+ },
536
+ {
537
+ name: 'huaweicloud_sandbox_upload_project',
538
+ description:
539
+ 'Package a local project directory and upload it to a sandbox workspace via HTTP tunnel. Falls back to base64 chunking if tunnel fails. Creates a tar.gz archive, uploads it, and extracts it on the sandbox by default.',
540
+ inputSchema: {
541
+ type: 'object',
542
+ required: ['local_dir'],
543
+ properties: {
544
+ local_dir: { type: 'string', description: 'Local project directory to upload.' },
545
+ remote_dir: {
546
+ type: 'string',
547
+ description:
548
+ 'Remote parent directory where project will be extracted (default: /workspace). Final layout: <remote_dir>/<dirname>/',
549
+ },
550
+ workspace_id: {
551
+ type: 'string',
552
+ description:
553
+ 'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
554
+ },
555
+ username: { type: 'string', description: 'Login username (default: root)' },
556
+ exclude: {
557
+ type: 'array',
558
+ items: { type: 'string' },
559
+ description: 'Patterns to exclude from archive (default: .git, node_modules, __pycache__, .venv)',
560
+ },
561
+ extract: {
562
+ type: 'boolean',
563
+ description: 'Extract tar.gz on sandbox after upload (default: true)',
564
+ },
565
+ timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 300000)' },
566
+ },
567
+ },
568
+ },
337
569
  {
338
570
  name: 'huaweicloud_sandbox_check_user',
339
- description: 'Check if the current user has completed real-name verification and signed the required agreements. Returns realname_verified and agreement_signed status.',
571
+ description:
572
+ 'Check if the current user has completed real-name verification and signed the required agreements. Returns 200 {realnameVerified, agreementSigned} when all good; throws 403 HDKIT_NOT_REALNAME / HDKIT_NOT_AGREEMENT / HDKIT_NOT_REALNAME_AND_AGREEMENT to indicate what is missing. Never signs anything itself.',
340
573
  inputSchema: {
341
574
  type: 'object',
342
575
  properties: {},
@@ -344,7 +577,8 @@ export const TOOL_DEFINITIONS = [
344
577
  },
345
578
  {
346
579
  name: 'huaweicloud_sandbox_sign_agreement',
347
- description: 'Sign all unsigned or outdated agreements for the current user. Required before huaweicloud_sandbox_connect if check-user returns agreement_signed=false.',
580
+ description:
581
+ "Sign all unsigned or outdated agreements for the current user. Required before huaweicloud_sandbox_connect if check-user returns agreementSigned=false. CRITICAL: only call after the user explicitly consents to signing — never sign agreements on the user's behalf without their explicit request.",
348
582
  inputSchema: {
349
583
  type: 'object',
350
584
  properties: {},
@@ -352,11 +586,16 @@ export const TOOL_DEFINITIONS = [
352
586
  },
353
587
  {
354
588
  name: 'huaweicloud_sandbox_connect',
355
- description: 'Connect to a sandbox via hdkitservice. One user one instance - reuses existing sandbox if available, otherwise creates a new one. Returns session_id, dev_stage_id, connection_id, and connection_address.',
589
+ description:
590
+ 'Connect to a sandbox via hdkitservice. One user one instance - reuses existing sandbox if available, otherwise creates a new one. Returns session_id, dev_stage_id, connection_id, and connection_address.',
356
591
  inputSchema: {
357
592
  type: 'object',
358
593
  properties: {
359
- source: { type: 'string', description: 'Source identifier (default: WEB). Options: VSCODE, CLI, WEB, WEBVNC, WEBPTY, WEBIDE, CURSOR, etc.' },
594
+ source: {
595
+ type: 'string',
596
+ description:
597
+ 'Source identifier (default: WEB). Options: VSCODE, CLI, WEB, WEBVNC, WEBPTY, WEBIDE, CURSOR, etc.',
598
+ },
360
599
  template_id: { type: 'string', description: 'Template ID; overrides server default (only for new sandbox)' },
361
600
  flavor_id: { type: 'string', description: 'Flavor ID; overrides server default (only for new sandbox)' },
362
601
  env: { type: 'object', description: 'Environment variables to set in the sandbox (only for new sandbox)' },
@@ -376,7 +615,8 @@ export const TOOL_DEFINITIONS = [
376
615
  },
377
616
  {
378
617
  name: 'huaweicloud_sandbox_credentials',
379
- description: 'Configure temporary AK/SK for a sandbox via hdkitservice. Injects temporary credentials into the sandbox. The sandbox must be in RUNNING state.',
618
+ description:
619
+ 'Configure temporary AK/SK for a sandbox via hdkitservice. Injects temporary credentials into the sandbox. The sandbox must be in RUNNING state.',
380
620
  inputSchema: {
381
621
  type: 'object',
382
622
  properties: {
@@ -386,7 +626,6 @@ export const TOOL_DEFINITIONS = [
386
626
  },
387
627
  },
388
628
  },
389
-
390
629
  ];
391
630
 
392
631
  export async function callTool(name, args = {}) {
@@ -424,35 +663,124 @@ export async function callTool(name, args = {}) {
424
663
  return listRegions();
425
664
  case 'huaweicloud_get_regional_availability':
426
665
  return getRegionalAvailability(args.service || '', args.region || '');
427
- case 'huaweicloud_explain_error':
666
+ case 'huaweicloud_explain_error':
428
667
  return explainError(args);
429
668
  case 'huaweicloud_search_marketplace':
430
669
  return searchMarketplace(args.query || '', args.category || '');
670
+ case 'huaweicloud_get_service_icon':
671
+ return getServiceIcon(args.service || '', args.category || '');
431
672
  case 'huaweicloud_setup_obs_config':
432
673
  return setupObsConfig(args.profile);
433
674
  case 'huaweicloud_auth_status':
434
675
  return getAuthStatus(args.target || 'all');
435
676
  case 'huaweicloud_auth_sync':
436
677
  return syncAuth(args.target || 'all');
678
+ case 'huaweicloud_auth_init':
679
+ if (args.clear) {
680
+ clearRuntimeCredentials();
681
+ return { status: 'cleared', message: 'Runtime credentials cleared. Fallback to env/file.' };
682
+ }
683
+ if (!args.ak || !args.sk) {
684
+ throw new Error('ak and sk are required. Set clear=true to clear runtime credentials.');
685
+ }
686
+ setRuntimeCredentials(args.ak, args.sk, undefined, args.region);
687
+ return { status: 'ok', message: 'Runtime credentials set for this MCP session.' };
437
688
  case 'huaweicloud_sandbox_exec_with_session': {
438
- const sandboxWsId2 = args.workspace_id || DEFAULT_WORKSPACE_ID;
689
+ const sandboxWsId2 = args.workspace_id || getCurrentWorkspaceId();
690
+ if (!sandboxWsId2) {
691
+ throw new Error(
692
+ 'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
693
+ 'or set HW_WORKSPACE_ID environment variable before starting the agent.',
694
+ );
695
+ }
439
696
  const sandboxUser2 = args.username || 'root';
440
- const sandboxTimeout2 = args.timeout_ms || 30000;
697
+ const sandboxTimeout2 = args.timeout_ms || 120000;
441
698
  const sandboxResult2 = await execWithSession(sandboxWsId2, args.command, sandboxUser2, sandboxTimeout2);
442
699
  return { stdout: sandboxResult2.stdout, exitCode: sandboxResult2.exitCode };
443
700
  }
444
- case 'huaweicloud_sandbox_close_session': {
445
- const sandboxWsId3 = args.workspace_id || DEFAULT_WORKSPACE_ID;
701
+ case 'huaweicloud_sandbox_exec_one_shot': {
702
+ const sandboxWsId3 = args.workspace_id || getCurrentWorkspaceId();
703
+ if (!sandboxWsId3) {
704
+ throw new Error(
705
+ 'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
706
+ 'or set HW_WORKSPACE_ID environment variable before starting the agent.',
707
+ );
708
+ }
446
709
  const sandboxUser3 = args.username || 'root';
447
- const closed = await closeSession(sandboxWsId3, sandboxUser3);
710
+ const sandboxTimeout3 = args.timeout_ms || 120000;
711
+ const sandboxResult3 = await execOneShot(sandboxWsId3, args.command, sandboxUser3, sandboxTimeout3);
712
+ return { stdout: sandboxResult3.stdout, exitCode: sandboxResult3.exitCode };
713
+ }
714
+ case 'huaweicloud_sandbox_close_session': {
715
+ const sandboxWsId4 = args.workspace_id || getCurrentWorkspaceId();
716
+ if (!sandboxWsId4) {
717
+ throw new Error(
718
+ 'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
719
+ 'or set HW_WORKSPACE_ID environment variable before starting the agent.',
720
+ );
721
+ }
722
+ const sandboxUser4 = args.username || 'root';
723
+ const closed = await closeSession(sandboxWsId4, sandboxUser4);
448
724
  return closed ? 'ok' : 'not_connected';
449
725
  }
726
+ case 'huaweicloud_sandbox_upload_file': {
727
+ if (!args.local_path || !args.remote_path) {
728
+ throw new Error('local_path and remote_path are required.');
729
+ }
730
+ const sandboxWsId5 = args.workspace_id || getCurrentWorkspaceId();
731
+ if (!sandboxWsId5) {
732
+ throw new Error(
733
+ 'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
734
+ 'or set HW_WORKSPACE_ID environment variable before starting the agent.',
735
+ );
736
+ }
737
+ const sandboxUser5 = args.username || 'root';
738
+ const sandboxTimeout5 = args.timeout_ms || 120000;
739
+ return await uploadFileWithSession(
740
+ sandboxWsId5,
741
+ args.local_path,
742
+ args.remote_path,
743
+ sandboxUser5,
744
+ sandboxTimeout5,
745
+ );
746
+ }
747
+ case 'huaweicloud_sandbox_upload_project': {
748
+ if (!args.local_dir) {
749
+ throw new Error('local_dir is required.');
750
+ }
751
+ const sandboxWsId6 = args.workspace_id || getCurrentWorkspaceId();
752
+ if (!sandboxWsId6) {
753
+ throw new Error(
754
+ 'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
755
+ 'or set HW_WORKSPACE_ID environment variable before starting the agent.',
756
+ );
757
+ }
758
+ const sandboxUser6 = args.username || 'root';
759
+ const sandboxTimeout6 = args.timeout_ms || 120000;
760
+ return await uploadProjectWithSession(
761
+ sandboxWsId6,
762
+ args.local_dir,
763
+ args.remote_dir,
764
+ sandboxUser6,
765
+ sandboxTimeout6,
766
+ {
767
+ exclude: args.exclude,
768
+ extract: args.extract,
769
+ },
770
+ );
771
+ }
450
772
  case 'huaweicloud_sandbox_check_user':
451
773
  return await hdkitCheckUser();
452
774
  case 'huaweicloud_sandbox_sign_agreement':
453
775
  return await hdkitSignAgreement();
454
- case 'huaweicloud_sandbox_connect':
455
- return await hdkitConnect(args);
776
+ case 'huaweicloud_sandbox_connect': {
777
+ const connectResult = await hdkitConnect(args);
778
+ const devStageId = connectResult?.dev_stage_id || connectResult?.devStageId;
779
+ if (devStageId) {
780
+ setWorkspaceId(devStageId);
781
+ }
782
+ return connectResult;
783
+ }
456
784
  case 'huaweicloud_sandbox_credentials':
457
785
  return await hdkitCredentials(args.session_id, args.dev_stage_id, args.enable_sts !== false);
458
786
  default:
@@ -465,11 +793,12 @@ function hookResult(result) {
465
793
  ok: result.decision !== 'deny',
466
794
  decision: result.decision,
467
795
  findings: result.findings,
468
- nextStep: result.decision === 'deny'
469
- ? 'Revise the command, artifact, or deployment plan before execution.'
470
- : result.decision === 'warn'
471
- ? 'Review the warnings with the user before proceeding.'
472
- : 'No Huawei Cloud hook risk rule matched.',
796
+ nextStep:
797
+ result.decision === 'deny'
798
+ ? 'Revise the command, artifact, or deployment plan before execution.'
799
+ : result.decision === 'warn'
800
+ ? 'Review the warnings with the user before proceeding.'
801
+ : 'No Huawei Cloud hook risk rule matched.',
473
802
  };
474
803
  }
475
804
 
@@ -490,7 +819,8 @@ export async function runVersionCheck(options = {}) {
490
819
  : isSpawnError
491
820
  ? 'hcloud executable not found. Set HCLOUD_BIN to the full hcloud path, or install KooCLI: npx huaweicloud-devkit install-hcloud. Then restart the agent.'
492
821
  : 'Install Huawei Cloud KooCLI: npx huaweicloud-devkit install-hcloud. Configure credentials outside the agent conversation.',
493
- authHint: 'If hcloud is installed but commands fail with "配置文件中不存在配置项", run `hcloud configure set --cli-access-key=<AK> --cli-secret-key=<SK> --cli-region=<region>` outside agent chat to configure credentials.',
822
+ authHint:
823
+ 'If hcloud is installed but commands fail with "配置文件中不存在配置项", run `hcloud configure set --cli-access-key=<AK> --cli-secret-key=<SK> --cli-region=<region>` outside agent chat to configure credentials.',
494
824
  };
495
825
  }
496
826
 
@@ -509,7 +839,8 @@ async function showProfileRedacted(profile) {
509
839
  ok: false,
510
840
  blockedByPolicy: true,
511
841
  reason: result.reason,
512
- safeAlternative: 'Use huaweicloud_show_profile_redacted so profile output is returned through the redaction pipeline.',
842
+ safeAlternative:
843
+ 'Use huaweicloud_show_profile_redacted so profile output is returned through the redaction pipeline.',
513
844
  };
514
845
  }
515
846
  return {
@@ -551,7 +882,12 @@ async function setupObsConfig(profile) {
551
882
  async function setupObsConfigFromHcloud(profile) {
552
883
  const obsConfigPath = join(homedir(), '.obsutilconfig');
553
884
  if (existsSync(obsConfigPath)) {
554
- return { ok: true, existed: true, path: obsConfigPath, note: 'OBS config already exists. Delete ~/.obsutilconfig first if you need to re-sync.' };
885
+ return {
886
+ ok: true,
887
+ existed: true,
888
+ path: obsConfigPath,
889
+ note: 'OBS config already exists. Delete ~/.obsutilconfig first if you need to re-sync.',
890
+ };
555
891
  }
556
892
 
557
893
  const args = ['configure', 'show'];
@@ -567,9 +903,9 @@ async function setupObsConfigFromHcloud(profile) {
567
903
  };
568
904
  }
569
905
 
570
- let accessKeyId = '';
571
- let secretAccessKey = '';
572
- let region = '';
906
+ let accessKeyId;
907
+ let secretAccessKey;
908
+ let region;
573
909
 
574
910
  try {
575
911
  const parsed = typeof result.stdout === 'string' ? JSON.parse(result.stdout) : result.stdout;
@@ -607,11 +943,11 @@ async function setupObsConfigFromHcloud(profile) {
607
943
 
608
944
  try {
609
945
  writeFileSync(obsConfigPath, configContent, { encoding: 'utf8', mode: 0o600 });
610
- } catch (e) {
946
+ } catch (error) {
611
947
  return {
612
948
  ok: false,
613
949
  error: 'Failed to write OBS config file.',
614
- detail: e.message,
950
+ detail: error.message,
615
951
  path: obsConfigPath,
616
952
  };
617
953
  }
@@ -630,12 +966,16 @@ async function setupObsConfigFromHcloud(profile) {
630
966
  const SERVICE_EXAMPLES = {
631
967
  ECS: { list: 'ECS ListServersDetails', create: 'ECS CreateServers', show: 'IMS GlanceShowImage' },
632
968
  VPC: { list: 'VPC ListVpcs', create: 'VPC CreateVpc', show: 'VPC ShowVpc' },
633
- FunctionGraph: { list: 'FunctionGraph ListFunctions', create: 'FunctionGraph CreateFunction', show: 'FunctionGraph ShowFunctionConfig' },
969
+ FUNCTIONGRAPH: {
970
+ list: 'FunctionGraph ListFunctions',
971
+ create: 'FunctionGraph CreateFunction',
972
+ show: 'FunctionGraph ShowFunctionConfig',
973
+ },
634
974
  APIG: { list: 'APIG ListInstancesV2', create: 'APIG CreateInstanceV2', show: 'APIG ShowDetailsOfInstanceV2' },
635
975
  OBS: { list: 'OBS ls', create: 'OBS mb obs://<bucket>', show: 'OBS stat obs://<bucket>/<key>' },
636
976
  RDS: { list: 'RDS ListInstances', create: 'RDS CreateInstance', show: 'RDS ShowInstance' },
637
977
  CES: { list: 'CES ListAlarms', create: 'CES CreateAlarm', show: 'CES ListMetrics' },
638
- GaussDB: { list: 'GaussDB ListInstances', create: 'GaussDB CreateInstance', show: 'GaussDB ShowInstance' },
978
+ GAUSSDB: { list: 'GaussDB ListInstances', create: 'GaussDB CreateInstance', show: 'GaussDB ShowInstance' },
639
979
  DDS: { list: 'DDS ListInstances', create: 'DDS CreateInstance', show: 'DDS ShowInstance' },
640
980
  DCS: { list: 'DCS ListInstances', create: 'DCS CreateInstance', show: 'DCS ShowInstance' },
641
981
  };
@@ -662,7 +1002,9 @@ async function listOperations(service, options = {}) {
662
1002
  service: serviceName,
663
1003
  command: isObs ? 'hcloud obs help' : `hcloud ${svc} --help`,
664
1004
  selectionRule: 'Use this help text to select the exact KooCLI operation name before planning any service command.',
665
- examples: SERVICE_EXAMPLES[serviceName.toUpperCase()] || { note: `No cached examples for ${serviceName}. Use the help text above to discover available operations.` },
1005
+ examples: SERVICE_EXAMPLES[serviceName.toUpperCase()] || {
1006
+ note: `No cached examples for ${serviceName}. Use the help text above to discover available operations.`,
1007
+ },
666
1008
  result,
667
1009
  };
668
1010
  }
@@ -691,41 +1033,131 @@ async function runApprovedCommand(args = {}) {
691
1033
  function serviceCatalog(intent = '') {
692
1034
  const it = String(intent).toLowerCase();
693
1035
  const routeMap = [
694
- { keywords: ['ecs', 'server', 'vm', 'instance', 'compute', 'flavor', 'image'], skills: ['huawei-ecs'], services: ['ECS'] },
695
- { keywords: ['vpc', 'subnet', 'network', 'security group', 'eip', 'nat', 'vpn', 'bandwidth'], skills: ['huawei-vpc'], services: ['VPC', 'EIP'] },
696
- { keywords: ['obs', 'bucket', 'storage', 'object', 'static website', 'static site', 'hosting'], skills: ['huawei-obs'], services: ['OBS'] },
697
- { keywords: ['functiongraph', 'serverless', 'function', 'lambda', 'trigger', 'faas'], skills: ['huawei-functiongraph'], services: ['FunctionGraph'] },
698
- { keywords: ['cce', 'kubernetes', 'k8s', 'container', 'cluster', 'node pool', 'swr', 'docker', 'image registry'], skills: ['huawei-cce'], services: ['CCE', 'SWR'] },
1036
+ {
1037
+ keywords: ['ecs', 'server', 'vm', 'instance', 'compute', 'flavor', 'image'],
1038
+ skills: ['huawei-ecs'],
1039
+ services: ['ECS'],
1040
+ },
1041
+ {
1042
+ keywords: ['vpc', 'subnet', 'network', 'security group', 'eip', 'nat', 'vpn', 'bandwidth'],
1043
+ skills: ['huawei-vpc'],
1044
+ services: ['VPC', 'EIP'],
1045
+ },
1046
+ {
1047
+ keywords: ['obs', 'bucket', 'storage', 'object', 'static website', 'static site', 'hosting'],
1048
+ skills: ['huawei-obs'],
1049
+ services: ['OBS'],
1050
+ },
1051
+ {
1052
+ keywords: ['functiongraph', 'serverless', 'function', 'lambda', 'trigger', 'faas'],
1053
+ skills: ['huawei-functiongraph'],
1054
+ services: ['FunctionGraph'],
1055
+ },
1056
+ {
1057
+ keywords: ['cce', 'kubernetes', 'k8s', 'container', 'cluster', 'node pool', 'swr', 'docker', 'image registry'],
1058
+ skills: ['huawei-cce'],
1059
+ services: ['CCE', 'SWR'],
1060
+ },
699
1061
  { keywords: ['apig', 'api gateway', 'publish', 'throttle'], skills: ['huawei-apig'], services: ['APIG'] },
700
1062
  { keywords: ['rds', 'mysql', 'postgresql', 'database', 'db'], skills: ['huawei-rds'], services: ['RDS'] },
701
- { keywords: ['gaussdb', 'distributed', 'sharding', 'opengauss'], skills: ['huawei-gaussdb'], services: ['GaussDB'] },
702
- { keywords: ['iam', 'permission', 'policy', 'role', 'user', 'ak/sk', 'access key', 'agency'], skills: ['huawei-iam'], services: ['IAM'] },
703
- { keywords: ['dew', 'secret', 'kms', 'encrypt', 'decrypt', 'certificate', 'csms'], skills: ['huawei-dew'], services: ['CSMS', 'KMS'] },
704
- { keywords: ['modelarts', 'ai', 'model', 'training', 'inference', 'machine learning'], skills: ['huawei-modelarts'], services: ['ModelArts'] },
705
- { keywords: ['billing', 'cost', 'bill', 'budget', 'expense', 'bss'], skills: ['huawei-billing'], services: ['BSS'] },
706
- { keywords: ['waf', 'aad', 'ddos', 'firewall', 'web protection'], skills: ['huawei-waf-aad'], services: ['WAF', 'AAD'] },
707
- { keywords: ['smn', 'dms', 'notification', 'message', 'kafka', 'rabbitmq'], skills: ['huawei-smn-dms'], services: ['SMN', 'DMS'] },
708
- { keywords: ['ces', 'monitor', 'alarm', 'metric', 'dashboard', 'cloud eye'], skills: ['huawei-cloud-eye'], services: ['CES'] },
1063
+ {
1064
+ keywords: ['gaussdb', 'distributed', 'sharding', 'opengauss'],
1065
+ skills: ['huawei-gaussdb'],
1066
+ services: ['GaussDB'],
1067
+ },
1068
+ {
1069
+ keywords: ['iam', 'permission', 'policy', 'role', 'user', 'ak/sk', 'access key', 'agency'],
1070
+ skills: ['huawei-iam'],
1071
+ services: ['IAM'],
1072
+ },
1073
+ {
1074
+ keywords: ['dew', 'secret', 'kms', 'encrypt', 'decrypt', 'certificate', 'csms'],
1075
+ skills: ['huawei-dew'],
1076
+ services: ['CSMS', 'KMS'],
1077
+ },
1078
+ {
1079
+ keywords: ['modelarts', 'ai', 'model', 'training', 'inference', 'machine learning'],
1080
+ skills: ['huawei-modelarts'],
1081
+ services: ['ModelArts'],
1082
+ },
1083
+ {
1084
+ keywords: ['billing', 'cost', 'bill', 'budget', 'expense', 'bss'],
1085
+ skills: ['huawei-billing'],
1086
+ services: ['BSS'],
1087
+ },
1088
+ {
1089
+ keywords: ['waf', 'aad', 'ddos', 'firewall', 'web protection'],
1090
+ skills: ['huawei-waf-aad'],
1091
+ services: ['WAF', 'AAD'],
1092
+ },
1093
+ {
1094
+ keywords: ['smn', 'dms', 'notification', 'message', 'kafka', 'rabbitmq'],
1095
+ skills: ['huawei-smn-dms'],
1096
+ services: ['SMN', 'DMS'],
1097
+ },
1098
+ {
1099
+ keywords: ['ces', 'monitor', 'alarm', 'metric', 'dashboard', 'cloud eye'],
1100
+ skills: ['huawei-cloud-eye'],
1101
+ services: ['CES'],
1102
+ },
709
1103
  { keywords: ['cts', 'audit', 'trace', 'tracker'], skills: ['huawei-cts'], services: ['CTS'] },
710
1104
  { keywords: ['cbr', 'backup', 'restore', 'vault', 'snapshot'], skills: ['huawei-cbr'], services: ['CBR'] },
711
- { keywords: ['deployment', 'deploy', 'ci/cd', 'pipeline', 'release'], skills: ['huawei-deployment'], services: ['CloudDeploy'] },
712
- { keywords: ['sandbox', 'devstation', 'workspace', 'terminal', 'preview', 'hwlink'], skills: ['huawei-sandbox'], services: ['Sandbox', 'DevStation'] },
713
- { keywords: ['dds', 'dcs', 'mongodb', 'redis', 'memcached', 'cache', 'document db'], skills: ['huawei-dds-dcs'], services: ['DDS', 'DCS'] },
714
- ];
1105
+ {
1106
+ keywords: ['deployment', 'deploy', 'ci/cd', 'pipeline', 'release'],
1107
+ skills: ['huawei-deployment'],
1108
+ services: ['CloudDeploy'],
1109
+ },
1110
+ {
1111
+ keywords: [
1112
+ 'sandbox',
1113
+ 'devstation',
1114
+ 'workspace',
1115
+ 'terminal',
1116
+ 'preview',
1117
+ 'hwlink',
1118
+ 'website',
1119
+ 'web app',
1120
+ 'webapp',
1121
+ 'hosting',
1122
+ '网站',
1123
+ '网页',
1124
+ '静态',
1125
+ ],
1126
+ skills: ['huawei-sandbox'],
1127
+ services: ['Sandbox', 'DevStation'],
1128
+ },
1129
+ {
1130
+ keywords: ['dds', 'dcs', 'mongodb', 'redis', 'memcached', 'cache', 'document db'],
1131
+ skills: ['huawei-dds-dcs'],
1132
+ services: ['DDS', 'DCS'],
1133
+ },
1134
+ ];
715
1135
  const matched = [];
716
- const tokens = it.split(/[\s,./-]+/).filter((t) => t.length > 0);
1136
+ const tokens = new Set(it.split(/[\s,./-]+/).filter((t) => t.length > 0));
1137
+ const cjk = /[\u4e00-\u9fff]/;
717
1138
  for (const route of routeMap) {
718
- if (route.keywords.some((kw) => tokens.includes(kw))) {
1139
+ if (route.keywords.some((kw) => (kw.includes(' ') || cjk.test(kw) ? it.includes(kw) : tokens.has(kw)))) {
719
1140
  matched.push(route);
720
1141
  }
721
1142
  }
722
1143
  const recommendedSkills = [...new Set(matched.flatMap((r) => r.skills))];
723
1144
  const recommendedServices = [...new Set(matched.flatMap((r) => r.services))].slice(0, 5);
724
1145
 
1146
+ // Deployment intent (deploy/host/publish a web app or static website) must never
1147
+ // default to a storage/other service — recommend the sandbox first.
1148
+ const deploymentIntent = /deploy|host|hosting|publish|website|web app|preview|部署|托管|发布|网站|网页/.test(it);
1149
+ if (deploymentIntent && recommendedSkills.includes('huawei-sandbox')) {
1150
+ const idx = recommendedSkills.indexOf('huawei-sandbox');
1151
+ recommendedSkills.splice(idx, 1);
1152
+ recommendedSkills.unshift('huawei-sandbox');
1153
+ }
1154
+
725
1155
  return {
726
1156
  intent,
727
1157
  recommendedSkills: recommendedSkills.length ? recommendedSkills : ['Use huaweicloud-core to route intent.'],
728
- recommendedServices: recommendedServices.length ? recommendedServices : ['Run hcloud --help to list available services.'],
1158
+ recommendedServices: recommendedServices.length
1159
+ ? recommendedServices
1160
+ : ['Run hcloud --help to list available services.'],
729
1161
  capabilityOrder: [
730
1162
  'Huawei Cloud Skills for task-specific workflows and examples',
731
1163
  'KooCLI hcloud for local authenticated operations and quick inspection',
@@ -751,32 +1183,38 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
751
1183
  const svc = String(service).toLowerCase();
752
1184
 
753
1185
  const SERVICE_ALIASES = {
754
- 'functiongraph': 'FSS',
755
- 'fgs': 'FSS',
1186
+ functiongraph: 'FSS',
1187
+ fgs: 'FSS',
756
1188
  };
757
1189
  const patternKey = SERVICE_ALIASES[svc] || service;
758
1190
 
759
1191
  const hwErrorPatterns = {
760
1192
  OBS: {
761
- 'InvalidAccessKeyId': 'OBS uses AK/SK directly (not IAM tokens). Verify AK/SK validity, OBS endpoint, and OBS permissions.',
762
- 'UserRestricted': 'This IAM user is restricted from OBS operations (403). Check IAM console → Users → Permissions: grant OBS bucket/object actions, or use an unrestricted account. If the account is a sub-account, the main account may have imposed restrictions.',
1193
+ InvalidAccessKeyId:
1194
+ 'OBS uses AK/SK directly (not IAM tokens). Verify AK/SK validity, OBS endpoint, and OBS permissions.',
1195
+ UserRestricted:
1196
+ 'This IAM user is restricted from OBS operations (403). Check IAM console → Users → Permissions: grant OBS bucket/object actions, or use an unrestricted account. If the account is a sub-account, the main account may have imposed restrictions.',
763
1197
  },
764
1198
  APIG: {
765
1199
  'APIC.7241': 'The enterprise_project_id is required for enterprise accounts. Add --enterprise_project_id=0.',
766
- 'APIC.7242': 'The EIP binding method depends on loadbalancer_provider. Use AddIngressEipV2 for elb, AddEipV2 for lvs.',
1200
+ 'APIC.7242':
1201
+ 'The EIP binding method depends on loadbalancer_provider. Use AddIngressEipV2 for elb, AddEipV2 for lvs.',
767
1202
  'APIC.7256': 'Bandwidth minimum is 5 Mbps. Use --bandwidth_size=5 or higher.',
768
1203
  'APIC.7310': 'available_zone_ids must use AZ codes (e.g. ap-southeast-3a), NOT UUIDs from ListAvailableZonesV2.',
769
1204
  },
770
1205
  FSS: {
771
- 'FSS.0403': 'Missing FunctionGraph IAM permissions. Attach FunctionGraph FullAccess role or grant specific actions.',
1206
+ 'FSS.0403':
1207
+ 'Missing FunctionGraph IAM permissions. Attach FunctionGraph FullAccess role or grant specific actions.',
772
1208
  'FSS.1078': '--code_filename is filename-only (no path). cd to the file directory before running the command.',
773
- 'FSS.1417': 'event_data field validation failed. Check parameter format: use dotted key=value, verify required hidden-optional fields.',
1209
+ 'FSS.1417':
1210
+ 'event_data field validation failed. Check parameter format: use dotted key=value, verify required hidden-optional fields.',
774
1211
  },
775
1212
  VPC: {
776
1213
  'VPC.0301': 'Bandwidth name is required for PER type EIPs, even though --help marks it optional.',
777
1214
  },
778
1215
  APIGW: {
779
- 'APIGW.0802': 'The current IAM user has no permissions in the requested region. Go to IAM console → Users → Permissions → add the target region, or switch to a different region.',
1216
+ 'APIGW.0802':
1217
+ 'The current IAM user has no permissions in the requested region. Go to IAM console → Users → Permissions → add the target region, or switch to a different region.',
780
1218
  },
781
1219
  };
782
1220
  if (hwErrorPatterns[patternKey] && hwErrorPatterns[patternKey][errorCode]) {
@@ -791,19 +1229,30 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
791
1229
 
792
1230
  if (/auth|token|credential|ak|sk|401|403|unauthorized|forbidden|Incorrect IAM/i.test(combined)) {
793
1231
  if (svc === 'obs') {
794
- suggestions.push('OBS uses AK/SK directly, not IAM tokens. Verify AK/SK validity and OBS bucket permissions via hcloud configure list or the Huawei Cloud console.');
1232
+ suggestions.push(
1233
+ 'OBS uses AK/SK directly, not IAM tokens. Verify AK/SK validity and OBS bucket permissions via hcloud configure list or the Huawei Cloud console.',
1234
+ );
795
1235
  } else {
796
1236
  suggestions.push('Check KooCLI profile, region, project_id, and IAM permissions without printing secrets.');
797
1237
  }
798
1238
  }
799
1239
  if (/APIGW\.(\d+)/i.test(errorCode)) {
800
- suggestions.push('APIGW.' + (errorCode.match(/APIGW\.(\d+)/i) || [])[1] + ': API Gateway layer error. ' + (errorCode === 'APIGW.0802' ? 'IAM user has no region permissions — check IAM console → User → Permissions → add target region.' : 'Verify the API request, region endpoint, and IAM permissions.'));
1240
+ suggestions.push(
1241
+ 'APIGW.' +
1242
+ (errorCode.match(/APIGW\.(\d+)/i) || [])[1] +
1243
+ ': API Gateway layer error. ' +
1244
+ (errorCode === 'APIGW.0802'
1245
+ ? 'IAM user has no region permissions — check IAM console → User → Permissions → add target region.'
1246
+ : 'Verify the API request, region endpoint, and IAM permissions.'),
1247
+ );
801
1248
  }
802
1249
  if (/region|endpoint|project/i.test(combined)) {
803
1250
  suggestions.push('Confirm the service endpoint, region, and project_id match the target resource.');
804
1251
  }
805
1252
  if (/quota|limit|insufficient|reach the limit/i.test(combined)) {
806
- suggestions.push('Check quota and resource limits before retrying a create or scale operation. Consider switching accounts or requesting a quota increase.');
1253
+ suggestions.push(
1254
+ 'Check quota and resource limits before retrying a create or scale operation. Consider switching accounts or requesting a quota increase.',
1255
+ );
807
1256
  }
808
1257
  if (/not.?found|404/i.test(combined)) {
809
1258
  if (/list.?regions/i.test(svc)) {
@@ -813,7 +1262,9 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
813
1262
  }
814
1263
  }
815
1264
  if (!suggestions.length) {
816
- suggestions.push('Collect service name, operation, region, project_id, request_id, and the full redacted error message.');
1265
+ suggestions.push(
1266
+ 'Collect service name, operation, region, project_id, request_id, and the full redacted error message.',
1267
+ );
817
1268
  }
818
1269
 
819
1270
  if (requestId) {
@@ -821,8 +1272,7 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
821
1272
  }
822
1273
 
823
1274
  const uniqueSuggestions = suggestions.filter((s, i, arr) => {
824
- return !arr.slice(0, i).some((prev) =>
825
- prev.substring(0, 50).toLowerCase() === s.substring(0, 50).toLowerCase());
1275
+ return !arr.slice(0, i).some((prev) => prev.substring(0, 50).toLowerCase() === s.substring(0, 50).toLowerCase());
826
1276
  });
827
1277
 
828
1278
  return {
@@ -839,9 +1289,7 @@ async function searchDocs(query, topic = 'all') {
839
1289
  const results = [];
840
1290
  try {
841
1291
  if (existsSync(SKILLS_ROOT)) {
842
- const dirs = readdirSync(SKILLS_ROOT, { withFileTypes: true })
843
- .filter((d) => d.isDirectory())
844
- .map((d) => d.name);
1292
+ const dirs = listSkillDirs(SKILLS_ROOT);
845
1293
  for (const dir of dirs) {
846
1294
  const skillPath = join(SKILLS_ROOT, dir, 'SKILL.md');
847
1295
  if (!existsSync(skillPath)) continue;
@@ -864,11 +1312,16 @@ async function searchDocs(query, topic = 'all') {
864
1312
  const nameLower = name.toLowerCase();
865
1313
  const contentLower = content.toLowerCase();
866
1314
  const relevance = tokens.reduce((score, token) => {
867
- return score + (descLower.includes(token) ? 3 : 0) + (nameLower.includes(token) ? 2 : 0) + (contentLower.includes(token) ? 1 : 0);
1315
+ return (
1316
+ score +
1317
+ (descLower.includes(token) ? 3 : 0) +
1318
+ (nameLower.includes(token) ? 2 : 0) +
1319
+ (contentLower.includes(token) ? 1 : 0)
1320
+ );
868
1321
  }, 0);
869
1322
  if (relevance > 0) {
870
1323
  results.push({
871
- source: "skills/" + dir + "/SKILL.md",
1324
+ source: 'skills/' + dir + '/SKILL.md',
872
1325
  name,
873
1326
  snippet: description.substring(0, 200),
874
1327
  relevance,
@@ -876,8 +1329,8 @@ async function searchDocs(query, topic = 'all') {
876
1329
  }
877
1330
  }
878
1331
  }
879
- } catch (err) {
880
- return { ok: false, error: err.message, results: [] };
1332
+ } catch (error) {
1333
+ return { ok: false, error: error.message, results: [] };
881
1334
  }
882
1335
  results.sort((a, b) => b.relevance - a.relevance);
883
1336
  return { ok: true, query: q, topic, count: results.length, results: results.slice(0, 10) };
@@ -888,9 +1341,7 @@ async function retrieveSkill(name) {
888
1341
  if (!skillName) return { ok: false, error: 'Skill name is required.' };
889
1342
  const skillPath = join(SKILLS_ROOT, skillName, 'SKILL.md');
890
1343
  if (!existsSync(skillPath)) {
891
- const dirs = existsSync(SKILLS_ROOT)
892
- ? readdirSync(SKILLS_ROOT, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name)
893
- : [];
1344
+ const dirs = listSkillDirs(SKILLS_ROOT);
894
1345
  return { ok: false, error: 'Skill "' + skillName + '" not found. Available: ' + dirs.join(', ') };
895
1346
  }
896
1347
  const content = readFileSync(skillPath, 'utf8');
@@ -903,7 +1354,8 @@ async function retrieveSkill(name) {
903
1354
  });
904
1355
  }
905
1356
  const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
906
- let version = 1, description = '';
1357
+ let version = 1,
1358
+ description = '';
907
1359
  if (frontmatter) {
908
1360
  const fm = frontmatter[1];
909
1361
  const vm = fm.match(/^version:\s*(.+)$/m);
@@ -915,8 +1367,12 @@ async function retrieveSkill(name) {
915
1367
  }
916
1368
 
917
1369
  async function listRegions() {
918
- const result = await runHcloud(['IAM', 'KeystoneListRegions'], { timeoutMs: 30000, maxRetries: 0 })
919
- .catch((err) => ({ ok: false, error: err.message }));
1370
+ const result = await runHcloud(['IAM', 'KeystoneListRegions'], { timeoutMs: 30000, maxRetries: 0 }).catch(
1371
+ (error) => ({
1372
+ ok: false,
1373
+ error: error.message,
1374
+ }),
1375
+ );
920
1376
  if (!result.ok) {
921
1377
  return {
922
1378
  ok: false,
@@ -936,7 +1392,7 @@ async function listRegions() {
936
1392
  note: 'hcloud unavailable. Showing static region list. For the complete list, visit the fallback URL.',
937
1393
  };
938
1394
  }
939
- let regions = [];
1395
+ let regions;
940
1396
  try {
941
1397
  const parsed = typeof result.stdout === 'string' ? JSON.parse(result.stdout) : result.stdout;
942
1398
  const rawRegions = parsed.regions || [];
@@ -954,37 +1410,128 @@ async function listRegions() {
954
1410
  }
955
1411
 
956
1412
  async function getRegionalAvailability(service, region) {
957
- const svc = String(service || '').toLowerCase().trim();
958
- const reg = String(region || '').toLowerCase().trim();
1413
+ const svc = String(service || '')
1414
+ .toLowerCase()
1415
+ .trim();
1416
+ const reg = String(region || '')
1417
+ .toLowerCase()
1418
+ .trim();
959
1419
  if (!svc || !reg) return { ok: false, error: 'Both service and region are required.' };
960
1420
  const known = {
961
- ecs: ['cn-south-1','cn-north-4','cn-north-1','cn-east-3','cn-east-2','ap-southeast-3','ap-southeast-2','ap-southeast-1','ap-southeast-4','af-south-1','tr-west-1','sa-brazil-1','la-north-2','na-mexico-1','me-east-1'],
962
- obs: ['cn-south-1','cn-north-4','cn-north-1','cn-east-3','cn-east-2','ap-southeast-3','ap-southeast-2','ap-southeast-1','af-south-1'],
963
- vpc: ['cn-south-1','cn-north-4','cn-north-1','cn-east-3','cn-east-2','ap-southeast-3','ap-southeast-2','ap-southeast-1','ap-southeast-4','af-south-1','tr-west-1','sa-brazil-1','la-north-2','me-east-1'],
1421
+ ecs: [
1422
+ 'cn-south-1',
1423
+ 'cn-north-4',
1424
+ 'cn-north-1',
1425
+ 'cn-east-3',
1426
+ 'cn-east-2',
1427
+ 'ap-southeast-3',
1428
+ 'ap-southeast-2',
1429
+ 'ap-southeast-1',
1430
+ 'ap-southeast-4',
1431
+ 'af-south-1',
1432
+ 'tr-west-1',
1433
+ 'sa-brazil-1',
1434
+ 'la-north-2',
1435
+ 'na-mexico-1',
1436
+ 'me-east-1',
1437
+ ],
1438
+ obs: [
1439
+ 'cn-south-1',
1440
+ 'cn-north-4',
1441
+ 'cn-north-1',
1442
+ 'cn-east-3',
1443
+ 'cn-east-2',
1444
+ 'ap-southeast-3',
1445
+ 'ap-southeast-2',
1446
+ 'ap-southeast-1',
1447
+ 'af-south-1',
1448
+ ],
1449
+ vpc: [
1450
+ 'cn-south-1',
1451
+ 'cn-north-4',
1452
+ 'cn-north-1',
1453
+ 'cn-east-3',
1454
+ 'cn-east-2',
1455
+ 'ap-southeast-3',
1456
+ 'ap-southeast-2',
1457
+ 'ap-southeast-1',
1458
+ 'ap-southeast-4',
1459
+ 'af-south-1',
1460
+ 'tr-west-1',
1461
+ 'sa-brazil-1',
1462
+ 'la-north-2',
1463
+ 'me-east-1',
1464
+ ],
964
1465
  iam: ['global'],
965
- rds: ['cn-south-1','cn-north-4','cn-north-1','cn-east-3','cn-east-2','ap-southeast-3','ap-southeast-2','ap-southeast-1'],
966
- gaussdb: ['cn-south-1','cn-north-4','cn-east-3'],
967
- cce: ['cn-south-1','cn-north-4','cn-north-1','cn-east-3','cn-east-2','ap-southeast-3','ap-southeast-2','ap-southeast-1'],
968
- modelarts: ['cn-south-1','cn-north-4','cn-east-3'],
969
- functiongraph: ['cn-south-1','cn-north-4','cn-north-1','cn-east-3','cn-east-2','ap-southeast-3','ap-southeast-2','ap-southeast-1'],
970
- dew: ['cn-south-1','cn-north-4','cn-east-3','ap-southeast-3'],
971
- smn: ['cn-south-1','cn-north-4','cn-east-3','ap-southeast-3'],
972
- ces: ['cn-south-1','cn-north-4','cn-east-3','ap-southeast-3'],
973
- cts: ['cn-south-1','cn-north-4','cn-east-3','ap-southeast-3'],
974
- apig: ['cn-south-1','cn-north-4','cn-east-3','ap-southeast-3'],
975
- cbr: ['cn-south-1','cn-north-4','cn-east-3','ap-southeast-3'],
976
- dds: ['cn-south-1','cn-north-4','cn-east-3','ap-southeast-3'],
977
- dcs: ['cn-south-1','cn-north-4','cn-east-3','ap-southeast-3'],
1466
+ rds: [
1467
+ 'cn-south-1',
1468
+ 'cn-north-4',
1469
+ 'cn-north-1',
1470
+ 'cn-east-3',
1471
+ 'cn-east-2',
1472
+ 'ap-southeast-3',
1473
+ 'ap-southeast-2',
1474
+ 'ap-southeast-1',
1475
+ ],
1476
+ gaussdb: ['cn-south-1', 'cn-north-4', 'cn-east-3'],
1477
+ cce: [
1478
+ 'cn-south-1',
1479
+ 'cn-north-4',
1480
+ 'cn-north-1',
1481
+ 'cn-east-3',
1482
+ 'cn-east-2',
1483
+ 'ap-southeast-3',
1484
+ 'ap-southeast-2',
1485
+ 'ap-southeast-1',
1486
+ ],
1487
+ modelarts: ['cn-south-1', 'cn-north-4', 'cn-east-3'],
1488
+ functiongraph: [
1489
+ 'cn-south-1',
1490
+ 'cn-north-4',
1491
+ 'cn-north-1',
1492
+ 'cn-east-3',
1493
+ 'cn-east-2',
1494
+ 'ap-southeast-3',
1495
+ 'ap-southeast-2',
1496
+ 'ap-southeast-1',
1497
+ ],
1498
+ dew: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
1499
+ smn: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
1500
+ ces: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
1501
+ cts: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
1502
+ apig: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
1503
+ cbr: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
1504
+ dds: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
1505
+ dcs: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
978
1506
  };
979
- if (!known[svc]) return { ok: false, service: svc, region: reg, available: false, note: 'Service ' + svc + ' is not in the regional availability cache. Run hcloud ' + svc.toUpperCase() + ' --help to verify, or check https://developer.huaweicloud.com/endpoint.' };
1507
+ if (!known[svc])
1508
+ return {
1509
+ ok: false,
1510
+ service: svc,
1511
+ region: reg,
1512
+ available: false,
1513
+ note:
1514
+ 'Service ' +
1515
+ svc +
1516
+ ' is not in the regional availability cache. Run hcloud ' +
1517
+ svc.toUpperCase() +
1518
+ ' --help to verify, or check https://developer.huaweicloud.com/endpoint.',
1519
+ };
980
1520
  const available = known[svc].includes(reg) || known[svc].includes('global');
981
1521
  return {
982
- ok: true, service: svc, region: reg, available,
1522
+ ok: true,
1523
+ service: svc,
1524
+ region: reg,
1525
+ available,
983
1526
  note: available
984
1527
  ? svc + ' is available in ' + reg + '.'
985
- : svc + ' availability in ' + reg + ' could not be confirmed. Verify at https://developer.huaweicloud.com/endpoint.',
1528
+ : svc +
1529
+ ' availability in ' +
1530
+ reg +
1531
+ ' could not be confirmed. Verify at https://developer.huaweicloud.com/endpoint.',
986
1532
  sourcedFrom: 'static cache, update via npm package upgrade',
987
- disclaimer: 'This result reflects service-level availability only. It does NOT guarantee that your IAM user has permissions in this region. Account-level restrictions (e.g., APIGW.0802) may block actual API calls even when the service is available.',
1533
+ disclaimer:
1534
+ 'This result reflects service-level availability only. It does NOT guarantee that your IAM user has permissions in this region. Account-level restrictions (e.g., APIGW.0802) may block actual API calls even when the service is available.',
988
1535
  };
989
1536
  }
990
1537