huaweicloud-devkit 1.0.2-dev.9 → 1.0.2-next.10
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 +20 -20
- package/LICENSE +201 -201
- package/README.md +178 -138
- package/README.zh-CN.md +176 -138
- package/bin/setup.cjs +10 -8
- package/integrations/opencode/commands/huaweicloud-doctor.md +14 -14
- package/integrations/opencode/opencode.json +11 -14
- package/integrations/opencode/skills/huaweicloud-core/SKILL.md +32 -32
- package/package.json +60 -44
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +31 -44
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +30 -43
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +31 -44
- package/plugins/huaweicloud-core/.mcp.json +13 -15
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +31 -44
- package/plugins/huaweicloud-core/hooks/hooks.json +26 -26
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +136 -136
- package/plugins/huaweicloud-core/safety/policy.json +76 -100
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +227 -199
- package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +164 -164
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +48 -48
- package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +50 -50
- package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +103 -101
- package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +90 -90
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +191 -190
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +171 -124
- package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +55 -55
- package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +70 -70
- package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +57 -56
- package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +106 -97
- package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +36 -29
- package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +29 -23
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +188 -185
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +144 -133
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/evs.md +31 -31
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +57 -55
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +52 -52
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +132 -131
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +60 -58
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +72 -71
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/deploy-workflow.md +119 -119
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +142 -142
- package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +56 -56
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +69 -65
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +111 -111
- package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +148 -122
- package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +56 -56
- package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +65 -65
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +134 -127
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +49 -49
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +31 -26
- package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +101 -100
- package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +40 -40
- package/plugins/huaweicloud-core/skills/huawei-obs/references/static-website.md +56 -56
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +229 -224
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +173 -134
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +69 -69
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +84 -84
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +77 -76
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +60 -59
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +50 -50
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +63 -63
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +56 -56
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +56 -47
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +138 -128
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +111 -96
- package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +20 -20
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +30 -28
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +59 -60
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +83 -83
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +54 -1
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +103 -95
- package/plugins/huaweicloud-core/src/auth/service.mjs +63 -63
- package/plugins/huaweicloud-core/src/data/icons-manifest.v1.json +3517 -0
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +283 -265
- package/plugins/huaweicloud-core/src/icon-library.mjs +171 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +130 -129
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +68 -0
- package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +94 -0
- package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +140 -137
- package/plugins/huaweicloud-core/src/safety-policy.mjs +356 -302
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +86 -87
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +150 -153
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +196 -105
- package/plugins/huaweicloud-core/src/search-market.mjs +208 -169
- package/plugins/huaweicloud-core/src/setup-cli.mjs +2622 -1594
- package/plugins/huaweicloud-core/src/tools.mjs +463 -111
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +447 -427
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +128 -132
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +215 -227
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +189 -202
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +164 -158
- package/plugins/huaweicloud-core/src/ws-exec/index.js +19 -19
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +338 -338
|
@@ -1,12 +1,18 @@
|
|
|
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
8
|
import { searchMarketplace } from './search-market.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import { getServiceIcon } from './icon-library.mjs';
|
|
10
|
+
import {
|
|
11
|
+
execWithSession,
|
|
12
|
+
closeSession,
|
|
13
|
+
uploadFileWithSession,
|
|
14
|
+
DEFAULT_WORKSPACE_ID,
|
|
15
|
+
} from './sandbox/session-manager.mjs';
|
|
10
16
|
import { hdkitCheckUser, hdkitSignAgreement, hdkitConnect, hdkitCredentials } from './sandbox/hdkitservice-api.mjs';
|
|
11
17
|
import { getAuthStatus, syncAuth } from './auth/service.mjs';
|
|
12
18
|
import { readGlobalCredentials, writeObsConfig as writeObsConfigFile } from './auth/credentials.mjs';
|
|
@@ -25,12 +31,53 @@ function workbuddySkillsDir() {
|
|
|
25
31
|
const home = homedir();
|
|
26
32
|
return join(home, '.workbuddy', 'skills');
|
|
27
33
|
}
|
|
34
|
+
function dshSkillsDir() {
|
|
35
|
+
const home = process.env.DSH_HOME || join(homedir(), '.dsh');
|
|
36
|
+
return join(home, 'skills');
|
|
37
|
+
}
|
|
38
|
+
function officeaceSkillsRoot() {
|
|
39
|
+
if (process.env.OFFICEACE_HOME) return join(process.env.OFFICEACE_HOME, 'skills');
|
|
40
|
+
const dotDir = join(homedir(), '.office-claw');
|
|
41
|
+
const dotCapFile = join(dotDir, 'capabilities.json');
|
|
42
|
+
if (existsSync(dotCapFile)) return join(dotDir, 'skills');
|
|
43
|
+
if (process.platform === 'win32') {
|
|
44
|
+
for (const base of [process.env.ProgramFiles, 'C:\\Program Files', 'D:\\Program Files']) {
|
|
45
|
+
if (!base) continue;
|
|
46
|
+
const dir = join(base, 'OfficeAce', '.office-claw');
|
|
47
|
+
if (existsSync(join(dir, 'capabilities.json'))) return join(dir, 'skills');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return join(dotDir, 'skills');
|
|
51
|
+
}
|
|
52
|
+
export function listSkillDirs(root) {
|
|
53
|
+
if (!existsSync(root)) return [];
|
|
54
|
+
try {
|
|
55
|
+
return readdirSync(root, { withFileTypes: true })
|
|
56
|
+
.filter((d) => (d.isDirectory() || d.isSymbolicLink()) && existsSync(join(root, d.name, 'SKILL.md')))
|
|
57
|
+
.map((d) => d.name);
|
|
58
|
+
} catch {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function findSkillsRoot(candidates) {
|
|
64
|
+
for (const dir of candidates) {
|
|
65
|
+
if (listSkillDirs(dir).length > 0) return dir;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
28
70
|
function resolveSkillsRoot() {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
71
|
+
return (
|
|
72
|
+
findSkillsRoot([
|
|
73
|
+
SKILLS_ROOT_DEV,
|
|
74
|
+
dshSkillsDir(),
|
|
75
|
+
codeartsSkillsDir(),
|
|
76
|
+
opencodeSkillsDir(),
|
|
77
|
+
workbuddySkillsDir(),
|
|
78
|
+
officeaceSkillsRoot(),
|
|
79
|
+
]) || SKILLS_ROOT_DEV
|
|
80
|
+
);
|
|
34
81
|
}
|
|
35
82
|
const SKILLS_ROOT = resolveSkillsRoot();
|
|
36
83
|
|
|
@@ -90,7 +137,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
90
137
|
},
|
|
91
138
|
{
|
|
92
139
|
name: 'huaweicloud_list_operations',
|
|
93
|
-
description:
|
|
140
|
+
description:
|
|
141
|
+
'List KooCLI operations for a Huawei Cloud service by running local/read-only hcloud <Service> --help.',
|
|
94
142
|
inputSchema: {
|
|
95
143
|
type: 'object',
|
|
96
144
|
required: ['service'],
|
|
@@ -108,7 +156,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
108
156
|
},
|
|
109
157
|
{
|
|
110
158
|
name: 'huaweicloud_run_approved_command',
|
|
111
|
-
description:
|
|
159
|
+
description:
|
|
160
|
+
'Run a write-capable hcloud command only after the exact command has been shown and explicitly approved by the user.',
|
|
112
161
|
inputSchema: {
|
|
113
162
|
type: 'object',
|
|
114
163
|
required: ['args', 'approvedCommand', 'approvedByUser'],
|
|
@@ -188,7 +237,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
188
237
|
},
|
|
189
238
|
{
|
|
190
239
|
name: 'huaweicloud_hook_check_deploy_plan',
|
|
191
|
-
description:
|
|
240
|
+
description:
|
|
241
|
+
'Check a structured or textual deployment plan for Huawei Cloud sandbox, exposure, IAM, and cost risks.',
|
|
192
242
|
inputSchema: {
|
|
193
243
|
type: 'object',
|
|
194
244
|
required: ['plan'],
|
|
@@ -227,19 +277,24 @@ export const TOOL_DEFINITIONS = [
|
|
|
227
277
|
},
|
|
228
278
|
{
|
|
229
279
|
name: 'huaweicloud_search_docs',
|
|
230
|
-
description:
|
|
280
|
+
description:
|
|
281
|
+
'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
282
|
inputSchema: {
|
|
232
283
|
type: 'object',
|
|
233
284
|
required: ['query'],
|
|
234
285
|
properties: {
|
|
235
286
|
query: { type: 'string', description: 'Search query across skill descriptions and documentation.' },
|
|
236
|
-
topic: {
|
|
287
|
+
topic: {
|
|
288
|
+
type: 'string',
|
|
289
|
+
description: 'Optional filter: all | ecs | obs | vpc | iam | rds | cce | modelarts | dew. Defaults to all.',
|
|
290
|
+
},
|
|
237
291
|
},
|
|
238
292
|
},
|
|
239
293
|
},
|
|
240
294
|
{
|
|
241
295
|
name: 'huaweicloud_retrieve_skill',
|
|
242
|
-
description:
|
|
296
|
+
description:
|
|
297
|
+
'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
298
|
inputSchema: {
|
|
244
299
|
type: 'object',
|
|
245
300
|
required: ['name'],
|
|
@@ -250,7 +305,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
250
305
|
},
|
|
251
306
|
{
|
|
252
307
|
name: 'huaweicloud_list_regions',
|
|
253
|
-
description:
|
|
308
|
+
description:
|
|
309
|
+
'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
310
|
inputSchema: {
|
|
255
311
|
type: 'object',
|
|
256
312
|
properties: {},
|
|
@@ -258,30 +314,58 @@ export const TOOL_DEFINITIONS = [
|
|
|
258
314
|
},
|
|
259
315
|
{
|
|
260
316
|
name: 'huaweicloud_get_regional_availability',
|
|
261
|
-
description:
|
|
317
|
+
description:
|
|
318
|
+
'Check if a specific Huawei Cloud service is available in a target region. Use before creating resources to prevent failures from regional unavailability.',
|
|
262
319
|
inputSchema: {
|
|
263
320
|
type: 'object',
|
|
264
321
|
required: ['service', 'region'],
|
|
265
322
|
properties: {
|
|
266
|
-
service: {
|
|
323
|
+
service: {
|
|
324
|
+
type: 'string',
|
|
325
|
+
description: 'Service name: ecs, obs, rds, gaussdb, cce, modelarts, functiongraph, etc.',
|
|
326
|
+
},
|
|
267
327
|
region: { type: 'string', description: 'Region ID: cn-south-1, cn-north-4, ap-southeast-3, etc.' },
|
|
268
328
|
},
|
|
269
329
|
},
|
|
270
330
|
},
|
|
271
331
|
{
|
|
272
332
|
name: 'huaweicloud_search_marketplace',
|
|
273
|
-
description:
|
|
333
|
+
description:
|
|
334
|
+
'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
335
|
inputSchema: {
|
|
275
336
|
type: 'object',
|
|
276
337
|
properties: {
|
|
277
338
|
query: { type: 'string', description: 'Search query across skill name, description, triggers, and service.' },
|
|
278
|
-
category: {
|
|
339
|
+
category: {
|
|
340
|
+
type: 'string',
|
|
341
|
+
description: 'Optional category filter: computing, storage, network, security, devtools, monitoring, etc.',
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: 'huaweicloud_get_service_icon',
|
|
348
|
+
description:
|
|
349
|
+
'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.',
|
|
350
|
+
inputSchema: {
|
|
351
|
+
type: 'object',
|
|
352
|
+
properties: {
|
|
353
|
+
service: {
|
|
354
|
+
type: 'string',
|
|
355
|
+
description:
|
|
356
|
+
'Service name, alias, or Chinese name, e.g. ecs, obs, modelarts, 对象存储, 虚拟私有云. Omit to browse by category only.',
|
|
357
|
+
},
|
|
358
|
+
category: {
|
|
359
|
+
type: 'string',
|
|
360
|
+
description: 'Optional category filter, e.g. 计算, 存储, 网络, 人工智能, 数据库, 安全, 企业应用.',
|
|
361
|
+
},
|
|
279
362
|
},
|
|
280
363
|
},
|
|
281
364
|
},
|
|
282
365
|
{
|
|
283
366
|
name: 'huaweicloud_setup_obs_config',
|
|
284
|
-
description:
|
|
367
|
+
description:
|
|
368
|
+
'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
369
|
inputSchema: {
|
|
286
370
|
type: 'object',
|
|
287
371
|
properties: {
|
|
@@ -291,27 +375,38 @@ export const TOOL_DEFINITIONS = [
|
|
|
291
375
|
},
|
|
292
376
|
{
|
|
293
377
|
name: 'huaweicloud_auth_status',
|
|
294
|
-
description:
|
|
378
|
+
description:
|
|
379
|
+
'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
380
|
inputSchema: {
|
|
296
381
|
type: 'object',
|
|
297
382
|
properties: {
|
|
298
|
-
target: {
|
|
383
|
+
target: {
|
|
384
|
+
type: 'string',
|
|
385
|
+
description:
|
|
386
|
+
'Agent target to check: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, or all (default).',
|
|
387
|
+
},
|
|
299
388
|
},
|
|
300
389
|
},
|
|
301
390
|
},
|
|
302
391
|
{
|
|
303
392
|
name: 'huaweicloud_auth_sync',
|
|
304
|
-
description:
|
|
393
|
+
description:
|
|
394
|
+
'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
395
|
inputSchema: {
|
|
306
396
|
type: 'object',
|
|
307
397
|
properties: {
|
|
308
|
-
target: {
|
|
398
|
+
target: {
|
|
399
|
+
type: 'string',
|
|
400
|
+
description:
|
|
401
|
+
'Agent target to report after sync: opencode, codex, codex-desktop, codearts, workbuddy, dsh, or all (default).',
|
|
402
|
+
},
|
|
309
403
|
},
|
|
310
404
|
},
|
|
311
405
|
},
|
|
312
406
|
{
|
|
313
407
|
name: 'huaweicloud_sandbox_exec_with_session',
|
|
314
|
-
description:
|
|
408
|
+
description:
|
|
409
|
+
'Execute a command on a workspace terminal with session reuse (state persists across calls). Shell state (cd, env vars, aliases) carries over between calls.',
|
|
315
410
|
inputSchema: {
|
|
316
411
|
type: 'object',
|
|
317
412
|
required: ['command'],
|
|
@@ -334,9 +429,26 @@ export const TOOL_DEFINITIONS = [
|
|
|
334
429
|
},
|
|
335
430
|
},
|
|
336
431
|
},
|
|
432
|
+
{
|
|
433
|
+
name: 'huaweicloud_sandbox_upload_file',
|
|
434
|
+
description:
|
|
435
|
+
'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.',
|
|
436
|
+
inputSchema: {
|
|
437
|
+
type: 'object',
|
|
438
|
+
required: ['local_path', 'remote_path'],
|
|
439
|
+
properties: {
|
|
440
|
+
local_path: { type: 'string', description: 'Absolute path to the local file to upload.' },
|
|
441
|
+
remote_path: { type: 'string', description: 'Target path in the sandbox, e.g. /workspace/<repo>/index.html.' },
|
|
442
|
+
workspace_id: { type: 'string', description: 'The workspace ID' },
|
|
443
|
+
username: { type: 'string', description: 'Login username (default: root)' },
|
|
444
|
+
timeout_ms: { type: 'number', description: 'Per-command execution timeout in milliseconds (default: 30000)' },
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
},
|
|
337
448
|
{
|
|
338
449
|
name: 'huaweicloud_sandbox_check_user',
|
|
339
|
-
description:
|
|
450
|
+
description:
|
|
451
|
+
'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
452
|
inputSchema: {
|
|
341
453
|
type: 'object',
|
|
342
454
|
properties: {},
|
|
@@ -344,7 +456,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
344
456
|
},
|
|
345
457
|
{
|
|
346
458
|
name: 'huaweicloud_sandbox_sign_agreement',
|
|
347
|
-
description:
|
|
459
|
+
description:
|
|
460
|
+
"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
461
|
inputSchema: {
|
|
349
462
|
type: 'object',
|
|
350
463
|
properties: {},
|
|
@@ -352,11 +465,16 @@ export const TOOL_DEFINITIONS = [
|
|
|
352
465
|
},
|
|
353
466
|
{
|
|
354
467
|
name: 'huaweicloud_sandbox_connect',
|
|
355
|
-
description:
|
|
468
|
+
description:
|
|
469
|
+
'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
470
|
inputSchema: {
|
|
357
471
|
type: 'object',
|
|
358
472
|
properties: {
|
|
359
|
-
source: {
|
|
473
|
+
source: {
|
|
474
|
+
type: 'string',
|
|
475
|
+
description:
|
|
476
|
+
'Source identifier (default: WEB). Options: VSCODE, CLI, WEB, WEBVNC, WEBPTY, WEBIDE, CURSOR, etc.',
|
|
477
|
+
},
|
|
360
478
|
template_id: { type: 'string', description: 'Template ID; overrides server default (only for new sandbox)' },
|
|
361
479
|
flavor_id: { type: 'string', description: 'Flavor ID; overrides server default (only for new sandbox)' },
|
|
362
480
|
env: { type: 'object', description: 'Environment variables to set in the sandbox (only for new sandbox)' },
|
|
@@ -376,7 +494,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
376
494
|
},
|
|
377
495
|
{
|
|
378
496
|
name: 'huaweicloud_sandbox_credentials',
|
|
379
|
-
description:
|
|
497
|
+
description:
|
|
498
|
+
'Configure temporary AK/SK for a sandbox via hdkitservice. Injects temporary credentials into the sandbox. The sandbox must be in RUNNING state.',
|
|
380
499
|
inputSchema: {
|
|
381
500
|
type: 'object',
|
|
382
501
|
properties: {
|
|
@@ -386,7 +505,6 @@ export const TOOL_DEFINITIONS = [
|
|
|
386
505
|
},
|
|
387
506
|
},
|
|
388
507
|
},
|
|
389
|
-
|
|
390
508
|
];
|
|
391
509
|
|
|
392
510
|
export async function callTool(name, args = {}) {
|
|
@@ -424,10 +542,12 @@ export async function callTool(name, args = {}) {
|
|
|
424
542
|
return listRegions();
|
|
425
543
|
case 'huaweicloud_get_regional_availability':
|
|
426
544
|
return getRegionalAvailability(args.service || '', args.region || '');
|
|
427
|
-
|
|
545
|
+
case 'huaweicloud_explain_error':
|
|
428
546
|
return explainError(args);
|
|
429
547
|
case 'huaweicloud_search_marketplace':
|
|
430
548
|
return searchMarketplace(args.query || '', args.category || '');
|
|
549
|
+
case 'huaweicloud_get_service_icon':
|
|
550
|
+
return getServiceIcon(args.service || '', args.category || '');
|
|
431
551
|
case 'huaweicloud_setup_obs_config':
|
|
432
552
|
return setupObsConfig(args.profile);
|
|
433
553
|
case 'huaweicloud_auth_status':
|
|
@@ -447,6 +567,21 @@ export async function callTool(name, args = {}) {
|
|
|
447
567
|
const closed = await closeSession(sandboxWsId3, sandboxUser3);
|
|
448
568
|
return closed ? 'ok' : 'not_connected';
|
|
449
569
|
}
|
|
570
|
+
case 'huaweicloud_sandbox_upload_file': {
|
|
571
|
+
if (!args.local_path || !args.remote_path) {
|
|
572
|
+
throw new Error('local_path and remote_path are required.');
|
|
573
|
+
}
|
|
574
|
+
const sandboxWsId4 = args.workspace_id || DEFAULT_WORKSPACE_ID;
|
|
575
|
+
const sandboxUser4 = args.username || 'root';
|
|
576
|
+
const sandboxTimeout4 = args.timeout_ms || 30000;
|
|
577
|
+
return await uploadFileWithSession(
|
|
578
|
+
sandboxWsId4,
|
|
579
|
+
args.local_path,
|
|
580
|
+
args.remote_path,
|
|
581
|
+
sandboxUser4,
|
|
582
|
+
sandboxTimeout4,
|
|
583
|
+
);
|
|
584
|
+
}
|
|
450
585
|
case 'huaweicloud_sandbox_check_user':
|
|
451
586
|
return await hdkitCheckUser();
|
|
452
587
|
case 'huaweicloud_sandbox_sign_agreement':
|
|
@@ -465,11 +600,12 @@ function hookResult(result) {
|
|
|
465
600
|
ok: result.decision !== 'deny',
|
|
466
601
|
decision: result.decision,
|
|
467
602
|
findings: result.findings,
|
|
468
|
-
nextStep:
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
603
|
+
nextStep:
|
|
604
|
+
result.decision === 'deny'
|
|
605
|
+
? 'Revise the command, artifact, or deployment plan before execution.'
|
|
606
|
+
: result.decision === 'warn'
|
|
607
|
+
? 'Review the warnings with the user before proceeding.'
|
|
608
|
+
: 'No Huawei Cloud hook risk rule matched.',
|
|
473
609
|
};
|
|
474
610
|
}
|
|
475
611
|
|
|
@@ -490,7 +626,8 @@ export async function runVersionCheck(options = {}) {
|
|
|
490
626
|
: isSpawnError
|
|
491
627
|
? '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
628
|
: 'Install Huawei Cloud KooCLI: npx huaweicloud-devkit install-hcloud. Configure credentials outside the agent conversation.',
|
|
493
|
-
authHint:
|
|
629
|
+
authHint:
|
|
630
|
+
'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
631
|
};
|
|
495
632
|
}
|
|
496
633
|
|
|
@@ -509,7 +646,8 @@ async function showProfileRedacted(profile) {
|
|
|
509
646
|
ok: false,
|
|
510
647
|
blockedByPolicy: true,
|
|
511
648
|
reason: result.reason,
|
|
512
|
-
safeAlternative:
|
|
649
|
+
safeAlternative:
|
|
650
|
+
'Use huaweicloud_show_profile_redacted so profile output is returned through the redaction pipeline.',
|
|
513
651
|
};
|
|
514
652
|
}
|
|
515
653
|
return {
|
|
@@ -551,7 +689,12 @@ async function setupObsConfig(profile) {
|
|
|
551
689
|
async function setupObsConfigFromHcloud(profile) {
|
|
552
690
|
const obsConfigPath = join(homedir(), '.obsutilconfig');
|
|
553
691
|
if (existsSync(obsConfigPath)) {
|
|
554
|
-
return {
|
|
692
|
+
return {
|
|
693
|
+
ok: true,
|
|
694
|
+
existed: true,
|
|
695
|
+
path: obsConfigPath,
|
|
696
|
+
note: 'OBS config already exists. Delete ~/.obsutilconfig first if you need to re-sync.',
|
|
697
|
+
};
|
|
555
698
|
}
|
|
556
699
|
|
|
557
700
|
const args = ['configure', 'show'];
|
|
@@ -567,9 +710,9 @@ async function setupObsConfigFromHcloud(profile) {
|
|
|
567
710
|
};
|
|
568
711
|
}
|
|
569
712
|
|
|
570
|
-
let accessKeyId
|
|
571
|
-
let secretAccessKey
|
|
572
|
-
let region
|
|
713
|
+
let accessKeyId;
|
|
714
|
+
let secretAccessKey;
|
|
715
|
+
let region;
|
|
573
716
|
|
|
574
717
|
try {
|
|
575
718
|
const parsed = typeof result.stdout === 'string' ? JSON.parse(result.stdout) : result.stdout;
|
|
@@ -630,7 +773,11 @@ async function setupObsConfigFromHcloud(profile) {
|
|
|
630
773
|
const SERVICE_EXAMPLES = {
|
|
631
774
|
ECS: { list: 'ECS ListServersDetails', create: 'ECS CreateServers', show: 'IMS GlanceShowImage' },
|
|
632
775
|
VPC: { list: 'VPC ListVpcs', create: 'VPC CreateVpc', show: 'VPC ShowVpc' },
|
|
633
|
-
FunctionGraph: {
|
|
776
|
+
FunctionGraph: {
|
|
777
|
+
list: 'FunctionGraph ListFunctions',
|
|
778
|
+
create: 'FunctionGraph CreateFunction',
|
|
779
|
+
show: 'FunctionGraph ShowFunctionConfig',
|
|
780
|
+
},
|
|
634
781
|
APIG: { list: 'APIG ListInstancesV2', create: 'APIG CreateInstanceV2', show: 'APIG ShowDetailsOfInstanceV2' },
|
|
635
782
|
OBS: { list: 'OBS ls', create: 'OBS mb obs://<bucket>', show: 'OBS stat obs://<bucket>/<key>' },
|
|
636
783
|
RDS: { list: 'RDS ListInstances', create: 'RDS CreateInstance', show: 'RDS ShowInstance' },
|
|
@@ -662,7 +809,9 @@ async function listOperations(service, options = {}) {
|
|
|
662
809
|
service: serviceName,
|
|
663
810
|
command: isObs ? 'hcloud obs help' : `hcloud ${svc} --help`,
|
|
664
811
|
selectionRule: 'Use this help text to select the exact KooCLI operation name before planning any service command.',
|
|
665
|
-
examples: SERVICE_EXAMPLES[serviceName.toUpperCase()] || {
|
|
812
|
+
examples: SERVICE_EXAMPLES[serviceName.toUpperCase()] || {
|
|
813
|
+
note: `No cached examples for ${serviceName}. Use the help text above to discover available operations.`,
|
|
814
|
+
},
|
|
666
815
|
result,
|
|
667
816
|
};
|
|
668
817
|
}
|
|
@@ -691,41 +840,131 @@ async function runApprovedCommand(args = {}) {
|
|
|
691
840
|
function serviceCatalog(intent = '') {
|
|
692
841
|
const it = String(intent).toLowerCase();
|
|
693
842
|
const routeMap = [
|
|
694
|
-
{
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
843
|
+
{
|
|
844
|
+
keywords: ['ecs', 'server', 'vm', 'instance', 'compute', 'flavor', 'image'],
|
|
845
|
+
skills: ['huawei-ecs'],
|
|
846
|
+
services: ['ECS'],
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
keywords: ['vpc', 'subnet', 'network', 'security group', 'eip', 'nat', 'vpn', 'bandwidth'],
|
|
850
|
+
skills: ['huawei-vpc'],
|
|
851
|
+
services: ['VPC', 'EIP'],
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
keywords: ['obs', 'bucket', 'storage', 'object', 'static website', 'static site', 'hosting'],
|
|
855
|
+
skills: ['huawei-obs'],
|
|
856
|
+
services: ['OBS'],
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
keywords: ['functiongraph', 'serverless', 'function', 'lambda', 'trigger', 'faas'],
|
|
860
|
+
skills: ['huawei-functiongraph'],
|
|
861
|
+
services: ['FunctionGraph'],
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
keywords: ['cce', 'kubernetes', 'k8s', 'container', 'cluster', 'node pool', 'swr', 'docker', 'image registry'],
|
|
865
|
+
skills: ['huawei-cce'],
|
|
866
|
+
services: ['CCE', 'SWR'],
|
|
867
|
+
},
|
|
699
868
|
{ keywords: ['apig', 'api gateway', 'publish', 'throttle'], skills: ['huawei-apig'], services: ['APIG'] },
|
|
700
869
|
{ keywords: ['rds', 'mysql', 'postgresql', 'database', 'db'], skills: ['huawei-rds'], services: ['RDS'] },
|
|
701
|
-
{
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
{
|
|
707
|
-
|
|
708
|
-
|
|
870
|
+
{
|
|
871
|
+
keywords: ['gaussdb', 'distributed', 'sharding', 'opengauss'],
|
|
872
|
+
skills: ['huawei-gaussdb'],
|
|
873
|
+
services: ['GaussDB'],
|
|
874
|
+
},
|
|
875
|
+
{
|
|
876
|
+
keywords: ['iam', 'permission', 'policy', 'role', 'user', 'ak/sk', 'access key', 'agency'],
|
|
877
|
+
skills: ['huawei-iam'],
|
|
878
|
+
services: ['IAM'],
|
|
879
|
+
},
|
|
880
|
+
{
|
|
881
|
+
keywords: ['dew', 'secret', 'kms', 'encrypt', 'decrypt', 'certificate', 'csms'],
|
|
882
|
+
skills: ['huawei-dew'],
|
|
883
|
+
services: ['CSMS', 'KMS'],
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
keywords: ['modelarts', 'ai', 'model', 'training', 'inference', 'machine learning'],
|
|
887
|
+
skills: ['huawei-modelarts'],
|
|
888
|
+
services: ['ModelArts'],
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
keywords: ['billing', 'cost', 'bill', 'budget', 'expense', 'bss'],
|
|
892
|
+
skills: ['huawei-billing'],
|
|
893
|
+
services: ['BSS'],
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
keywords: ['waf', 'aad', 'ddos', 'firewall', 'web protection'],
|
|
897
|
+
skills: ['huawei-waf-aad'],
|
|
898
|
+
services: ['WAF', 'AAD'],
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
keywords: ['smn', 'dms', 'notification', 'message', 'kafka', 'rabbitmq'],
|
|
902
|
+
skills: ['huawei-smn-dms'],
|
|
903
|
+
services: ['SMN', 'DMS'],
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
keywords: ['ces', 'monitor', 'alarm', 'metric', 'dashboard', 'cloud eye'],
|
|
907
|
+
skills: ['huawei-cloud-eye'],
|
|
908
|
+
services: ['CES'],
|
|
909
|
+
},
|
|
709
910
|
{ keywords: ['cts', 'audit', 'trace', 'tracker'], skills: ['huawei-cts'], services: ['CTS'] },
|
|
710
911
|
{ keywords: ['cbr', 'backup', 'restore', 'vault', 'snapshot'], skills: ['huawei-cbr'], services: ['CBR'] },
|
|
711
|
-
{
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
]
|
|
912
|
+
{
|
|
913
|
+
keywords: ['deployment', 'deploy', 'ci/cd', 'pipeline', 'release'],
|
|
914
|
+
skills: ['huawei-deployment'],
|
|
915
|
+
services: ['CloudDeploy'],
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
keywords: [
|
|
919
|
+
'sandbox',
|
|
920
|
+
'devstation',
|
|
921
|
+
'workspace',
|
|
922
|
+
'terminal',
|
|
923
|
+
'preview',
|
|
924
|
+
'hwlink',
|
|
925
|
+
'website',
|
|
926
|
+
'web app',
|
|
927
|
+
'webapp',
|
|
928
|
+
'hosting',
|
|
929
|
+
'网站',
|
|
930
|
+
'网页',
|
|
931
|
+
'静态',
|
|
932
|
+
],
|
|
933
|
+
skills: ['huawei-sandbox'],
|
|
934
|
+
services: ['Sandbox', 'DevStation'],
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
keywords: ['dds', 'dcs', 'mongodb', 'redis', 'memcached', 'cache', 'document db'],
|
|
938
|
+
skills: ['huawei-dds-dcs'],
|
|
939
|
+
services: ['DDS', 'DCS'],
|
|
940
|
+
},
|
|
941
|
+
];
|
|
715
942
|
const matched = [];
|
|
716
943
|
const tokens = it.split(/[\s,./-]+/).filter((t) => t.length > 0);
|
|
944
|
+
const cjk = /[\u4e00-\u9fff]/;
|
|
717
945
|
for (const route of routeMap) {
|
|
718
|
-
if (route.keywords.some((kw) => tokens.includes(kw))) {
|
|
946
|
+
if (route.keywords.some((kw) => (kw.includes(' ') || cjk.test(kw) ? it.includes(kw) : tokens.includes(kw)))) {
|
|
719
947
|
matched.push(route);
|
|
720
948
|
}
|
|
721
949
|
}
|
|
722
950
|
const recommendedSkills = [...new Set(matched.flatMap((r) => r.skills))];
|
|
723
951
|
const recommendedServices = [...new Set(matched.flatMap((r) => r.services))].slice(0, 5);
|
|
724
952
|
|
|
953
|
+
// Deployment intent (deploy/host/publish a web app or static website) must never
|
|
954
|
+
// default to a storage/other service — recommend the sandbox first.
|
|
955
|
+
const deploymentIntent = /deploy|host|hosting|publish|website|web app|preview|部署|托管|发布|网站|网页/.test(it);
|
|
956
|
+
if (deploymentIntent && recommendedSkills.includes('huawei-sandbox')) {
|
|
957
|
+
const idx = recommendedSkills.indexOf('huawei-sandbox');
|
|
958
|
+
recommendedSkills.splice(idx, 1);
|
|
959
|
+
recommendedSkills.unshift('huawei-sandbox');
|
|
960
|
+
}
|
|
961
|
+
|
|
725
962
|
return {
|
|
726
963
|
intent,
|
|
727
964
|
recommendedSkills: recommendedSkills.length ? recommendedSkills : ['Use huaweicloud-core to route intent.'],
|
|
728
|
-
recommendedServices: recommendedServices.length
|
|
965
|
+
recommendedServices: recommendedServices.length
|
|
966
|
+
? recommendedServices
|
|
967
|
+
: ['Run hcloud --help to list available services.'],
|
|
729
968
|
capabilityOrder: [
|
|
730
969
|
'Huawei Cloud Skills for task-specific workflows and examples',
|
|
731
970
|
'KooCLI hcloud for local authenticated operations and quick inspection',
|
|
@@ -751,32 +990,38 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
751
990
|
const svc = String(service).toLowerCase();
|
|
752
991
|
|
|
753
992
|
const SERVICE_ALIASES = {
|
|
754
|
-
|
|
755
|
-
|
|
993
|
+
functiongraph: 'FSS',
|
|
994
|
+
fgs: 'FSS',
|
|
756
995
|
};
|
|
757
996
|
const patternKey = SERVICE_ALIASES[svc] || service;
|
|
758
997
|
|
|
759
998
|
const hwErrorPatterns = {
|
|
760
999
|
OBS: {
|
|
761
|
-
|
|
762
|
-
|
|
1000
|
+
InvalidAccessKeyId:
|
|
1001
|
+
'OBS uses AK/SK directly (not IAM tokens). Verify AK/SK validity, OBS endpoint, and OBS permissions.',
|
|
1002
|
+
UserRestricted:
|
|
1003
|
+
'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
1004
|
},
|
|
764
1005
|
APIG: {
|
|
765
1006
|
'APIC.7241': 'The enterprise_project_id is required for enterprise accounts. Add --enterprise_project_id=0.',
|
|
766
|
-
'APIC.7242':
|
|
1007
|
+
'APIC.7242':
|
|
1008
|
+
'The EIP binding method depends on loadbalancer_provider. Use AddIngressEipV2 for elb, AddEipV2 for lvs.',
|
|
767
1009
|
'APIC.7256': 'Bandwidth minimum is 5 Mbps. Use --bandwidth_size=5 or higher.',
|
|
768
1010
|
'APIC.7310': 'available_zone_ids must use AZ codes (e.g. ap-southeast-3a), NOT UUIDs from ListAvailableZonesV2.',
|
|
769
1011
|
},
|
|
770
1012
|
FSS: {
|
|
771
|
-
'FSS.0403':
|
|
1013
|
+
'FSS.0403':
|
|
1014
|
+
'Missing FunctionGraph IAM permissions. Attach FunctionGraph FullAccess role or grant specific actions.',
|
|
772
1015
|
'FSS.1078': '--code_filename is filename-only (no path). cd to the file directory before running the command.',
|
|
773
|
-
'FSS.1417':
|
|
1016
|
+
'FSS.1417':
|
|
1017
|
+
'event_data field validation failed. Check parameter format: use dotted key=value, verify required hidden-optional fields.',
|
|
774
1018
|
},
|
|
775
1019
|
VPC: {
|
|
776
1020
|
'VPC.0301': 'Bandwidth name is required for PER type EIPs, even though --help marks it optional.',
|
|
777
1021
|
},
|
|
778
1022
|
APIGW: {
|
|
779
|
-
'APIGW.0802':
|
|
1023
|
+
'APIGW.0802':
|
|
1024
|
+
'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
1025
|
},
|
|
781
1026
|
};
|
|
782
1027
|
if (hwErrorPatterns[patternKey] && hwErrorPatterns[patternKey][errorCode]) {
|
|
@@ -791,19 +1036,30 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
791
1036
|
|
|
792
1037
|
if (/auth|token|credential|ak|sk|401|403|unauthorized|forbidden|Incorrect IAM/i.test(combined)) {
|
|
793
1038
|
if (svc === 'obs') {
|
|
794
|
-
suggestions.push(
|
|
1039
|
+
suggestions.push(
|
|
1040
|
+
'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.',
|
|
1041
|
+
);
|
|
795
1042
|
} else {
|
|
796
1043
|
suggestions.push('Check KooCLI profile, region, project_id, and IAM permissions without printing secrets.');
|
|
797
1044
|
}
|
|
798
1045
|
}
|
|
799
1046
|
if (/APIGW\.(\d+)/i.test(errorCode)) {
|
|
800
|
-
suggestions.push(
|
|
1047
|
+
suggestions.push(
|
|
1048
|
+
'APIGW.' +
|
|
1049
|
+
(errorCode.match(/APIGW\.(\d+)/i) || [])[1] +
|
|
1050
|
+
': API Gateway layer error. ' +
|
|
1051
|
+
(errorCode === 'APIGW.0802'
|
|
1052
|
+
? 'IAM user has no region permissions — check IAM console → User → Permissions → add target region.'
|
|
1053
|
+
: 'Verify the API request, region endpoint, and IAM permissions.'),
|
|
1054
|
+
);
|
|
801
1055
|
}
|
|
802
1056
|
if (/region|endpoint|project/i.test(combined)) {
|
|
803
1057
|
suggestions.push('Confirm the service endpoint, region, and project_id match the target resource.');
|
|
804
1058
|
}
|
|
805
1059
|
if (/quota|limit|insufficient|reach the limit/i.test(combined)) {
|
|
806
|
-
suggestions.push(
|
|
1060
|
+
suggestions.push(
|
|
1061
|
+
'Check quota and resource limits before retrying a create or scale operation. Consider switching accounts or requesting a quota increase.',
|
|
1062
|
+
);
|
|
807
1063
|
}
|
|
808
1064
|
if (/not.?found|404/i.test(combined)) {
|
|
809
1065
|
if (/list.?regions/i.test(svc)) {
|
|
@@ -813,7 +1069,9 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
813
1069
|
}
|
|
814
1070
|
}
|
|
815
1071
|
if (!suggestions.length) {
|
|
816
|
-
suggestions.push(
|
|
1072
|
+
suggestions.push(
|
|
1073
|
+
'Collect service name, operation, region, project_id, request_id, and the full redacted error message.',
|
|
1074
|
+
);
|
|
817
1075
|
}
|
|
818
1076
|
|
|
819
1077
|
if (requestId) {
|
|
@@ -821,8 +1079,7 @@ function explainError({ service = 'unknown', errorCode = '', message = '', reque
|
|
|
821
1079
|
}
|
|
822
1080
|
|
|
823
1081
|
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());
|
|
1082
|
+
return !arr.slice(0, i).some((prev) => prev.substring(0, 50).toLowerCase() === s.substring(0, 50).toLowerCase());
|
|
826
1083
|
});
|
|
827
1084
|
|
|
828
1085
|
return {
|
|
@@ -839,9 +1096,7 @@ async function searchDocs(query, topic = 'all') {
|
|
|
839
1096
|
const results = [];
|
|
840
1097
|
try {
|
|
841
1098
|
if (existsSync(SKILLS_ROOT)) {
|
|
842
|
-
const dirs =
|
|
843
|
-
.filter((d) => d.isDirectory())
|
|
844
|
-
.map((d) => d.name);
|
|
1099
|
+
const dirs = listSkillDirs(SKILLS_ROOT);
|
|
845
1100
|
for (const dir of dirs) {
|
|
846
1101
|
const skillPath = join(SKILLS_ROOT, dir, 'SKILL.md');
|
|
847
1102
|
if (!existsSync(skillPath)) continue;
|
|
@@ -864,11 +1119,16 @@ async function searchDocs(query, topic = 'all') {
|
|
|
864
1119
|
const nameLower = name.toLowerCase();
|
|
865
1120
|
const contentLower = content.toLowerCase();
|
|
866
1121
|
const relevance = tokens.reduce((score, token) => {
|
|
867
|
-
return
|
|
1122
|
+
return (
|
|
1123
|
+
score +
|
|
1124
|
+
(descLower.includes(token) ? 3 : 0) +
|
|
1125
|
+
(nameLower.includes(token) ? 2 : 0) +
|
|
1126
|
+
(contentLower.includes(token) ? 1 : 0)
|
|
1127
|
+
);
|
|
868
1128
|
}, 0);
|
|
869
1129
|
if (relevance > 0) {
|
|
870
1130
|
results.push({
|
|
871
|
-
source:
|
|
1131
|
+
source: 'skills/' + dir + '/SKILL.md',
|
|
872
1132
|
name,
|
|
873
1133
|
snippet: description.substring(0, 200),
|
|
874
1134
|
relevance,
|
|
@@ -888,9 +1148,7 @@ async function retrieveSkill(name) {
|
|
|
888
1148
|
if (!skillName) return { ok: false, error: 'Skill name is required.' };
|
|
889
1149
|
const skillPath = join(SKILLS_ROOT, skillName, 'SKILL.md');
|
|
890
1150
|
if (!existsSync(skillPath)) {
|
|
891
|
-
const dirs =
|
|
892
|
-
? readdirSync(SKILLS_ROOT, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
893
|
-
: [];
|
|
1151
|
+
const dirs = listSkillDirs(SKILLS_ROOT);
|
|
894
1152
|
return { ok: false, error: 'Skill "' + skillName + '" not found. Available: ' + dirs.join(', ') };
|
|
895
1153
|
}
|
|
896
1154
|
const content = readFileSync(skillPath, 'utf8');
|
|
@@ -903,7 +1161,8 @@ async function retrieveSkill(name) {
|
|
|
903
1161
|
});
|
|
904
1162
|
}
|
|
905
1163
|
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
906
|
-
let version = 1,
|
|
1164
|
+
let version = 1,
|
|
1165
|
+
description = '';
|
|
907
1166
|
if (frontmatter) {
|
|
908
1167
|
const fm = frontmatter[1];
|
|
909
1168
|
const vm = fm.match(/^version:\s*(.+)$/m);
|
|
@@ -915,8 +1174,10 @@ async function retrieveSkill(name) {
|
|
|
915
1174
|
}
|
|
916
1175
|
|
|
917
1176
|
async function listRegions() {
|
|
918
|
-
const result = await runHcloud(['IAM', 'KeystoneListRegions'], { timeoutMs: 30000, maxRetries: 0 })
|
|
919
|
-
|
|
1177
|
+
const result = await runHcloud(['IAM', 'KeystoneListRegions'], { timeoutMs: 30000, maxRetries: 0 }).catch((err) => ({
|
|
1178
|
+
ok: false,
|
|
1179
|
+
error: err.message,
|
|
1180
|
+
}));
|
|
920
1181
|
if (!result.ok) {
|
|
921
1182
|
return {
|
|
922
1183
|
ok: false,
|
|
@@ -936,7 +1197,7 @@ async function listRegions() {
|
|
|
936
1197
|
note: 'hcloud unavailable. Showing static region list. For the complete list, visit the fallback URL.',
|
|
937
1198
|
};
|
|
938
1199
|
}
|
|
939
|
-
let regions
|
|
1200
|
+
let regions;
|
|
940
1201
|
try {
|
|
941
1202
|
const parsed = typeof result.stdout === 'string' ? JSON.parse(result.stdout) : result.stdout;
|
|
942
1203
|
const rawRegions = parsed.regions || [];
|
|
@@ -954,37 +1215,128 @@ async function listRegions() {
|
|
|
954
1215
|
}
|
|
955
1216
|
|
|
956
1217
|
async function getRegionalAvailability(service, region) {
|
|
957
|
-
const svc = String(service || '')
|
|
958
|
-
|
|
1218
|
+
const svc = String(service || '')
|
|
1219
|
+
.toLowerCase()
|
|
1220
|
+
.trim();
|
|
1221
|
+
const reg = String(region || '')
|
|
1222
|
+
.toLowerCase()
|
|
1223
|
+
.trim();
|
|
959
1224
|
if (!svc || !reg) return { ok: false, error: 'Both service and region are required.' };
|
|
960
1225
|
const known = {
|
|
961
|
-
ecs: [
|
|
962
|
-
|
|
963
|
-
|
|
1226
|
+
ecs: [
|
|
1227
|
+
'cn-south-1',
|
|
1228
|
+
'cn-north-4',
|
|
1229
|
+
'cn-north-1',
|
|
1230
|
+
'cn-east-3',
|
|
1231
|
+
'cn-east-2',
|
|
1232
|
+
'ap-southeast-3',
|
|
1233
|
+
'ap-southeast-2',
|
|
1234
|
+
'ap-southeast-1',
|
|
1235
|
+
'ap-southeast-4',
|
|
1236
|
+
'af-south-1',
|
|
1237
|
+
'tr-west-1',
|
|
1238
|
+
'sa-brazil-1',
|
|
1239
|
+
'la-north-2',
|
|
1240
|
+
'na-mexico-1',
|
|
1241
|
+
'me-east-1',
|
|
1242
|
+
],
|
|
1243
|
+
obs: [
|
|
1244
|
+
'cn-south-1',
|
|
1245
|
+
'cn-north-4',
|
|
1246
|
+
'cn-north-1',
|
|
1247
|
+
'cn-east-3',
|
|
1248
|
+
'cn-east-2',
|
|
1249
|
+
'ap-southeast-3',
|
|
1250
|
+
'ap-southeast-2',
|
|
1251
|
+
'ap-southeast-1',
|
|
1252
|
+
'af-south-1',
|
|
1253
|
+
],
|
|
1254
|
+
vpc: [
|
|
1255
|
+
'cn-south-1',
|
|
1256
|
+
'cn-north-4',
|
|
1257
|
+
'cn-north-1',
|
|
1258
|
+
'cn-east-3',
|
|
1259
|
+
'cn-east-2',
|
|
1260
|
+
'ap-southeast-3',
|
|
1261
|
+
'ap-southeast-2',
|
|
1262
|
+
'ap-southeast-1',
|
|
1263
|
+
'ap-southeast-4',
|
|
1264
|
+
'af-south-1',
|
|
1265
|
+
'tr-west-1',
|
|
1266
|
+
'sa-brazil-1',
|
|
1267
|
+
'la-north-2',
|
|
1268
|
+
'me-east-1',
|
|
1269
|
+
],
|
|
964
1270
|
iam: ['global'],
|
|
965
|
-
rds: [
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1271
|
+
rds: [
|
|
1272
|
+
'cn-south-1',
|
|
1273
|
+
'cn-north-4',
|
|
1274
|
+
'cn-north-1',
|
|
1275
|
+
'cn-east-3',
|
|
1276
|
+
'cn-east-2',
|
|
1277
|
+
'ap-southeast-3',
|
|
1278
|
+
'ap-southeast-2',
|
|
1279
|
+
'ap-southeast-1',
|
|
1280
|
+
],
|
|
1281
|
+
gaussdb: ['cn-south-1', 'cn-north-4', 'cn-east-3'],
|
|
1282
|
+
cce: [
|
|
1283
|
+
'cn-south-1',
|
|
1284
|
+
'cn-north-4',
|
|
1285
|
+
'cn-north-1',
|
|
1286
|
+
'cn-east-3',
|
|
1287
|
+
'cn-east-2',
|
|
1288
|
+
'ap-southeast-3',
|
|
1289
|
+
'ap-southeast-2',
|
|
1290
|
+
'ap-southeast-1',
|
|
1291
|
+
],
|
|
1292
|
+
modelarts: ['cn-south-1', 'cn-north-4', 'cn-east-3'],
|
|
1293
|
+
functiongraph: [
|
|
1294
|
+
'cn-south-1',
|
|
1295
|
+
'cn-north-4',
|
|
1296
|
+
'cn-north-1',
|
|
1297
|
+
'cn-east-3',
|
|
1298
|
+
'cn-east-2',
|
|
1299
|
+
'ap-southeast-3',
|
|
1300
|
+
'ap-southeast-2',
|
|
1301
|
+
'ap-southeast-1',
|
|
1302
|
+
],
|
|
1303
|
+
dew: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1304
|
+
smn: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1305
|
+
ces: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1306
|
+
cts: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1307
|
+
apig: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1308
|
+
cbr: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1309
|
+
dds: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
1310
|
+
dcs: ['cn-south-1', 'cn-north-4', 'cn-east-3', 'ap-southeast-3'],
|
|
978
1311
|
};
|
|
979
|
-
if (!known[svc])
|
|
1312
|
+
if (!known[svc])
|
|
1313
|
+
return {
|
|
1314
|
+
ok: false,
|
|
1315
|
+
service: svc,
|
|
1316
|
+
region: reg,
|
|
1317
|
+
available: false,
|
|
1318
|
+
note:
|
|
1319
|
+
'Service ' +
|
|
1320
|
+
svc +
|
|
1321
|
+
' is not in the regional availability cache. Run hcloud ' +
|
|
1322
|
+
svc.toUpperCase() +
|
|
1323
|
+
' --help to verify, or check https://developer.huaweicloud.com/endpoint.',
|
|
1324
|
+
};
|
|
980
1325
|
const available = known[svc].includes(reg) || known[svc].includes('global');
|
|
981
1326
|
return {
|
|
982
|
-
ok: true,
|
|
1327
|
+
ok: true,
|
|
1328
|
+
service: svc,
|
|
1329
|
+
region: reg,
|
|
1330
|
+
available,
|
|
983
1331
|
note: available
|
|
984
1332
|
? svc + ' is available in ' + reg + '.'
|
|
985
|
-
: svc +
|
|
1333
|
+
: svc +
|
|
1334
|
+
' availability in ' +
|
|
1335
|
+
reg +
|
|
1336
|
+
' could not be confirmed. Verify at https://developer.huaweicloud.com/endpoint.',
|
|
986
1337
|
sourcedFrom: 'static cache, update via npm package upgrade',
|
|
987
|
-
disclaimer:
|
|
1338
|
+
disclaimer:
|
|
1339
|
+
'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
1340
|
};
|
|
989
1341
|
}
|
|
990
1342
|
|