huaweicloud-devkit 1.0.2-next.5 → 1.0.2-next.7
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/package.json +1 -1
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +9 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +1 -0
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +16 -102
- package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +7 -3
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +2 -2
- package/plugins/huaweicloud-core/src/setup-cli.mjs +5 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
|
-
"version": "1.0.2-next.
|
|
3
|
+
"version": "1.0.2-next.7",
|
|
4
4
|
"description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"mcpServers": "./.mcp.json",
|
|
21
21
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
22
22
|
"skills": "./skills/",
|
|
23
|
-
"version": "1.0.2-next.
|
|
23
|
+
"version": "1.0.2-next.7",
|
|
24
24
|
"author": {
|
|
25
25
|
"name": "HuaweiCloud Mate",
|
|
26
26
|
"url": "https://github.com/huaweicloud"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-core",
|
|
3
|
-
"version": "1.0.2-next.
|
|
3
|
+
"version": "1.0.2-next.7",
|
|
4
4
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "HuaweiCloud Mate",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"mcpServers": "./.mcp.json",
|
|
21
21
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
22
22
|
"skills": "./skills/",
|
|
23
|
-
"version": "1.0.2-next.
|
|
23
|
+
"version": "1.0.2-next.7",
|
|
24
24
|
"author": {
|
|
25
25
|
"name": "HuaweiCloud Mate",
|
|
26
26
|
"url": "https://github.com/huaweicloud"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-core",
|
|
3
|
-
"version": "1.0.2-next.
|
|
3
|
+
"version": "1.0.2-next.7",
|
|
4
4
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "HuaweiCloud Mate",
|
|
@@ -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;
|
|
@@ -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
|
});
|
|
@@ -3,6 +3,11 @@ import { getProxyUrlForTarget } from './proxy-config.mjs';
|
|
|
3
3
|
let cachedDispatcher = undefined;
|
|
4
4
|
let cachedDispatcherProxyUrl = null;
|
|
5
5
|
|
|
6
|
+
async function importUndici() {
|
|
7
|
+
try { return await import('node:undici'); }
|
|
8
|
+
catch { return await import('undici'); }
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
export async function getProxyDispatcher(targetUrl) {
|
|
7
12
|
const proxyUrl = getProxyUrlForTarget(targetUrl);
|
|
8
13
|
if (!proxyUrl) return undefined;
|
|
@@ -11,7 +16,7 @@ export async function getProxyDispatcher(targetUrl) {
|
|
|
11
16
|
return cachedDispatcher;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
const { ProxyAgent } = await
|
|
19
|
+
const { ProxyAgent } = await importUndici();
|
|
15
20
|
cachedDispatcher = new ProxyAgent(proxyUrl);
|
|
16
21
|
cachedDispatcherProxyUrl = proxyUrl;
|
|
17
22
|
return cachedDispatcher;
|
|
@@ -29,7 +34,7 @@ export async function createProxyWebSocket(url, protocols) {
|
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
const dispatcher = await getProxyDispatcher(url);
|
|
32
|
-
const { WebSocket: UndiciWebSocket } = await
|
|
37
|
+
const { WebSocket: UndiciWebSocket } = await importUndici();
|
|
33
38
|
|
|
34
39
|
const wsOptions = { dispatcher };
|
|
35
40
|
if (protocols) {
|
|
@@ -43,113 +48,22 @@ export async function createProxyWebSocket(url, protocols) {
|
|
|
43
48
|
return new UndiciWebSocket(url, wsOptions);
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
export function getWebSocketImpl(targetUrl) {
|
|
51
|
+
export async function getWebSocketImpl(targetUrl) {
|
|
47
52
|
const proxyUrl = getProxyUrlForTarget(targetUrl);
|
|
48
53
|
if (!proxyUrl) return globalThis.WebSocket;
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (!proxyUrlForTarget) {
|
|
53
|
-
return new globalThis.WebSocket(url, protocols);
|
|
54
|
-
}
|
|
55
|
+
const dispatcher = await getProxyDispatcher(targetUrl);
|
|
56
|
+
const { WebSocket: UndiciWebSocket } = await importUndici();
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const {
|
|
59
|
-
|
|
60
|
-
const wsOptions = { dispatcher };
|
|
58
|
+
class ProxyWebSocket extends UndiciWebSocket {
|
|
59
|
+
constructor(url, protocols) {
|
|
60
|
+
const options = { dispatcher };
|
|
61
61
|
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);
|
|
62
|
+
options.protocols = Array.isArray(protocols) ? protocols : [protocols];
|
|
132
63
|
}
|
|
64
|
+
super(url, options);
|
|
133
65
|
}
|
|
134
|
-
pendingEvents.length = 0;
|
|
135
66
|
}
|
|
136
67
|
|
|
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;
|
|
68
|
+
return ProxyWebSocket;
|
|
155
69
|
}
|
|
@@ -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,9 +64,9 @@ 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
|
|
|
@@ -58,7 +58,7 @@ async function getSession(workspaceId, username, timeoutMs) {
|
|
|
58
58
|
const { ak, sk, securitytoken } = getCredentials();
|
|
59
59
|
const { wsUrl, source } = await createConnection(workspaceId, ak, sk, securitytoken);
|
|
60
60
|
|
|
61
|
-
const WebSocketImpl = getWebSocketImpl(wsUrl);
|
|
61
|
+
const WebSocketImpl = await getWebSocketImpl(wsUrl);
|
|
62
62
|
|
|
63
63
|
const { connectHwlinkTerminalSession } = await import(WS_EXEC_INDEX_URL);
|
|
64
64
|
const session = await connectHwlinkTerminalSession({
|
|
@@ -77,7 +77,7 @@ export async function execOneShot(workspaceId, command, username, timeoutMs) {
|
|
|
77
77
|
const { ak, sk, securitytoken } = getCredentials();
|
|
78
78
|
const { wsUrl, source } = await createConnection(workspaceId, ak, sk, securitytoken);
|
|
79
79
|
|
|
80
|
-
const WebSocketImpl = getWebSocketImpl(wsUrl);
|
|
80
|
+
const WebSocketImpl = await getWebSocketImpl(wsUrl);
|
|
81
81
|
|
|
82
82
|
const { executeHwlinkCommand } = await import(WS_EXEC_INDEX_URL);
|
|
83
83
|
return await executeHwlinkCommand({
|
|
@@ -1131,8 +1131,9 @@ async function cmdInstall() {
|
|
|
1131
1131
|
|
|
1132
1132
|
console.log(`\n\x1b[1m下一步:\x1b[0m`);
|
|
1133
1133
|
console.log(` 1. 配置统一凭据:npx huaweicloud-devkit auth init`);
|
|
1134
|
-
console.log(` 2.
|
|
1135
|
-
console.log(` 3.
|
|
1134
|
+
console.log(` 2. 配置代理(企业内网):npx huaweicloud-devkit proxy init`);
|
|
1135
|
+
console.log(` 3. 重启 ${appName} 会话(MCP 工具重启后生效)`);
|
|
1136
|
+
console.log(` 4. 运行自检:npx huaweicloud-devkit doctor`);
|
|
1136
1137
|
|
|
1137
1138
|
// Write install marker for doctor to detect
|
|
1138
1139
|
const markerDir = target === 'dsh' ? dshPluginsDir()
|
|
@@ -1874,12 +1875,12 @@ async function cmdProxyInit() {
|
|
|
1874
1875
|
|
|
1875
1876
|
const httpsProxy = await readLineQuestion(`HTTPS proxy [${existing.https_proxy || 'none'}]: `);
|
|
1876
1877
|
const httpProxy = await readLineQuestion(`HTTP proxy [${existing.http_proxy || 'none'}]: `);
|
|
1877
|
-
const noProxy = await readLineQuestion(`NO_PROXY hosts [${existing.no_proxy || '
|
|
1878
|
+
const noProxy = await readLineQuestion(`NO_PROXY hosts [${existing.no_proxy || '127.0.0.1,localhost,.huawei.com'}]: `);
|
|
1878
1879
|
|
|
1879
1880
|
const config = {
|
|
1880
1881
|
https_proxy: httpsProxy || existing.https_proxy || '',
|
|
1881
1882
|
http_proxy: httpProxy || existing.http_proxy || '',
|
|
1882
|
-
no_proxy: noProxy || existing.no_proxy || '
|
|
1883
|
+
no_proxy: noProxy || existing.no_proxy || '127.0.0.1,localhost,.huawei.com',
|
|
1883
1884
|
};
|
|
1884
1885
|
|
|
1885
1886
|
const path = writeProxyConfig(config);
|