huaweicloud-devkit 1.0.3-dev.0 → 1.0.3-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.
- package/.agents/plugins/marketplace.json +18 -18
- package/README.md +99 -41
- package/README.zh-CN.md +99 -41
- package/bin/setup.cjs +5 -3
- package/integrations/opencode/opencode.json +1 -4
- package/package.json +61 -42
- 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-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 +33 -25
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +51 -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 +128 -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 +25 -2
- 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 +65 -47
- 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 +123 -2
- 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 +73 -0
- package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +94 -0
- 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 +14 -10
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +34 -27
- package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +521 -8
- package/plugins/huaweicloud-core/src/search-market.mjs +53 -14
- package/plugins/huaweicloud-core/src/setup-cli.mjs +1741 -215
- package/plugins/huaweicloud-core/src/tools.mjs +667 -123
- 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
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +0 -190
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +0 -124
|
@@ -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');
|
|
@@ -25,12 +40,86 @@ function workbuddySkillsDir() {
|
|
|
25
40
|
const home = homedir();
|
|
26
41
|
return join(home, '.workbuddy', 'skills');
|
|
27
42
|
}
|
|
43
|
+
function dshSkillsDir() {
|
|
44
|
+
const home = process.env.DSH_HOME || join(homedir(), '.dsh');
|
|
45
|
+
return join(home, 'skills');
|
|
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
|
+
|
|
28
111
|
function resolveSkillsRoot() {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
+
);
|
|
34
123
|
}
|
|
35
124
|
const SKILLS_ROOT = resolveSkillsRoot();
|
|
36
125
|
|
|
@@ -90,7 +179,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
90
179
|
},
|
|
91
180
|
{
|
|
92
181
|
name: 'huaweicloud_list_operations',
|
|
93
|
-
description:
|
|
182
|
+
description:
|
|
183
|
+
'List KooCLI operations for a Huawei Cloud service by running local/read-only hcloud <Service> --help.',
|
|
94
184
|
inputSchema: {
|
|
95
185
|
type: 'object',
|
|
96
186
|
required: ['service'],
|
|
@@ -108,7 +198,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
108
198
|
},
|
|
109
199
|
{
|
|
110
200
|
name: 'huaweicloud_run_approved_command',
|
|
111
|
-
description:
|
|
201
|
+
description:
|
|
202
|
+
'Run a write-capable hcloud command only after the exact command has been shown and explicitly approved by the user.',
|
|
112
203
|
inputSchema: {
|
|
113
204
|
type: 'object',
|
|
114
205
|
required: ['args', 'approvedCommand', 'approvedByUser'],
|
|
@@ -188,7 +279,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
188
279
|
},
|
|
189
280
|
{
|
|
190
281
|
name: 'huaweicloud_hook_check_deploy_plan',
|
|
191
|
-
description:
|
|
282
|
+
description:
|
|
283
|
+
'Check a structured or textual deployment plan for Huawei Cloud sandbox, exposure, IAM, and cost risks.',
|
|
192
284
|
inputSchema: {
|
|
193
285
|
type: 'object',
|
|
194
286
|
required: ['plan'],
|
|
@@ -227,19 +319,24 @@ export const TOOL_DEFINITIONS = [
|
|
|
227
319
|
},
|
|
228
320
|
{
|
|
229
321
|
name: 'huaweicloud_search_docs',
|
|
230
|
-
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.',
|
|
231
324
|
inputSchema: {
|
|
232
325
|
type: 'object',
|
|
233
326
|
required: ['query'],
|
|
234
327
|
properties: {
|
|
235
328
|
query: { type: 'string', description: 'Search query across skill descriptions and documentation.' },
|
|
236
|
-
topic: {
|
|
329
|
+
topic: {
|
|
330
|
+
type: 'string',
|
|
331
|
+
description: 'Optional filter: all | ecs | obs | vpc | iam | rds | cce | modelarts | dew. Defaults to all.',
|
|
332
|
+
},
|
|
237
333
|
},
|
|
238
334
|
},
|
|
239
335
|
},
|
|
240
336
|
{
|
|
241
337
|
name: 'huaweicloud_retrieve_skill',
|
|
242
|
-
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.',
|
|
243
340
|
inputSchema: {
|
|
244
341
|
type: 'object',
|
|
245
342
|
required: ['name'],
|
|
@@ -250,7 +347,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
250
347
|
},
|
|
251
348
|
{
|
|
252
349
|
name: 'huaweicloud_list_regions',
|
|
253
|
-
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.',
|
|
254
352
|
inputSchema: {
|
|
255
353
|
type: 'object',
|
|
256
354
|
properties: {},
|
|
@@ -258,30 +356,58 @@ export const TOOL_DEFINITIONS = [
|
|
|
258
356
|
},
|
|
259
357
|
{
|
|
260
358
|
name: 'huaweicloud_get_regional_availability',
|
|
261
|
-
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.',
|
|
262
361
|
inputSchema: {
|
|
263
362
|
type: 'object',
|
|
264
363
|
required: ['service', 'region'],
|
|
265
364
|
properties: {
|
|
266
|
-
service: {
|
|
365
|
+
service: {
|
|
366
|
+
type: 'string',
|
|
367
|
+
description: 'Service name: ecs, obs, rds, gaussdb, cce, modelarts, functiongraph, etc.',
|
|
368
|
+
},
|
|
267
369
|
region: { type: 'string', description: 'Region ID: cn-south-1, cn-north-4, ap-southeast-3, etc.' },
|
|
268
370
|
},
|
|
269
371
|
},
|
|
270
372
|
},
|
|
271
373
|
{
|
|
272
374
|
name: 'huaweicloud_search_marketplace',
|
|
273
|
-
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.',
|
|
274
377
|
inputSchema: {
|
|
275
378
|
type: 'object',
|
|
276
379
|
properties: {
|
|
277
380
|
query: { type: 'string', description: 'Search query across skill name, description, triggers, and service.' },
|
|
278
|
-
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
|
+
},
|
|
279
404
|
},
|
|
280
405
|
},
|
|
281
406
|
},
|
|
282
407
|
{
|
|
283
408
|
name: 'huaweicloud_setup_obs_config',
|
|
284
|
-
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.',
|
|
285
411
|
inputSchema: {
|
|
286
412
|
type: 'object',
|
|
287
413
|
properties: {
|
|
@@ -291,35 +417,83 @@ export const TOOL_DEFINITIONS = [
|
|
|
291
417
|
},
|
|
292
418
|
{
|
|
293
419
|
name: 'huaweicloud_auth_status',
|
|
294
|
-
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.',
|
|
295
422
|
inputSchema: {
|
|
296
423
|
type: 'object',
|
|
297
424
|
properties: {
|
|
298
|
-
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
|
+
},
|
|
299
430
|
},
|
|
300
431
|
},
|
|
301
432
|
},
|
|
302
433
|
{
|
|
303
434
|
name: 'huaweicloud_auth_sync',
|
|
304
|
-
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.',
|
|
305
437
|
inputSchema: {
|
|
306
438
|
type: 'object',
|
|
307
439
|
properties: {
|
|
308
|
-
target: {
|
|
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.',
|
|
452
|
+
inputSchema: {
|
|
453
|
+
type: 'object',
|
|
454
|
+
properties: {
|
|
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' },
|
|
309
459
|
},
|
|
310
460
|
},
|
|
311
461
|
},
|
|
312
462
|
{
|
|
313
463
|
name: 'huaweicloud_sandbox_exec_with_session',
|
|
314
|
-
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.',
|
|
466
|
+
inputSchema: {
|
|
467
|
+
type: 'object',
|
|
468
|
+
required: ['command'],
|
|
469
|
+
properties: {
|
|
470
|
+
command: { type: 'string', description: 'The shell command to execute on the remote workspace' },
|
|
471
|
+
workspace_id: {
|
|
472
|
+
type: 'string',
|
|
473
|
+
description:
|
|
474
|
+
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
475
|
+
},
|
|
476
|
+
username: { type: 'string', description: 'Login username for the remote terminal (default: root)' },
|
|
477
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 120000)' },
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
name: 'huaweicloud_sandbox_exec_one_shot',
|
|
483
|
+
description:
|
|
484
|
+
'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.',
|
|
315
485
|
inputSchema: {
|
|
316
486
|
type: 'object',
|
|
317
487
|
required: ['command'],
|
|
318
488
|
properties: {
|
|
319
489
|
command: { type: 'string', description: 'The shell command to execute on the remote workspace' },
|
|
320
|
-
workspace_id: {
|
|
490
|
+
workspace_id: {
|
|
491
|
+
type: 'string',
|
|
492
|
+
description:
|
|
493
|
+
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
494
|
+
},
|
|
321
495
|
username: { type: 'string', description: 'Login username for the remote terminal (default: root)' },
|
|
322
|
-
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default:
|
|
496
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 120000)' },
|
|
323
497
|
},
|
|
324
498
|
},
|
|
325
499
|
},
|
|
@@ -329,14 +503,72 @@ export const TOOL_DEFINITIONS = [
|
|
|
329
503
|
inputSchema: {
|
|
330
504
|
type: 'object',
|
|
331
505
|
properties: {
|
|
332
|
-
workspace_id: {
|
|
506
|
+
workspace_id: {
|
|
507
|
+
type: 'string',
|
|
508
|
+
description:
|
|
509
|
+
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
510
|
+
},
|
|
333
511
|
username: { type: 'string', description: 'Login username (default: root)' },
|
|
334
512
|
},
|
|
335
513
|
},
|
|
336
514
|
},
|
|
515
|
+
{
|
|
516
|
+
name: 'huaweicloud_sandbox_upload_file',
|
|
517
|
+
description:
|
|
518
|
+
'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.',
|
|
519
|
+
inputSchema: {
|
|
520
|
+
type: 'object',
|
|
521
|
+
required: ['local_path', 'remote_path'],
|
|
522
|
+
properties: {
|
|
523
|
+
local_path: { type: 'string', description: 'Absolute path to the local file to upload.' },
|
|
524
|
+
remote_path: { type: 'string', description: 'Target path in the sandbox, e.g. /workspace/<repo>/index.html.' },
|
|
525
|
+
workspace_id: {
|
|
526
|
+
type: 'string',
|
|
527
|
+
description:
|
|
528
|
+
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
529
|
+
},
|
|
530
|
+
username: { type: 'string', description: 'Login username (default: root)' },
|
|
531
|
+
timeout_ms: { type: 'number', description: 'Per-command execution timeout in milliseconds (default: 120000)' },
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
name: 'huaweicloud_sandbox_upload_project',
|
|
537
|
+
description:
|
|
538
|
+
'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.',
|
|
539
|
+
inputSchema: {
|
|
540
|
+
type: 'object',
|
|
541
|
+
required: ['local_dir'],
|
|
542
|
+
properties: {
|
|
543
|
+
local_dir: { type: 'string', description: 'Local project directory to upload.' },
|
|
544
|
+
remote_dir: {
|
|
545
|
+
type: 'string',
|
|
546
|
+
description:
|
|
547
|
+
'Remote parent directory where project will be extracted (default: /workspace). Final layout: <remote_dir>/<dirname>/',
|
|
548
|
+
},
|
|
549
|
+
workspace_id: {
|
|
550
|
+
type: 'string',
|
|
551
|
+
description:
|
|
552
|
+
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
553
|
+
},
|
|
554
|
+
username: { type: 'string', description: 'Login username (default: root)' },
|
|
555
|
+
exclude: {
|
|
556
|
+
type: 'array',
|
|
557
|
+
items: { type: 'string' },
|
|
558
|
+
description: 'Patterns to exclude from archive (default: .git, node_modules, __pycache__, .venv)',
|
|
559
|
+
},
|
|
560
|
+
extract: {
|
|
561
|
+
type: 'boolean',
|
|
562
|
+
description: 'Extract tar.gz on sandbox after upload (default: true)',
|
|
563
|
+
},
|
|
564
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 300000)' },
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
},
|
|
337
568
|
{
|
|
338
569
|
name: 'huaweicloud_sandbox_check_user',
|
|
339
|
-
description:
|
|
570
|
+
description:
|
|
571
|
+
'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
572
|
inputSchema: {
|
|
341
573
|
type: 'object',
|
|
342
574
|
properties: {},
|
|
@@ -344,7 +576,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
344
576
|
},
|
|
345
577
|
{
|
|
346
578
|
name: 'huaweicloud_sandbox_sign_agreement',
|
|
347
|
-
description:
|
|
579
|
+
description:
|
|
580
|
+
"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
581
|
inputSchema: {
|
|
349
582
|
type: 'object',
|
|
350
583
|
properties: {},
|
|
@@ -352,11 +585,16 @@ export const TOOL_DEFINITIONS = [
|
|
|
352
585
|
},
|
|
353
586
|
{
|
|
354
587
|
name: 'huaweicloud_sandbox_connect',
|
|
355
|
-
description:
|
|
588
|
+
description:
|
|
589
|
+
'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
590
|
inputSchema: {
|
|
357
591
|
type: 'object',
|
|
358
592
|
properties: {
|
|
359
|
-
source: {
|
|
593
|
+
source: {
|
|
594
|
+
type: 'string',
|
|
595
|
+
description:
|
|
596
|
+
'Source identifier (default: WEB). Options: VSCODE, CLI, WEB, WEBVNC, WEBPTY, WEBIDE, CURSOR, etc.',
|
|
597
|
+
},
|
|
360
598
|
template_id: { type: 'string', description: 'Template ID; overrides server default (only for new sandbox)' },
|
|
361
599
|
flavor_id: { type: 'string', description: 'Flavor ID; overrides server default (only for new sandbox)' },
|
|
362
600
|
env: { type: 'object', description: 'Environment variables to set in the sandbox (only for new sandbox)' },
|
|
@@ -376,7 +614,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
376
614
|
},
|
|
377
615
|
{
|
|
378
616
|
name: 'huaweicloud_sandbox_credentials',
|
|
379
|
-
description:
|
|
617
|
+
description:
|
|
618
|
+
'Configure temporary AK/SK for a sandbox via hdkitservice. Injects temporary credentials into the sandbox. The sandbox must be in RUNNING state.',
|
|
380
619
|
inputSchema: {
|
|
381
620
|
type: 'object',
|
|
382
621
|
properties: {
|
|
@@ -386,7 +625,6 @@ export const TOOL_DEFINITIONS = [
|
|
|
386
625
|
},
|
|
387
626
|
},
|
|
388
627
|
},
|
|
389
|
-
|
|
390
628
|
];
|
|
391
629
|
|
|
392
630
|
export async function callTool(name, args = {}) {
|
|
@@ -424,35 +662,124 @@ export async function callTool(name, args = {}) {
|
|
|
424
662
|
return listRegions();
|
|
425
663
|
case 'huaweicloud_get_regional_availability':
|
|
426
664
|
return getRegionalAvailability(args.service || '', args.region || '');
|
|
427
|
-
|
|
665
|
+
case 'huaweicloud_explain_error':
|
|
428
666
|
return explainError(args);
|
|
429
667
|
case 'huaweicloud_search_marketplace':
|
|
430
668
|
return searchMarketplace(args.query || '', args.category || '');
|
|
669
|
+
case 'huaweicloud_get_service_icon':
|
|
670
|
+
return getServiceIcon(args.service || '', args.category || '');
|
|
431
671
|
case 'huaweicloud_setup_obs_config':
|
|
432
672
|
return setupObsConfig(args.profile);
|
|
433
673
|
case 'huaweicloud_auth_status':
|
|
434
674
|
return getAuthStatus(args.target || 'all');
|
|
435
675
|
case 'huaweicloud_auth_sync':
|
|
436
676
|
return syncAuth(args.target || 'all');
|
|
677
|
+
case 'huaweicloud_auth_init':
|
|
678
|
+
if (args.clear) {
|
|
679
|
+
clearRuntimeCredentials();
|
|
680
|
+
return { status: 'cleared', message: 'Runtime credentials cleared. Fallback to env/file.' };
|
|
681
|
+
}
|
|
682
|
+
if (!args.ak || !args.sk) {
|
|
683
|
+
throw new Error('ak and sk are required. Set clear=true to clear runtime credentials.');
|
|
684
|
+
}
|
|
685
|
+
setRuntimeCredentials(args.ak, args.sk, undefined, args.region);
|
|
686
|
+
return { status: 'ok', message: 'Runtime credentials set for this MCP session.' };
|
|
437
687
|
case 'huaweicloud_sandbox_exec_with_session': {
|
|
438
|
-
const sandboxWsId2 = args.workspace_id ||
|
|
688
|
+
const sandboxWsId2 = args.workspace_id || currentWorkspaceId;
|
|
689
|
+
if (!sandboxWsId2) {
|
|
690
|
+
throw new Error(
|
|
691
|
+
'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
|
|
692
|
+
'or set HW_WORKSPACE_ID environment variable before starting the agent.',
|
|
693
|
+
);
|
|
694
|
+
}
|
|
439
695
|
const sandboxUser2 = args.username || 'root';
|
|
440
|
-
const sandboxTimeout2 = args.timeout_ms ||
|
|
696
|
+
const sandboxTimeout2 = args.timeout_ms || 120000;
|
|
441
697
|
const sandboxResult2 = await execWithSession(sandboxWsId2, args.command, sandboxUser2, sandboxTimeout2);
|
|
442
698
|
return { stdout: sandboxResult2.stdout, exitCode: sandboxResult2.exitCode };
|
|
443
699
|
}
|
|
444
|
-
case '
|
|
445
|
-
const sandboxWsId3 = args.workspace_id ||
|
|
700
|
+
case 'huaweicloud_sandbox_exec_one_shot': {
|
|
701
|
+
const sandboxWsId3 = args.workspace_id || currentWorkspaceId;
|
|
702
|
+
if (!sandboxWsId3) {
|
|
703
|
+
throw new Error(
|
|
704
|
+
'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
|
|
705
|
+
'or set HW_WORKSPACE_ID environment variable before starting the agent.',
|
|
706
|
+
);
|
|
707
|
+
}
|
|
446
708
|
const sandboxUser3 = args.username || 'root';
|
|
447
|
-
const
|
|
709
|
+
const sandboxTimeout3 = args.timeout_ms || 120000;
|
|
710
|
+
const sandboxResult3 = await execOneShot(sandboxWsId3, args.command, sandboxUser3, sandboxTimeout3);
|
|
711
|
+
return { stdout: sandboxResult3.stdout, exitCode: sandboxResult3.exitCode };
|
|
712
|
+
}
|
|
713
|
+
case 'huaweicloud_sandbox_close_session': {
|
|
714
|
+
const sandboxWsId4 = args.workspace_id || currentWorkspaceId;
|
|
715
|
+
if (!sandboxWsId4) {
|
|
716
|
+
throw new Error(
|
|
717
|
+
'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
|
|
718
|
+
'or set HW_WORKSPACE_ID environment variable before starting the agent.',
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
const sandboxUser4 = args.username || 'root';
|
|
722
|
+
const closed = await closeSession(sandboxWsId4, sandboxUser4);
|
|
448
723
|
return closed ? 'ok' : 'not_connected';
|
|
449
724
|
}
|
|
725
|
+
case 'huaweicloud_sandbox_upload_file': {
|
|
726
|
+
if (!args.local_path || !args.remote_path) {
|
|
727
|
+
throw new Error('local_path and remote_path are required.');
|
|
728
|
+
}
|
|
729
|
+
const sandboxWsId5 = args.workspace_id || currentWorkspaceId;
|
|
730
|
+
if (!sandboxWsId5) {
|
|
731
|
+
throw new Error(
|
|
732
|
+
'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
|
|
733
|
+
'or set HW_WORKSPACE_ID environment variable before starting the agent.',
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
const sandboxUser5 = args.username || 'root';
|
|
737
|
+
const sandboxTimeout5 = args.timeout_ms || 120000;
|
|
738
|
+
return await uploadFileWithSession(
|
|
739
|
+
sandboxWsId5,
|
|
740
|
+
args.local_path,
|
|
741
|
+
args.remote_path,
|
|
742
|
+
sandboxUser5,
|
|
743
|
+
sandboxTimeout5,
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
case 'huaweicloud_sandbox_upload_project': {
|
|
747
|
+
if (!args.local_dir) {
|
|
748
|
+
throw new Error('local_dir is required.');
|
|
749
|
+
}
|
|
750
|
+
const sandboxWsId6 = args.workspace_id || currentWorkspaceId;
|
|
751
|
+
if (!sandboxWsId6) {
|
|
752
|
+
throw new Error(
|
|
753
|
+
'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
|
|
754
|
+
'or set HW_WORKSPACE_ID environment variable before starting the agent.',
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
const sandboxUser6 = args.username || 'root';
|
|
758
|
+
const sandboxTimeout6 = args.timeout_ms || 120000;
|
|
759
|
+
return await uploadProjectWithSession(
|
|
760
|
+
sandboxWsId6,
|
|
761
|
+
args.local_dir,
|
|
762
|
+
args.remote_dir,
|
|
763
|
+
sandboxUser6,
|
|
764
|
+
sandboxTimeout6,
|
|
765
|
+
{
|
|
766
|
+
exclude: args.exclude,
|
|
767
|
+
extract: args.extract,
|
|
768
|
+
},
|
|
769
|
+
);
|
|
770
|
+
}
|
|
450
771
|
case 'huaweicloud_sandbox_check_user':
|
|
451
772
|
return await hdkitCheckUser();
|
|
452
773
|
case 'huaweicloud_sandbox_sign_agreement':
|
|
453
774
|
return await hdkitSignAgreement();
|
|
454
|
-
case 'huaweicloud_sandbox_connect':
|
|
455
|
-
|
|
775
|
+
case 'huaweicloud_sandbox_connect': {
|
|
776
|
+
const connectResult = await hdkitConnect(args);
|
|
777
|
+
const devStageId = connectResult?.dev_stage_id || connectResult?.devStageId;
|
|
778
|
+
if (devStageId) {
|
|
779
|
+
setWorkspaceId(devStageId);
|
|
780
|
+
}
|
|
781
|
+
return connectResult;
|
|
782
|
+
}
|
|
456
783
|
case 'huaweicloud_sandbox_credentials':
|
|
457
784
|
return await hdkitCredentials(args.session_id, args.dev_stage_id, args.enable_sts !== false);
|
|
458
785
|
default:
|
|
@@ -465,11 +792,12 @@ function hookResult(result) {
|
|
|
465
792
|
ok: result.decision !== 'deny',
|
|
466
793
|
decision: result.decision,
|
|
467
794
|
findings: result.findings,
|
|
468
|
-
nextStep:
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
795
|
+
nextStep:
|
|
796
|
+
result.decision === 'deny'
|
|
797
|
+
? 'Revise the command, artifact, or deployment plan before execution.'
|
|
798
|
+
: result.decision === 'warn'
|
|
799
|
+
? 'Review the warnings with the user before proceeding.'
|
|
800
|
+
: 'No Huawei Cloud hook risk rule matched.',
|
|
473
801
|
};
|
|
474
802
|
}
|
|
475
803
|
|
|
@@ -490,7 +818,8 @@ export async function runVersionCheck(options = {}) {
|
|
|
490
818
|
: isSpawnError
|
|
491
819
|
? '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
820
|
: 'Install Huawei Cloud KooCLI: npx huaweicloud-devkit install-hcloud. Configure credentials outside the agent conversation.',
|
|
493
|
-
authHint:
|
|
821
|
+
authHint:
|
|
822
|
+
'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
823
|
};
|
|
495
824
|
}
|
|
496
825
|
|
|
@@ -509,7 +838,8 @@ async function showProfileRedacted(profile) {
|
|
|
509
838
|
ok: false,
|
|
510
839
|
blockedByPolicy: true,
|
|
511
840
|
reason: result.reason,
|
|
512
|
-
safeAlternative:
|
|
841
|
+
safeAlternative:
|
|
842
|
+
'Use huaweicloud_show_profile_redacted so profile output is returned through the redaction pipeline.',
|
|
513
843
|
};
|
|
514
844
|
}
|
|
515
845
|
return {
|
|
@@ -551,7 +881,12 @@ async function setupObsConfig(profile) {
|
|
|
551
881
|
async function setupObsConfigFromHcloud(profile) {
|
|
552
882
|
const obsConfigPath = join(homedir(), '.obsutilconfig');
|
|
553
883
|
if (existsSync(obsConfigPath)) {
|
|
554
|
-
return {
|
|
884
|
+
return {
|
|
885
|
+
ok: true,
|
|
886
|
+
existed: true,
|
|
887
|
+
path: obsConfigPath,
|
|
888
|
+
note: 'OBS config already exists. Delete ~/.obsutilconfig first if you need to re-sync.',
|
|
889
|
+
};
|
|
555
890
|
}
|
|
556
891
|
|
|
557
892
|
const args = ['configure', 'show'];
|
|
@@ -567,9 +902,9 @@ async function setupObsConfigFromHcloud(profile) {
|
|
|
567
902
|
};
|
|
568
903
|
}
|
|
569
904
|
|
|
570
|
-
let accessKeyId
|
|
571
|
-
let secretAccessKey
|
|
572
|
-
let region
|
|
905
|
+
let accessKeyId;
|
|
906
|
+
let secretAccessKey;
|
|
907
|
+
let region;
|
|
573
908
|
|
|
574
909
|
try {
|
|
575
910
|
const parsed = typeof result.stdout === 'string' ? JSON.parse(result.stdout) : result.stdout;
|
|
@@ -630,12 +965,16 @@ async function setupObsConfigFromHcloud(profile) {
|
|
|
630
965
|
const SERVICE_EXAMPLES = {
|
|
631
966
|
ECS: { list: 'ECS ListServersDetails', create: 'ECS CreateServers', show: 'IMS GlanceShowImage' },
|
|
632
967
|
VPC: { list: 'VPC ListVpcs', create: 'VPC CreateVpc', show: 'VPC ShowVpc' },
|
|
633
|
-
|
|
968
|
+
FUNCTIONGRAPH: {
|
|
969
|
+
list: 'FunctionGraph ListFunctions',
|
|
970
|
+
create: 'FunctionGraph CreateFunction',
|
|
971
|
+
show: 'FunctionGraph ShowFunctionConfig',
|
|
972
|
+
},
|
|
634
973
|
APIG: { list: 'APIG ListInstancesV2', create: 'APIG CreateInstanceV2', show: 'APIG ShowDetailsOfInstanceV2' },
|
|
635
974
|
OBS: { list: 'OBS ls', create: 'OBS mb obs://<bucket>', show: 'OBS stat obs://<bucket>/<key>' },
|
|
636
975
|
RDS: { list: 'RDS ListInstances', create: 'RDS CreateInstance', show: 'RDS ShowInstance' },
|
|
637
976
|
CES: { list: 'CES ListAlarms', create: 'CES CreateAlarm', show: 'CES ListMetrics' },
|
|
638
|
-
|
|
977
|
+
GAUSSDB: { list: 'GaussDB ListInstances', create: 'GaussDB CreateInstance', show: 'GaussDB ShowInstance' },
|
|
639
978
|
DDS: { list: 'DDS ListInstances', create: 'DDS CreateInstance', show: 'DDS ShowInstance' },
|
|
640
979
|
DCS: { list: 'DCS ListInstances', create: 'DCS CreateInstance', show: 'DCS ShowInstance' },
|
|
641
980
|
};
|
|
@@ -662,7 +1001,9 @@ async function listOperations(service, options = {}) {
|
|
|
662
1001
|
service: serviceName,
|
|
663
1002
|
command: isObs ? 'hcloud obs help' : `hcloud ${svc} --help`,
|
|
664
1003
|
selectionRule: 'Use this help text to select the exact KooCLI operation name before planning any service command.',
|
|
665
|
-
examples: SERVICE_EXAMPLES[serviceName.toUpperCase()] || {
|
|
1004
|
+
examples: SERVICE_EXAMPLES[serviceName.toUpperCase()] || {
|
|
1005
|
+
note: `No cached examples for ${serviceName}. Use the help text above to discover available operations.`,
|
|
1006
|
+
},
|
|
666
1007
|
result,
|
|
667
1008
|
};
|
|
668
1009
|
}
|
|
@@ -691,41 +1032,131 @@ async function runApprovedCommand(args = {}) {
|
|
|
691
1032
|
function serviceCatalog(intent = '') {
|
|
692
1033
|
const it = String(intent).toLowerCase();
|
|
693
1034
|
const routeMap = [
|
|
694
|
-
{
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
1035
|
+
{
|
|
1036
|
+
keywords: ['ecs', 'server', 'vm', 'instance', 'compute', 'flavor', 'image'],
|
|
1037
|
+
skills: ['huawei-ecs'],
|
|
1038
|
+
services: ['ECS'],
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
keywords: ['vpc', 'subnet', 'network', 'security group', 'eip', 'nat', 'vpn', 'bandwidth'],
|
|
1042
|
+
skills: ['huawei-vpc'],
|
|
1043
|
+
services: ['VPC', 'EIP'],
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
keywords: ['obs', 'bucket', 'storage', 'object', 'static website', 'static site', 'hosting'],
|
|
1047
|
+
skills: ['huawei-obs'],
|
|
1048
|
+
services: ['OBS'],
|
|
1049
|
+
},
|
|
1050
|
+
{
|
|
1051
|
+
keywords: ['functiongraph', 'serverless', 'function', 'lambda', 'trigger', 'faas'],
|
|
1052
|
+
skills: ['huawei-functiongraph'],
|
|
1053
|
+
services: ['FunctionGraph'],
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
keywords: ['cce', 'kubernetes', 'k8s', 'container', 'cluster', 'node pool', 'swr', 'docker', 'image registry'],
|
|
1057
|
+
skills: ['huawei-cce'],
|
|
1058
|
+
services: ['CCE', 'SWR'],
|
|
1059
|
+
},
|
|
699
1060
|
{ keywords: ['apig', 'api gateway', 'publish', 'throttle'], skills: ['huawei-apig'], services: ['APIG'] },
|
|
700
1061
|
{ keywords: ['rds', 'mysql', 'postgresql', 'database', 'db'], skills: ['huawei-rds'], services: ['RDS'] },
|
|
701
|
-
{
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
{
|
|
707
|
-
|
|
708
|
-
|
|
1062
|
+
{
|
|
1063
|
+
keywords: ['gaussdb', 'distributed', 'sharding', 'opengauss'],
|
|
1064
|
+
skills: ['huawei-gaussdb'],
|
|
1065
|
+
services: ['GaussDB'],
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
keywords: ['iam', 'permission', 'policy', 'role', 'user', 'ak/sk', 'access key', 'agency'],
|
|
1069
|
+
skills: ['huawei-iam'],
|
|
1070
|
+
services: ['IAM'],
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
keywords: ['dew', 'secret', 'kms', 'encrypt', 'decrypt', 'certificate', 'csms'],
|
|
1074
|
+
skills: ['huawei-dew'],
|
|
1075
|
+
services: ['CSMS', 'KMS'],
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
keywords: ['modelarts', 'ai', 'model', 'training', 'inference', 'machine learning'],
|
|
1079
|
+
skills: ['huawei-modelarts'],
|
|
1080
|
+
services: ['ModelArts'],
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
keywords: ['billing', 'cost', 'bill', 'budget', 'expense', 'bss'],
|
|
1084
|
+
skills: ['huawei-billing'],
|
|
1085
|
+
services: ['BSS'],
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
keywords: ['waf', 'aad', 'ddos', 'firewall', 'web protection'],
|
|
1089
|
+
skills: ['huawei-waf-aad'],
|
|
1090
|
+
services: ['WAF', 'AAD'],
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
keywords: ['smn', 'dms', 'notification', 'message', 'kafka', 'rabbitmq'],
|
|
1094
|
+
skills: ['huawei-smn-dms'],
|
|
1095
|
+
services: ['SMN', 'DMS'],
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
keywords: ['ces', 'monitor', 'alarm', 'metric', 'dashboard', 'cloud eye'],
|
|
1099
|
+
skills: ['huawei-cloud-eye'],
|
|
1100
|
+
services: ['CES'],
|
|
1101
|
+
},
|
|
709
1102
|
{ keywords: ['cts', 'audit', 'trace', 'tracker'], skills: ['huawei-cts'], services: ['CTS'] },
|
|
710
1103
|
{ keywords: ['cbr', 'backup', 'restore', 'vault', 'snapshot'], skills: ['huawei-cbr'], services: ['CBR'] },
|
|
711
|
-
{
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
]
|
|
1104
|
+
{
|
|
1105
|
+
keywords: ['deployment', 'deploy', 'ci/cd', 'pipeline', 'release'],
|
|
1106
|
+
skills: ['huawei-deployment'],
|
|
1107
|
+
services: ['CloudDeploy'],
|
|
1108
|
+
},
|
|
1109
|
+
{
|
|
1110
|
+
keywords: [
|
|
1111
|
+
'sandbox',
|
|
1112
|
+
'devstation',
|
|
1113
|
+
'workspace',
|
|
1114
|
+
'terminal',
|
|
1115
|
+
'preview',
|
|
1116
|
+
'hwlink',
|
|
1117
|
+
'website',
|
|
1118
|
+
'web app',
|
|
1119
|
+
'webapp',
|
|
1120
|
+
'hosting',
|
|
1121
|
+
'网站',
|
|
1122
|
+
'网页',
|
|
1123
|
+
'静态',
|
|
1124
|
+
],
|
|
1125
|
+
skills: ['huawei-sandbox'],
|
|
1126
|
+
services: ['Sandbox', 'DevStation'],
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
keywords: ['dds', 'dcs', 'mongodb', 'redis', 'memcached', 'cache', 'document db'],
|
|
1130
|
+
skills: ['huawei-dds-dcs'],
|
|
1131
|
+
services: ['DDS', 'DCS'],
|
|
1132
|
+
},
|
|
1133
|
+
];
|
|
715
1134
|
const matched = [];
|
|
716
1135
|
const tokens = it.split(/[\s,./-]+/).filter((t) => t.length > 0);
|
|
1136
|
+
const cjk = /[\u4e00-\u9fff]/;
|
|
717
1137
|
for (const route of routeMap) {
|
|
718
|
-
if (route.keywords.some((kw) => tokens.includes(kw))) {
|
|
1138
|
+
if (route.keywords.some((kw) => (kw.includes(' ') || cjk.test(kw) ? it.includes(kw) : tokens.includes(kw)))) {
|
|
719
1139
|
matched.push(route);
|
|
720
1140
|
}
|
|
721
1141
|
}
|
|
722
1142
|
const recommendedSkills = [...new Set(matched.flatMap((r) => r.skills))];
|
|
723
1143
|
const recommendedServices = [...new Set(matched.flatMap((r) => r.services))].slice(0, 5);
|
|
724
1144
|
|
|
1145
|
+
// Deployment intent (deploy/host/publish a web app or static website) must never
|
|
1146
|
+
// default to a storage/other service — recommend the sandbox first.
|
|
1147
|
+
const deploymentIntent = /deploy|host|hosting|publish|website|web app|preview|部署|托管|发布|网站|网页/.test(it);
|
|
1148
|
+
if (deploymentIntent && recommendedSkills.includes('huawei-sandbox')) {
|
|
1149
|
+
const idx = recommendedSkills.indexOf('huawei-sandbox');
|
|
1150
|
+
recommendedSkills.splice(idx, 1);
|
|
1151
|
+
recommendedSkills.unshift('huawei-sandbox');
|
|
1152
|
+
}
|
|
1153
|
+
|
|
725
1154
|
return {
|
|
726
1155
|
intent,
|
|
727
1156
|
recommendedSkills: recommendedSkills.length ? recommendedSkills : ['Use huaweicloud-core to route intent.'],
|
|
728
|
-
recommendedServices: recommendedServices.length
|
|
1157
|
+
recommendedServices: recommendedServices.length
|
|
1158
|
+
? recommendedServices
|
|
1159
|
+
: ['Run hcloud --help to list available services.'],
|
|
729
1160
|
capabilityOrder: [
|
|
730
1161
|
'Huawei Cloud Skills for task-specific workflows and examples',
|
|
731
1162
|
'KooCLI hcloud for local authenticated operations and quick inspection',
|
|
@@ -751,32 +1182,38 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
751
1182
|
const svc = String(service).toLowerCase();
|
|
752
1183
|
|
|
753
1184
|
const SERVICE_ALIASES = {
|
|
754
|
-
|
|
755
|
-
|
|
1185
|
+
functiongraph: 'FSS',
|
|
1186
|
+
fgs: 'FSS',
|
|
756
1187
|
};
|
|
757
1188
|
const patternKey = SERVICE_ALIASES[svc] || service;
|
|
758
1189
|
|
|
759
1190
|
const hwErrorPatterns = {
|
|
760
1191
|
OBS: {
|
|
761
|
-
|
|
762
|
-
|
|
1192
|
+
InvalidAccessKeyId:
|
|
1193
|
+
'OBS uses AK/SK directly (not IAM tokens). Verify AK/SK validity, OBS endpoint, and OBS permissions.',
|
|
1194
|
+
UserRestricted:
|
|
1195
|
+
'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
1196
|
},
|
|
764
1197
|
APIG: {
|
|
765
1198
|
'APIC.7241': 'The enterprise_project_id is required for enterprise accounts. Add --enterprise_project_id=0.',
|
|
766
|
-
'APIC.7242':
|
|
1199
|
+
'APIC.7242':
|
|
1200
|
+
'The EIP binding method depends on loadbalancer_provider. Use AddIngressEipV2 for elb, AddEipV2 for lvs.',
|
|
767
1201
|
'APIC.7256': 'Bandwidth minimum is 5 Mbps. Use --bandwidth_size=5 or higher.',
|
|
768
1202
|
'APIC.7310': 'available_zone_ids must use AZ codes (e.g. ap-southeast-3a), NOT UUIDs from ListAvailableZonesV2.',
|
|
769
1203
|
},
|
|
770
1204
|
FSS: {
|
|
771
|
-
'FSS.0403':
|
|
1205
|
+
'FSS.0403':
|
|
1206
|
+
'Missing FunctionGraph IAM permissions. Attach FunctionGraph FullAccess role or grant specific actions.',
|
|
772
1207
|
'FSS.1078': '--code_filename is filename-only (no path). cd to the file directory before running the command.',
|
|
773
|
-
'FSS.1417':
|
|
1208
|
+
'FSS.1417':
|
|
1209
|
+
'event_data field validation failed. Check parameter format: use dotted key=value, verify required hidden-optional fields.',
|
|
774
1210
|
},
|
|
775
1211
|
VPC: {
|
|
776
1212
|
'VPC.0301': 'Bandwidth name is required for PER type EIPs, even though --help marks it optional.',
|
|
777
1213
|
},
|
|
778
1214
|
APIGW: {
|
|
779
|
-
'APIGW.0802':
|
|
1215
|
+
'APIGW.0802':
|
|
1216
|
+
'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
1217
|
},
|
|
781
1218
|
};
|
|
782
1219
|
if (hwErrorPatterns[patternKey] && hwErrorPatterns[patternKey][errorCode]) {
|
|
@@ -791,19 +1228,30 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
791
1228
|
|
|
792
1229
|
if (/auth|token|credential|ak|sk|401|403|unauthorized|forbidden|Incorrect IAM/i.test(combined)) {
|
|
793
1230
|
if (svc === 'obs') {
|
|
794
|
-
suggestions.push(
|
|
1231
|
+
suggestions.push(
|
|
1232
|
+
'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.',
|
|
1233
|
+
);
|
|
795
1234
|
} else {
|
|
796
1235
|
suggestions.push('Check KooCLI profile, region, project_id, and IAM permissions without printing secrets.');
|
|
797
1236
|
}
|
|
798
1237
|
}
|
|
799
1238
|
if (/APIGW\.(\d+)/i.test(errorCode)) {
|
|
800
|
-
suggestions.push(
|
|
1239
|
+
suggestions.push(
|
|
1240
|
+
'APIGW.' +
|
|
1241
|
+
(errorCode.match(/APIGW\.(\d+)/i) || [])[1] +
|
|
1242
|
+
': API Gateway layer error. ' +
|
|
1243
|
+
(errorCode === 'APIGW.0802'
|
|
1244
|
+
? 'IAM user has no region permissions — check IAM console → User → Permissions → add target region.'
|
|
1245
|
+
: 'Verify the API request, region endpoint, and IAM permissions.'),
|
|
1246
|
+
);
|
|
801
1247
|
}
|
|
802
1248
|
if (/region|endpoint|project/i.test(combined)) {
|
|
803
1249
|
suggestions.push('Confirm the service endpoint, region, and project_id match the target resource.');
|
|
804
1250
|
}
|
|
805
1251
|
if (/quota|limit|insufficient|reach the limit/i.test(combined)) {
|
|
806
|
-
suggestions.push(
|
|
1252
|
+
suggestions.push(
|
|
1253
|
+
'Check quota and resource limits before retrying a create or scale operation. Consider switching accounts or requesting a quota increase.',
|
|
1254
|
+
);
|
|
807
1255
|
}
|
|
808
1256
|
if (/not.?found|404/i.test(combined)) {
|
|
809
1257
|
if (/list.?regions/i.test(svc)) {
|
|
@@ -813,7 +1261,9 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
813
1261
|
}
|
|
814
1262
|
}
|
|
815
1263
|
if (!suggestions.length) {
|
|
816
|
-
suggestions.push(
|
|
1264
|
+
suggestions.push(
|
|
1265
|
+
'Collect service name, operation, region, project_id, request_id, and the full redacted error message.',
|
|
1266
|
+
);
|
|
817
1267
|
}
|
|
818
1268
|
|
|
819
1269
|
if (requestId) {
|
|
@@ -821,8 +1271,7 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
821
1271
|
}
|
|
822
1272
|
|
|
823
1273
|
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());
|
|
1274
|
+
return !arr.slice(0, i).some((prev) => prev.substring(0, 50).toLowerCase() === s.substring(0, 50).toLowerCase());
|
|
826
1275
|
});
|
|
827
1276
|
|
|
828
1277
|
return {
|
|
@@ -839,9 +1288,7 @@ async function searchDocs(query, topic = 'all') {
|
|
|
839
1288
|
const results = [];
|
|
840
1289
|
try {
|
|
841
1290
|
if (existsSync(SKILLS_ROOT)) {
|
|
842
|
-
const dirs =
|
|
843
|
-
.filter((d) => d.isDirectory())
|
|
844
|
-
.map((d) => d.name);
|
|
1291
|
+
const dirs = listSkillDirs(SKILLS_ROOT);
|
|
845
1292
|
for (const dir of dirs) {
|
|
846
1293
|
const skillPath = join(SKILLS_ROOT, dir, 'SKILL.md');
|
|
847
1294
|
if (!existsSync(skillPath)) continue;
|
|
@@ -864,11 +1311,16 @@ async function searchDocs(query, topic = 'all') {
|
|
|
864
1311
|
const nameLower = name.toLowerCase();
|
|
865
1312
|
const contentLower = content.toLowerCase();
|
|
866
1313
|
const relevance = tokens.reduce((score, token) => {
|
|
867
|
-
return
|
|
1314
|
+
return (
|
|
1315
|
+
score +
|
|
1316
|
+
(descLower.includes(token) ? 3 : 0) +
|
|
1317
|
+
(nameLower.includes(token) ? 2 : 0) +
|
|
1318
|
+
(contentLower.includes(token) ? 1 : 0)
|
|
1319
|
+
);
|
|
868
1320
|
}, 0);
|
|
869
1321
|
if (relevance > 0) {
|
|
870
1322
|
results.push({
|
|
871
|
-
source:
|
|
1323
|
+
source: 'skills/' + dir + '/SKILL.md',
|
|
872
1324
|
name,
|
|
873
1325
|
snippet: description.substring(0, 200),
|
|
874
1326
|
relevance,
|
|
@@ -888,9 +1340,7 @@ async function retrieveSkill(name) {
|
|
|
888
1340
|
if (!skillName) return { ok: false, error: 'Skill name is required.' };
|
|
889
1341
|
const skillPath = join(SKILLS_ROOT, skillName, 'SKILL.md');
|
|
890
1342
|
if (!existsSync(skillPath)) {
|
|
891
|
-
const dirs =
|
|
892
|
-
? readdirSync(SKILLS_ROOT, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
893
|
-
: [];
|
|
1343
|
+
const dirs = listSkillDirs(SKILLS_ROOT);
|
|
894
1344
|
return { ok: false, error: 'Skill "' + skillName + '" not found. Available: ' + dirs.join(', ') };
|
|
895
1345
|
}
|
|
896
1346
|
const content = readFileSync(skillPath, 'utf8');
|
|
@@ -903,7 +1353,8 @@ async function retrieveSkill(name) {
|
|
|
903
1353
|
});
|
|
904
1354
|
}
|
|
905
1355
|
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
906
|
-
let version = 1,
|
|
1356
|
+
let version = 1,
|
|
1357
|
+
description = '';
|
|
907
1358
|
if (frontmatter) {
|
|
908
1359
|
const fm = frontmatter[1];
|
|
909
1360
|
const vm = fm.match(/^version:\s*(.+)$/m);
|
|
@@ -915,8 +1366,10 @@ async function retrieveSkill(name) {
|
|
|
915
1366
|
}
|
|
916
1367
|
|
|
917
1368
|
async function listRegions() {
|
|
918
|
-
const result = await runHcloud(['IAM', 'KeystoneListRegions'], { timeoutMs: 30000, maxRetries: 0 })
|
|
919
|
-
|
|
1369
|
+
const result = await runHcloud(['IAM', 'KeystoneListRegions'], { timeoutMs: 30000, maxRetries: 0 }).catch((err) => ({
|
|
1370
|
+
ok: false,
|
|
1371
|
+
error: err.message,
|
|
1372
|
+
}));
|
|
920
1373
|
if (!result.ok) {
|
|
921
1374
|
return {
|
|
922
1375
|
ok: false,
|
|
@@ -936,7 +1389,7 @@ async function listRegions() {
|
|
|
936
1389
|
note: 'hcloud unavailable. Showing static region list. For the complete list, visit the fallback URL.',
|
|
937
1390
|
};
|
|
938
1391
|
}
|
|
939
|
-
let regions
|
|
1392
|
+
let regions;
|
|
940
1393
|
try {
|
|
941
1394
|
const parsed = typeof result.stdout === 'string' ? JSON.parse(result.stdout) : result.stdout;
|
|
942
1395
|
const rawRegions = parsed.regions || [];
|
|
@@ -954,37 +1407,128 @@ async function listRegions() {
|
|
|
954
1407
|
}
|
|
955
1408
|
|
|
956
1409
|
async function getRegionalAvailability(service, region) {
|
|
957
|
-
const svc = String(service || '')
|
|
958
|
-
|
|
1410
|
+
const svc = String(service || '')
|
|
1411
|
+
.toLowerCase()
|
|
1412
|
+
.trim();
|
|
1413
|
+
const reg = String(region || '')
|
|
1414
|
+
.toLowerCase()
|
|
1415
|
+
.trim();
|
|
959
1416
|
if (!svc || !reg) return { ok: false, error: 'Both service and region are required.' };
|
|
960
1417
|
const known = {
|
|
961
|
-
ecs: [
|
|
962
|
-
|
|
963
|
-
|
|
1418
|
+
ecs: [
|
|
1419
|
+
'cn-south-1',
|
|
1420
|
+
'cn-north-4',
|
|
1421
|
+
'cn-north-1',
|
|
1422
|
+
'cn-east-3',
|
|
1423
|
+
'cn-east-2',
|
|
1424
|
+
'ap-southeast-3',
|
|
1425
|
+
'ap-southeast-2',
|
|
1426
|
+
'ap-southeast-1',
|
|
1427
|
+
'ap-southeast-4',
|
|
1428
|
+
'af-south-1',
|
|
1429
|
+
'tr-west-1',
|
|
1430
|
+
'sa-brazil-1',
|
|
1431
|
+
'la-north-2',
|
|
1432
|
+
'na-mexico-1',
|
|
1433
|
+
'me-east-1',
|
|
1434
|
+
],
|
|
1435
|
+
obs: [
|
|
1436
|
+
'cn-south-1',
|
|
1437
|
+
'cn-north-4',
|
|
1438
|
+
'cn-north-1',
|
|
1439
|
+
'cn-east-3',
|
|
1440
|
+
'cn-east-2',
|
|
1441
|
+
'ap-southeast-3',
|
|
1442
|
+
'ap-southeast-2',
|
|
1443
|
+
'ap-southeast-1',
|
|
1444
|
+
'af-south-1',
|
|
1445
|
+
],
|
|
1446
|
+
vpc: [
|
|
1447
|
+
'cn-south-1',
|
|
1448
|
+
'cn-north-4',
|
|
1449
|
+
'cn-north-1',
|
|
1450
|
+
'cn-east-3',
|
|
1451
|
+
'cn-east-2',
|
|
1452
|
+
'ap-southeast-3',
|
|
1453
|
+
'ap-southeast-2',
|
|
1454
|
+
'ap-southeast-1',
|
|
1455
|
+
'ap-southeast-4',
|
|
1456
|
+
'af-south-1',
|
|
1457
|
+
'tr-west-1',
|
|
1458
|
+
'sa-brazil-1',
|
|
1459
|
+
'la-north-2',
|
|
1460
|
+
'me-east-1',
|
|
1461
|
+
],
|
|
964
1462
|
iam: ['global'],
|
|
965
|
-
rds: [
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1463
|
+
rds: [
|
|
1464
|
+
'cn-south-1',
|
|
1465
|
+
'cn-north-4',
|
|
1466
|
+
'cn-north-1',
|
|
1467
|
+
'cn-east-3',
|
|
1468
|
+
'cn-east-2',
|
|
1469
|
+
'ap-southeast-3',
|
|
1470
|
+
'ap-southeast-2',
|
|
1471
|
+
'ap-southeast-1',
|
|
1472
|
+
],
|
|
1473
|
+
gaussdb: ['cn-south-1', 'cn-north-4', 'cn-east-3'],
|
|
1474
|
+
cce: [
|
|
1475
|
+
'cn-south-1',
|
|
1476
|
+
'cn-north-4',
|
|
1477
|
+
'cn-north-1',
|
|
1478
|
+
'cn-east-3',
|
|
1479
|
+
'cn-east-2',
|
|
1480
|
+
'ap-southeast-3',
|
|
1481
|
+
'ap-southeast-2',
|
|
1482
|
+
'ap-southeast-1',
|
|
1483
|
+
],
|
|
1484
|
+
modelarts: ['cn-south-1', 'cn-north-4', 'cn-east-3'],
|
|
1485
|
+
functiongraph: [
|
|
1486
|
+
'cn-south-1',
|
|
1487
|
+
'cn-north-4',
|
|
1488
|
+
'cn-north-1',
|
|
1489
|
+
'cn-east-3',
|
|
1490
|
+
'cn-east-2',
|
|
1491
|
+
'ap-southeast-3',
|
|
1492
|
+
'ap-southeast-2',
|
|
1493
|
+
'ap-southeast-1',
|
|
1494
|
+
],
|
|
1495
|
+
dew: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1496
|
+
smn: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1497
|
+
ces: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1498
|
+
cts: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1499
|
+
apig: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1500
|
+
cbr: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1501
|
+
dds: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1502
|
+
dcs: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
978
1503
|
};
|
|
979
|
-
if (!known[svc])
|
|
1504
|
+
if (!known[svc])
|
|
1505
|
+
return {
|
|
1506
|
+
ok: false,
|
|
1507
|
+
service: svc,
|
|
1508
|
+
region: reg,
|
|
1509
|
+
available: false,
|
|
1510
|
+
note:
|
|
1511
|
+
'Service ' +
|
|
1512
|
+
svc +
|
|
1513
|
+
' is not in the regional availability cache. Run hcloud ' +
|
|
1514
|
+
svc.toUpperCase() +
|
|
1515
|
+
' --help to verify, or check https://developer.huaweicloud.com/endpoint.',
|
|
1516
|
+
};
|
|
980
1517
|
const available = known[svc].includes(reg) || known[svc].includes('global');
|
|
981
1518
|
return {
|
|
982
|
-
ok: true,
|
|
1519
|
+
ok: true,
|
|
1520
|
+
service: svc,
|
|
1521
|
+
region: reg,
|
|
1522
|
+
available,
|
|
983
1523
|
note: available
|
|
984
1524
|
? svc + ' is available in ' + reg + '.'
|
|
985
|
-
: svc +
|
|
1525
|
+
: svc +
|
|
1526
|
+
' availability in ' +
|
|
1527
|
+
reg +
|
|
1528
|
+
' could not be confirmed. Verify at https://developer.huaweicloud.com/endpoint.',
|
|
986
1529
|
sourcedFrom: 'static cache, update via npm package upgrade',
|
|
987
|
-
disclaimer:
|
|
1530
|
+
disclaimer:
|
|
1531
|
+
'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
1532
|
};
|
|
989
1533
|
}
|
|
990
1534
|
|