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