huaweicloud-devkit 1.1.2-next.0 → 1.1.2-next.2

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 (125) hide show
  1. package/.agents/plugins/marketplace.json +18 -18
  2. package/README.md +182 -38
  3. package/README.zh-CN.md +180 -36
  4. package/bin/dsh-postinstall.cjs +77 -0
  5. package/bin/setup.cjs +7 -4
  6. package/cordis.patch.yml +13 -0
  7. package/integrations/atomcode/hooks/huaweicloud-telemetry.js +181 -0
  8. package/integrations/atomcode/hooks.json +9 -0
  9. package/integrations/dsh/hook-plugin.mjs +148 -0
  10. package/integrations/hermes/hooks/huaweicloud-telemetry.py +238 -0
  11. package/integrations/hermes/manifest.yaml +43 -0
  12. package/integrations/opencode/hooks/skill-tracker.js +115 -0
  13. package/integrations/opencode/opencode.json +1 -4
  14. package/integrations/workbuddy/hooks/telemetry-tracker.py +226 -0
  15. package/package.json +74 -43
  16. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -17
  17. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +23 -33
  18. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -17
  19. package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
  20. package/plugins/huaweicloud-core/.mcp.json +2 -5
  21. package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -18
  22. package/plugins/huaweicloud-core/assets/icon.png +0 -0
  23. package/plugins/huaweicloud-core/assets/logo-dark.png +0 -0
  24. package/plugins/huaweicloud-core/assets/logo.png +0 -0
  25. package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +104 -28
  26. package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
  27. package/plugins/huaweicloud-core/safety/policy.json +75 -99
  28. package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +196 -35
  29. package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +38 -34
  30. package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +21 -20
  31. package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +19 -19
  32. package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +38 -32
  33. package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +30 -30
  34. package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +20 -20
  35. package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +31 -31
  36. package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +21 -20
  37. package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +40 -31
  38. package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +8 -1
  39. package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +6 -0
  40. package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +45 -37
  41. package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +57 -6
  42. package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +11 -9
  43. package/plugins/huaweicloud-core/skills/huawei-ecs/references/hc-activity.md +27 -0
  44. package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +13 -13
  45. package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +44 -39
  46. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +12 -10
  47. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +21 -20
  48. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +19 -19
  49. package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +20 -20
  50. package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +27 -23
  51. package/plugins/huaweicloud-core/skills/huawei-iac/SKILL.md +93 -0
  52. package/plugins/huaweicloud-core/skills/huawei-iac/references/architectures.md +83 -0
  53. package/plugins/huaweicloud-core/skills/huawei-iac/references/resource-catalog.md +107 -0
  54. package/plugins/huaweicloud-core/skills/huawei-iac/references/state-file.md +104 -0
  55. package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +30 -30
  56. package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +65 -39
  57. package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +9 -9
  58. package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +33 -25
  59. package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +46 -45
  60. package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +19 -19
  61. package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +12 -7
  62. package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +13 -12
  63. package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +4 -4
  64. package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +71 -61
  65. package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +776 -44
  66. package/plugins/huaweicloud-core/skills/huawei-sandbox/references/framework-commands.md +84 -0
  67. package/plugins/huaweicloud-core/skills/huawei-sandbox/references/nginx-templates.md +100 -0
  68. package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +36 -29
  69. package/plugins/huaweicloud-core/skills/huawei-voucher/SKILL.md +57 -0
  70. package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +39 -39
  71. package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +22 -15
  72. package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +20 -19
  73. package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +15 -15
  74. package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +23 -20
  75. package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +13 -13
  76. package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +3 -3
  77. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +21 -2
  78. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +3 -3
  79. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +30 -4
  80. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +3 -3
  81. package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +68 -54
  82. package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +3 -3
  83. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +6 -6
  84. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +15 -1
  85. package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +0 -1
  86. package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +3 -3
  87. package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +36 -34
  88. package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +3 -3
  89. package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +142 -5
  90. package/plugins/huaweicloud-core/src/auth/credentials.mjs +217 -5
  91. package/plugins/huaweicloud-core/src/auth/reconcile.mjs +174 -0
  92. package/plugins/huaweicloud-core/src/auth/service.mjs +57 -2
  93. package/plugins/huaweicloud-core/src/data/icons-manifest.v1.json +3517 -0
  94. package/plugins/huaweicloud-core/src/detect-framework.mjs +366 -0
  95. package/plugins/huaweicloud-core/src/hcloud-cli.mjs +203 -16
  96. package/plugins/huaweicloud-core/src/icon-library.mjs +176 -0
  97. package/plugins/huaweicloud-core/src/mcp-protocol.mjs +88 -0
  98. package/plugins/huaweicloud-core/src/mcp-server-remote.mjs +107 -0
  99. package/plugins/huaweicloud-core/src/mcp-server.mjs +154 -99
  100. package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +20 -102
  101. package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +11 -4
  102. package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +4 -1
  103. package/plugins/huaweicloud-core/src/safety-policy.mjs +123 -68
  104. package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +48 -12
  105. package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +25 -26
  106. package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
  107. package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +943 -10
  108. package/plugins/huaweicloud-core/src/sandbox/uninstall-cleanup.mjs +50 -0
  109. package/plugins/huaweicloud-core/src/search-market.mjs +49 -14
  110. package/plugins/huaweicloud-core/src/setup-cli.mjs +3091 -498
  111. package/plugins/huaweicloud-core/src/telemetry/agent-detect.mjs +40 -0
  112. package/plugins/huaweicloud-core/src/telemetry/agent-registry.mjs +328 -0
  113. package/plugins/huaweicloud-core/src/telemetry/telemetry.mjs +378 -0
  114. package/plugins/huaweicloud-core/src/tools.mjs +1272 -146
  115. package/plugins/huaweicloud-core/src/update-check.mjs +383 -0
  116. package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +72 -38
  117. package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +10 -5
  118. package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +13 -16
  119. package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +12 -16
  120. package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +46 -31
  121. package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +268 -0
  122. package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
  123. package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +40 -13
  124. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +0 -190
  125. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +0 -124
@@ -0,0 +1,77 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const os = require('node:os');
6
+
7
+ // ── Detect DSH profile context ──
8
+ const cwd = process.cwd();
9
+ const dshHome = process.env.DSH_HOME || path.join(os.homedir(), '.dsh');
10
+ const profilesRoot = path.join(dshHome, 'profiles');
11
+
12
+ if (!cwd.startsWith(profilesRoot)) {
13
+ // Not inside a DSH profile — traditional install, just print message
14
+ console.log('\nHuaweiCloud DevKit installed. Run: npx huaweicloud-devkit install\n');
15
+ process.exit(0);
16
+ }
17
+
18
+ // ── Inside a DSH profile: copy skills to ~/.dsh/skills/ ──
19
+ const packageRoot = path.resolve(__dirname, '..');
20
+
21
+ // Rewrite the bundled patch's relative MCP server path to the absolute package
22
+ // path. Relative paths resolve against the DSH process cwd and break when DSH
23
+ // is started outside the profile directory. Idempotent: absolute paths are kept.
24
+ const bundledPatch = path.join(packageRoot, 'cordis.patch.yml');
25
+ if (fs.existsSync(bundledPatch)) {
26
+ const relativeArg = './node_modules/huaweicloud-devkit/plugins/huaweicloud-core/src/mcp-server.mjs';
27
+ const absoluteArg = path
28
+ .join(packageRoot, 'plugins', 'huaweicloud-core', 'src', 'mcp-server.mjs')
29
+ .replace(/\\/g, '/')
30
+ .replace(/'/g, "''");
31
+ const patch = fs.readFileSync(bundledPatch, 'utf8');
32
+ const updated = patch.split(`'${relativeArg}'`).join(`'${absoluteArg}'`);
33
+ if (updated !== patch) {
34
+ fs.writeFileSync(bundledPatch, updated);
35
+ console.log('HuaweiCloud DevKit: rewrote DSH patch MCP path to absolute package path');
36
+ }
37
+ }
38
+
39
+ const skillsSrc = path.join(packageRoot, 'plugins', 'huaweicloud-core', 'skills');
40
+ const skillsDest = path.join(dshHome, 'skills');
41
+
42
+ if (!fs.existsSync(skillsSrc)) {
43
+ console.log('HuaweiCloud DevKit: skills source not found, skipping auto-setup');
44
+ process.exit(0);
45
+ }
46
+
47
+ fs.mkdirSync(skillsDest, { recursive: true });
48
+
49
+ function copyDir(src, dest) {
50
+ if (!fs.existsSync(src)) return;
51
+ fs.mkdirSync(dest, { recursive: true });
52
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
53
+ const s = path.join(src, entry.name);
54
+ const d = path.join(dest, entry.name);
55
+ if (entry.isDirectory()) {
56
+ copyDir(s, d);
57
+ } else {
58
+ fs.copyFileSync(s, d);
59
+ }
60
+ }
61
+ }
62
+
63
+ let count = 0;
64
+ for (const entry of fs.readdirSync(skillsSrc, { withFileTypes: true })) {
65
+ if (entry.isDirectory()) {
66
+ copyDir(path.join(skillsSrc, entry.name), path.join(skillsDest, entry.name));
67
+ count++;
68
+ }
69
+ }
70
+
71
+ console.log(`\nHuaweiCloud DevKit: ${count} skills installed to ~/.dsh/skills/`);
72
+ console.log('MCP server will be available after DSH restart.');
73
+ console.log('\n\u001b[1m\u001b[36m 首次使用请配置环境:\u001b[0m');
74
+ console.log(' 1. 安装 KooCLI:npx huaweicloud-devkit install-hcloud');
75
+ console.log(' 2. 配置凭证: npx huaweicloud-devkit auth init');
76
+ console.log(' 3. 重启 DSH 会话后即可使用');
77
+ console.log(' 或者直接在 DSH 中对 Agent 说:帮我安装华为云 KooCLI 并配置凭证\n');
package/bin/setup.cjs CHANGED
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
- const path = require('path');
4
- const fileUrl = require('url').pathToFileURL(path.join(__dirname, '..', 'plugins', 'huaweicloud-core', 'src', 'setup-cli.mjs')).href;
5
- import(fileUrl).catch(e => {
6
- console.error(e.message);
3
+ const path = require('node:path');
4
+ const fileUrl = require('node:url').pathToFileURL(
5
+ path.join(__dirname, '..', 'plugins', 'huaweicloud-core', 'src', 'setup-cli.mjs'),
6
+ ).href;
7
+
8
+ import(fileUrl).catch((error) => {
9
+ console.error(error.message);
7
10
  process.exit(1);
8
11
  });
@@ -0,0 +1,13 @@
1
+ - insert:
2
+ - id: huaweicloud-devkit
3
+ name: '@deepseek-ai/dsh-mcp-client'
4
+ config:
5
+ serverName: huaweicloud
6
+ transport: stdio
7
+ command: node
8
+ args:
9
+ - './node_modules/huaweicloud-devkit/plugins/huaweicloud-core/src/mcp-server.mjs'
10
+ env:
11
+ HUAWEICLOUD_AGENT_TOOLKIT_MODE: local
12
+ HDKITSERVICE_ENDPOINT: ''
13
+ failOnStartupError: false
@@ -0,0 +1,181 @@
1
+ /**
2
+ * huaweicloud-telemetry.js — AtomCode PreToolUse Hook
3
+ *
4
+ * Captures Skill loading, hcloud CLI commands, and MCP tool invocations,
5
+ * writing structured events to hook-events.jsonl for the devkit telemetry
6
+ * module to ingest and report.
7
+ *
8
+ * Classification logic mirrors the existing four platform hooks exactly.
9
+ * This hook NEVER blocks execution — it always exits 0.
10
+ */
11
+
12
+ import { appendFileSync, mkdirSync, existsSync, readFileSync } from 'node:fs';
13
+ import { join, dirname } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+
16
+ // ══════════════════════════════════════════════════════════════════
17
+ // Debug
18
+ // ══════════════════════════════════════════════════════════════════
19
+
20
+ const DEBUG = process.env.HUAWEICLOUD_DEVKIT_DEBUG === 'true';
21
+
22
+ function debugLog(msg) {
23
+ if (!DEBUG) return;
24
+ try {
25
+ const logPath = join(TELEMETRY_DIR, 'atomcode-debug.log');
26
+ appendFileSync(logPath, `${new Date().toISOString()} ${msg}\n`);
27
+ } catch (_) {
28
+ /* best-effort */
29
+ }
30
+ }
31
+
32
+ // ══════════════════════════════════════════════════════════════════
33
+ // Paths — hook is installed at <plugin_dir>/hooks/
34
+ // ══════════════════════════════════════════════════════════════════
35
+
36
+ const __filename = fileURLToPath(import.meta.url);
37
+ const HOOK_DIR = dirname(__filename);
38
+ const PLUGIN_DIR = dirname(HOOK_DIR);
39
+ const TELEMETRY_DIR = join(PLUGIN_DIR, 'telemetry');
40
+ const HOOK_EVENTS_PATH = join(TELEMETRY_DIR, 'hook-events.jsonl');
41
+
42
+ // ══════════════════════════════════════════════════════════════════
43
+ // Helpers
44
+ // ══════════════════════════════════════════════════════════════════
45
+
46
+ function ensureDir(dirPath) {
47
+ if (!existsSync(dirPath)) mkdirSync(dirPath, { recursive: true });
48
+ }
49
+
50
+ function writeEvent(key, value, extra = {}) {
51
+ try {
52
+ ensureDir(TELEMETRY_DIR);
53
+ const line = JSON.stringify({ key, value, ...extra }) + '\n';
54
+ appendFileSync(HOOK_EVENTS_PATH, line, 'utf8');
55
+ debugLog(`writeEvent key=${key} value=${value}`);
56
+ } catch (error) {
57
+ debugLog(`writeEvent FAILED: ${error.message}`);
58
+ }
59
+ }
60
+
61
+ // ══════════════════════════════════════════════════════════════════
62
+ // Skill identification
63
+ // ══════════════════════════════════════════════════════════════════
64
+
65
+ function isHuaweiCloudSkill(name) {
66
+ return typeof name === 'string' && /^huawei/i.test(name);
67
+ }
68
+
69
+ // ══════════════════════════════════════════════════════════════════
70
+ // CLI command classification (matches skill-tracker.js exactly)
71
+ // ══════════════════════════════════════════════════════════════════
72
+
73
+ const HCLOUD_RE = /(?:^|[;&|]\s*)hcloud(?:\.exe)?\s+(.+)/i;
74
+ const READ_VERBS = /\b(List|Show|Get|Describe|NovaList|NovaShow)\w*/i;
75
+ const WRITE_VERBS =
76
+ /\b(Create|Delete|Update|Modify|Remove|Revoke|Grant|Attach|Detach|Enable|Disable|Set|Add|Bind|Unbind|Reset|Change|Activate|Deactivate|Register|Unregister|Import|Export|Download|Upload|Copy|Move|Convert|Migrate|Run|Execute|Invoke|Trigger|Deploy|Push|Start|Stop|Restart|Reboot|Suspend|Resume|Terminate|Release|Allocate)\w*/i;
77
+
78
+ function classifyHcloud(text) {
79
+ const m = HCLOUD_RE.exec(text);
80
+ if (!m) return null;
81
+ const rest = m[1].trim();
82
+ const parts = rest.split(/\s+/).filter((p) => !p.startsWith('--'));
83
+ const cmd = parts.join(' ');
84
+ if (!cmd) return null;
85
+ if (READ_VERBS.test(cmd)) return { key: 'cli:read', value: `hcloud ${cmd}` };
86
+ if (WRITE_VERBS.test(cmd)) return { key: 'cli:write', value: `hcloud ${cmd}` };
87
+ return { key: 'cli:invoke', value: `hcloud ${cmd}` };
88
+ }
89
+
90
+ // ══════════════════════════════════════════════════════════════════
91
+ // Input parsing — stdin JSON, with env-var fallback
92
+ // ══════════════════════════════════════════════════════════════════
93
+
94
+ function parseInput() {
95
+ // Primary: AtomCode passes data via env vars
96
+ const toolName = process.env.ATOMCODE_TOOL_NAME || '';
97
+ const context = process.env.ATOMCODE_HOOK_CONTEXT || '';
98
+ if (toolName || context) {
99
+ const input = { tool_name: toolName };
100
+ if (context) {
101
+ try {
102
+ input.tool_input = JSON.parse(context);
103
+ } catch (_) {
104
+ input.tool_input = { command: context };
105
+ }
106
+ } else {
107
+ input.tool_input = {};
108
+ }
109
+ return input;
110
+ }
111
+
112
+ // Fallback: stdin JSON (piped for manual testing)
113
+ try {
114
+ const data = readFileSync(0, 'utf8').trim();
115
+ if (data) return JSON.parse(data);
116
+ } catch (_) {
117
+ /* stdin unavailable */
118
+ }
119
+
120
+ return { tool_name: '', tool_input: {} };
121
+ }
122
+
123
+ function extractCommand(toolInput) {
124
+ if (typeof toolInput === 'string') return toolInput;
125
+ if (toolInput && typeof toolInput === 'object') {
126
+ return toolInput.command || toolInput.arguments?.command || '';
127
+ }
128
+ return '';
129
+ }
130
+
131
+ function extractSkillName(toolInput) {
132
+ if (typeof toolInput === 'string') return toolInput;
133
+ if (toolInput && typeof toolInput === 'object') {
134
+ return toolInput.name || toolInput.command || '';
135
+ }
136
+ return '';
137
+ }
138
+
139
+ // ══════════════════════════════════════════════════════════════════
140
+ // Main
141
+ // ══════════════════════════════════════════════════════════════════
142
+
143
+ try {
144
+ const input = parseInput();
145
+ const toolName = input.tool_name || input.tool || '';
146
+ const toolInput = input.tool_input || input.arguments || {};
147
+
148
+ debugLog(`tool=${toolName}`);
149
+
150
+ // ── Category 1: Skill tool calls ──
151
+ if (toolName === 'skill' || toolName === 'use_skill') {
152
+ const skillName = extractSkillName(toolInput);
153
+ debugLog(`skill name=${skillName}`);
154
+ if (isHuaweiCloudSkill(skillName)) {
155
+ writeEvent('skill:retrieve', skillName);
156
+ }
157
+ }
158
+
159
+ // ── Category 2: Bash/pwsh with hcloud commands ──
160
+ if (toolName === 'bash' || toolName === 'pwsh') {
161
+ const command = extractCommand(toolInput);
162
+ if (command && HCLOUD_RE.test(command)) {
163
+ debugLog(`hcloud command: ${command.slice(0, 120)}`);
164
+ const classified = classifyHcloud(command);
165
+ if (classified) {
166
+ writeEvent(classified.key, classified.value, { capability: 'cli' });
167
+ }
168
+ }
169
+ }
170
+
171
+ // ── Category 3: MCP Huawei Cloud tools ──
172
+ if (toolName.startsWith('mcp__huaweicloud')) {
173
+ writeEvent(`tool:${toolName}`, '1', { capability: 'mcp' });
174
+ debugLog(`MCP tool: ${toolName}`);
175
+ }
176
+ } catch (error) {
177
+ debugLog(`FATAL: ${error?.message || error}`);
178
+ }
179
+
180
+ // Always allow — telemetry hook never blocks; the process exits 0 naturally
181
+ // since all I/O is synchronous and nothing holds the event loop open.
@@ -0,0 +1,9 @@
1
+ {
2
+ "hooks": {
3
+ "hw-telemetry": {
4
+ "event": "pre_tool_use",
5
+ "command": "<node_path> <plugin_dir>/hooks/huaweicloud-telemetry.js",
6
+ "timeout_ms": 2000
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,148 @@
1
+ // hook-plugin.mjs — DSH Cordis Hook Plugin
2
+ // Intercepts Huawei Cloud skill calls and hcloud CLI commands,
3
+ // writes events to hook-events.jsonl for the telemetry module to consume.
4
+
5
+ import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
6
+ import { join, dirname } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ // ══════════════════════════════════════════════════════════════════
10
+ // Cordis plugin identity (required by DSH loader)
11
+ // ══════════════════════════════════════════════════════════════════
12
+ export const name = 'huaweicloud-devkit-hook';
13
+ export const inject = ['tools'];
14
+
15
+ // ══════════════════════════════════════════════════════════════════
16
+ // Paths — this file lives in $DSH_HOME/huaweicloud-plugins/
17
+ // ══════════════════════════════════════════════════════════════════
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const PLUGIN_DIR = dirname(__filename);
20
+ // telemetry/ dir is the single shared location for event files
21
+ const TELEMETRY_DIR = join(PLUGIN_DIR, 'telemetry');
22
+ const HOOK_EVENTS_PATH = join(TELEMETRY_DIR, 'hook-events.jsonl');
23
+
24
+ const DEBUG = process.env.HUAWEICLOUD_DEVKIT_DEBUG === 'true';
25
+
26
+ // ══════════════════════════════════════════════════════════════════
27
+ // Helpers
28
+ // ══════════════════════════════════════════════════════════════════
29
+
30
+ function ensureDir(dirPath) {
31
+ if (!existsSync(dirPath)) mkdirSync(dirPath, { recursive: true });
32
+ }
33
+
34
+ function debugLog(msg) {
35
+ if (!DEBUG) return;
36
+ try {
37
+ const logPath = join(TELEMETRY_DIR, 'telemetry-debug.log');
38
+ ensureDir(TELEMETRY_DIR);
39
+ appendFileSync(logPath, `${new Date().toISOString()} [hook] ${msg}\n`, 'utf8');
40
+ } catch {
41
+ // debug log is best-effort; never fail the plugin
42
+ }
43
+ }
44
+
45
+ function writeEvent(key, value, extra = {}) {
46
+ try {
47
+ ensureDir(TELEMETRY_DIR);
48
+ const line = JSON.stringify({ key, value, ...extra }) + '\n';
49
+ appendFileSync(HOOK_EVENTS_PATH, line, 'utf8');
50
+ debugLog(`writeEvent key=${key} value=${value}`);
51
+ } catch (error) {
52
+ debugLog(`writeEvent FAILED: ${error.message}`);
53
+ }
54
+ }
55
+
56
+ // ══════════════════════════════════════════════════════════════════
57
+ // Huawei Cloud identification
58
+ // ══════════════════════════════════════════════════════════════════
59
+
60
+ function isHuaweiCloudSkill(name) {
61
+ return typeof name === 'string' && /^huawei/i.test(name);
62
+ }
63
+
64
+ // ══════════════════════════════════════════════════════════════════
65
+ // hcloud command classification (mirrors skill-tracker.js)
66
+ // ══════════════════════════════════════════════════════════════════
67
+
68
+ const HCLOUD_RE = /(?:^|[;&|]\s*)hcloud(?:\.exe)?\s+(.+)/i;
69
+ const READ_VERBS = /\b(List|Show|Get|Describe|NovaList|NovaShow)\w*/i;
70
+ const WRITE_VERBS =
71
+ /\b(Create|Delete|Update|Modify|Remove|Revoke|Grant|Attach|Detach|Enable|Disable|Set|Add|Bind|Unbind|Reset|Change|Activate|Deactivate|Register|Unregister|Import|Export|Download|Upload|Copy|Move|Convert|Migrate|Run|Execute|Invoke|Trigger|Deploy|Push|Start|Stop|Restart|Reboot|Suspend|Resume|Terminate|Release|Allocate)\w*/i;
72
+
73
+ function classifyHcloud(text) {
74
+ const m = HCLOUD_RE.exec(text);
75
+ if (!m) return null;
76
+ const rest = m[1].trim();
77
+ const parts = rest.split(/\s+/).filter((p) => !p.startsWith('--'));
78
+ const cmd = parts.join(' ');
79
+ if (!cmd) return null;
80
+ if (READ_VERBS.test(cmd)) return { key: 'cli:read', value: `hcloud ${cmd}`, capability: 'cli' };
81
+ if (WRITE_VERBS.test(cmd)) return { key: 'cli:write', value: `hcloud ${cmd}`, capability: 'cli' };
82
+ return { key: 'cli:invoke', value: `hcloud ${cmd}`, capability: 'cli' };
83
+ }
84
+
85
+ // ══════════════════════════════════════════════════════════════════
86
+ // apply() — Cordis plugin entry point
87
+ // ══════════════════════════════════════════════════════════════════
88
+
89
+ export function apply(ctx) {
90
+ debugLog('=== PLUGIN LOADED ===');
91
+
92
+ // ── Layer 1: tools/pre-execute waterfall ──────────────────────
93
+ ctx.on('tools/pre-execute', async (exec, next) => {
94
+ try {
95
+ debugLog(`pre-execute tool=${exec.name}`);
96
+
97
+ // ── Intercept 1: skill tool calls ──
98
+ if (exec.name === 'skill') {
99
+ const skillName = exec.arguments?.name || '';
100
+ debugLog(`skill name=${skillName}`);
101
+ if (isHuaweiCloudSkill(skillName)) {
102
+ writeEvent('skill:retrieve', skillName);
103
+ ctx.logger.info(`[hw-hook] skill called: ${skillName}`);
104
+ debugLog(`SKILL TRACKED: ${skillName}`);
105
+ }
106
+ }
107
+
108
+ // ── Intercept 2: pwsh/bash with hcloud commands ──
109
+ if (exec.name === 'pwsh' || exec.name === 'bash') {
110
+ const command = exec.arguments?.command || '';
111
+ if (!command) return next();
112
+
113
+ if (HCLOUD_RE.test(command)) {
114
+ debugLog(`hcloud command: ${command.slice(0, 120)}`);
115
+
116
+ // Classify and write event
117
+ const classified = classifyHcloud(command);
118
+ if (classified) {
119
+ writeEvent(classified.key, classified.value, { capability: 'cli' });
120
+ debugLog(`CLI TRACKED: ${classified.key} ${classified.value}`);
121
+ }
122
+ }
123
+ }
124
+
125
+ // ── Intercept 3: MCP Huawei Cloud tools (record only) ──
126
+ if (exec.name.startsWith('mcp__huaweicloud__')) {
127
+ writeEvent(`tool:${exec.name}`, '1', { capability: 'mcp' });
128
+ debugLog(`MCP tool: ${exec.name}`);
129
+ }
130
+ } catch (error) {
131
+ debugLog(`pre-execute ERROR: ${error?.message || error}`);
132
+ ctx.logger.warn(`[hw-hook] pre-execute error: ${error.message}`);
133
+ }
134
+ return next();
135
+ });
136
+
137
+ // ── Layer 2: tools/result observe ─────────────────────────────
138
+ ctx.on('tools/result', (exec, result) => {
139
+ if (exec.name.startsWith('mcp__huaweicloud__') && result.isError) {
140
+ const msg = result.error?.message || 'unknown error';
141
+ debugLog(`tool error: ${exec.name}: ${msg}`);
142
+ ctx.logger.info(`[hw-hook] tool error: ${exec.name}: ${msg}`);
143
+ }
144
+ });
145
+
146
+ ctx.logger.info('[hw-hook] DSH Huawei Cloud hook plugin activated');
147
+ debugLog('=== PLUGIN INIT DONE ===');
148
+ }