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.
Files changed (99) hide show
  1. package/.agents/plugins/marketplace.json +20 -20
  2. package/LICENSE +201 -201
  3. package/README.md +178 -138
  4. package/README.zh-CN.md +176 -138
  5. package/bin/setup.cjs +10 -8
  6. package/integrations/opencode/commands/huaweicloud-doctor.md +14 -14
  7. package/integrations/opencode/opencode.json +11 -14
  8. package/integrations/opencode/skills/huaweicloud-core/SKILL.md +32 -32
  9. package/package.json +60 -44
  10. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +31 -44
  11. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +30 -43
  12. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +31 -44
  13. package/plugins/huaweicloud-core/.mcp.json +13 -15
  14. package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +31 -44
  15. package/plugins/huaweicloud-core/hooks/hooks.json +26 -26
  16. package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +136 -136
  17. package/plugins/huaweicloud-core/safety/policy.json +76 -100
  18. package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +227 -199
  19. package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +164 -164
  20. package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +48 -48
  21. package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +50 -50
  22. package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +103 -101
  23. package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +90 -90
  24. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +191 -190
  25. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +171 -124
  26. package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +55 -55
  27. package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +70 -70
  28. package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +57 -56
  29. package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +106 -97
  30. package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +36 -29
  31. package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +29 -23
  32. package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +188 -185
  33. package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +144 -133
  34. package/plugins/huaweicloud-core/skills/huawei-ecs/references/evs.md +31 -31
  35. package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +57 -55
  36. package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +52 -52
  37. package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +132 -131
  38. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +60 -58
  39. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +72 -71
  40. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/deploy-workflow.md +119 -119
  41. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +142 -142
  42. package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +56 -56
  43. package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +69 -65
  44. package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +111 -111
  45. package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +148 -122
  46. package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +56 -56
  47. package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +65 -65
  48. package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +134 -127
  49. package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +49 -49
  50. package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +31 -26
  51. package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +101 -100
  52. package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +40 -40
  53. package/plugins/huaweicloud-core/skills/huawei-obs/references/static-website.md +56 -56
  54. package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +229 -224
  55. package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +173 -134
  56. package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +69 -69
  57. package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +84 -84
  58. package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +77 -76
  59. package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +60 -59
  60. package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +50 -50
  61. package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +63 -63
  62. package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +56 -56
  63. package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +4 -4
  64. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +56 -47
  65. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +4 -4
  66. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +138 -128
  67. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +4 -4
  68. package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +111 -96
  69. package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +4 -4
  70. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +20 -20
  71. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +30 -28
  72. package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +59 -60
  73. package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +4 -4
  74. package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +83 -83
  75. package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +4 -4
  76. package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +54 -1
  77. package/plugins/huaweicloud-core/src/auth/credentials.mjs +103 -95
  78. package/plugins/huaweicloud-core/src/auth/service.mjs +63 -63
  79. package/plugins/huaweicloud-core/src/data/icons-manifest.v1.json +3517 -0
  80. package/plugins/huaweicloud-core/src/hcloud-cli.mjs +283 -265
  81. package/plugins/huaweicloud-core/src/icon-library.mjs +171 -0
  82. package/plugins/huaweicloud-core/src/mcp-server.mjs +130 -129
  83. package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +68 -0
  84. package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +94 -0
  85. package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +140 -137
  86. package/plugins/huaweicloud-core/src/safety-policy.mjs +356 -302
  87. package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +86 -87
  88. package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +150 -153
  89. package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +196 -105
  90. package/plugins/huaweicloud-core/src/search-market.mjs +208 -169
  91. package/plugins/huaweicloud-core/src/setup-cli.mjs +2622 -1594
  92. package/plugins/huaweicloud-core/src/tools.mjs +463 -111
  93. package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +447 -427
  94. package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +128 -132
  95. package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +215 -227
  96. package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +189 -202
  97. package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +164 -158
  98. package/plugins/huaweicloud-core/src/ws-exec/index.js +19 -19
  99. package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +338 -338
@@ -0,0 +1,171 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join, dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { getProxyDispatcher } from './proxy/proxy-agent.mjs';
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const SNAPSHOT_PATH = join(__dirname, 'data', 'icons-manifest.v1.json');
8
+ const MANIFEST_URL = 'https://open.huaweicloud.com/openplatform/icons/manifest.v1.json';
9
+ const ICONS_PAGE_URL = 'https://open.huaweicloud.com/openplatform/icons.html';
10
+ const HTTP_TIMEOUT_MS = 10000;
11
+ const MAX_RESULTS = 5;
12
+
13
+ let cachedManifest = null;
14
+
15
+ export function clearIconCache() {
16
+ cachedManifest = null;
17
+ }
18
+
19
+ async function fetchLiveManifest() {
20
+ const controller = new AbortController();
21
+ const timer = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
22
+ try {
23
+ const dispatcher = await getProxyDispatcher(MANIFEST_URL);
24
+ const fetchOpts = {
25
+ headers: { 'User-Agent': 'huaweicloud-devkit/1.0' },
26
+ signal: controller.signal,
27
+ };
28
+ if (dispatcher) fetchOpts.dispatcher = dispatcher;
29
+ const resp = await fetch(MANIFEST_URL, fetchOpts);
30
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
31
+ const data = await resp.json();
32
+ if (!data || !Array.isArray(data.icons)) {
33
+ throw new Error('Manifest is missing the icons array.');
34
+ }
35
+ return data;
36
+ } finally {
37
+ clearTimeout(timer);
38
+ }
39
+ }
40
+
41
+ function loadSnapshot() {
42
+ const manifest = JSON.parse(readFileSync(SNAPSHOT_PATH, 'utf8'));
43
+ if (!manifest || !Array.isArray(manifest.icons)) {
44
+ throw new Error('Bundled icons manifest is corrupted.');
45
+ }
46
+ return manifest;
47
+ }
48
+
49
+ async function loadManifest() {
50
+ if (cachedManifest) return cachedManifest;
51
+ if (process.env.HUAWEICLOUD_ICONS_OFFLINE === '1') {
52
+ cachedManifest = { manifest: loadSnapshot(), source: 'snapshot' };
53
+ return cachedManifest;
54
+ }
55
+ try {
56
+ const live = await fetchLiveManifest();
57
+ cachedManifest = { manifest: live, source: 'live' };
58
+ } catch {
59
+ cachedManifest = { manifest: loadSnapshot(), source: 'snapshot' };
60
+ }
61
+ return cachedManifest;
62
+ }
63
+
64
+ function normalizeToken(token) {
65
+ return token.toLowerCase();
66
+ }
67
+
68
+ function scoreIcon(icon, tokens, index) {
69
+ let total = 0;
70
+ const matched = [];
71
+ for (const token of tokens) {
72
+ let best = 0;
73
+ const id = (icon.id || '').toLowerCase();
74
+ const name = (icon.name || '').toLowerCase();
75
+ if (id === token || (index.aliases[token] || []).includes(id)) {
76
+ best = 10;
77
+ } else if (name === token) {
78
+ best = 9;
79
+ } else if (name.startsWith(token)) {
80
+ best = 8;
81
+ } else if (name.includes(token)) {
82
+ best = 7;
83
+ } else if (id.includes(token)) {
84
+ best = 5;
85
+ } else if ((index.aliases[id] || []).some((a) => a.includes(token))) {
86
+ best = 6;
87
+ } else if ((index.tags[id] || []).some((t) => t.includes(token))) {
88
+ best = 4;
89
+ } else if ((index.categories[id] || '').includes(token)) {
90
+ best = 3;
91
+ } else if ((index.descriptions[id] || '').includes(token)) {
92
+ best = 2;
93
+ }
94
+ if (best > 0) {
95
+ total += best;
96
+ matched.push(token);
97
+ }
98
+ }
99
+ return [total, matched];
100
+ }
101
+
102
+ export async function getServiceIcon(service = '', category = '') {
103
+ const query = String(service || '').trim();
104
+ const catFilter = String(category || '')
105
+ .trim()
106
+ .toLowerCase();
107
+ if (!query && !catFilter) {
108
+ return {
109
+ ok: false,
110
+ error:
111
+ 'service or category is required. Examples: ecs, obs, modelarts, 对象存储, 虚拟私有云, or a category such as 计算 / 存储 / 人工智能.',
112
+ iconsPageUrl: ICONS_PAGE_URL,
113
+ };
114
+ }
115
+ const { manifest, source } = await loadManifest();
116
+ const tokens = query.replace(/[,;]/g, ' ').split(/\s+/).filter(Boolean).map(normalizeToken);
117
+
118
+ const index = { aliases: {}, tags: {}, categories: {}, descriptions: {} };
119
+ for (const icon of manifest.icons) {
120
+ const id = (icon.id || '').toLowerCase();
121
+ index.aliases[id] = (icon.aliases || []).map((a) => String(a).toLowerCase());
122
+ index.tags[id] = (icon.tags || []).map((t) => String(t).toLowerCase());
123
+ index.categories[id] = [
124
+ String(icon.category || '').toLowerCase(),
125
+ String(icon.subcategory || '').toLowerCase(),
126
+ ].join(' ');
127
+ index.descriptions[id] = String(icon.description || '').toLowerCase();
128
+ }
129
+
130
+ const results = [];
131
+ for (const icon of manifest.icons) {
132
+ const categoryMatches = !catFilter || String(icon.category || '').toLowerCase() === catFilter;
133
+ if (!categoryMatches) continue;
134
+ const [score, matched] = tokens.length ? scoreIcon(icon, tokens, index) : [0, []];
135
+ if (tokens.length && score === 0) continue;
136
+ results.push({
137
+ id: icon.id,
138
+ name: icon.name,
139
+ category: icon.category,
140
+ subcategory: icon.subcategory || undefined,
141
+ description: icon.description || undefined,
142
+ aliases: icon.aliases,
143
+ product_url: icon.product_url,
144
+ logo: {
145
+ source_url: icon.logo?.source_url,
146
+ local_path: icon.logo?.local_path,
147
+ },
148
+ architecture: icon.architecture
149
+ ? { status: icon.architecture.status, local_path: icon.architecture.local_path }
150
+ : undefined,
151
+ score,
152
+ matched,
153
+ });
154
+ }
155
+ results.sort((a, b) => b.score - a.score);
156
+
157
+ return {
158
+ ok: true,
159
+ query,
160
+ category: catFilter || undefined,
161
+ source,
162
+ manifestGeneratedAt: manifest.generated_at,
163
+ count: results.length,
164
+ iconsPageUrl: ICONS_PAGE_URL,
165
+ note:
166
+ source === 'live'
167
+ ? 'Live manifest from open.huaweicloud.com.'
168
+ : 'Live manifest unreachable; using bundled snapshot. Upgrade the package to refresh the bundled snapshot.',
169
+ results: results.slice(0, MAX_RESULTS),
170
+ };
171
+ }
@@ -1,129 +1,130 @@
1
- #!/usr/bin/env node
2
- import { stdin, stdout } from 'node:process';
3
- import { rmSync, existsSync } from 'node:fs';
4
- import { resolve, dirname } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
- import { TOOL_DEFINITIONS, callTool } from './tools.mjs';
7
-
8
- // The MCP server is now loaded by a live agent session. Clear the install marker
9
- // in this plugin dir so `doctor` no longer reports "restart needed".
10
- try {
11
- const pluginDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
12
- const marker = resolve(pluginDir, '.installed');
13
- if (existsSync(marker)) rmSync(marker, { force: true });
14
- } catch {}
15
-
16
- let buffer = Buffer.alloc(0);
17
- let useContentLengthFraming = true;
18
-
19
- stdin.on('data', (chunk) => {
20
- buffer = Buffer.concat([buffer, chunk]);
21
- readFrames();
22
- });
23
-
24
- function readFrames() {
25
- while (true) {
26
- const headerEnd = buffer.indexOf('\r\n\r\n');
27
- if (headerEnd !== -1) {
28
- useContentLengthFraming = true;
29
- const consumed = parseContentLengthFrame(headerEnd);
30
- if (!consumed) return;
31
- continue;
32
- }
33
-
34
- const lf = buffer.indexOf('\n');
35
- if (lf !== -1) {
36
- useContentLengthFraming = false;
37
- const line = buffer.subarray(0, lf).toString('utf8').trim();
38
- buffer = buffer.subarray(lf + 1);
39
- if (line) void handleMessage(JSON.parse(line));
40
- continue;
41
- }
42
-
43
- return;
44
- }
45
- }
46
-
47
- function parseContentLengthFrame(headerEnd) {
48
- const header = buffer.subarray(0, headerEnd).toString('utf8');
49
- const match = header.match(/Content-Length:\s*(\d+)/i);
50
- if (!match) {
51
- buffer = Buffer.alloc(0);
52
- return true;
53
- }
54
- const length = Number(match[1]);
55
- const bodyStart = headerEnd + 4;
56
- const bodyEnd = bodyStart + length;
57
- if (buffer.length < bodyEnd) return false;
58
- const body = buffer.subarray(bodyStart, bodyEnd).toString('utf8');
59
- buffer = buffer.subarray(bodyEnd);
60
- void handleMessage(JSON.parse(body));
61
- return true;
62
- }
63
-
64
- async function handleMessage(message) {
65
- if (!Object.hasOwn(message, 'id')) {
66
- if (message.method === 'notifications/initialized') return;
67
- return;
68
- }
69
- try {
70
- const result = await dispatch(message.method, message.params || {});
71
- writeMessage({ jsonrpc: '2.0', id: message.id, result });
72
- } catch (error) {
73
- writeMessage({
74
- jsonrpc: '2.0',
75
- id: message.id,
76
- error: {
77
- code: -32603,
78
- message: error.message,
79
- },
80
- });
81
- }
82
- }
83
-
84
- async function dispatch(method, params) {
85
- if (method === 'initialize') {
86
- return {
87
- protocolVersion: params.protocolVersion || '2024-11-05',
88
- capabilities: {
89
- tools: {},
90
- },
91
- serverInfo: {
92
- name: 'huaweicloud-devkit',
93
- version: '0.1.0',
94
- },
95
- };
96
- }
97
-
98
- if (method === 'tools/list') {
99
- return { tools: TOOL_DEFINITIONS };
100
- }
101
-
102
- if (method === 'tools/call') {
103
- const result = await callTool(params.name, params.arguments || {});
104
- return {
105
- content: [
106
- {
107
- type: 'text',
108
- text: JSON.stringify(result, null, 2),
109
- },
110
- ],
111
- isError: false,
112
- };
113
- }
114
-
115
- if (method === 'resources/list') {
116
- return { resources: [] };
117
- }
118
-
119
- throw new Error(`Unsupported method: ${method}`);
120
- }
121
-
122
- function writeMessage(message) {
123
- const json = JSON.stringify(message);
124
- if (useContentLengthFraming) {
125
- stdout.write(`Content-Length: ${Buffer.byteLength(json, 'utf8')}\r\n\r\n${json}`);
126
- } else {
127
- stdout.write(json + '\n');
128
- }
129
- }
1
+ #!/usr/bin/env node
2
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
3
+ import { stdin, stdout } from 'node:process';
4
+ import { rmSync, existsSync } from 'node:fs';
5
+ import { resolve, dirname } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { TOOL_DEFINITIONS, callTool } from './tools.mjs';
8
+
9
+ // The MCP server is now loaded by a live agent session. Clear the install marker
10
+ // in this plugin dir so `doctor` no longer reports "restart needed".
11
+ try {
12
+ const pluginDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
13
+ const marker = resolve(pluginDir, '.installed');
14
+ if (existsSync(marker)) rmSync(marker, { force: true });
15
+ } catch {}
16
+
17
+ let buffer = Buffer.alloc(0);
18
+ let useContentLengthFraming = true;
19
+
20
+ stdin.on('data', (chunk) => {
21
+ buffer = Buffer.concat([buffer, chunk]);
22
+ readFrames();
23
+ });
24
+
25
+ function readFrames() {
26
+ while (true) {
27
+ const headerEnd = buffer.indexOf('\r\n\r\n');
28
+ if (headerEnd !== -1) {
29
+ useContentLengthFraming = true;
30
+ const consumed = parseContentLengthFrame(headerEnd);
31
+ if (!consumed) return;
32
+ continue;
33
+ }
34
+
35
+ const lf = buffer.indexOf('\n');
36
+ if (lf !== -1) {
37
+ useContentLengthFraming = false;
38
+ const line = buffer.subarray(0, lf).toString('utf8').trim();
39
+ buffer = buffer.subarray(lf + 1);
40
+ if (line) void handleMessage(JSON.parse(line));
41
+ continue;
42
+ }
43
+
44
+ return;
45
+ }
46
+ }
47
+
48
+ function parseContentLengthFrame(headerEnd) {
49
+ const header = buffer.subarray(0, headerEnd).toString('utf8');
50
+ const match = header.match(/Content-Length:\s*(\d+)/i);
51
+ if (!match) {
52
+ buffer = Buffer.alloc(0);
53
+ return true;
54
+ }
55
+ const length = Number(match[1]);
56
+ const bodyStart = headerEnd + 4;
57
+ const bodyEnd = bodyStart + length;
58
+ if (buffer.length < bodyEnd) return false;
59
+ const body = buffer.subarray(bodyStart, bodyEnd).toString('utf8');
60
+ buffer = buffer.subarray(bodyEnd);
61
+ void handleMessage(JSON.parse(body));
62
+ return true;
63
+ }
64
+
65
+ async function handleMessage(message) {
66
+ if (!Object.hasOwn(message, 'id')) {
67
+ if (message.method === 'notifications/initialized') return;
68
+ return;
69
+ }
70
+ try {
71
+ const result = await dispatch(message.method, message.params || {});
72
+ writeMessage({ jsonrpc: '2.0', id: message.id, result });
73
+ } catch (error) {
74
+ writeMessage({
75
+ jsonrpc: '2.0',
76
+ id: message.id,
77
+ error: {
78
+ code: -32603,
79
+ message: error.message,
80
+ },
81
+ });
82
+ }
83
+ }
84
+
85
+ async function dispatch(method, params) {
86
+ if (method === 'initialize') {
87
+ return {
88
+ protocolVersion: params.protocolVersion || '2024-11-05',
89
+ capabilities: {
90
+ tools: {},
91
+ },
92
+ serverInfo: {
93
+ name: 'huaweicloud-devkit',
94
+ version: '0.1.0',
95
+ },
96
+ };
97
+ }
98
+
99
+ if (method === 'tools/list') {
100
+ return { tools: TOOL_DEFINITIONS };
101
+ }
102
+
103
+ if (method === 'tools/call') {
104
+ const result = await callTool(params.name, params.arguments || {});
105
+ return {
106
+ content: [
107
+ {
108
+ type: 'text',
109
+ text: JSON.stringify(result, null, 2),
110
+ },
111
+ ],
112
+ isError: false,
113
+ };
114
+ }
115
+
116
+ if (method === 'resources/list') {
117
+ return { resources: [] };
118
+ }
119
+
120
+ throw new Error(`Unsupported method: ${method}`);
121
+ }
122
+
123
+ function writeMessage(message) {
124
+ const json = JSON.stringify(message);
125
+ if (useContentLengthFraming) {
126
+ stdout.write(`Content-Length: ${Buffer.byteLength(json, 'utf8')}\r\n\r\n${json}`);
127
+ } else {
128
+ stdout.write(json + '\n');
129
+ }
130
+ }
@@ -0,0 +1,68 @@
1
+ import { getProxyUrlForTarget } from './proxy-config.mjs';
2
+
3
+ let cachedDispatcher = undefined;
4
+ let cachedDispatcherProxyUrl = null;
5
+
6
+ async function importUndici() {
7
+ return await import('undici');
8
+ }
9
+
10
+ export async function getProxyDispatcher(targetUrl) {
11
+ const proxyUrl = getProxyUrlForTarget(targetUrl);
12
+ if (!proxyUrl) return undefined;
13
+
14
+ if (cachedDispatcher && cachedDispatcherProxyUrl === proxyUrl) {
15
+ return cachedDispatcher;
16
+ }
17
+
18
+ const { ProxyAgent } = await importUndici();
19
+ cachedDispatcher = new ProxyAgent(proxyUrl);
20
+ cachedDispatcherProxyUrl = proxyUrl;
21
+ return cachedDispatcher;
22
+ }
23
+
24
+ export function clearProxyDispatcherCache() {
25
+ cachedDispatcher = undefined;
26
+ cachedDispatcherProxyUrl = null;
27
+ }
28
+
29
+ export async function createProxyWebSocket(url, protocols) {
30
+ const proxyUrl = getProxyUrlForTarget(url);
31
+ if (!proxyUrl) {
32
+ return new globalThis.WebSocket(url, protocols);
33
+ }
34
+
35
+ const dispatcher = await getProxyDispatcher(url);
36
+ const { WebSocket: UndiciWebSocket } = await importUndici();
37
+
38
+ const wsOptions = { dispatcher };
39
+ if (protocols) {
40
+ if (Array.isArray(protocols)) {
41
+ wsOptions.protocols = protocols;
42
+ } else {
43
+ wsOptions.protocols = [protocols];
44
+ }
45
+ }
46
+
47
+ return new UndiciWebSocket(url, wsOptions);
48
+ }
49
+
50
+ export async function getWebSocketImpl(targetUrl) {
51
+ const proxyUrl = getProxyUrlForTarget(targetUrl);
52
+ if (!proxyUrl) return globalThis.WebSocket;
53
+
54
+ const dispatcher = await getProxyDispatcher(targetUrl);
55
+ const { WebSocket: UndiciWebSocket } = await importUndici();
56
+
57
+ class ProxyWebSocket extends UndiciWebSocket {
58
+ constructor(url, protocols) {
59
+ const options = { dispatcher };
60
+ if (protocols) {
61
+ options.protocols = Array.isArray(protocols) ? protocols : [protocols];
62
+ }
63
+ super(url, options);
64
+ }
65
+ }
66
+
67
+ return ProxyWebSocket;
68
+ }
@@ -0,0 +1,94 @@
1
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+
5
+ function baseHome() {
6
+ return process.env.HUAWEICLOUD_HOME || homedir();
7
+ }
8
+
9
+ export function proxyConfigPath() {
10
+ return join(baseHome(), '.config', 'huaweicloud', 'proxy.json');
11
+ }
12
+
13
+ export function readProxyConfig() {
14
+ const path = proxyConfigPath();
15
+ if (!existsSync(path)) return null;
16
+ try {
17
+ return JSON.parse(readFileSync(path, 'utf8'));
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+
23
+ export function writeProxyConfig(config = {}) {
24
+ const path = proxyConfigPath();
25
+ mkdirSync(dirname(path), { recursive: true });
26
+ const payload = {
27
+ https_proxy: String(config.https_proxy || ''),
28
+ http_proxy: String(config.http_proxy || ''),
29
+ no_proxy: String(config.no_proxy || ''),
30
+ };
31
+ writeFileSync(path, JSON.stringify(payload, null, 2), { encoding: 'utf8' });
32
+ return path;
33
+ }
34
+
35
+ export function clearProxyConfig() {
36
+ const path = proxyConfigPath();
37
+ if (!existsSync(path)) return false;
38
+ rmSync(path);
39
+ return true;
40
+ }
41
+
42
+ function shouldBypassProxy(hostname, noProxyList) {
43
+ if (!noProxyList.length) return false;
44
+ const lower = hostname.toLowerCase();
45
+ for (const pattern of noProxyList) {
46
+ const p = pattern.trim().toLowerCase();
47
+ if (!p) continue;
48
+ if (p === lower) return true;
49
+ if (p === '*') return true;
50
+ if (p.startsWith('*.')) {
51
+ const domain = p.slice(1);
52
+ if (lower.endsWith(domain) || lower === p.slice(2)) return true;
53
+ }
54
+ if (p.startsWith('.') && lower.endsWith(p)) return true;
55
+ if (lower.endsWith('.' + p)) return true;
56
+ }
57
+ return false;
58
+ }
59
+
60
+ export function getProxySettings(targetUrl) {
61
+ const envHttps = process.env.HTTPS_PROXY || process.env.https_proxy || '';
62
+ const envHttp = process.env.HTTP_PROXY || process.env.http_proxy || '';
63
+ const envNoProxy = process.env.NO_PROXY || process.env.no_proxy || '';
64
+
65
+ const fileConfig = readProxyConfig();
66
+
67
+ const https_proxy = envHttps || fileConfig?.https_proxy || fileConfig?.HTTPS_PROXY || '';
68
+ const http_proxy = envHttp || fileConfig?.http_proxy || fileConfig?.HTTP_PROXY || '';
69
+ const no_proxy = envNoProxy || fileConfig?.no_proxy || fileConfig?.NO_PROXY || '';
70
+
71
+ if (!https_proxy && !http_proxy) return null;
72
+
73
+ const noProxyList = no_proxy
74
+ .split(',')
75
+ .map((s) => s.trim())
76
+ .filter(Boolean);
77
+
78
+ if (targetUrl) {
79
+ const parsed = new URL(targetUrl);
80
+ if (shouldBypassProxy(parsed.hostname, noProxyList)) return null;
81
+ const isHttps = parsed.protocol === 'https:';
82
+ const proxyUrl = isHttps ? https_proxy : http_proxy;
83
+ if (!proxyUrl) return null;
84
+ return { proxyUrl, noProxyList, targetProtocol: parsed.protocol };
85
+ }
86
+
87
+ return { https_proxy, http_proxy, no_proxy, noProxyList };
88
+ }
89
+
90
+ export function getProxyUrlForTarget(targetUrl) {
91
+ const settings = getProxySettings(targetUrl);
92
+ if (!settings) return null;
93
+ return settings.proxyUrl || null;
94
+ }