huaweicloud-devkit 1.0.2-next.3 → 1.0.3-dev.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 +139 -163
- package/README.zh-CN.md +139 -161
- 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 +44 -47
- 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 -99
- 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 +127 -133
- 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 +134 -139
- 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 +47 -51
- 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 -128
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +4 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +95 -110
- 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 +87 -105
- 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/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 +87 -91
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +153 -162
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +105 -112
- package/plugins/huaweicloud-core/src/search-market.mjs +169 -174
- package/plugins/huaweicloud-core/src/setup-cli.mjs +1594 -2017
- package/plugins/huaweicloud-core/src/tools.mjs +993 -1008
- 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
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +0 -155
- package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +0 -87
|
@@ -1,427 +1,427 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEFAULT_TIMEOUT_MS,
|
|
3
|
-
WebSocketExecError,
|
|
4
|
-
buildMarkers,
|
|
5
|
-
buildShellCommands,
|
|
6
|
-
cleanCommandOutput,
|
|
7
|
-
} from './ws-exec-client.js';
|
|
8
|
-
import { HwlinkWebSocketMultiplexer } from './hwlink-multiplexer.js';
|
|
9
|
-
import { HwlinkTerminalChannel } from './hwlink-terminal-channel.js';
|
|
10
|
-
|
|
11
|
-
const decoder = new TextDecoder();
|
|
12
|
-
const encoder = new TextEncoder();
|
|
13
|
-
|
|
14
|
-
function normalizeCommand(command) {
|
|
15
|
-
if (!command || !String(command).trim()) {
|
|
16
|
-
throw new WebSocketExecError('missing command', 2);
|
|
17
|
-
}
|
|
18
|
-
return String(command).trim();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function normalizeTimeout(timeoutMs) {
|
|
22
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
23
|
-
throw new WebSocketExecError('timeoutMs must be a positive number of milliseconds', 2);
|
|
24
|
-
}
|
|
25
|
-
return timeoutMs;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function normalizeSource(source) {
|
|
29
|
-
const numericSource = Number(source);
|
|
30
|
-
if (!Number.isInteger(numericSource) || numericSource < -0x80000000 || numericSource > 0xffffffff) {
|
|
31
|
-
throw new WebSocketExecError('hwlink source must be an int32 or uint32 number', 2);
|
|
32
|
-
}
|
|
33
|
-
return numericSource;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function normalizeUrl(url) {
|
|
37
|
-
if (!url || !String(url).trim()) {
|
|
38
|
-
throw new WebSocketExecError('hwlink url is required', 2);
|
|
39
|
-
}
|
|
40
|
-
return String(url);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function createHwlinkTerminal(options = {}) {
|
|
44
|
-
const {
|
|
45
|
-
url,
|
|
46
|
-
source,
|
|
47
|
-
username = 'root',
|
|
48
|
-
WebSocketImpl = globalThis.WebSocket,
|
|
49
|
-
protocol = 'devenv',
|
|
50
|
-
cols,
|
|
51
|
-
rows,
|
|
52
|
-
onFrame,
|
|
53
|
-
onData,
|
|
54
|
-
onError,
|
|
55
|
-
onClose,
|
|
56
|
-
trace = false,
|
|
57
|
-
} = options;
|
|
58
|
-
|
|
59
|
-
const normalizedUrl = normalizeUrl(url);
|
|
60
|
-
const normalizedSource = normalizeSource(source);
|
|
61
|
-
const mux = new HwlinkWebSocketMultiplexer(normalizedUrl, normalizedSource, {
|
|
62
|
-
WebSocketImpl,
|
|
63
|
-
protocol,
|
|
64
|
-
onFrame,
|
|
65
|
-
trace,
|
|
66
|
-
});
|
|
67
|
-
const term = new HwlinkTerminalChannel(username);
|
|
68
|
-
let closed = false;
|
|
69
|
-
let readySettled = false;
|
|
70
|
-
let readyResolve;
|
|
71
|
-
let readyReject;
|
|
72
|
-
|
|
73
|
-
const ready = new Promise((resolve, reject) => {
|
|
74
|
-
readyResolve = resolve;
|
|
75
|
-
readyReject = reject;
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
function settleReady(error) {
|
|
79
|
-
if (readySettled) return;
|
|
80
|
-
readySettled = true;
|
|
81
|
-
if (error) readyReject(error);
|
|
82
|
-
else readyResolve(handle);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function handleError(error, prefix) {
|
|
86
|
-
const wrapped = new WebSocketExecError(`${prefix}: ${error.message}`, 1, {
|
|
87
|
-
cause: error,
|
|
88
|
-
phase: readySettled ? 'open' : 'opening',
|
|
89
|
-
});
|
|
90
|
-
settleReady(wrapped);
|
|
91
|
-
if (onError) onError(wrapped);
|
|
92
|
-
close();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function handleClose() {
|
|
96
|
-
settleReady(new WebSocketExecError('hwlink terminal closed before ready', 1, {
|
|
97
|
-
phase: 'opening',
|
|
98
|
-
}));
|
|
99
|
-
if (closed) return;
|
|
100
|
-
closed = true;
|
|
101
|
-
if (onClose) onClose();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
term.onReady(() => {
|
|
105
|
-
if (Number.isFinite(cols) && Number.isFinite(rows)) {
|
|
106
|
-
term.resize(cols, rows);
|
|
107
|
-
}
|
|
108
|
-
settleReady();
|
|
109
|
-
});
|
|
110
|
-
term.onData((data) => {
|
|
111
|
-
if (onData) onData(data);
|
|
112
|
-
});
|
|
113
|
-
term.onError((error) => handleError(error, 'hwlink terminal error'));
|
|
114
|
-
term.onClose(handleClose);
|
|
115
|
-
mux.onError = (error) => handleError(error, 'hwlink websocket error');
|
|
116
|
-
mux.onClose = handleClose;
|
|
117
|
-
|
|
118
|
-
function close() {
|
|
119
|
-
if (closed) return;
|
|
120
|
-
closed = true;
|
|
121
|
-
term.close();
|
|
122
|
-
mux.close();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const handle = {
|
|
126
|
-
url: normalizedUrl,
|
|
127
|
-
source: normalizedSource,
|
|
128
|
-
username,
|
|
129
|
-
mux,
|
|
130
|
-
term,
|
|
131
|
-
ready,
|
|
132
|
-
close,
|
|
133
|
-
resize: (nextCols, nextRows) => term.resize(nextCols, nextRows),
|
|
134
|
-
sendInput: (data) => term.sendInput(data),
|
|
135
|
-
sendText: (text) => term.sendText(text),
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
term.attach(mux);
|
|
139
|
-
return handle;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
class HwlinkTerminalExecSession {
|
|
143
|
-
constructor(options = {}) {
|
|
144
|
-
const {
|
|
145
|
-
url,
|
|
146
|
-
source,
|
|
147
|
-
username = 'root',
|
|
148
|
-
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
149
|
-
WebSocketImpl = globalThis.WebSocket,
|
|
150
|
-
protocol = 'devenv',
|
|
151
|
-
cols,
|
|
152
|
-
rows,
|
|
153
|
-
onFrame,
|
|
154
|
-
onData,
|
|
155
|
-
trace = false,
|
|
156
|
-
} = options;
|
|
157
|
-
|
|
158
|
-
this.url = normalizeUrl(url);
|
|
159
|
-
this.source = normalizeSource(source);
|
|
160
|
-
this.username = username;
|
|
161
|
-
this.timeoutMs = normalizeTimeout(timeoutMs);
|
|
162
|
-
this.onData = onData;
|
|
163
|
-
this.initialCols = cols;
|
|
164
|
-
this.initialRows = rows;
|
|
165
|
-
this.state = 'opening';
|
|
166
|
-
this.readyBuffer = '';
|
|
167
|
-
this.inputEchoed = false;
|
|
168
|
-
this.pending = null;
|
|
169
|
-
this.queue = Promise.resolve();
|
|
170
|
-
this.readyMarkers = buildMarkers();
|
|
171
|
-
const { readyCommand } = buildShellCommands(this.readyMarkers);
|
|
172
|
-
this.readyCommand = readyCommand;
|
|
173
|
-
|
|
174
|
-
this.readyPromise = new Promise((resolve, reject) => {
|
|
175
|
-
this.resolveReady = resolve;
|
|
176
|
-
this.rejectReady = reject;
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
this.readyTimeout = setTimeout(() => {
|
|
180
|
-
this.fail(new WebSocketExecError(`hwlink terminal ready timeout after ${this.timeoutMs}ms`, 124, {
|
|
181
|
-
partialOutput: this.readyBuffer,
|
|
182
|
-
phase: 'opening',
|
|
183
|
-
}));
|
|
184
|
-
}, this.timeoutMs);
|
|
185
|
-
|
|
186
|
-
try {
|
|
187
|
-
this.mux = new HwlinkWebSocketMultiplexer(this.url, this.source, {
|
|
188
|
-
WebSocketImpl,
|
|
189
|
-
protocol,
|
|
190
|
-
onFrame,
|
|
191
|
-
trace,
|
|
192
|
-
});
|
|
193
|
-
} catch (error) {
|
|
194
|
-
clearTimeout(this.readyTimeout);
|
|
195
|
-
throw new WebSocketExecError(error.message, 2, { cause: error, phase: 'opening' });
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
this.term = new HwlinkTerminalChannel(username);
|
|
199
|
-
this.term.onData((data) => this.handleTerminalData(data));
|
|
200
|
-
this.term.onError((error) => {
|
|
201
|
-
this.fail(new WebSocketExecError(`hwlink terminal error: ${error.message}`, 1, {
|
|
202
|
-
cause: error,
|
|
203
|
-
phase: this.state,
|
|
204
|
-
}));
|
|
205
|
-
});
|
|
206
|
-
this.term.onReady(() => {
|
|
207
|
-
if (Number.isFinite(this.initialCols) && Number.isFinite(this.initialRows)) {
|
|
208
|
-
this.term.resize(this.initialCols, this.initialRows);
|
|
209
|
-
}
|
|
210
|
-
this.sendLine(this.readyCommand);
|
|
211
|
-
});
|
|
212
|
-
this.term.onClose(() => {
|
|
213
|
-
if (this.state !== 'closed') {
|
|
214
|
-
this.fail(new WebSocketExecError('hwlink terminal closed before completion marker', 1, {
|
|
215
|
-
phase: this.state,
|
|
216
|
-
}));
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
this.mux.onClose = () => {
|
|
220
|
-
if (this.state !== 'closed') {
|
|
221
|
-
this.fail(new WebSocketExecError('hwlink websocket closed before completion marker', 1, {
|
|
222
|
-
phase: this.state,
|
|
223
|
-
}));
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
this.mux.onError = (error) => {
|
|
227
|
-
if (this.state !== 'closed') {
|
|
228
|
-
this.fail(new WebSocketExecError(`hwlink websocket error: ${error.message}`, 1, {
|
|
229
|
-
cause: error,
|
|
230
|
-
phase: this.state,
|
|
231
|
-
}));
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
this.term.attach(this.mux);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
ready() {
|
|
239
|
-
return this.readyPromise;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
exec(command, options = {}) {
|
|
243
|
-
const run = () => this.runExec(command, options);
|
|
244
|
-
const result = this.queue.then(run, run);
|
|
245
|
-
this.queue = result.catch(() => {});
|
|
246
|
-
return result;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
resize(cols, rows) {
|
|
250
|
-
this.term.resize(cols, rows);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
close() {
|
|
254
|
-
if (this.state === 'closed') return;
|
|
255
|
-
const wasOpening = this.state === 'opening';
|
|
256
|
-
const pending = this.pending;
|
|
257
|
-
this.state = 'closed';
|
|
258
|
-
clearTimeout(this.readyTimeout);
|
|
259
|
-
this.pending = null;
|
|
260
|
-
|
|
261
|
-
if (wasOpening) {
|
|
262
|
-
this.rejectReady(new WebSocketExecError('hwlink terminal session closed before ready', 1, {
|
|
263
|
-
phase: 'opening',
|
|
264
|
-
}));
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (pending) {
|
|
268
|
-
clearTimeout(pending.timeout);
|
|
269
|
-
pending.reject(new WebSocketExecError('hwlink terminal session closed before completion marker', 1, {
|
|
270
|
-
phase: 'running',
|
|
271
|
-
}));
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
this.term.close();
|
|
275
|
-
this.mux.close();
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
fail(error) {
|
|
279
|
-
if (this.state === 'closed') return;
|
|
280
|
-
|
|
281
|
-
const wasOpening = this.state === 'opening';
|
|
282
|
-
const pending = this.pending;
|
|
283
|
-
this.state = 'closed';
|
|
284
|
-
this.pending = null;
|
|
285
|
-
clearTimeout(this.readyTimeout);
|
|
286
|
-
|
|
287
|
-
if (wasOpening) {
|
|
288
|
-
this.rejectReady(error);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (pending) {
|
|
292
|
-
clearTimeout(pending.timeout);
|
|
293
|
-
pending.reject(error);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
this.term.close();
|
|
297
|
-
this.mux.close();
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
handleTerminalData(data) {
|
|
301
|
-
if (this.state === 'closed') return;
|
|
302
|
-
|
|
303
|
-
const chunk = decoder.decode(data, { stream: true });
|
|
304
|
-
if (this.onData) this.onData(data, chunk);
|
|
305
|
-
|
|
306
|
-
if (this.state === 'opening') {
|
|
307
|
-
this.readyBuffer += chunk;
|
|
308
|
-
this.tryCompleteReady();
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
if (this.pending) {
|
|
313
|
-
this.pending.buffer += chunk;
|
|
314
|
-
this.tryCompletePending();
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
tryCompleteReady() {
|
|
319
|
-
const readyIndex = this.readyBuffer.indexOf(this.readyMarkers.readyMarker);
|
|
320
|
-
if (readyIndex === -1) return;
|
|
321
|
-
|
|
322
|
-
this.inputEchoed = this.readyBuffer.slice(0, readyIndex).includes(this.readyCommand);
|
|
323
|
-
this.readyBuffer = '';
|
|
324
|
-
this.state = 'ready';
|
|
325
|
-
clearTimeout(this.readyTimeout);
|
|
326
|
-
this.resolveReady(this);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
runExec(command, options = {}) {
|
|
330
|
-
if (this.state !== 'ready') {
|
|
331
|
-
return Promise.reject(new WebSocketExecError('hwlink terminal exec session is not ready', 1, {
|
|
332
|
-
phase: this.state,
|
|
333
|
-
}));
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const shellCommand = normalizeCommand(command);
|
|
337
|
-
const timeoutMs = normalizeTimeout(options.timeoutMs === undefined ? this.timeoutMs : options.timeoutMs);
|
|
338
|
-
const markers = buildMarkers();
|
|
339
|
-
const { doneCommand } = buildShellCommands(markers);
|
|
340
|
-
|
|
341
|
-
return new Promise((resolve, reject) => {
|
|
342
|
-
this.pending = {
|
|
343
|
-
buffer: '',
|
|
344
|
-
command: shellCommand,
|
|
345
|
-
doneCommand,
|
|
346
|
-
markers,
|
|
347
|
-
reject,
|
|
348
|
-
resolve,
|
|
349
|
-
timeout: setTimeout(() => {
|
|
350
|
-
this.fail(new WebSocketExecError(`hwlink terminal exec timeout after ${timeoutMs}ms`, 124, {
|
|
351
|
-
partialOutput: this.pending ? this.pending.buffer : '',
|
|
352
|
-
phase: 'running',
|
|
353
|
-
}));
|
|
354
|
-
}, timeoutMs),
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
this.sendLine(shellCommand);
|
|
358
|
-
this.sendLine(doneCommand);
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
tryCompletePending() {
|
|
363
|
-
const pending = this.pending;
|
|
364
|
-
if (!pending) return;
|
|
365
|
-
|
|
366
|
-
const doneMatch = pending.buffer.match(pending.markers.donePattern);
|
|
367
|
-
if (!doneMatch || doneMatch.index === undefined) return;
|
|
368
|
-
|
|
369
|
-
const rawOutput = pending.buffer.slice(0, doneMatch.index);
|
|
370
|
-
const stdout = cleanCommandOutput(rawOutput, {
|
|
371
|
-
inputEchoed: this.inputEchoed,
|
|
372
|
-
command: pending.command,
|
|
373
|
-
doneCommand: pending.doneCommand,
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
clearTimeout(pending.timeout);
|
|
377
|
-
this.pending = null;
|
|
378
|
-
pending.resolve({
|
|
379
|
-
stdout,
|
|
380
|
-
exitCode: Number(doneMatch[1]),
|
|
381
|
-
url: this.url,
|
|
382
|
-
source: this.source,
|
|
383
|
-
username: this.username,
|
|
384
|
-
command: pending.command,
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
sendLine(line) {
|
|
389
|
-
this.term.sendInput(encoder.encode(`${line}\n`));
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
async function connectHwlinkTerminalSession(options = {}) {
|
|
394
|
-
const session = new HwlinkTerminalExecSession(options);
|
|
395
|
-
await session.ready();
|
|
396
|
-
return session;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
async function connectHwlinkInteractiveTerminal(options = {}) {
|
|
400
|
-
const terminal = createHwlinkTerminal(options);
|
|
401
|
-
await terminal.ready;
|
|
402
|
-
return terminal;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
async function executeHwlinkCommand(options = {}) {
|
|
406
|
-
const { command, timeoutMs = DEFAULT_TIMEOUT_MS, ...sessionOptions } = options;
|
|
407
|
-
const shellCommand = normalizeCommand(command);
|
|
408
|
-
const normalizedTimeoutMs = normalizeTimeout(timeoutMs);
|
|
409
|
-
const session = await connectHwlinkTerminalSession({
|
|
410
|
-
...sessionOptions,
|
|
411
|
-
timeoutMs: normalizedTimeoutMs,
|
|
412
|
-
});
|
|
413
|
-
try {
|
|
414
|
-
return await session.exec(shellCommand, { timeoutMs: normalizedTimeoutMs });
|
|
415
|
-
} finally {
|
|
416
|
-
session.close();
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
export {
|
|
421
|
-
HwlinkTerminalExecSession,
|
|
422
|
-
connectHwlinkInteractiveTerminal,
|
|
423
|
-
connectHwlinkTerminalSession,
|
|
424
|
-
createHwlinkTerminal,
|
|
425
|
-
executeHwlinkCommand,
|
|
426
|
-
normalizeSource,
|
|
427
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_TIMEOUT_MS,
|
|
3
|
+
WebSocketExecError,
|
|
4
|
+
buildMarkers,
|
|
5
|
+
buildShellCommands,
|
|
6
|
+
cleanCommandOutput,
|
|
7
|
+
} from './ws-exec-client.js';
|
|
8
|
+
import { HwlinkWebSocketMultiplexer } from './hwlink-multiplexer.js';
|
|
9
|
+
import { HwlinkTerminalChannel } from './hwlink-terminal-channel.js';
|
|
10
|
+
|
|
11
|
+
const decoder = new TextDecoder();
|
|
12
|
+
const encoder = new TextEncoder();
|
|
13
|
+
|
|
14
|
+
function normalizeCommand(command) {
|
|
15
|
+
if (!command || !String(command).trim()) {
|
|
16
|
+
throw new WebSocketExecError('missing command', 2);
|
|
17
|
+
}
|
|
18
|
+
return String(command).trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeTimeout(timeoutMs) {
|
|
22
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
23
|
+
throw new WebSocketExecError('timeoutMs must be a positive number of milliseconds', 2);
|
|
24
|
+
}
|
|
25
|
+
return timeoutMs;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function normalizeSource(source) {
|
|
29
|
+
const numericSource = Number(source);
|
|
30
|
+
if (!Number.isInteger(numericSource) || numericSource < -0x80000000 || numericSource > 0xffffffff) {
|
|
31
|
+
throw new WebSocketExecError('hwlink source must be an int32 or uint32 number', 2);
|
|
32
|
+
}
|
|
33
|
+
return numericSource;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizeUrl(url) {
|
|
37
|
+
if (!url || !String(url).trim()) {
|
|
38
|
+
throw new WebSocketExecError('hwlink url is required', 2);
|
|
39
|
+
}
|
|
40
|
+
return String(url);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createHwlinkTerminal(options = {}) {
|
|
44
|
+
const {
|
|
45
|
+
url,
|
|
46
|
+
source,
|
|
47
|
+
username = 'root',
|
|
48
|
+
WebSocketImpl = globalThis.WebSocket,
|
|
49
|
+
protocol = 'devenv',
|
|
50
|
+
cols,
|
|
51
|
+
rows,
|
|
52
|
+
onFrame,
|
|
53
|
+
onData,
|
|
54
|
+
onError,
|
|
55
|
+
onClose,
|
|
56
|
+
trace = false,
|
|
57
|
+
} = options;
|
|
58
|
+
|
|
59
|
+
const normalizedUrl = normalizeUrl(url);
|
|
60
|
+
const normalizedSource = normalizeSource(source);
|
|
61
|
+
const mux = new HwlinkWebSocketMultiplexer(normalizedUrl, normalizedSource, {
|
|
62
|
+
WebSocketImpl,
|
|
63
|
+
protocol,
|
|
64
|
+
onFrame,
|
|
65
|
+
trace,
|
|
66
|
+
});
|
|
67
|
+
const term = new HwlinkTerminalChannel(username);
|
|
68
|
+
let closed = false;
|
|
69
|
+
let readySettled = false;
|
|
70
|
+
let readyResolve;
|
|
71
|
+
let readyReject;
|
|
72
|
+
|
|
73
|
+
const ready = new Promise((resolve, reject) => {
|
|
74
|
+
readyResolve = resolve;
|
|
75
|
+
readyReject = reject;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
function settleReady(error) {
|
|
79
|
+
if (readySettled) return;
|
|
80
|
+
readySettled = true;
|
|
81
|
+
if (error) readyReject(error);
|
|
82
|
+
else readyResolve(handle);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function handleError(error, prefix) {
|
|
86
|
+
const wrapped = new WebSocketExecError(`${prefix}: ${error.message}`, 1, {
|
|
87
|
+
cause: error,
|
|
88
|
+
phase: readySettled ? 'open' : 'opening',
|
|
89
|
+
});
|
|
90
|
+
settleReady(wrapped);
|
|
91
|
+
if (onError) onError(wrapped);
|
|
92
|
+
close();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function handleClose() {
|
|
96
|
+
settleReady(new WebSocketExecError('hwlink terminal closed before ready', 1, {
|
|
97
|
+
phase: 'opening',
|
|
98
|
+
}));
|
|
99
|
+
if (closed) return;
|
|
100
|
+
closed = true;
|
|
101
|
+
if (onClose) onClose();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
term.onReady(() => {
|
|
105
|
+
if (Number.isFinite(cols) && Number.isFinite(rows)) {
|
|
106
|
+
term.resize(cols, rows);
|
|
107
|
+
}
|
|
108
|
+
settleReady();
|
|
109
|
+
});
|
|
110
|
+
term.onData((data) => {
|
|
111
|
+
if (onData) onData(data);
|
|
112
|
+
});
|
|
113
|
+
term.onError((error) => handleError(error, 'hwlink terminal error'));
|
|
114
|
+
term.onClose(handleClose);
|
|
115
|
+
mux.onError = (error) => handleError(error, 'hwlink websocket error');
|
|
116
|
+
mux.onClose = handleClose;
|
|
117
|
+
|
|
118
|
+
function close() {
|
|
119
|
+
if (closed) return;
|
|
120
|
+
closed = true;
|
|
121
|
+
term.close();
|
|
122
|
+
mux.close();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const handle = {
|
|
126
|
+
url: normalizedUrl,
|
|
127
|
+
source: normalizedSource,
|
|
128
|
+
username,
|
|
129
|
+
mux,
|
|
130
|
+
term,
|
|
131
|
+
ready,
|
|
132
|
+
close,
|
|
133
|
+
resize: (nextCols, nextRows) => term.resize(nextCols, nextRows),
|
|
134
|
+
sendInput: (data) => term.sendInput(data),
|
|
135
|
+
sendText: (text) => term.sendText(text),
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
term.attach(mux);
|
|
139
|
+
return handle;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
class HwlinkTerminalExecSession {
|
|
143
|
+
constructor(options = {}) {
|
|
144
|
+
const {
|
|
145
|
+
url,
|
|
146
|
+
source,
|
|
147
|
+
username = 'root',
|
|
148
|
+
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
149
|
+
WebSocketImpl = globalThis.WebSocket,
|
|
150
|
+
protocol = 'devenv',
|
|
151
|
+
cols,
|
|
152
|
+
rows,
|
|
153
|
+
onFrame,
|
|
154
|
+
onData,
|
|
155
|
+
trace = false,
|
|
156
|
+
} = options;
|
|
157
|
+
|
|
158
|
+
this.url = normalizeUrl(url);
|
|
159
|
+
this.source = normalizeSource(source);
|
|
160
|
+
this.username = username;
|
|
161
|
+
this.timeoutMs = normalizeTimeout(timeoutMs);
|
|
162
|
+
this.onData = onData;
|
|
163
|
+
this.initialCols = cols;
|
|
164
|
+
this.initialRows = rows;
|
|
165
|
+
this.state = 'opening';
|
|
166
|
+
this.readyBuffer = '';
|
|
167
|
+
this.inputEchoed = false;
|
|
168
|
+
this.pending = null;
|
|
169
|
+
this.queue = Promise.resolve();
|
|
170
|
+
this.readyMarkers = buildMarkers();
|
|
171
|
+
const { readyCommand } = buildShellCommands(this.readyMarkers);
|
|
172
|
+
this.readyCommand = readyCommand;
|
|
173
|
+
|
|
174
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
175
|
+
this.resolveReady = resolve;
|
|
176
|
+
this.rejectReady = reject;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
this.readyTimeout = setTimeout(() => {
|
|
180
|
+
this.fail(new WebSocketExecError(`hwlink terminal ready timeout after ${this.timeoutMs}ms`, 124, {
|
|
181
|
+
partialOutput: this.readyBuffer,
|
|
182
|
+
phase: 'opening',
|
|
183
|
+
}));
|
|
184
|
+
}, this.timeoutMs);
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
this.mux = new HwlinkWebSocketMultiplexer(this.url, this.source, {
|
|
188
|
+
WebSocketImpl,
|
|
189
|
+
protocol,
|
|
190
|
+
onFrame,
|
|
191
|
+
trace,
|
|
192
|
+
});
|
|
193
|
+
} catch (error) {
|
|
194
|
+
clearTimeout(this.readyTimeout);
|
|
195
|
+
throw new WebSocketExecError(error.message, 2, { cause: error, phase: 'opening' });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.term = new HwlinkTerminalChannel(username);
|
|
199
|
+
this.term.onData((data) => this.handleTerminalData(data));
|
|
200
|
+
this.term.onError((error) => {
|
|
201
|
+
this.fail(new WebSocketExecError(`hwlink terminal error: ${error.message}`, 1, {
|
|
202
|
+
cause: error,
|
|
203
|
+
phase: this.state,
|
|
204
|
+
}));
|
|
205
|
+
});
|
|
206
|
+
this.term.onReady(() => {
|
|
207
|
+
if (Number.isFinite(this.initialCols) && Number.isFinite(this.initialRows)) {
|
|
208
|
+
this.term.resize(this.initialCols, this.initialRows);
|
|
209
|
+
}
|
|
210
|
+
this.sendLine(this.readyCommand);
|
|
211
|
+
});
|
|
212
|
+
this.term.onClose(() => {
|
|
213
|
+
if (this.state !== 'closed') {
|
|
214
|
+
this.fail(new WebSocketExecError('hwlink terminal closed before completion marker', 1, {
|
|
215
|
+
phase: this.state,
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
this.mux.onClose = () => {
|
|
220
|
+
if (this.state !== 'closed') {
|
|
221
|
+
this.fail(new WebSocketExecError('hwlink websocket closed before completion marker', 1, {
|
|
222
|
+
phase: this.state,
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
this.mux.onError = (error) => {
|
|
227
|
+
if (this.state !== 'closed') {
|
|
228
|
+
this.fail(new WebSocketExecError(`hwlink websocket error: ${error.message}`, 1, {
|
|
229
|
+
cause: error,
|
|
230
|
+
phase: this.state,
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
this.term.attach(this.mux);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
ready() {
|
|
239
|
+
return this.readyPromise;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
exec(command, options = {}) {
|
|
243
|
+
const run = () => this.runExec(command, options);
|
|
244
|
+
const result = this.queue.then(run, run);
|
|
245
|
+
this.queue = result.catch(() => {});
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
resize(cols, rows) {
|
|
250
|
+
this.term.resize(cols, rows);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
close() {
|
|
254
|
+
if (this.state === 'closed') return;
|
|
255
|
+
const wasOpening = this.state === 'opening';
|
|
256
|
+
const pending = this.pending;
|
|
257
|
+
this.state = 'closed';
|
|
258
|
+
clearTimeout(this.readyTimeout);
|
|
259
|
+
this.pending = null;
|
|
260
|
+
|
|
261
|
+
if (wasOpening) {
|
|
262
|
+
this.rejectReady(new WebSocketExecError('hwlink terminal session closed before ready', 1, {
|
|
263
|
+
phase: 'opening',
|
|
264
|
+
}));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (pending) {
|
|
268
|
+
clearTimeout(pending.timeout);
|
|
269
|
+
pending.reject(new WebSocketExecError('hwlink terminal session closed before completion marker', 1, {
|
|
270
|
+
phase: 'running',
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
this.term.close();
|
|
275
|
+
this.mux.close();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
fail(error) {
|
|
279
|
+
if (this.state === 'closed') return;
|
|
280
|
+
|
|
281
|
+
const wasOpening = this.state === 'opening';
|
|
282
|
+
const pending = this.pending;
|
|
283
|
+
this.state = 'closed';
|
|
284
|
+
this.pending = null;
|
|
285
|
+
clearTimeout(this.readyTimeout);
|
|
286
|
+
|
|
287
|
+
if (wasOpening) {
|
|
288
|
+
this.rejectReady(error);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (pending) {
|
|
292
|
+
clearTimeout(pending.timeout);
|
|
293
|
+
pending.reject(error);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
this.term.close();
|
|
297
|
+
this.mux.close();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
handleTerminalData(data) {
|
|
301
|
+
if (this.state === 'closed') return;
|
|
302
|
+
|
|
303
|
+
const chunk = decoder.decode(data, { stream: true });
|
|
304
|
+
if (this.onData) this.onData(data, chunk);
|
|
305
|
+
|
|
306
|
+
if (this.state === 'opening') {
|
|
307
|
+
this.readyBuffer += chunk;
|
|
308
|
+
this.tryCompleteReady();
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (this.pending) {
|
|
313
|
+
this.pending.buffer += chunk;
|
|
314
|
+
this.tryCompletePending();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
tryCompleteReady() {
|
|
319
|
+
const readyIndex = this.readyBuffer.indexOf(this.readyMarkers.readyMarker);
|
|
320
|
+
if (readyIndex === -1) return;
|
|
321
|
+
|
|
322
|
+
this.inputEchoed = this.readyBuffer.slice(0, readyIndex).includes(this.readyCommand);
|
|
323
|
+
this.readyBuffer = '';
|
|
324
|
+
this.state = 'ready';
|
|
325
|
+
clearTimeout(this.readyTimeout);
|
|
326
|
+
this.resolveReady(this);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
runExec(command, options = {}) {
|
|
330
|
+
if (this.state !== 'ready') {
|
|
331
|
+
return Promise.reject(new WebSocketExecError('hwlink terminal exec session is not ready', 1, {
|
|
332
|
+
phase: this.state,
|
|
333
|
+
}));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const shellCommand = normalizeCommand(command);
|
|
337
|
+
const timeoutMs = normalizeTimeout(options.timeoutMs === undefined ? this.timeoutMs : options.timeoutMs);
|
|
338
|
+
const markers = buildMarkers();
|
|
339
|
+
const { doneCommand } = buildShellCommands(markers);
|
|
340
|
+
|
|
341
|
+
return new Promise((resolve, reject) => {
|
|
342
|
+
this.pending = {
|
|
343
|
+
buffer: '',
|
|
344
|
+
command: shellCommand,
|
|
345
|
+
doneCommand,
|
|
346
|
+
markers,
|
|
347
|
+
reject,
|
|
348
|
+
resolve,
|
|
349
|
+
timeout: setTimeout(() => {
|
|
350
|
+
this.fail(new WebSocketExecError(`hwlink terminal exec timeout after ${timeoutMs}ms`, 124, {
|
|
351
|
+
partialOutput: this.pending ? this.pending.buffer : '',
|
|
352
|
+
phase: 'running',
|
|
353
|
+
}));
|
|
354
|
+
}, timeoutMs),
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
this.sendLine(shellCommand);
|
|
358
|
+
this.sendLine(doneCommand);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
tryCompletePending() {
|
|
363
|
+
const pending = this.pending;
|
|
364
|
+
if (!pending) return;
|
|
365
|
+
|
|
366
|
+
const doneMatch = pending.buffer.match(pending.markers.donePattern);
|
|
367
|
+
if (!doneMatch || doneMatch.index === undefined) return;
|
|
368
|
+
|
|
369
|
+
const rawOutput = pending.buffer.slice(0, doneMatch.index);
|
|
370
|
+
const stdout = cleanCommandOutput(rawOutput, {
|
|
371
|
+
inputEchoed: this.inputEchoed,
|
|
372
|
+
command: pending.command,
|
|
373
|
+
doneCommand: pending.doneCommand,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
clearTimeout(pending.timeout);
|
|
377
|
+
this.pending = null;
|
|
378
|
+
pending.resolve({
|
|
379
|
+
stdout,
|
|
380
|
+
exitCode: Number(doneMatch[1]),
|
|
381
|
+
url: this.url,
|
|
382
|
+
source: this.source,
|
|
383
|
+
username: this.username,
|
|
384
|
+
command: pending.command,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
sendLine(line) {
|
|
389
|
+
this.term.sendInput(encoder.encode(`${line}\n`));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function connectHwlinkTerminalSession(options = {}) {
|
|
394
|
+
const session = new HwlinkTerminalExecSession(options);
|
|
395
|
+
await session.ready();
|
|
396
|
+
return session;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async function connectHwlinkInteractiveTerminal(options = {}) {
|
|
400
|
+
const terminal = createHwlinkTerminal(options);
|
|
401
|
+
await terminal.ready;
|
|
402
|
+
return terminal;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async function executeHwlinkCommand(options = {}) {
|
|
406
|
+
const { command, timeoutMs = DEFAULT_TIMEOUT_MS, ...sessionOptions } = options;
|
|
407
|
+
const shellCommand = normalizeCommand(command);
|
|
408
|
+
const normalizedTimeoutMs = normalizeTimeout(timeoutMs);
|
|
409
|
+
const session = await connectHwlinkTerminalSession({
|
|
410
|
+
...sessionOptions,
|
|
411
|
+
timeoutMs: normalizedTimeoutMs,
|
|
412
|
+
});
|
|
413
|
+
try {
|
|
414
|
+
return await session.exec(shellCommand, { timeoutMs: normalizedTimeoutMs });
|
|
415
|
+
} finally {
|
|
416
|
+
session.close();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export {
|
|
421
|
+
HwlinkTerminalExecSession,
|
|
422
|
+
connectHwlinkInteractiveTerminal,
|
|
423
|
+
connectHwlinkTerminalSession,
|
|
424
|
+
createHwlinkTerminal,
|
|
425
|
+
executeHwlinkCommand,
|
|
426
|
+
normalizeSource,
|
|
427
|
+
};
|