huaweicloud-devkit 1.1.2-next.1 → 1.1.2-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +24 -6
  2. package/README.zh-CN.md +24 -6
  3. package/integrations/hermes/skills-snapshot.json +175 -0
  4. package/package.json +3 -1
  5. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
  6. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
  7. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
  8. package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +1 -1
  9. package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
  10. package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
  11. package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +1 -1
  12. package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +6 -7
  13. package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +7 -6
  14. package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +1 -1
  15. package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +1 -1
  16. package/plugins/huaweicloud-core/skills/huawei-ecs/references/evs.md +3 -1
  17. package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +10 -10
  18. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +2 -2
  19. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/deploy-workflow.md +1 -1
  20. package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +15 -9
  21. package/plugins/huaweicloud-core/skills/huawei-iac/SKILL.md +1 -1
  22. package/plugins/huaweicloud-core/skills/huawei-iac/references/resource-catalog.md +1 -1
  23. package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +3 -3
  24. package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +15 -15
  25. package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +1 -1
  26. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +18 -16
  27. package/plugins/huaweicloud-core/src/hcloud-cli.mjs +11 -0
  28. package/plugins/huaweicloud-core/src/koocli-version.mjs +47 -0
  29. package/plugins/huaweicloud-core/src/mcp-protocol.mjs +88 -0
  30. package/plugins/huaweicloud-core/src/mcp-server-remote.mjs +107 -0
  31. package/plugins/huaweicloud-core/src/mcp-server.mjs +125 -183
  32. package/plugins/huaweicloud-core/src/setup-cli.mjs +42 -15
  33. package/plugins/huaweicloud-core/src/tools.mjs +17 -2
@@ -54,7 +54,7 @@ hcloud EIP AssociatePublicips --publicip_id=<id> \
54
54
  --publicip.associate_instance_id=<port-id> --publicip.associate_instance_type=PORT
55
55
 
56
56
  # Unbind
57
- hcloud EIP DisassociatePublicip --publicip_id=<id>
57
+ hcloud EIP DisassociatePublicips --publicip_id=<id>
58
58
 
59
59
  # List
60
60
  hcloud EIP ListPublicips
@@ -13,56 +13,58 @@ Use KooCLI `hcloud` for local inspection and reviewed operations. Never ask the
13
13
 
14
14
  Official guide: `https://support.huaweicloud.com/qs-hcli/hcli_02_003.html`.
15
15
 
16
+ > **Version pairing**: this plugin is paired with **KooCLI v7.2.12** (`kooCliVersion` in `package.json`). Prefer the pinned download below. The one-liner script `hcloud_install.sh` always installs the `latest` version and cannot be pinned — if you use it, `check_cli`/`doctor` will warn when the installed version does not match v7.2.12.
17
+
16
18
  ### Windows
17
19
 
18
- 1. Download and unzip: `https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/huaweicloud-cli-windows-amd64.zip`
20
+ 1. Download and unzip: `https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.2.12/huaweicloud-cli-windows-amd64.zip`
19
21
  2. Extract to `%USERPROFILE%\hcloud`, add to user `PATH`
20
22
  3. Verify: `hcloud version`
21
23
 
22
24
  ### Linux (amd64 / arm64)
23
25
 
24
- One-liner (recommended):
25
-
26
- ```bash
27
- curl -sSL https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y
28
- ```
29
-
30
- Or manual download:
26
+ Fixed download (recommended):
31
27
 
32
28
  ```bash
33
29
  # amd64
34
- curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/huaweicloud-cli-linux-amd64.tar.gz"
30
+ curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.2.12/huaweicloud-cli-linux-amd64.tar.gz"
35
31
  tar -zxvf huaweicloud-cli-linux-amd64.tar.gz
36
32
  # arm64
37
- curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/huaweicloud-cli-linux-arm64.tar.gz"
33
+ curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.2.12/huaweicloud-cli-linux-arm64.tar.gz"
38
34
  tar -zxvf huaweicloud-cli-linux-arm64.tar.gz
39
35
  ```
40
36
 
41
37
  Move to PATH: `mv $(pwd)/hcloud ~/.local/bin/`
42
38
  Verify: `hcloud version`
43
39
 
44
- ### macOS (amd64 / arm64)
45
-
46
- One-liner (recommended):
40
+ One-liner (installs `latest`, may drift from v7.2.12):
47
41
 
48
42
  ```bash
49
43
  curl -sSL https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y
50
44
  ```
51
45
 
52
- Or manual download:
46
+ ### macOS (amd64 / arm64)
47
+
48
+ Fixed download (recommended):
53
49
 
54
50
  ```bash
55
51
  # amd64
56
- curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/huaweicloud-cli-mac-amd64.tar.gz"
52
+ curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.2.12/huaweicloud-cli-mac-amd64.tar.gz"
57
53
  tar -zxvf huaweicloud-cli-mac-amd64.tar.gz
58
54
  # arm64 (Apple Silicon)
59
- curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/huaweicloud-cli-mac-arm64.tar.gz"
55
+ curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.2.12/huaweicloud-cli-mac-arm64.tar.gz"
60
56
  tar -zxvf huaweicloud-cli-mac-arm64.tar.gz
61
57
  ```
62
58
 
63
59
  Move to PATH: `mv $(pwd)/hcloud /usr/local/bin/`
64
60
  Verify: `hcloud version`
65
61
 
62
+ One-liner (installs `latest`, may drift from v7.2.12):
63
+
64
+ ```bash
65
+ curl -sSL https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y
66
+ ```
67
+
66
68
  Agent processes find executables through `PATH`. If OpenCode/Codex cannot find `hcloud`, restart after updating `PATH`, or set `HCLOUD_BIN`.
67
69
 
68
70
  ## Configure Credentials Outside Chat
@@ -131,11 +131,22 @@ function applyPreflightFindings(classification, sgFindings) {
131
131
  return classification;
132
132
  }
133
133
 
134
+ const OBS_WRITE_SUBCOMMANDS = new Set(['mb', 'cp', 'mv', 'rm', 'chattri', 'restore']);
135
+
136
+ function obsWriteHint(args) {
137
+ if (!Array.isArray(args) || args.length < 2) return null;
138
+ if (String(args[0]).toUpperCase() !== 'OBS') return null;
139
+ if (!OBS_WRITE_SUBCOMMANDS.has(String(args[1]).toLowerCase())) return null;
140
+ return 'OBS write operations are obsutil-style and always write-class. Before executing, present the full resource manifest (bucket/object list) to the user for ONE batch approval, then run each command through plan → approve (see huawei-iac skill, Provisioning Rules).';
141
+ }
142
+
134
143
  export function planHcloudCommand(args, options = {}) {
135
144
  const normalizedArgs = Array.isArray(args) ? args.map(String) : [];
136
145
  const classification = classifyHcloudArgs(normalizedArgs, options);
137
146
  const command = ['hcloud', ...normalizedArgs].map((arg) => quoteShellArg(arg)).join(' ');
138
147
  const warnings = planningWarnings(normalizedArgs);
148
+ const obsHint = obsWriteHint(normalizedArgs);
149
+ if (obsHint) warnings.push(obsHint);
139
150
  const sgFindings = preflightSecurityGroupCheck(normalizedArgs);
140
151
  if (sgFindings.length > 0) {
141
152
  for (const f of sgFindings) warnings.push(f);
@@ -0,0 +1,47 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve, dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const PACKAGE_JSON = resolve(__dirname, '..', '..', '..', 'package.json');
7
+
8
+ export const KOO_CLI_BASE = 'https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli';
9
+
10
+ const VERSION_RE = /\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/;
11
+
12
+ export function getKooCliVersion() {
13
+ try {
14
+ const pkg = JSON.parse(readFileSync(PACKAGE_JSON, 'utf8'));
15
+ return pkg.kooCliVersion || null;
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+
21
+ export function parseHcloudVersion(out) {
22
+ if (!out) return null;
23
+ const match = String(out).match(VERSION_RE);
24
+ return match ? match[0] : null;
25
+ }
26
+
27
+ export function compareVersion(a, b) {
28
+ const pa = String(a || '')
29
+ .split(/[-+]/)[0]
30
+ .split('.')
31
+ .map((n) => Number.parseInt(n, 10) || 0);
32
+ const pb = String(b || '')
33
+ .split(/[-+]/)[0]
34
+ .split('.')
35
+ .map((n) => Number.parseInt(n, 10) || 0);
36
+ for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
37
+ const va = pa[i] || 0;
38
+ const vb = pb[i] || 0;
39
+ if (va > vb) return 1;
40
+ if (va < vb) return -1;
41
+ }
42
+ return 0;
43
+ }
44
+
45
+ export function kooCliDownloadBase() {
46
+ return `${KOO_CLI_BASE}/${getKooCliVersion() || 'latest'}`;
47
+ }
@@ -0,0 +1,88 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve, dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ import { TOOL_DEFINITIONS, callTool } from './tools.mjs';
6
+ import { peekCachedUpdateInfo, applyUpdateHint } from './update-check.mjs';
7
+ import { initTelemetry } from './telemetry/telemetry.mjs';
8
+ import { detectAgent } from './telemetry/agent-detect.mjs';
9
+
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const pluginRoot = resolve(__dirname, '..');
12
+ const packageRoot = resolve(pluginRoot, '..', '..');
13
+ let pkgVersion = '0.0.0';
14
+ for (const base of [pluginRoot, packageRoot]) {
15
+ try {
16
+ const version = JSON.parse(readFileSync(join(base, 'package.json'), 'utf8')).version;
17
+ if (version) {
18
+ pkgVersion = version;
19
+ break;
20
+ }
21
+ } catch {}
22
+ }
23
+
24
+ // 会话内首个非 check/upgrade 工具调用附加 _updateInfo,只消费一次。
25
+ let hintConsumed = false;
26
+ function decorateResult(name, result) {
27
+ if (hintConsumed) return result;
28
+ try {
29
+ const hint = peekCachedUpdateInfo();
30
+ if (!hint) return result;
31
+ const decorated = applyUpdateHint(result, name, hint);
32
+ if (decorated !== result) hintConsumed = true;
33
+ return decorated;
34
+ } catch {
35
+ return result; // 兜底装饰失败绝不影响工具调用
36
+ }
37
+ }
38
+
39
+ export async function dispatch(method, params) {
40
+ if (method === 'initialize') {
41
+ const ci = params.clientInfo || {};
42
+
43
+ try {
44
+ const { hdkitGenerateUserHash } = await import('./sandbox/hdkitservice-api.mjs');
45
+ await Promise.race([
46
+ hdkitGenerateUserHash(),
47
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 3000)),
48
+ ]);
49
+ } catch {}
50
+
51
+ const agent = detectAgent(ci);
52
+ initTelemetry({ harness: agent.harness, version: agent.version });
53
+ return {
54
+ protocolVersion: params.protocolVersion || '2024-11-05',
55
+ capabilities: {
56
+ tools: {},
57
+ },
58
+ serverInfo: {
59
+ name: 'huaweicloud-devkit',
60
+ version: pkgVersion,
61
+ },
62
+ };
63
+ }
64
+
65
+ if (method === 'tools/list') {
66
+ return { tools: TOOL_DEFINITIONS };
67
+ }
68
+
69
+ if (method === 'tools/call') {
70
+ const result = await callTool(params.name, params.arguments || {});
71
+ const decorated = decorateResult(params.name, result);
72
+ return {
73
+ content: [
74
+ {
75
+ type: 'text',
76
+ text: JSON.stringify(decorated, null, 2),
77
+ },
78
+ ],
79
+ isError: false,
80
+ };
81
+ }
82
+
83
+ if (method === 'resources/list') {
84
+ return { resources: [] };
85
+ }
86
+
87
+ throw new Error(`Unsupported method: ${method}`);
88
+ }
@@ -0,0 +1,107 @@
1
+ import { createServer } from 'node:http';
2
+ import { resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { format } from 'node:util';
5
+
6
+ import { dispatch } from './mcp-protocol.mjs';
7
+
8
+ export const DEFAULT_PORT = 9528;
9
+ export const DEFAULT_HOST = '127.0.0.1';
10
+
11
+ export async function startRemoteServer({ port = DEFAULT_PORT, host = DEFAULT_HOST } = {}) {
12
+ const server = createServer(async (req, res) => {
13
+ res.setHeader('Access-Control-Allow-Origin', '*');
14
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
15
+ res.setHeader(
16
+ 'Access-Control-Allow-Headers',
17
+ 'Content-Type, MCP-Protocol-Version, Mcp-Session-Id, Accept, Authorization',
18
+ );
19
+ res.setHeader('Access-Control-Expose-Headers', 'MCP-Protocol-Version, Mcp-Session-Id');
20
+
21
+ if (req.method === 'OPTIONS') {
22
+ res.writeHead(204);
23
+ res.end();
24
+ return;
25
+ }
26
+
27
+ if (req.method !== 'POST') {
28
+ res.writeHead(405, { Allow: 'POST, OPTIONS' });
29
+ res.end();
30
+ return;
31
+ }
32
+
33
+ let message;
34
+ try {
35
+ const chunks = [];
36
+ for await (const chunk of req) chunks.push(chunk);
37
+ message = JSON.parse(Buffer.concat(chunks).toString('utf8'));
38
+ } catch {
39
+ res.writeHead(400, { 'Content-Type': 'application/json' });
40
+ res.end(JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } }));
41
+ return;
42
+ }
43
+
44
+ if (!Object.hasOwn(message, 'id')) {
45
+ // 通知类消息(含 notifications/initialized)无需响应体,HTTP 层直接 202。
46
+ res.writeHead(202);
47
+ res.end();
48
+ return;
49
+ }
50
+
51
+ let response;
52
+ try {
53
+ const result = await dispatch(message.method, message.params || {});
54
+ response = { jsonrpc: '2.0', id: message.id, result };
55
+ if (message.method === 'initialize') {
56
+ res.setHeader('MCP-Protocol-Version', result.protocolVersion || '2024-11-05');
57
+ }
58
+ } catch (error) {
59
+ response = {
60
+ jsonrpc: '2.0',
61
+ id: message.id,
62
+ error: { code: -32603, message: error.message },
63
+ };
64
+ }
65
+
66
+ writeMCPResponse(res, response, req.headers.accept || '');
67
+ });
68
+
69
+ await new Promise((resolvePromise, rejectPromise) => {
70
+ server.once('error', rejectPromise);
71
+ server.listen(port, host, () => resolvePromise());
72
+ });
73
+
74
+ const address = server.address();
75
+ process.stdout.write(format('huaweicloud-devkit MCP server (remote) listening on %s:%s\n', host, address.port));
76
+
77
+ return {
78
+ server,
79
+ port: address.port,
80
+ close: () => new Promise((resolvePromise) => server.close(resolvePromise)),
81
+ };
82
+ }
83
+
84
+ function writeMCPResponse(res, response, accept) {
85
+ const json = JSON.stringify(response);
86
+ if (accept.includes('application/json')) {
87
+ res.writeHead(200, { 'Content-Type': 'application/json' });
88
+ res.end(json);
89
+ return;
90
+ }
91
+ // 客户端只接受 SSE 时的兜底:单帧 event 后关闭流。
92
+ res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' });
93
+ res.write(`event: message\ndata: ${json}\n\n`);
94
+ res.end();
95
+ }
96
+
97
+ if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
98
+ const portIdx = process.argv.indexOf('--port');
99
+ const port = portIdx > -1 ? Number(process.argv[portIdx + 1]) : DEFAULT_PORT;
100
+ const hostIdx = process.argv.indexOf('--host');
101
+ const host = hostIdx > -1 && process.argv[hostIdx + 1] ? process.argv[hostIdx + 1] : DEFAULT_HOST;
102
+ startRemoteServer({ port, host }).catch((error) => {
103
+ process.stderr.write(`Failed to start MCP remote server: ${error.message}\n`);
104
+ // eslint-disable-next-line n/no-process-exit -- fatal startup error in standalone CLI mode
105
+ process.exit(1);
106
+ });
107
+ }