huaweicloud-devkit 1.0.2-next.2 → 1.0.2-next.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +18 -18
- package/README.md +81 -51
- package/README.zh-CN.md +81 -49
- package/bin/setup.cjs +5 -3
- package/integrations/opencode/opencode.json +1 -4
- package/package.json +61 -44
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
- package/plugins/huaweicloud-core/.mcp.json +2 -5
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -18
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +32 -9
- package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
- package/plugins/huaweicloud-core/safety/policy.json +75 -99
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +41 -13
- package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +34 -34
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +18 -18
- package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +34 -32
- package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +30 -30
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +13 -12
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +64 -17
- package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +20 -20
- package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +31 -31
- package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +21 -20
- package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +39 -30
- package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +8 -1
- package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +6 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +39 -36
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +15 -4
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +11 -9
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +13 -13
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +40 -39
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +12 -10
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +21 -20
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +20 -20
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +27 -23
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +27 -27
- package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +65 -39
- package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +9 -9
- package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +25 -25
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +45 -44
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +12 -7
- package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +13 -12
- package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +4 -4
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +63 -58
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +127 -42
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +26 -26
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +39 -39
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +13 -12
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +20 -19
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +15 -15
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +18 -18
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +13 -13
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +17 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +27 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +56 -53
- package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +6 -6
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +15 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +0 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +33 -33
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +108 -5
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +77 -4
- package/plugins/huaweicloud-core/src/auth/service.mjs +1 -1
- package/plugins/huaweicloud-core/src/data/icons-manifest.v1.json +3517 -0
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +25 -7
- package/plugins/huaweicloud-core/src/icon-library.mjs +171 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +30 -3
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +20 -102
- package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +11 -4
- package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +4 -1
- package/plugins/huaweicloud-core/src/safety-policy.mjs +122 -68
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +9 -9
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +24 -26
- package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +510 -10
- package/plugins/huaweicloud-core/src/search-market.mjs +46 -12
- package/plugins/huaweicloud-core/src/setup-cli.mjs +1251 -181
- package/plugins/huaweicloud-core/src/tools.mjs +600 -122
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +72 -38
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +10 -5
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +12 -15
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +12 -16
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +46 -31
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +267 -0
- package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +9 -5
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { classifyHcloudArgs, redactSecrets, assertAllowed } from './safety-policy.mjs';
|
|
3
|
+
import { getProxySettings } from './proxy/proxy-config.mjs';
|
|
3
4
|
|
|
4
5
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
5
6
|
const DEFAULT_FORCE_KILL_AFTER_MS = 2_000;
|
|
@@ -46,7 +47,7 @@ export async function runHcloud(args, options = {}) {
|
|
|
46
47
|
attempts: attempt + 1,
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
|
-
await wait((options.retryBaseDelayMs ?? 500) *
|
|
50
|
+
await wait((options.retryBaseDelayMs ?? 500) * 2 ** attempt);
|
|
50
51
|
}
|
|
51
52
|
throw new Error('Unreachable retry state.');
|
|
52
53
|
}
|
|
@@ -59,12 +60,20 @@ function runHcloudOnce(plan, options) {
|
|
|
59
60
|
const cwd = options.cwd || undefined;
|
|
60
61
|
|
|
61
62
|
return new Promise((resolve) => {
|
|
63
|
+
const proxySettings = getProxySettings();
|
|
64
|
+
const proxyEnv = {};
|
|
65
|
+
if (proxySettings) {
|
|
66
|
+
if (proxySettings.https_proxy) proxyEnv.HTTPS_PROXY = proxySettings.https_proxy;
|
|
67
|
+
if (proxySettings.http_proxy) proxyEnv.HTTP_PROXY = proxySettings.http_proxy;
|
|
68
|
+
if (proxySettings.no_proxy) proxyEnv.NO_PROXY = proxySettings.no_proxy;
|
|
69
|
+
}
|
|
62
70
|
const child = spawn(executable, [...executableArgs, ...plan.rawArgs], {
|
|
63
71
|
shell: false,
|
|
64
72
|
windowsHide: true,
|
|
65
73
|
cwd,
|
|
66
74
|
env: {
|
|
67
75
|
...process.env,
|
|
76
|
+
...proxyEnv,
|
|
68
77
|
...options.env,
|
|
69
78
|
},
|
|
70
79
|
});
|
|
@@ -151,7 +160,10 @@ function runHcloudOnce(plan, options) {
|
|
|
151
160
|
return;
|
|
152
161
|
}
|
|
153
162
|
finish({
|
|
154
|
-
ok:
|
|
163
|
+
ok:
|
|
164
|
+
code === 0 ||
|
|
165
|
+
(code !== 0 &&
|
|
166
|
+
/successfully|succ?ess.*\[200\]|create bucket successfully|upload successfully/i.test(stdout + stderr)),
|
|
155
167
|
exitCode: code,
|
|
156
168
|
signal,
|
|
157
169
|
stdout: redactOutput(stdout),
|
|
@@ -165,7 +177,9 @@ function runHcloudOnce(plan, options) {
|
|
|
165
177
|
function isRetryableNetworkError(result) {
|
|
166
178
|
if (result.code === 'TIMEOUT') return false;
|
|
167
179
|
const text = `${result.error || ''}\n${result.stdout || ''}\n${result.stderr || ''}`;
|
|
168
|
-
return /\[NETWORK_ERROR\]|connection timed out|ECONNRESET|ETIMEDOUT|temporary failure|TLS handshake timeout/i.test(
|
|
180
|
+
return /\[NETWORK_ERROR\]|connection timed out|ECONNRESET|ETIMEDOUT|temporary failure|TLS handshake timeout/i.test(
|
|
181
|
+
text,
|
|
182
|
+
);
|
|
169
183
|
}
|
|
170
184
|
|
|
171
185
|
function wait(ms) {
|
|
@@ -194,7 +208,11 @@ const REQUIRED_PARAMS = {
|
|
|
194
208
|
'ECS CreateServers': ['server.flavorRef', 'server.imageRef', 'server.nics.1.subnet_id'],
|
|
195
209
|
'VPC CreateVpc': ['vpc.cidr'],
|
|
196
210
|
'VPC CreateSubnet': ['subnet.vpc_id', 'subnet.cidr'],
|
|
197
|
-
'VPC CreateSecurityGroupRule': [
|
|
211
|
+
'VPC CreateSecurityGroupRule': [
|
|
212
|
+
'security_group_rule.security_group_id',
|
|
213
|
+
'security_group_rule.direction',
|
|
214
|
+
'security_group_rule.protocol',
|
|
215
|
+
],
|
|
198
216
|
'EIP CreatePublicip': ['bandwidth.share_type', 'publicip.type'],
|
|
199
217
|
'FunctionGraph CreateFunction': ['func_name', 'runtime', 'handler', 'memory_size', 'package', 'timeout'],
|
|
200
218
|
'FunctionGraph CreateFunctionTrigger': ['function_urn', 'trigger_type_code'],
|
|
@@ -208,9 +226,9 @@ const PARAM_VALUE_HINTS = {
|
|
|
208
226
|
'server.imageRef': 'Run `hcloud IMS ListImages --cli-region=<r> --__imagetype=gold` to find valid image IDs',
|
|
209
227
|
'server.nics.1.subnet_id': 'Run `hcloud VPC ListSubnets --cli-region=<r>` to find subnet IDs',
|
|
210
228
|
'subnet.vpc_id': 'Run `hcloud VPC ListVpcs --cli-region=<r>` to find VPC IDs',
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
229
|
+
func_name: 'Function name must be unique within project',
|
|
230
|
+
runtime: 'Run `hcloud FunctionGraph ListRuntimes` to see available runtimes',
|
|
231
|
+
spec_id: 'APIG spec: BASIC (no public IP) or PROFESSIONAL (requires --loadbalancer_provider)',
|
|
214
232
|
'obs://': 'Bucket name must be globally unique and DNS-compliant (lowercase, numbers, hyphens only)',
|
|
215
233
|
};
|
|
216
234
|
|
|
@@ -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,10 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { stdin, stdout } from 'node:process';
|
|
3
|
-
import { rmSync, existsSync } from 'node:fs';
|
|
4
|
-
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import { rmSync, existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { resolve, dirname, join } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { TOOL_DEFINITIONS, callTool } from './tools.mjs';
|
|
7
7
|
|
|
8
|
+
try {
|
|
9
|
+
const { readProxyConfig } = await import('./proxy/proxy-config.mjs');
|
|
10
|
+
const proxyConfig = readProxyConfig();
|
|
11
|
+
if (proxyConfig) {
|
|
12
|
+
if (proxyConfig.https_proxy || proxyConfig.HTTPS_PROXY) {
|
|
13
|
+
process.env.HTTPS_PROXY = process.env.HTTPS_PROXY || proxyConfig.https_proxy || proxyConfig.HTTPS_PROXY;
|
|
14
|
+
}
|
|
15
|
+
if (proxyConfig.http_proxy || proxyConfig.HTTP_PROXY) {
|
|
16
|
+
process.env.HTTP_PROXY = process.env.HTTP_PROXY || proxyConfig.http_proxy || proxyConfig.HTTP_PROXY;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
} catch {}
|
|
20
|
+
|
|
8
21
|
// The MCP server is now loaded by a live agent session. Clear the install marker
|
|
9
22
|
// in this plugin dir so `doctor` no longer reports "restart needed".
|
|
10
23
|
try {
|
|
@@ -13,6 +26,20 @@ try {
|
|
|
13
26
|
if (existsSync(marker)) rmSync(marker, { force: true });
|
|
14
27
|
} catch {}
|
|
15
28
|
|
|
29
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
30
|
+
const pluginRoot = resolve(__dirname, '..');
|
|
31
|
+
const packageRoot = resolve(pluginRoot, '..', '..');
|
|
32
|
+
let pkgVersion = '0.0.0';
|
|
33
|
+
for (const base of [pluginRoot, packageRoot]) {
|
|
34
|
+
try {
|
|
35
|
+
const version = JSON.parse(readFileSync(join(base, 'package.json'), 'utf8')).version;
|
|
36
|
+
if (version) {
|
|
37
|
+
pkgVersion = version;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
} catch {}
|
|
41
|
+
}
|
|
42
|
+
|
|
16
43
|
let buffer = Buffer.alloc(0);
|
|
17
44
|
let useContentLengthFraming = true;
|
|
18
45
|
|
|
@@ -90,7 +117,7 @@ async function dispatch(method, params) {
|
|
|
90
117
|
},
|
|
91
118
|
serverInfo: {
|
|
92
119
|
name: 'huaweicloud-devkit',
|
|
93
|
-
version:
|
|
120
|
+
version: pkgVersion,
|
|
94
121
|
},
|
|
95
122
|
};
|
|
96
123
|
}
|
|
@@ -3,6 +3,15 @@ import { getProxyUrlForTarget } from './proxy-config.mjs';
|
|
|
3
3
|
let cachedDispatcher = undefined;
|
|
4
4
|
let cachedDispatcherProxyUrl = null;
|
|
5
5
|
|
|
6
|
+
async function importUndici() {
|
|
7
|
+
try {
|
|
8
|
+
/* eslint-disable-next-line n/no-missing-import */
|
|
9
|
+
return await import('node:undici');
|
|
10
|
+
} catch {
|
|
11
|
+
return await import('undici');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
export async function getProxyDispatcher(targetUrl) {
|
|
7
16
|
const proxyUrl = getProxyUrlForTarget(targetUrl);
|
|
8
17
|
if (!proxyUrl) return undefined;
|
|
@@ -11,7 +20,7 @@ export async function getProxyDispatcher(targetUrl) {
|
|
|
11
20
|
return cachedDispatcher;
|
|
12
21
|
}
|
|
13
22
|
|
|
14
|
-
const { ProxyAgent } = await
|
|
23
|
+
const { ProxyAgent } = await importUndici();
|
|
15
24
|
cachedDispatcher = new ProxyAgent(proxyUrl);
|
|
16
25
|
cachedDispatcherProxyUrl = proxyUrl;
|
|
17
26
|
return cachedDispatcher;
|
|
@@ -29,7 +38,7 @@ export async function createProxyWebSocket(url, protocols) {
|
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
const dispatcher = await getProxyDispatcher(url);
|
|
32
|
-
const { WebSocket: UndiciWebSocket } = await
|
|
41
|
+
const { WebSocket: UndiciWebSocket } = await importUndici();
|
|
33
42
|
|
|
34
43
|
const wsOptions = { dispatcher };
|
|
35
44
|
if (protocols) {
|
|
@@ -43,113 +52,22 @@ export async function createProxyWebSocket(url, protocols) {
|
|
|
43
52
|
return new UndiciWebSocket(url, wsOptions);
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
export function getWebSocketImpl(targetUrl) {
|
|
55
|
+
export async function getWebSocketImpl(targetUrl) {
|
|
47
56
|
const proxyUrl = getProxyUrlForTarget(targetUrl);
|
|
48
57
|
if (!proxyUrl) return globalThis.WebSocket;
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (!proxyUrlForTarget) {
|
|
53
|
-
return new globalThis.WebSocket(url, protocols);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const wsPromise = (async () => {
|
|
57
|
-
const dispatcher = await getProxyDispatcher(url);
|
|
58
|
-
const { WebSocket: UndiciWebSocket } = await import('undici');
|
|
59
|
+
const dispatcher = await getProxyDispatcher(targetUrl);
|
|
60
|
+
const { WebSocket: UndiciWebSocket } = await importUndici();
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
class ProxyWebSocket extends UndiciWebSocket {
|
|
63
|
+
constructor(url, protocols) {
|
|
64
|
+
const options = { dispatcher };
|
|
61
65
|
if (protocols) {
|
|
62
|
-
|
|
63
|
-
wsOptions.protocols = protocols;
|
|
64
|
-
} else {
|
|
65
|
-
wsOptions.protocols = [protocols];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return new UndiciWebSocket(url, wsOptions);
|
|
70
|
-
})();
|
|
71
|
-
|
|
72
|
-
return createSyncProxyWebSocketWrapper(wsPromise);
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function createSyncProxyWebSocketWrapper(wsPromise) {
|
|
77
|
-
const pendingEvents = [];
|
|
78
|
-
const eventHandlers = new Map();
|
|
79
|
-
let ws = null;
|
|
80
|
-
let settled = false;
|
|
81
|
-
let readyState = 0;
|
|
82
|
-
let binaryType = 'arraybuffer';
|
|
83
|
-
|
|
84
|
-
const wrapper = {
|
|
85
|
-
get readyState() { return ws ? ws.readyState : readyState; },
|
|
86
|
-
get url() { return ws ? ws.url : ''; },
|
|
87
|
-
get protocol() { return ws ? ws.protocol : ''; },
|
|
88
|
-
get binaryType() { return ws ? ws.binaryType : binaryType; },
|
|
89
|
-
set binaryType(v) {
|
|
90
|
-
binaryType = v;
|
|
91
|
-
if (ws) ws.binaryType = v;
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
send(data, callback) {
|
|
95
|
-
if (ws) {
|
|
96
|
-
if (ws.send.length >= 2) return ws.send(data, callback);
|
|
97
|
-
try { ws.send(data); if (callback) callback(null); } catch (e) { if (callback) callback(e); }
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
pendingEvents.push({ type: 'send', data, callback });
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
close(code, reason) {
|
|
104
|
-
if (ws) { ws.close(code, reason); return; }
|
|
105
|
-
readyState = 3;
|
|
106
|
-
pendingEvents.push({ type: 'close', code, reason });
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
addEventListener(type, handler) {
|
|
110
|
-
if (!eventHandlers.has(type)) eventHandlers.set(type, new Set());
|
|
111
|
-
eventHandlers.get(type).add(handler);
|
|
112
|
-
if (ws) ws.addEventListener(type, handler);
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
removeEventListener(type, handler) {
|
|
116
|
-
const handlers = eventHandlers.get(type);
|
|
117
|
-
if (handlers) handlers.delete(handler);
|
|
118
|
-
if (ws) ws.removeEventListener(type, handler);
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
on(type, handler) { wrapper.addEventListener(type, handler); },
|
|
122
|
-
off(type, handler) { wrapper.removeEventListener(type, handler); },
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
function flushPendingEvents() {
|
|
126
|
-
for (const evt of pendingEvents) {
|
|
127
|
-
if (evt.type === 'send') {
|
|
128
|
-
if (ws.send.length >= 2) ws.send(evt.data, evt.callback);
|
|
129
|
-
else { try { ws.send(evt.data); if (evt.callback) evt.callback(null); } catch (e) { if (evt.callback) evt.callback(e); } }
|
|
130
|
-
} else if (evt.type === 'close') {
|
|
131
|
-
ws.close(evt.code, evt.reason);
|
|
66
|
+
options.protocols = Array.isArray(protocols) ? protocols : [protocols];
|
|
132
67
|
}
|
|
68
|
+
super(url, options);
|
|
133
69
|
}
|
|
134
|
-
pendingEvents.length = 0;
|
|
135
70
|
}
|
|
136
71
|
|
|
137
|
-
|
|
138
|
-
ws = resolvedWs;
|
|
139
|
-
if ('binaryType' in ws) ws.binaryType = binaryType;
|
|
140
|
-
for (const [type, handlers] of eventHandlers) {
|
|
141
|
-
for (const handler of handlers) {
|
|
142
|
-
ws.addEventListener(type, handler);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
flushPendingEvents();
|
|
146
|
-
}).catch((err) => {
|
|
147
|
-
readyState = 3;
|
|
148
|
-
const handlers = eventHandlers.get('error');
|
|
149
|
-
if (handlers) for (const h of handlers) h(err instanceof Error ? err : new Error(String(err)));
|
|
150
|
-
const closeHandlers = eventHandlers.get('close');
|
|
151
|
-
if (closeHandlers) for (const h of closeHandlers) h({ code: 1006, reason: err.message || String(err) });
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
return wrapper;
|
|
72
|
+
return ProxyWebSocket;
|
|
155
73
|
}
|
|
@@ -47,6 +47,10 @@ function shouldBypassProxy(hostname, noProxyList) {
|
|
|
47
47
|
if (!p) continue;
|
|
48
48
|
if (p === lower) return true;
|
|
49
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
|
+
}
|
|
50
54
|
if (p.startsWith('.') && lower.endsWith(p)) return true;
|
|
51
55
|
if (lower.endsWith('.' + p)) return true;
|
|
52
56
|
}
|
|
@@ -60,13 +64,16 @@ export function getProxySettings(targetUrl) {
|
|
|
60
64
|
|
|
61
65
|
const fileConfig = readProxyConfig();
|
|
62
66
|
|
|
63
|
-
const https_proxy = envHttps ||
|
|
64
|
-
const http_proxy = envHttp ||
|
|
65
|
-
const no_proxy = envNoProxy ||
|
|
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 || '';
|
|
66
70
|
|
|
67
71
|
if (!https_proxy && !http_proxy) return null;
|
|
68
72
|
|
|
69
|
-
const noProxyList = no_proxy
|
|
73
|
+
const noProxyList = no_proxy
|
|
74
|
+
.split(',')
|
|
75
|
+
.map((s) => s.trim())
|
|
76
|
+
.filter(Boolean);
|
|
70
77
|
|
|
71
78
|
if (targetUrl) {
|
|
72
79
|
const parsed = new URL(targetUrl);
|
|
@@ -18,7 +18,10 @@ export function loadRiskRules(options = {}) {
|
|
|
18
18
|
|
|
19
19
|
function redactEvidence(text) {
|
|
20
20
|
return String(text)
|
|
21
|
-
.replace(
|
|
21
|
+
.replace(
|
|
22
|
+
/((?:access[_-]?key|secret[_-]?key|security[_-]?token|x[_-]?auth[_-]?token|authorization|password|passwd|adminPass|credential)\s*[:=]\s*)("[^"]*"|'[^']*'|[^\s,;]+)/gi,
|
|
23
|
+
'$1<redacted>',
|
|
24
|
+
)
|
|
22
25
|
.replace(/(AK|SK)\s*[:=]\s*("[^"]*"|'[^']*'|[^\s,;]+)/g, '$1=<redacted>');
|
|
23
26
|
}
|
|
24
27
|
|