huaweicloud-devkit 1.0.2-dev.8 → 1.0.2-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +20 -20
- package/LICENSE +201 -201
- package/README.md +163 -138
- package/README.zh-CN.md +161 -138
- package/bin/setup.cjs +8 -8
- package/integrations/opencode/commands/huaweicloud-doctor.md +14 -14
- package/integrations/opencode/opencode.json +14 -14
- package/integrations/opencode/skills/huaweicloud-core/SKILL.md +32 -32
- package/package.json +46 -44
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +44 -44
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +43 -43
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +44 -44
- package/plugins/huaweicloud-core/.mcp.json +15 -15
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +44 -44
- package/plugins/huaweicloud-core/hooks/hooks.json +26 -26
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +136 -136
- package/plugins/huaweicloud-core/safety/policy.json +99 -100
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +199 -199
- package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +164 -164
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +48 -48
- package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +50 -50
- package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +101 -101
- package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +90 -90
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +190 -190
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +124 -124
- package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +55 -55
- package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +70 -70
- package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +56 -56
- package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +97 -97
- package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +29 -29
- package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +23 -23
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +185 -185
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +133 -133
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/evs.md +31 -31
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +55 -55
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +52 -52
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +131 -131
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +58 -58
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +71 -71
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/deploy-workflow.md +119 -119
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +142 -142
- package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +56 -56
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +65 -65
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +111 -111
- package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +122 -122
- package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +56 -56
- package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +65 -65
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +133 -127
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +49 -49
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +26 -26
- package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +100 -100
- package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +40 -40
- package/plugins/huaweicloud-core/skills/huawei-obs/references/static-website.md +56 -56
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +224 -224
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +135 -131
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +69 -69
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +84 -84
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +76 -76
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +59 -59
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +50 -50
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +63 -63
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +56 -56
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +51 -47
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +128 -127
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +110 -95
- package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +19 -19
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +27 -27
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +60 -60
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +83 -83
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +105 -87
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +95 -95
- package/plugins/huaweicloud-core/src/auth/service.mjs +63 -63
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +265 -265
- package/plugins/huaweicloud-core/src/mcp-server.mjs +129 -129
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +155 -0
- package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +87 -0
- package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +137 -137
- package/plugins/huaweicloud-core/src/safety-policy.mjs +302 -302
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +91 -97
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +162 -153
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +112 -105
- package/plugins/huaweicloud-core/src/search-market.mjs +174 -169
- package/plugins/huaweicloud-core/src/setup-cli.mjs +2017 -1576
- package/plugins/huaweicloud-core/src/tools.mjs +1008 -1005
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +427 -427
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +132 -132
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +227 -227
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +202 -202
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +158 -158
- package/plugins/huaweicloud-core/src/ws-exec/index.js +19 -19
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +338 -338
|
@@ -1,129 +1,129 @@
|
|
|
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
|
+
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
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { getProxyUrlForTarget } from './proxy-config.mjs';
|
|
2
|
+
|
|
3
|
+
let cachedDispatcher = undefined;
|
|
4
|
+
let cachedDispatcherProxyUrl = null;
|
|
5
|
+
|
|
6
|
+
export async function getProxyDispatcher(targetUrl) {
|
|
7
|
+
const proxyUrl = getProxyUrlForTarget(targetUrl);
|
|
8
|
+
if (!proxyUrl) return undefined;
|
|
9
|
+
|
|
10
|
+
if (cachedDispatcher && cachedDispatcherProxyUrl === proxyUrl) {
|
|
11
|
+
return cachedDispatcher;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { ProxyAgent } = await import('undici');
|
|
15
|
+
cachedDispatcher = new ProxyAgent(proxyUrl);
|
|
16
|
+
cachedDispatcherProxyUrl = proxyUrl;
|
|
17
|
+
return cachedDispatcher;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function clearProxyDispatcherCache() {
|
|
21
|
+
cachedDispatcher = undefined;
|
|
22
|
+
cachedDispatcherProxyUrl = null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function createProxyWebSocket(url, protocols) {
|
|
26
|
+
const proxyUrl = getProxyUrlForTarget(url);
|
|
27
|
+
if (!proxyUrl) {
|
|
28
|
+
return new globalThis.WebSocket(url, protocols);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const dispatcher = await getProxyDispatcher(url);
|
|
32
|
+
const { WebSocket: UndiciWebSocket } = await import('undici');
|
|
33
|
+
|
|
34
|
+
const wsOptions = { dispatcher };
|
|
35
|
+
if (protocols) {
|
|
36
|
+
if (Array.isArray(protocols)) {
|
|
37
|
+
wsOptions.protocols = protocols;
|
|
38
|
+
} else {
|
|
39
|
+
wsOptions.protocols = [protocols];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return new UndiciWebSocket(url, wsOptions);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getWebSocketImpl(targetUrl) {
|
|
47
|
+
const proxyUrl = getProxyUrlForTarget(targetUrl);
|
|
48
|
+
if (!proxyUrl) return globalThis.WebSocket;
|
|
49
|
+
|
|
50
|
+
return (url, protocols) => {
|
|
51
|
+
const proxyUrlForTarget = getProxyUrlForTarget(url);
|
|
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
|
+
|
|
60
|
+
const wsOptions = { dispatcher };
|
|
61
|
+
if (protocols) {
|
|
62
|
+
if (Array.isArray(protocols)) {
|
|
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);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
pendingEvents.length = 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
wsPromise.then((resolvedWs) => {
|
|
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;
|
|
155
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
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('.') && lower.endsWith(p)) return true;
|
|
51
|
+
if (lower.endsWith('.' + p)) return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function getProxySettings(targetUrl) {
|
|
57
|
+
const envHttps = process.env.HTTPS_PROXY || process.env.https_proxy || '';
|
|
58
|
+
const envHttp = process.env.HTTP_PROXY || process.env.http_proxy || '';
|
|
59
|
+
const envNoProxy = process.env.NO_PROXY || process.env.no_proxy || '';
|
|
60
|
+
|
|
61
|
+
const fileConfig = readProxyConfig();
|
|
62
|
+
|
|
63
|
+
const https_proxy = envHttps || (fileConfig?.https_proxy || '');
|
|
64
|
+
const http_proxy = envHttp || (fileConfig?.http_proxy || '');
|
|
65
|
+
const no_proxy = envNoProxy || (fileConfig?.no_proxy || '');
|
|
66
|
+
|
|
67
|
+
if (!https_proxy && !http_proxy) return null;
|
|
68
|
+
|
|
69
|
+
const noProxyList = no_proxy.split(',').map((s) => s.trim()).filter(Boolean);
|
|
70
|
+
|
|
71
|
+
if (targetUrl) {
|
|
72
|
+
const parsed = new URL(targetUrl);
|
|
73
|
+
if (shouldBypassProxy(parsed.hostname, noProxyList)) return null;
|
|
74
|
+
const isHttps = parsed.protocol === 'https:';
|
|
75
|
+
const proxyUrl = isHttps ? https_proxy : http_proxy;
|
|
76
|
+
if (!proxyUrl) return null;
|
|
77
|
+
return { proxyUrl, noProxyList, targetProtocol: parsed.protocol };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { https_proxy, http_proxy, no_proxy, noProxyList };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getProxyUrlForTarget(targetUrl) {
|
|
84
|
+
const settings = getProxySettings(targetUrl);
|
|
85
|
+
if (!settings) return null;
|
|
86
|
+
return settings.proxyUrl || null;
|
|
87
|
+
}
|