huaweicloud-devkit 1.0.2-next.2 → 1.0.2-next.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +18 -18
- package/README.md +81 -51
- package/README.zh-CN.md +81 -49
- package/bin/setup.cjs +5 -3
- package/integrations/opencode/opencode.json +1 -4
- package/package.json +61 -44
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
- package/plugins/huaweicloud-core/.mcp.json +2 -5
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -18
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +32 -9
- package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
- package/plugins/huaweicloud-core/safety/policy.json +75 -99
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +41 -13
- package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +34 -34
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +18 -18
- package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +34 -32
- package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +30 -30
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +13 -12
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +64 -17
- package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +20 -20
- package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +31 -31
- package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +21 -20
- package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +39 -30
- package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +8 -1
- package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +6 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +39 -36
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +15 -4
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +11 -9
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +13 -13
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +40 -39
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +12 -10
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +21 -20
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +20 -20
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +27 -23
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +27 -27
- package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +65 -39
- package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +9 -9
- package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +25 -25
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +45 -44
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +12 -7
- package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +13 -12
- package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +4 -4
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +63 -58
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +127 -42
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +26 -26
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +39 -39
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +13 -12
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +20 -19
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +15 -15
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +18 -18
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +13 -13
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +17 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +27 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +56 -53
- package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +6 -6
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +15 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +0 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +33 -33
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +108 -5
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +77 -4
- package/plugins/huaweicloud-core/src/auth/service.mjs +1 -1
- package/plugins/huaweicloud-core/src/data/icons-manifest.v1.json +3517 -0
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +25 -7
- package/plugins/huaweicloud-core/src/icon-library.mjs +171 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +30 -3
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +20 -102
- package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +11 -4
- package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +4 -1
- package/plugins/huaweicloud-core/src/safety-policy.mjs +122 -68
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +9 -9
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +24 -26
- package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +510 -10
- package/plugins/huaweicloud-core/src/search-market.mjs +46 -12
- package/plugins/huaweicloud-core/src/setup-cli.mjs +1251 -181
- package/plugins/huaweicloud-core/src/tools.mjs +600 -122
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +72 -38
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +10 -5
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +12 -15
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +12 -16
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +46 -31
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +267 -0
- package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +9 -5
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import { planHcloudCommand, runHcloud } from './hcloud-cli.mjs';
|
|
2
2
|
import { classifyTextCommand, redactSecrets } from './safety-policy.mjs';
|
|
3
3
|
import { evaluateArtifacts, evaluateCommandRisk, evaluateDeployPlan } from './risk-rule-engine.mjs';
|
|
4
|
-
import { readFileSync, readdirSync, existsSync, writeFileSync
|
|
4
|
+
import { readFileSync, readdirSync, existsSync, writeFileSync } from 'node:fs';
|
|
5
5
|
import { join, dirname } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
|
+
import { spawnSync } from 'node:child_process';
|
|
8
9
|
import { searchMarketplace } from './search-market.mjs';
|
|
9
|
-
import {
|
|
10
|
+
import { getServiceIcon } from './icon-library.mjs';
|
|
11
|
+
import {
|
|
12
|
+
execWithSession,
|
|
13
|
+
execOneShot,
|
|
14
|
+
closeSession,
|
|
15
|
+
uploadFileWithSession,
|
|
16
|
+
uploadProjectWithSession,
|
|
17
|
+
currentWorkspaceId,
|
|
18
|
+
setWorkspaceId,
|
|
19
|
+
} from './sandbox/session-manager.mjs';
|
|
10
20
|
import { hdkitCheckUser, hdkitSignAgreement, hdkitConnect, hdkitCredentials } from './sandbox/hdkitservice-api.mjs';
|
|
11
21
|
import { getAuthStatus, syncAuth } from './auth/service.mjs';
|
|
12
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
readGlobalCredentials,
|
|
24
|
+
writeObsConfig as writeObsConfigFile,
|
|
25
|
+
setRuntimeCredentials,
|
|
26
|
+
clearRuntimeCredentials,
|
|
27
|
+
} from './auth/credentials.mjs';
|
|
13
28
|
|
|
14
29
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
30
|
const SKILLS_ROOT_DEV = join(__dirname, '..', 'skills');
|
|
@@ -29,13 +44,82 @@ function dshSkillsDir() {
|
|
|
29
44
|
const home = process.env.DSH_HOME || join(homedir(), '.dsh');
|
|
30
45
|
return join(home, 'skills');
|
|
31
46
|
}
|
|
47
|
+
function readOfficeaceRegistryInstallDir() {
|
|
48
|
+
if (process.platform !== 'win32') return null;
|
|
49
|
+
try {
|
|
50
|
+
const r = spawnSync('reg', ['query', 'HKCU\\SOFTWARE\\OfficeAce\\OfficeAce', '/v', 'InstallDir'], {
|
|
51
|
+
encoding: 'utf8',
|
|
52
|
+
windowsHide: true,
|
|
53
|
+
timeout: 5000,
|
|
54
|
+
});
|
|
55
|
+
if (r.status === 0) {
|
|
56
|
+
const m = r.stdout.match(/InstallDir\s+REG_SZ\s+(.+)/);
|
|
57
|
+
if (m) return m[1].trim();
|
|
58
|
+
}
|
|
59
|
+
} catch {}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function officeaceSkillsRoot() {
|
|
64
|
+
const configRoot = process.env.OFFICE_CLAW_CONFIG_ROOT;
|
|
65
|
+
if (configRoot && existsSync(join(configRoot, 'capabilities.json'))) return join(configRoot, 'skills');
|
|
66
|
+
const regDir = readOfficeaceRegistryInstallDir();
|
|
67
|
+
if (regDir) {
|
|
68
|
+
const dir = join(regDir, '.office-claw', 'skills');
|
|
69
|
+
if (existsSync(dir)) return dir;
|
|
70
|
+
}
|
|
71
|
+
if (process.platform === 'win32') {
|
|
72
|
+
const bases = [process.env.ProgramFiles, 'C:\\Program Files', 'D:\\Program Files'];
|
|
73
|
+
if (process.env.LOCALAPPDATA) bases.push(join(process.env.LOCALAPPDATA, 'Programs'));
|
|
74
|
+
for (const base of bases) {
|
|
75
|
+
if (!base) continue;
|
|
76
|
+
const dir = join(base, 'OfficeAce', '.office-claw', 'skills');
|
|
77
|
+
if (existsSync(dir)) return dir;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function hermesSkillsDir() {
|
|
84
|
+
if (process.env.HERMES_HOME) return join(process.env.HERMES_HOME, 'skills');
|
|
85
|
+
// Hermes on Windows stores under LOCALAPPDATA, not ~/.hermes
|
|
86
|
+
if (process.platform === 'win32' && process.env.LOCALAPPDATA) {
|
|
87
|
+
return join(process.env.LOCALAPPDATA, 'hermes', 'skills');
|
|
88
|
+
}
|
|
89
|
+
const home = homedir();
|
|
90
|
+
return join(home, '.hermes', 'skills');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function listSkillDirs(root) {
|
|
94
|
+
if (!existsSync(root)) return [];
|
|
95
|
+
try {
|
|
96
|
+
return readdirSync(root, { withFileTypes: true })
|
|
97
|
+
.filter((d) => (d.isDirectory() || d.isSymbolicLink()) && existsSync(join(root, d.name, 'SKILL.md')))
|
|
98
|
+
.map((d) => d.name);
|
|
99
|
+
} catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function findSkillsRoot(candidates) {
|
|
105
|
+
for (const dir of candidates) {
|
|
106
|
+
if (listSkillDirs(dir).length > 0) return dir;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
32
111
|
function resolveSkillsRoot() {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
112
|
+
return (
|
|
113
|
+
findSkillsRoot([
|
|
114
|
+
SKILLS_ROOT_DEV,
|
|
115
|
+
dshSkillsDir(),
|
|
116
|
+
codeartsSkillsDir(),
|
|
117
|
+
opencodeSkillsDir(),
|
|
118
|
+
workbuddySkillsDir(),
|
|
119
|
+
officeaceSkillsRoot(),
|
|
120
|
+
hermesSkillsDir(),
|
|
121
|
+
]) || SKILLS_ROOT_DEV
|
|
122
|
+
);
|
|
39
123
|
}
|
|
40
124
|
const SKILLS_ROOT = resolveSkillsRoot();
|
|
41
125
|
|
|
@@ -95,7 +179,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
95
179
|
},
|
|
96
180
|
{
|
|
97
181
|
name: 'huaweicloud_list_operations',
|
|
98
|
-
description:
|
|
182
|
+
description:
|
|
183
|
+
'List KooCLI operations for a Huawei Cloud service by running local/read-only hcloud <Service> --help.',
|
|
99
184
|
inputSchema: {
|
|
100
185
|
type: 'object',
|
|
101
186
|
required: ['service'],
|
|
@@ -113,7 +198,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
113
198
|
},
|
|
114
199
|
{
|
|
115
200
|
name: 'huaweicloud_run_approved_command',
|
|
116
|
-
description:
|
|
201
|
+
description:
|
|
202
|
+
'Run a write-capable hcloud command only after the exact command has been shown and explicitly approved by the user.',
|
|
117
203
|
inputSchema: {
|
|
118
204
|
type: 'object',
|
|
119
205
|
required: ['args', 'approvedCommand', 'approvedByUser'],
|
|
@@ -193,7 +279,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
193
279
|
},
|
|
194
280
|
{
|
|
195
281
|
name: 'huaweicloud_hook_check_deploy_plan',
|
|
196
|
-
description:
|
|
282
|
+
description:
|
|
283
|
+
'Check a structured or textual deployment plan for Huawei Cloud sandbox, exposure, IAM, and cost risks.',
|
|
197
284
|
inputSchema: {
|
|
198
285
|
type: 'object',
|
|
199
286
|
required: ['plan'],
|
|
@@ -232,19 +319,24 @@ export const TOOL_DEFINITIONS = [
|
|
|
232
319
|
},
|
|
233
320
|
{
|
|
234
321
|
name: 'huaweicloud_search_docs',
|
|
235
|
-
description:
|
|
322
|
+
description:
|
|
323
|
+
'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.',
|
|
236
324
|
inputSchema: {
|
|
237
325
|
type: 'object',
|
|
238
326
|
required: ['query'],
|
|
239
327
|
properties: {
|
|
240
328
|
query: { type: 'string', description: 'Search query across skill descriptions and documentation.' },
|
|
241
|
-
topic: {
|
|
329
|
+
topic: {
|
|
330
|
+
type: 'string',
|
|
331
|
+
description: 'Optional filter: all | ecs | obs | vpc | iam | rds | cce | modelarts | dew. Defaults to all.',
|
|
332
|
+
},
|
|
242
333
|
},
|
|
243
334
|
},
|
|
244
335
|
},
|
|
245
336
|
{
|
|
246
337
|
name: 'huaweicloud_retrieve_skill',
|
|
247
|
-
description:
|
|
338
|
+
description:
|
|
339
|
+
'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.',
|
|
248
340
|
inputSchema: {
|
|
249
341
|
type: 'object',
|
|
250
342
|
required: ['name'],
|
|
@@ -255,7 +347,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
255
347
|
},
|
|
256
348
|
{
|
|
257
349
|
name: 'huaweicloud_list_regions',
|
|
258
|
-
description:
|
|
350
|
+
description:
|
|
351
|
+
'List available Huawei Cloud regions. Returns region IDs, display names, and endpoints. Use when the agent needs to discover available regions before creating resources.',
|
|
259
352
|
inputSchema: {
|
|
260
353
|
type: 'object',
|
|
261
354
|
properties: {},
|
|
@@ -263,30 +356,58 @@ export const TOOL_DEFINITIONS = [
|
|
|
263
356
|
},
|
|
264
357
|
{
|
|
265
358
|
name: 'huaweicloud_get_regional_availability',
|
|
266
|
-
description:
|
|
359
|
+
description:
|
|
360
|
+
'Check if a specific Huawei Cloud service is available in a target region. Use before creating resources to prevent failures from regional unavailability.',
|
|
267
361
|
inputSchema: {
|
|
268
362
|
type: 'object',
|
|
269
363
|
required: ['service', 'region'],
|
|
270
364
|
properties: {
|
|
271
|
-
service: {
|
|
365
|
+
service: {
|
|
366
|
+
type: 'string',
|
|
367
|
+
description: 'Service name: ecs, obs, rds, gaussdb, cce, modelarts, functiongraph, etc.',
|
|
368
|
+
},
|
|
272
369
|
region: { type: 'string', description: 'Region ID: cn-south-1, cn-north-4, ap-southeast-3, etc.' },
|
|
273
370
|
},
|
|
274
371
|
},
|
|
275
372
|
},
|
|
276
373
|
{
|
|
277
374
|
name: 'huaweicloud_search_marketplace',
|
|
278
|
-
description:
|
|
375
|
+
description:
|
|
376
|
+
'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.',
|
|
279
377
|
inputSchema: {
|
|
280
378
|
type: 'object',
|
|
281
379
|
properties: {
|
|
282
380
|
query: { type: 'string', description: 'Search query across skill name, description, triggers, and service.' },
|
|
283
|
-
category: {
|
|
381
|
+
category: {
|
|
382
|
+
type: 'string',
|
|
383
|
+
description: 'Optional category filter: computing, storage, network, security, devtools, monitoring, etc.',
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: 'huaweicloud_get_service_icon',
|
|
390
|
+
description:
|
|
391
|
+
'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.',
|
|
392
|
+
inputSchema: {
|
|
393
|
+
type: 'object',
|
|
394
|
+
properties: {
|
|
395
|
+
service: {
|
|
396
|
+
type: 'string',
|
|
397
|
+
description:
|
|
398
|
+
'Service name, alias, or Chinese name, e.g. ecs, obs, modelarts, 对象存储, 虚拟私有云. Omit to browse by category only.',
|
|
399
|
+
},
|
|
400
|
+
category: {
|
|
401
|
+
type: 'string',
|
|
402
|
+
description: 'Optional category filter, e.g. 计算, 存储, 网络, 人工智能, 数据库, 安全, 企业应用.',
|
|
403
|
+
},
|
|
284
404
|
},
|
|
285
405
|
},
|
|
286
406
|
},
|
|
287
407
|
{
|
|
288
408
|
name: 'huaweicloud_setup_obs_config',
|
|
289
|
-
description:
|
|
409
|
+
description:
|
|
410
|
+
'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.',
|
|
290
411
|
inputSchema: {
|
|
291
412
|
type: 'object',
|
|
292
413
|
properties: {
|
|
@@ -296,27 +417,52 @@ export const TOOL_DEFINITIONS = [
|
|
|
296
417
|
},
|
|
297
418
|
{
|
|
298
419
|
name: 'huaweicloud_auth_status',
|
|
299
|
-
description:
|
|
420
|
+
description:
|
|
421
|
+
'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.',
|
|
300
422
|
inputSchema: {
|
|
301
423
|
type: 'object',
|
|
302
424
|
properties: {
|
|
303
|
-
target: {
|
|
425
|
+
target: {
|
|
426
|
+
type: 'string',
|
|
427
|
+
description:
|
|
428
|
+
'Agent target to check: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, hermes, openclaw, or all (default).',
|
|
429
|
+
},
|
|
304
430
|
},
|
|
305
431
|
},
|
|
306
432
|
},
|
|
307
433
|
{
|
|
308
434
|
name: 'huaweicloud_auth_sync',
|
|
309
|
-
description:
|
|
435
|
+
description:
|
|
436
|
+
'Synchronize credentials from the global Huawei Cloud credential vault to OBS and report agent registration status. Does not write secrets into any agent config.',
|
|
437
|
+
inputSchema: {
|
|
438
|
+
type: 'object',
|
|
439
|
+
properties: {
|
|
440
|
+
target: {
|
|
441
|
+
type: 'string',
|
|
442
|
+
description:
|
|
443
|
+
'Agent target to report after sync: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, hermes, or all (default).',
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
name: 'huaweicloud_auth_init',
|
|
450
|
+
description:
|
|
451
|
+
'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.',
|
|
310
452
|
inputSchema: {
|
|
311
453
|
type: 'object',
|
|
312
454
|
properties: {
|
|
313
|
-
|
|
455
|
+
ak: { type: 'string', description: 'Huawei Cloud Access Key (required unless clear=true)' },
|
|
456
|
+
sk: { type: 'string', description: 'Huawei Cloud Secret Key (required unless clear=true)' },
|
|
457
|
+
region: { type: 'string', description: 'Default region (optional)' },
|
|
458
|
+
clear: { type: 'boolean', description: 'Set to true to clear runtime credentials and revert to env/file' },
|
|
314
459
|
},
|
|
315
460
|
},
|
|
316
461
|
},
|
|
317
462
|
{
|
|
318
463
|
name: 'huaweicloud_sandbox_exec_with_session',
|
|
319
|
-
description:
|
|
464
|
+
description:
|
|
465
|
+
'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.',
|
|
320
466
|
inputSchema: {
|
|
321
467
|
type: 'object',
|
|
322
468
|
required: ['command'],
|
|
@@ -324,7 +470,22 @@ export const TOOL_DEFINITIONS = [
|
|
|
324
470
|
command: { type: 'string', description: 'The shell command to execute on the remote workspace' },
|
|
325
471
|
workspace_id: { type: 'string', description: 'The workspace ID' },
|
|
326
472
|
username: { type: 'string', description: 'Login username for the remote terminal (default: root)' },
|
|
327
|
-
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default:
|
|
473
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 120000)' },
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
name: 'huaweicloud_sandbox_exec_one_shot',
|
|
479
|
+
description:
|
|
480
|
+
'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.',
|
|
481
|
+
inputSchema: {
|
|
482
|
+
type: 'object',
|
|
483
|
+
required: ['command'],
|
|
484
|
+
properties: {
|
|
485
|
+
command: { type: 'string', description: 'The shell command to execute on the remote workspace' },
|
|
486
|
+
workspace_id: { type: 'string', description: 'The workspace ID' },
|
|
487
|
+
username: { type: 'string', description: 'Login username for the remote terminal (default: root)' },
|
|
488
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 120000)' },
|
|
328
489
|
},
|
|
329
490
|
},
|
|
330
491
|
},
|
|
@@ -339,9 +500,55 @@ export const TOOL_DEFINITIONS = [
|
|
|
339
500
|
},
|
|
340
501
|
},
|
|
341
502
|
},
|
|
503
|
+
{
|
|
504
|
+
name: 'huaweicloud_sandbox_upload_file',
|
|
505
|
+
description:
|
|
506
|
+
'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.',
|
|
507
|
+
inputSchema: {
|
|
508
|
+
type: 'object',
|
|
509
|
+
required: ['local_path', 'remote_path'],
|
|
510
|
+
properties: {
|
|
511
|
+
local_path: { type: 'string', description: 'Absolute path to the local file to upload.' },
|
|
512
|
+
remote_path: { type: 'string', description: 'Target path in the sandbox, e.g. /workspace/<repo>/index.html.' },
|
|
513
|
+
workspace_id: { type: 'string', description: 'The workspace ID' },
|
|
514
|
+
username: { type: 'string', description: 'Login username (default: root)' },
|
|
515
|
+
timeout_ms: { type: 'number', description: 'Per-command execution timeout in milliseconds (default: 120000)' },
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
name: 'huaweicloud_sandbox_upload_project',
|
|
521
|
+
description:
|
|
522
|
+
'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.',
|
|
523
|
+
inputSchema: {
|
|
524
|
+
type: 'object',
|
|
525
|
+
required: ['local_dir'],
|
|
526
|
+
properties: {
|
|
527
|
+
local_dir: { type: 'string', description: 'Local project directory to upload.' },
|
|
528
|
+
remote_dir: {
|
|
529
|
+
type: 'string',
|
|
530
|
+
description:
|
|
531
|
+
'Remote parent directory where project will be extracted (default: /workspace). Final layout: <remote_dir>/<dirname>/',
|
|
532
|
+
},
|
|
533
|
+
workspace_id: { type: 'string', description: 'Workspace ID (defaults to HW_WORKSPACE_ID env var)' },
|
|
534
|
+
username: { type: 'string', description: 'Login username (default: root)' },
|
|
535
|
+
exclude: {
|
|
536
|
+
type: 'array',
|
|
537
|
+
items: { type: 'string' },
|
|
538
|
+
description: 'Patterns to exclude from archive (default: .git, node_modules, __pycache__, .venv)',
|
|
539
|
+
},
|
|
540
|
+
extract: {
|
|
541
|
+
type: 'boolean',
|
|
542
|
+
description: 'Extract tar.gz on sandbox after upload (default: true)',
|
|
543
|
+
},
|
|
544
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 300000)' },
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
},
|
|
342
548
|
{
|
|
343
549
|
name: 'huaweicloud_sandbox_check_user',
|
|
344
|
-
description:
|
|
550
|
+
description:
|
|
551
|
+
'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.',
|
|
345
552
|
inputSchema: {
|
|
346
553
|
type: 'object',
|
|
347
554
|
properties: {},
|
|
@@ -349,7 +556,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
349
556
|
},
|
|
350
557
|
{
|
|
351
558
|
name: 'huaweicloud_sandbox_sign_agreement',
|
|
352
|
-
description:
|
|
559
|
+
description:
|
|
560
|
+
"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.",
|
|
353
561
|
inputSchema: {
|
|
354
562
|
type: 'object',
|
|
355
563
|
properties: {},
|
|
@@ -357,11 +565,16 @@ export const TOOL_DEFINITIONS = [
|
|
|
357
565
|
},
|
|
358
566
|
{
|
|
359
567
|
name: 'huaweicloud_sandbox_connect',
|
|
360
|
-
description:
|
|
568
|
+
description:
|
|
569
|
+
'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.',
|
|
361
570
|
inputSchema: {
|
|
362
571
|
type: 'object',
|
|
363
572
|
properties: {
|
|
364
|
-
source: {
|
|
573
|
+
source: {
|
|
574
|
+
type: 'string',
|
|
575
|
+
description:
|
|
576
|
+
'Source identifier (default: WEB). Options: VSCODE, CLI, WEB, WEBVNC, WEBPTY, WEBIDE, CURSOR, etc.',
|
|
577
|
+
},
|
|
365
578
|
template_id: { type: 'string', description: 'Template ID; overrides server default (only for new sandbox)' },
|
|
366
579
|
flavor_id: { type: 'string', description: 'Flavor ID; overrides server default (only for new sandbox)' },
|
|
367
580
|
env: { type: 'object', description: 'Environment variables to set in the sandbox (only for new sandbox)' },
|
|
@@ -381,7 +594,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
381
594
|
},
|
|
382
595
|
{
|
|
383
596
|
name: 'huaweicloud_sandbox_credentials',
|
|
384
|
-
description:
|
|
597
|
+
description:
|
|
598
|
+
'Configure temporary AK/SK for a sandbox via hdkitservice. Injects temporary credentials into the sandbox. The sandbox must be in RUNNING state.',
|
|
385
599
|
inputSchema: {
|
|
386
600
|
type: 'object',
|
|
387
601
|
properties: {
|
|
@@ -391,7 +605,6 @@ export const TOOL_DEFINITIONS = [
|
|
|
391
605
|
},
|
|
392
606
|
},
|
|
393
607
|
},
|
|
394
|
-
|
|
395
608
|
];
|
|
396
609
|
|
|
397
610
|
export async function callTool(name, args = {}) {
|
|
@@ -429,35 +642,93 @@ export async function callTool(name, args = {}) {
|
|
|
429
642
|
return listRegions();
|
|
430
643
|
case 'huaweicloud_get_regional_availability':
|
|
431
644
|
return getRegionalAvailability(args.service || '', args.region || '');
|
|
432
|
-
|
|
645
|
+
case 'huaweicloud_explain_error':
|
|
433
646
|
return explainError(args);
|
|
434
647
|
case 'huaweicloud_search_marketplace':
|
|
435
648
|
return searchMarketplace(args.query || '', args.category || '');
|
|
649
|
+
case 'huaweicloud_get_service_icon':
|
|
650
|
+
return getServiceIcon(args.service || '', args.category || '');
|
|
436
651
|
case 'huaweicloud_setup_obs_config':
|
|
437
652
|
return setupObsConfig(args.profile);
|
|
438
653
|
case 'huaweicloud_auth_status':
|
|
439
654
|
return getAuthStatus(args.target || 'all');
|
|
440
655
|
case 'huaweicloud_auth_sync':
|
|
441
656
|
return syncAuth(args.target || 'all');
|
|
657
|
+
case 'huaweicloud_auth_init':
|
|
658
|
+
if (args.clear) {
|
|
659
|
+
clearRuntimeCredentials();
|
|
660
|
+
return { status: 'cleared', message: 'Runtime credentials cleared. Fallback to env/file.' };
|
|
661
|
+
}
|
|
662
|
+
if (!args.ak || !args.sk) {
|
|
663
|
+
throw new Error('ak and sk are required. Set clear=true to clear runtime credentials.');
|
|
664
|
+
}
|
|
665
|
+
setRuntimeCredentials(args.ak, args.sk, undefined, args.region);
|
|
666
|
+
return { status: 'ok', message: 'Runtime credentials set for this MCP session.' };
|
|
442
667
|
case 'huaweicloud_sandbox_exec_with_session': {
|
|
443
|
-
const sandboxWsId2 = args.workspace_id ||
|
|
668
|
+
const sandboxWsId2 = args.workspace_id || currentWorkspaceId;
|
|
444
669
|
const sandboxUser2 = args.username || 'root';
|
|
445
|
-
const sandboxTimeout2 = args.timeout_ms ||
|
|
670
|
+
const sandboxTimeout2 = args.timeout_ms || 120000;
|
|
446
671
|
const sandboxResult2 = await execWithSession(sandboxWsId2, args.command, sandboxUser2, sandboxTimeout2);
|
|
447
672
|
return { stdout: sandboxResult2.stdout, exitCode: sandboxResult2.exitCode };
|
|
448
673
|
}
|
|
449
|
-
case '
|
|
450
|
-
const sandboxWsId3 = args.workspace_id ||
|
|
674
|
+
case 'huaweicloud_sandbox_exec_one_shot': {
|
|
675
|
+
const sandboxWsId3 = args.workspace_id || currentWorkspaceId;
|
|
451
676
|
const sandboxUser3 = args.username || 'root';
|
|
452
|
-
const
|
|
677
|
+
const sandboxTimeout3 = args.timeout_ms || 120000;
|
|
678
|
+
const sandboxResult3 = await execOneShot(sandboxWsId3, args.command, sandboxUser3, sandboxTimeout3);
|
|
679
|
+
return { stdout: sandboxResult3.stdout, exitCode: sandboxResult3.exitCode };
|
|
680
|
+
}
|
|
681
|
+
case 'huaweicloud_sandbox_close_session': {
|
|
682
|
+
const sandboxWsId4 = args.workspace_id || currentWorkspaceId;
|
|
683
|
+
const sandboxUser4 = args.username || 'root';
|
|
684
|
+
const closed = await closeSession(sandboxWsId4, sandboxUser4);
|
|
453
685
|
return closed ? 'ok' : 'not_connected';
|
|
454
686
|
}
|
|
687
|
+
case 'huaweicloud_sandbox_upload_file': {
|
|
688
|
+
if (!args.local_path || !args.remote_path) {
|
|
689
|
+
throw new Error('local_path and remote_path are required.');
|
|
690
|
+
}
|
|
691
|
+
const sandboxWsId5 = args.workspace_id || currentWorkspaceId;
|
|
692
|
+
const sandboxUser5 = args.username || 'root';
|
|
693
|
+
const sandboxTimeout5 = args.timeout_ms || 120000;
|
|
694
|
+
return await uploadFileWithSession(
|
|
695
|
+
sandboxWsId5,
|
|
696
|
+
args.local_path,
|
|
697
|
+
args.remote_path,
|
|
698
|
+
sandboxUser5,
|
|
699
|
+
sandboxTimeout5,
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
case 'huaweicloud_sandbox_upload_project': {
|
|
703
|
+
if (!args.local_dir) {
|
|
704
|
+
throw new Error('local_dir is required.');
|
|
705
|
+
}
|
|
706
|
+
const sandboxWsId6 = args.workspace_id || currentWorkspaceId;
|
|
707
|
+
const sandboxUser6 = args.username || 'root';
|
|
708
|
+
const sandboxTimeout6 = args.timeout_ms || 120000;
|
|
709
|
+
return await uploadProjectWithSession(
|
|
710
|
+
sandboxWsId6,
|
|
711
|
+
args.local_dir,
|
|
712
|
+
args.remote_dir,
|
|
713
|
+
sandboxUser6,
|
|
714
|
+
sandboxTimeout6,
|
|
715
|
+
{
|
|
716
|
+
exclude: args.exclude,
|
|
717
|
+
extract: args.extract,
|
|
718
|
+
},
|
|
719
|
+
);
|
|
720
|
+
}
|
|
455
721
|
case 'huaweicloud_sandbox_check_user':
|
|
456
722
|
return await hdkitCheckUser();
|
|
457
723
|
case 'huaweicloud_sandbox_sign_agreement':
|
|
458
724
|
return await hdkitSignAgreement();
|
|
459
|
-
case 'huaweicloud_sandbox_connect':
|
|
460
|
-
|
|
725
|
+
case 'huaweicloud_sandbox_connect': {
|
|
726
|
+
const connectResult = await hdkitConnect(args);
|
|
727
|
+
if (connectResult?.dev_stage_id) {
|
|
728
|
+
setWorkspaceId(connectResult.dev_stage_id);
|
|
729
|
+
}
|
|
730
|
+
return connectResult;
|
|
731
|
+
}
|
|
461
732
|
case 'huaweicloud_sandbox_credentials':
|
|
462
733
|
return await hdkitCredentials(args.session_id, args.dev_stage_id, args.enable_sts !== false);
|
|
463
734
|
default:
|
|
@@ -470,11 +741,12 @@ function hookResult(result) {
|
|
|
470
741
|
ok: result.decision !== 'deny',
|
|
471
742
|
decision: result.decision,
|
|
472
743
|
findings: result.findings,
|
|
473
|
-
nextStep:
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
744
|
+
nextStep:
|
|
745
|
+
result.decision === 'deny'
|
|
746
|
+
? 'Revise the command, artifact, or deployment plan before execution.'
|
|
747
|
+
: result.decision === 'warn'
|
|
748
|
+
? 'Review the warnings with the user before proceeding.'
|
|
749
|
+
: 'No Huawei Cloud hook risk rule matched.',
|
|
478
750
|
};
|
|
479
751
|
}
|
|
480
752
|
|
|
@@ -495,7 +767,8 @@ export async function runVersionCheck(options = {}) {
|
|
|
495
767
|
: isSpawnError
|
|
496
768
|
? 'hcloud executable not found. Set HCLOUD_BIN to the full hcloud path, or install KooCLI: npx huaweicloud-devkit install-hcloud. Then restart the agent.'
|
|
497
769
|
: 'Install Huawei Cloud KooCLI: npx huaweicloud-devkit install-hcloud. Configure credentials outside the agent conversation.',
|
|
498
|
-
authHint:
|
|
770
|
+
authHint:
|
|
771
|
+
'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.',
|
|
499
772
|
};
|
|
500
773
|
}
|
|
501
774
|
|
|
@@ -514,7 +787,8 @@ async function showProfileRedacted(profile) {
|
|
|
514
787
|
ok: false,
|
|
515
788
|
blockedByPolicy: true,
|
|
516
789
|
reason: result.reason,
|
|
517
|
-
safeAlternative:
|
|
790
|
+
safeAlternative:
|
|
791
|
+
'Use huaweicloud_show_profile_redacted so profile output is returned through the redaction pipeline.',
|
|
518
792
|
};
|
|
519
793
|
}
|
|
520
794
|
return {
|
|
@@ -556,7 +830,12 @@ async function setupObsConfig(profile) {
|
|
|
556
830
|
async function setupObsConfigFromHcloud(profile) {
|
|
557
831
|
const obsConfigPath = join(homedir(), '.obsutilconfig');
|
|
558
832
|
if (existsSync(obsConfigPath)) {
|
|
559
|
-
return {
|
|
833
|
+
return {
|
|
834
|
+
ok: true,
|
|
835
|
+
existed: true,
|
|
836
|
+
path: obsConfigPath,
|
|
837
|
+
note: 'OBS config already exists. Delete ~/.obsutilconfig first if you need to re-sync.',
|
|
838
|
+
};
|
|
560
839
|
}
|
|
561
840
|
|
|
562
841
|
const args = ['configure', 'show'];
|
|
@@ -572,9 +851,9 @@ async function setupObsConfigFromHcloud(profile) {
|
|
|
572
851
|
};
|
|
573
852
|
}
|
|
574
853
|
|
|
575
|
-
let accessKeyId
|
|
576
|
-
let secretAccessKey
|
|
577
|
-
let region
|
|
854
|
+
let accessKeyId;
|
|
855
|
+
let secretAccessKey;
|
|
856
|
+
let region;
|
|
578
857
|
|
|
579
858
|
try {
|
|
580
859
|
const parsed = typeof result.stdout === 'string' ? JSON.parse(result.stdout) : result.stdout;
|
|
@@ -635,12 +914,16 @@ async function setupObsConfigFromHcloud(profile) {
|
|
|
635
914
|
const SERVICE_EXAMPLES = {
|
|
636
915
|
ECS: { list: 'ECS ListServersDetails', create: 'ECS CreateServers', show: 'IMS GlanceShowImage' },
|
|
637
916
|
VPC: { list: 'VPC ListVpcs', create: 'VPC CreateVpc', show: 'VPC ShowVpc' },
|
|
638
|
-
|
|
917
|
+
FUNCTIONGRAPH: {
|
|
918
|
+
list: 'FunctionGraph ListFunctions',
|
|
919
|
+
create: 'FunctionGraph CreateFunction',
|
|
920
|
+
show: 'FunctionGraph ShowFunctionConfig',
|
|
921
|
+
},
|
|
639
922
|
APIG: { list: 'APIG ListInstancesV2', create: 'APIG CreateInstanceV2', show: 'APIG ShowDetailsOfInstanceV2' },
|
|
640
923
|
OBS: { list: 'OBS ls', create: 'OBS mb obs://<bucket>', show: 'OBS stat obs://<bucket>/<key>' },
|
|
641
924
|
RDS: { list: 'RDS ListInstances', create: 'RDS CreateInstance', show: 'RDS ShowInstance' },
|
|
642
925
|
CES: { list: 'CES ListAlarms', create: 'CES CreateAlarm', show: 'CES ListMetrics' },
|
|
643
|
-
|
|
926
|
+
GAUSSDB: { list: 'GaussDB ListInstances', create: 'GaussDB CreateInstance', show: 'GaussDB ShowInstance' },
|
|
644
927
|
DDS: { list: 'DDS ListInstances', create: 'DDS CreateInstance', show: 'DDS ShowInstance' },
|
|
645
928
|
DCS: { list: 'DCS ListInstances', create: 'DCS CreateInstance', show: 'DCS ShowInstance' },
|
|
646
929
|
};
|
|
@@ -667,7 +950,9 @@ async function listOperations(service, options = {}) {
|
|
|
667
950
|
service: serviceName,
|
|
668
951
|
command: isObs ? 'hcloud obs help' : `hcloud ${svc} --help`,
|
|
669
952
|
selectionRule: 'Use this help text to select the exact KooCLI operation name before planning any service command.',
|
|
670
|
-
examples: SERVICE_EXAMPLES[serviceName.toUpperCase()] || {
|
|
953
|
+
examples: SERVICE_EXAMPLES[serviceName.toUpperCase()] || {
|
|
954
|
+
note: `No cached examples for ${serviceName}. Use the help text above to discover available operations.`,
|
|
955
|
+
},
|
|
671
956
|
result,
|
|
672
957
|
};
|
|
673
958
|
}
|
|
@@ -696,32 +981,110 @@ async function runApprovedCommand(args = {}) {
|
|
|
696
981
|
function serviceCatalog(intent = '') {
|
|
697
982
|
const it = String(intent).toLowerCase();
|
|
698
983
|
const routeMap = [
|
|
699
|
-
{
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
984
|
+
{
|
|
985
|
+
keywords: ['ecs', 'server', 'vm', 'instance', 'compute', 'flavor', 'image'],
|
|
986
|
+
skills: ['huawei-ecs'],
|
|
987
|
+
services: ['ECS'],
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
keywords: ['vpc', 'subnet', 'network', 'security group', 'eip', 'nat', 'vpn', 'bandwidth'],
|
|
991
|
+
skills: ['huawei-vpc'],
|
|
992
|
+
services: ['VPC', 'EIP'],
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
keywords: ['obs', 'bucket', 'storage', 'object', 'static website', 'static site', 'hosting'],
|
|
996
|
+
skills: ['huawei-obs'],
|
|
997
|
+
services: ['OBS'],
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
keywords: ['functiongraph', 'serverless', 'function', 'lambda', 'trigger', 'faas'],
|
|
1001
|
+
skills: ['huawei-functiongraph'],
|
|
1002
|
+
services: ['FunctionGraph'],
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
keywords: ['cce', 'kubernetes', 'k8s', 'container', 'cluster', 'node pool', 'swr', 'docker', 'image registry'],
|
|
1006
|
+
skills: ['huawei-cce'],
|
|
1007
|
+
services: ['CCE', 'SWR'],
|
|
1008
|
+
},
|
|
704
1009
|
{ keywords: ['apig', 'api gateway', 'publish', 'throttle'], skills: ['huawei-apig'], services: ['APIG'] },
|
|
705
1010
|
{ keywords: ['rds', 'mysql', 'postgresql', 'database', 'db'], skills: ['huawei-rds'], services: ['RDS'] },
|
|
706
|
-
{
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
{
|
|
712
|
-
|
|
713
|
-
|
|
1011
|
+
{
|
|
1012
|
+
keywords: ['gaussdb', 'distributed', 'sharding', 'opengauss'],
|
|
1013
|
+
skills: ['huawei-gaussdb'],
|
|
1014
|
+
services: ['GaussDB'],
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
keywords: ['iam', 'permission', 'policy', 'role', 'user', 'ak/sk', 'access key', 'agency'],
|
|
1018
|
+
skills: ['huawei-iam'],
|
|
1019
|
+
services: ['IAM'],
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
keywords: ['dew', 'secret', 'kms', 'encrypt', 'decrypt', 'certificate', 'csms'],
|
|
1023
|
+
skills: ['huawei-dew'],
|
|
1024
|
+
services: ['CSMS', 'KMS'],
|
|
1025
|
+
},
|
|
1026
|
+
{
|
|
1027
|
+
keywords: ['modelarts', 'ai', 'model', 'training', 'inference', 'machine learning'],
|
|
1028
|
+
skills: ['huawei-modelarts'],
|
|
1029
|
+
services: ['ModelArts'],
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
keywords: ['billing', 'cost', 'bill', 'budget', 'expense', 'bss'],
|
|
1033
|
+
skills: ['huawei-billing'],
|
|
1034
|
+
services: ['BSS'],
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
keywords: ['waf', 'aad', 'ddos', 'firewall', 'web protection'],
|
|
1038
|
+
skills: ['huawei-waf-aad'],
|
|
1039
|
+
services: ['WAF', 'AAD'],
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
keywords: ['smn', 'dms', 'notification', 'message', 'kafka', 'rabbitmq'],
|
|
1043
|
+
skills: ['huawei-smn-dms'],
|
|
1044
|
+
services: ['SMN', 'DMS'],
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
keywords: ['ces', 'monitor', 'alarm', 'metric', 'dashboard', 'cloud eye'],
|
|
1048
|
+
skills: ['huawei-cloud-eye'],
|
|
1049
|
+
services: ['CES'],
|
|
1050
|
+
},
|
|
714
1051
|
{ keywords: ['cts', 'audit', 'trace', 'tracker'], skills: ['huawei-cts'], services: ['CTS'] },
|
|
715
1052
|
{ keywords: ['cbr', 'backup', 'restore', 'vault', 'snapshot'], skills: ['huawei-cbr'], services: ['CBR'] },
|
|
716
|
-
{
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
]
|
|
1053
|
+
{
|
|
1054
|
+
keywords: ['deployment', 'deploy', 'ci/cd', 'pipeline', 'release'],
|
|
1055
|
+
skills: ['huawei-deployment'],
|
|
1056
|
+
services: ['CloudDeploy'],
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
keywords: [
|
|
1060
|
+
'sandbox',
|
|
1061
|
+
'devstation',
|
|
1062
|
+
'workspace',
|
|
1063
|
+
'terminal',
|
|
1064
|
+
'preview',
|
|
1065
|
+
'hwlink',
|
|
1066
|
+
'website',
|
|
1067
|
+
'web app',
|
|
1068
|
+
'webapp',
|
|
1069
|
+
'hosting',
|
|
1070
|
+
'网站',
|
|
1071
|
+
'网页',
|
|
1072
|
+
'静态',
|
|
1073
|
+
],
|
|
1074
|
+
skills: ['huawei-sandbox'],
|
|
1075
|
+
services: ['Sandbox', 'DevStation'],
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
keywords: ['dds', 'dcs', 'mongodb', 'redis', 'memcached', 'cache', 'document db'],
|
|
1079
|
+
skills: ['huawei-dds-dcs'],
|
|
1080
|
+
services: ['DDS', 'DCS'],
|
|
1081
|
+
},
|
|
1082
|
+
];
|
|
720
1083
|
const matched = [];
|
|
721
1084
|
const tokens = it.split(/[\s,./-]+/).filter((t) => t.length > 0);
|
|
722
1085
|
const cjk = /[\u4e00-\u9fff]/;
|
|
723
1086
|
for (const route of routeMap) {
|
|
724
|
-
if (route.keywords.some((kw) => (kw.includes(' ') || cjk.test(kw)
|
|
1087
|
+
if (route.keywords.some((kw) => (kw.includes(' ') || cjk.test(kw) ? it.includes(kw) : tokens.includes(kw)))) {
|
|
725
1088
|
matched.push(route);
|
|
726
1089
|
}
|
|
727
1090
|
}
|
|
@@ -740,7 +1103,9 @@ function serviceCatalog(intent = '') {
|
|
|
740
1103
|
return {
|
|
741
1104
|
intent,
|
|
742
1105
|
recommendedSkills: recommendedSkills.length ? recommendedSkills : ['Use huaweicloud-core to route intent.'],
|
|
743
|
-
recommendedServices: recommendedServices.length
|
|
1106
|
+
recommendedServices: recommendedServices.length
|
|
1107
|
+
? recommendedServices
|
|
1108
|
+
: ['Run hcloud --help to list available services.'],
|
|
744
1109
|
capabilityOrder: [
|
|
745
1110
|
'Huawei Cloud Skills for task-specific workflows and examples',
|
|
746
1111
|
'KooCLI hcloud for local authenticated operations and quick inspection',
|
|
@@ -766,32 +1131,38 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
766
1131
|
const svc = String(service).toLowerCase();
|
|
767
1132
|
|
|
768
1133
|
const SERVICE_ALIASES = {
|
|
769
|
-
|
|
770
|
-
|
|
1134
|
+
functiongraph: 'FSS',
|
|
1135
|
+
fgs: 'FSS',
|
|
771
1136
|
};
|
|
772
1137
|
const patternKey = SERVICE_ALIASES[svc] || service;
|
|
773
1138
|
|
|
774
1139
|
const hwErrorPatterns = {
|
|
775
1140
|
OBS: {
|
|
776
|
-
|
|
777
|
-
|
|
1141
|
+
InvalidAccessKeyId:
|
|
1142
|
+
'OBS uses AK/SK directly (not IAM tokens). Verify AK/SK validity, OBS endpoint, and OBS permissions.',
|
|
1143
|
+
UserRestricted:
|
|
1144
|
+
'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.',
|
|
778
1145
|
},
|
|
779
1146
|
APIG: {
|
|
780
1147
|
'APIC.7241': 'The enterprise_project_id is required for enterprise accounts. Add --enterprise_project_id=0.',
|
|
781
|
-
'APIC.7242':
|
|
1148
|
+
'APIC.7242':
|
|
1149
|
+
'The EIP binding method depends on loadbalancer_provider. Use AddIngressEipV2 for elb, AddEipV2 for lvs.',
|
|
782
1150
|
'APIC.7256': 'Bandwidth minimum is 5 Mbps. Use --bandwidth_size=5 or higher.',
|
|
783
1151
|
'APIC.7310': 'available_zone_ids must use AZ codes (e.g. ap-southeast-3a), NOT UUIDs from ListAvailableZonesV2.',
|
|
784
1152
|
},
|
|
785
1153
|
FSS: {
|
|
786
|
-
'FSS.0403':
|
|
1154
|
+
'FSS.0403':
|
|
1155
|
+
'Missing FunctionGraph IAM permissions. Attach FunctionGraph FullAccess role or grant specific actions.',
|
|
787
1156
|
'FSS.1078': '--code_filename is filename-only (no path). cd to the file directory before running the command.',
|
|
788
|
-
'FSS.1417':
|
|
1157
|
+
'FSS.1417':
|
|
1158
|
+
'event_data field validation failed. Check parameter format: use dotted key=value, verify required hidden-optional fields.',
|
|
789
1159
|
},
|
|
790
1160
|
VPC: {
|
|
791
1161
|
'VPC.0301': 'Bandwidth name is required for PER type EIPs, even though --help marks it optional.',
|
|
792
1162
|
},
|
|
793
1163
|
APIGW: {
|
|
794
|
-
'APIGW.0802':
|
|
1164
|
+
'APIGW.0802':
|
|
1165
|
+
'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.',
|
|
795
1166
|
},
|
|
796
1167
|
};
|
|
797
1168
|
if (hwErrorPatterns[patternKey] && hwErrorPatterns[patternKey][errorCode]) {
|
|
@@ -806,19 +1177,30 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
806
1177
|
|
|
807
1178
|
if (/auth|token|credential|ak|sk|401|403|unauthorized|forbidden|Incorrect IAM/i.test(combined)) {
|
|
808
1179
|
if (svc === 'obs') {
|
|
809
|
-
suggestions.push(
|
|
1180
|
+
suggestions.push(
|
|
1181
|
+
'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.',
|
|
1182
|
+
);
|
|
810
1183
|
} else {
|
|
811
1184
|
suggestions.push('Check KooCLI profile, region, project_id, and IAM permissions without printing secrets.');
|
|
812
1185
|
}
|
|
813
1186
|
}
|
|
814
1187
|
if (/APIGW\.(\d+)/i.test(errorCode)) {
|
|
815
|
-
suggestions.push(
|
|
1188
|
+
suggestions.push(
|
|
1189
|
+
'APIGW.' +
|
|
1190
|
+
(errorCode.match(/APIGW\.(\d+)/i) || [])[1] +
|
|
1191
|
+
': API Gateway layer error. ' +
|
|
1192
|
+
(errorCode === 'APIGW.0802'
|
|
1193
|
+
? 'IAM user has no region permissions — check IAM console → User → Permissions → add target region.'
|
|
1194
|
+
: 'Verify the API request, region endpoint, and IAM permissions.'),
|
|
1195
|
+
);
|
|
816
1196
|
}
|
|
817
1197
|
if (/region|endpoint|project/i.test(combined)) {
|
|
818
1198
|
suggestions.push('Confirm the service endpoint, region, and project_id match the target resource.');
|
|
819
1199
|
}
|
|
820
1200
|
if (/quota|limit|insufficient|reach the limit/i.test(combined)) {
|
|
821
|
-
suggestions.push(
|
|
1201
|
+
suggestions.push(
|
|
1202
|
+
'Check quota and resource limits before retrying a create or scale operation. Consider switching accounts or requesting a quota increase.',
|
|
1203
|
+
);
|
|
822
1204
|
}
|
|
823
1205
|
if (/not.?found|404/i.test(combined)) {
|
|
824
1206
|
if (/list.?regions/i.test(svc)) {
|
|
@@ -828,7 +1210,9 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
828
1210
|
}
|
|
829
1211
|
}
|
|
830
1212
|
if (!suggestions.length) {
|
|
831
|
-
suggestions.push(
|
|
1213
|
+
suggestions.push(
|
|
1214
|
+
'Collect service name, operation, region, project_id, request_id, and the full redacted error message.',
|
|
1215
|
+
);
|
|
832
1216
|
}
|
|
833
1217
|
|
|
834
1218
|
if (requestId) {
|
|
@@ -836,8 +1220,7 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
836
1220
|
}
|
|
837
1221
|
|
|
838
1222
|
const uniqueSuggestions = suggestions.filter((s, i, arr) => {
|
|
839
|
-
return !arr.slice(0, i).some((prev) =>
|
|
840
|
-
prev.substring(0, 50).toLowerCase() === s.substring(0, 50).toLowerCase());
|
|
1223
|
+
return !arr.slice(0, i).some((prev) => prev.substring(0, 50).toLowerCase() === s.substring(0, 50).toLowerCase());
|
|
841
1224
|
});
|
|
842
1225
|
|
|
843
1226
|
return {
|
|
@@ -854,9 +1237,7 @@ async function searchDocs(query, topic = 'all') {
|
|
|
854
1237
|
const results = [];
|
|
855
1238
|
try {
|
|
856
1239
|
if (existsSync(SKILLS_ROOT)) {
|
|
857
|
-
const dirs =
|
|
858
|
-
.filter((d) => d.isDirectory())
|
|
859
|
-
.map((d) => d.name);
|
|
1240
|
+
const dirs = listSkillDirs(SKILLS_ROOT);
|
|
860
1241
|
for (const dir of dirs) {
|
|
861
1242
|
const skillPath = join(SKILLS_ROOT, dir, 'SKILL.md');
|
|
862
1243
|
if (!existsSync(skillPath)) continue;
|
|
@@ -879,11 +1260,16 @@ async function searchDocs(query, topic = 'all') {
|
|
|
879
1260
|
const nameLower = name.toLowerCase();
|
|
880
1261
|
const contentLower = content.toLowerCase();
|
|
881
1262
|
const relevance = tokens.reduce((score, token) => {
|
|
882
|
-
return
|
|
1263
|
+
return (
|
|
1264
|
+
score +
|
|
1265
|
+
(descLower.includes(token) ? 3 : 0) +
|
|
1266
|
+
(nameLower.includes(token) ? 2 : 0) +
|
|
1267
|
+
(contentLower.includes(token) ? 1 : 0)
|
|
1268
|
+
);
|
|
883
1269
|
}, 0);
|
|
884
1270
|
if (relevance > 0) {
|
|
885
1271
|
results.push({
|
|
886
|
-
source:
|
|
1272
|
+
source: 'skills/' + dir + '/SKILL.md',
|
|
887
1273
|
name,
|
|
888
1274
|
snippet: description.substring(0, 200),
|
|
889
1275
|
relevance,
|
|
@@ -903,9 +1289,7 @@ async function retrieveSkill(name) {
|
|
|
903
1289
|
if (!skillName) return { ok: false, error: 'Skill name is required.' };
|
|
904
1290
|
const skillPath = join(SKILLS_ROOT, skillName, 'SKILL.md');
|
|
905
1291
|
if (!existsSync(skillPath)) {
|
|
906
|
-
const dirs =
|
|
907
|
-
? readdirSync(SKILLS_ROOT, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
908
|
-
: [];
|
|
1292
|
+
const dirs = listSkillDirs(SKILLS_ROOT);
|
|
909
1293
|
return { ok: false, error: 'Skill "' + skillName + '" not found. Available: ' + dirs.join(', ') };
|
|
910
1294
|
}
|
|
911
1295
|
const content = readFileSync(skillPath, 'utf8');
|
|
@@ -918,7 +1302,8 @@ async function retrieveSkill(name) {
|
|
|
918
1302
|
});
|
|
919
1303
|
}
|
|
920
1304
|
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
921
|
-
let version = 1,
|
|
1305
|
+
let version = 1,
|
|
1306
|
+
description = '';
|
|
922
1307
|
if (frontmatter) {
|
|
923
1308
|
const fm = frontmatter[1];
|
|
924
1309
|
const vm = fm.match(/^version:\s*(.+)$/m);
|
|
@@ -930,8 +1315,10 @@ async function retrieveSkill(name) {
|
|
|
930
1315
|
}
|
|
931
1316
|
|
|
932
1317
|
async function listRegions() {
|
|
933
|
-
const result = await runHcloud(['IAM', 'KeystoneListRegions'], { timeoutMs: 30000, maxRetries: 0 })
|
|
934
|
-
|
|
1318
|
+
const result = await runHcloud(['IAM', 'KeystoneListRegions'], { timeoutMs: 30000, maxRetries: 0 }).catch((err) => ({
|
|
1319
|
+
ok: false,
|
|
1320
|
+
error: err.message,
|
|
1321
|
+
}));
|
|
935
1322
|
if (!result.ok) {
|
|
936
1323
|
return {
|
|
937
1324
|
ok: false,
|
|
@@ -951,7 +1338,7 @@ async function listRegions() {
|
|
|
951
1338
|
note: 'hcloud unavailable. Showing static region list. For the complete list, visit the fallback URL.',
|
|
952
1339
|
};
|
|
953
1340
|
}
|
|
954
|
-
let regions
|
|
1341
|
+
let regions;
|
|
955
1342
|
try {
|
|
956
1343
|
const parsed = typeof result.stdout === 'string' ? JSON.parse(result.stdout) : result.stdout;
|
|
957
1344
|
const rawRegions = parsed.regions || [];
|
|
@@ -969,37 +1356,128 @@ async function listRegions() {
|
|
|
969
1356
|
}
|
|
970
1357
|
|
|
971
1358
|
async function getRegionalAvailability(service, region) {
|
|
972
|
-
const svc = String(service || '')
|
|
973
|
-
|
|
1359
|
+
const svc = String(service || '')
|
|
1360
|
+
.toLowerCase()
|
|
1361
|
+
.trim();
|
|
1362
|
+
const reg = String(region || '')
|
|
1363
|
+
.toLowerCase()
|
|
1364
|
+
.trim();
|
|
974
1365
|
if (!svc || !reg) return { ok: false, error: 'Both service and region are required.' };
|
|
975
1366
|
const known = {
|
|
976
|
-
ecs: [
|
|
977
|
-
|
|
978
|
-
|
|
1367
|
+
ecs: [
|
|
1368
|
+
'cn-south-1',
|
|
1369
|
+
'cn-north-4',
|
|
1370
|
+
'cn-north-1',
|
|
1371
|
+
'cn-east-3',
|
|
1372
|
+
'cn-east-2',
|
|
1373
|
+
'ap-southeast-3',
|
|
1374
|
+
'ap-southeast-2',
|
|
1375
|
+
'ap-southeast-1',
|
|
1376
|
+
'ap-southeast-4',
|
|
1377
|
+
'af-south-1',
|
|
1378
|
+
'tr-west-1',
|
|
1379
|
+
'sa-brazil-1',
|
|
1380
|
+
'la-north-2',
|
|
1381
|
+
'na-mexico-1',
|
|
1382
|
+
'me-east-1',
|
|
1383
|
+
],
|
|
1384
|
+
obs: [
|
|
1385
|
+
'cn-south-1',
|
|
1386
|
+
'cn-north-4',
|
|
1387
|
+
'cn-north-1',
|
|
1388
|
+
'cn-east-3',
|
|
1389
|
+
'cn-east-2',
|
|
1390
|
+
'ap-southeast-3',
|
|
1391
|
+
'ap-southeast-2',
|
|
1392
|
+
'ap-southeast-1',
|
|
1393
|
+
'af-south-1',
|
|
1394
|
+
],
|
|
1395
|
+
vpc: [
|
|
1396
|
+
'cn-south-1',
|
|
1397
|
+
'cn-north-4',
|
|
1398
|
+
'cn-north-1',
|
|
1399
|
+
'cn-east-3',
|
|
1400
|
+
'cn-east-2',
|
|
1401
|
+
'ap-southeast-3',
|
|
1402
|
+
'ap-southeast-2',
|
|
1403
|
+
'ap-southeast-1',
|
|
1404
|
+
'ap-southeast-4',
|
|
1405
|
+
'af-south-1',
|
|
1406
|
+
'tr-west-1',
|
|
1407
|
+
'sa-brazil-1',
|
|
1408
|
+
'la-north-2',
|
|
1409
|
+
'me-east-1',
|
|
1410
|
+
],
|
|
979
1411
|
iam: ['global'],
|
|
980
|
-
rds: [
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1412
|
+
rds: [
|
|
1413
|
+
'cn-south-1',
|
|
1414
|
+
'cn-north-4',
|
|
1415
|
+
'cn-north-1',
|
|
1416
|
+
'cn-east-3',
|
|
1417
|
+
'cn-east-2',
|
|
1418
|
+
'ap-southeast-3',
|
|
1419
|
+
'ap-southeast-2',
|
|
1420
|
+
'ap-southeast-1',
|
|
1421
|
+
],
|
|
1422
|
+
gaussdb: ['cn-south-1', 'cn-north-4', 'cn-east-3'],
|
|
1423
|
+
cce: [
|
|
1424
|
+
'cn-south-1',
|
|
1425
|
+
'cn-north-4',
|
|
1426
|
+
'cn-north-1',
|
|
1427
|
+
'cn-east-3',
|
|
1428
|
+
'cn-east-2',
|
|
1429
|
+
'ap-southeast-3',
|
|
1430
|
+
'ap-southeast-2',
|
|
1431
|
+
'ap-southeast-1',
|
|
1432
|
+
],
|
|
1433
|
+
modelarts: ['cn-south-1', 'cn-north-4', 'cn-east-3'],
|
|
1434
|
+
functiongraph: [
|
|
1435
|
+
'cn-south-1',
|
|
1436
|
+
'cn-north-4',
|
|
1437
|
+
'cn-north-1',
|
|
1438
|
+
'cn-east-3',
|
|
1439
|
+
'cn-east-2',
|
|
1440
|
+
'ap-southeast-3',
|
|
1441
|
+
'ap-southeast-2',
|
|
1442
|
+
'ap-southeast-1',
|
|
1443
|
+
],
|
|
1444
|
+
dew: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1445
|
+
smn: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1446
|
+
ces: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1447
|
+
cts: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1448
|
+
apig: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1449
|
+
cbr: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1450
|
+
dds: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1451
|
+
dcs: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
993
1452
|
};
|
|
994
|
-
if (!known[svc])
|
|
1453
|
+
if (!known[svc])
|
|
1454
|
+
return {
|
|
1455
|
+
ok: false,
|
|
1456
|
+
service: svc,
|
|
1457
|
+
region: reg,
|
|
1458
|
+
available: false,
|
|
1459
|
+
note:
|
|
1460
|
+
'Service ' +
|
|
1461
|
+
svc +
|
|
1462
|
+
' is not in the regional availability cache. Run hcloud ' +
|
|
1463
|
+
svc.toUpperCase() +
|
|
1464
|
+
' --help to verify, or check https://developer.huaweicloud.com/endpoint.',
|
|
1465
|
+
};
|
|
995
1466
|
const available = known[svc].includes(reg) || known[svc].includes('global');
|
|
996
1467
|
return {
|
|
997
|
-
ok: true,
|
|
1468
|
+
ok: true,
|
|
1469
|
+
service: svc,
|
|
1470
|
+
region: reg,
|
|
1471
|
+
available,
|
|
998
1472
|
note: available
|
|
999
1473
|
? svc + ' is available in ' + reg + '.'
|
|
1000
|
-
: svc +
|
|
1474
|
+
: svc +
|
|
1475
|
+
' availability in ' +
|
|
1476
|
+
reg +
|
|
1477
|
+
' could not be confirmed. Verify at https://developer.huaweicloud.com/endpoint.',
|
|
1001
1478
|
sourcedFrom: 'static cache, update via npm package upgrade',
|
|
1002
|
-
disclaimer:
|
|
1479
|
+
disclaimer:
|
|
1480
|
+
'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.',
|
|
1003
1481
|
};
|
|
1004
1482
|
}
|
|
1005
1483
|
|