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.
Files changed (96) hide show
  1. package/.agents/plugins/marketplace.json +18 -18
  2. package/README.md +81 -51
  3. package/README.zh-CN.md +81 -49
  4. package/bin/setup.cjs +5 -3
  5. package/integrations/opencode/opencode.json +1 -4
  6. package/package.json +61 -44
  7. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -17
  8. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +4 -17
  9. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -17
  10. package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
  11. package/plugins/huaweicloud-core/.mcp.json +2 -5
  12. package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -18
  13. package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +32 -9
  14. package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
  15. package/plugins/huaweicloud-core/safety/policy.json +75 -99
  16. package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +41 -13
  17. package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +34 -34
  18. package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +18 -18
  19. package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +19 -19
  20. package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +34 -32
  21. package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +30 -30
  22. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +13 -12
  23. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +64 -17
  24. package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +20 -20
  25. package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +31 -31
  26. package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +21 -20
  27. package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +39 -30
  28. package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +8 -1
  29. package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +6 -0
  30. package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +39 -36
  31. package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +15 -4
  32. package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +11 -9
  33. package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +13 -13
  34. package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +40 -39
  35. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +12 -10
  36. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +21 -20
  37. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +19 -19
  38. package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +20 -20
  39. package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +27 -23
  40. package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +27 -27
  41. package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +65 -39
  42. package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +9 -9
  43. package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +25 -25
  44. package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +45 -44
  45. package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +19 -19
  46. package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +12 -7
  47. package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +13 -12
  48. package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +4 -4
  49. package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +63 -58
  50. package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +127 -42
  51. package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +26 -26
  52. package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +39 -39
  53. package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +13 -12
  54. package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +20 -19
  55. package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +15 -15
  56. package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +18 -18
  57. package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +13 -13
  58. package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +3 -3
  59. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +17 -1
  60. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +3 -3
  61. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +27 -1
  62. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +3 -3
  63. package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +56 -53
  64. package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +3 -3
  65. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +6 -6
  66. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +15 -1
  67. package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +0 -1
  68. package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +3 -3
  69. package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +33 -33
  70. package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +3 -3
  71. package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +108 -5
  72. package/plugins/huaweicloud-core/src/auth/credentials.mjs +77 -4
  73. package/plugins/huaweicloud-core/src/auth/service.mjs +1 -1
  74. package/plugins/huaweicloud-core/src/data/icons-manifest.v1.json +3517 -0
  75. package/plugins/huaweicloud-core/src/hcloud-cli.mjs +25 -7
  76. package/plugins/huaweicloud-core/src/icon-library.mjs +171 -0
  77. package/plugins/huaweicloud-core/src/mcp-server.mjs +30 -3
  78. package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +20 -102
  79. package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +11 -4
  80. package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +4 -1
  81. package/plugins/huaweicloud-core/src/safety-policy.mjs +122 -68
  82. package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +9 -9
  83. package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +24 -26
  84. package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
  85. package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +510 -10
  86. package/plugins/huaweicloud-core/src/search-market.mjs +46 -12
  87. package/plugins/huaweicloud-core/src/setup-cli.mjs +1251 -181
  88. package/plugins/huaweicloud-core/src/tools.mjs +600 -122
  89. package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +72 -38
  90. package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +10 -5
  91. package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +12 -15
  92. package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +12 -16
  93. package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +46 -31
  94. package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +267 -0
  95. package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
  96. package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +9 -5
@@ -1,3 +1,12 @@
1
+ /*
2
+ * This file contains code derived from hwlink.
3
+ *
4
+ * Source:
5
+ * https://gitcode.com/huawei-developers/hwlink
6
+ *
7
+ * Licensed under the ISC License.
8
+ */
9
+
1
10
  import {
2
11
  DEFAULT_TIMEOUT_MS,
3
12
  WebSocketExecError,
@@ -93,9 +102,11 @@ function createHwlinkTerminal(options = {}) {
93
102
  }
94
103
 
95
104
  function handleClose() {
96
- settleReady(new WebSocketExecError('hwlink terminal closed before ready', 1, {
97
- phase: 'opening',
98
- }));
105
+ settleReady(
106
+ new WebSocketExecError('hwlink terminal closed before ready', 1, {
107
+ phase: 'opening',
108
+ }),
109
+ );
99
110
  if (closed) return;
100
111
  closed = true;
101
112
  if (onClose) onClose();
@@ -177,10 +188,12 @@ class HwlinkTerminalExecSession {
177
188
  });
178
189
 
179
190
  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
- }));
191
+ this.fail(
192
+ new WebSocketExecError(`hwlink terminal ready timeout after ${this.timeoutMs}ms`, 124, {
193
+ partialOutput: this.readyBuffer,
194
+ phase: 'opening',
195
+ }),
196
+ );
184
197
  }, this.timeoutMs);
185
198
 
186
199
  try {
@@ -198,10 +211,12 @@ class HwlinkTerminalExecSession {
198
211
  this.term = new HwlinkTerminalChannel(username);
199
212
  this.term.onData((data) => this.handleTerminalData(data));
200
213
  this.term.onError((error) => {
201
- this.fail(new WebSocketExecError(`hwlink terminal error: ${error.message}`, 1, {
202
- cause: error,
203
- phase: this.state,
204
- }));
214
+ this.fail(
215
+ new WebSocketExecError(`hwlink terminal error: ${error.message}`, 1, {
216
+ cause: error,
217
+ phase: this.state,
218
+ }),
219
+ );
205
220
  });
206
221
  this.term.onReady(() => {
207
222
  if (Number.isFinite(this.initialCols) && Number.isFinite(this.initialRows)) {
@@ -211,24 +226,30 @@ class HwlinkTerminalExecSession {
211
226
  });
212
227
  this.term.onClose(() => {
213
228
  if (this.state !== 'closed') {
214
- this.fail(new WebSocketExecError('hwlink terminal closed before completion marker', 1, {
215
- phase: this.state,
216
- }));
229
+ this.fail(
230
+ new WebSocketExecError('hwlink terminal closed before completion marker', 1, {
231
+ phase: this.state,
232
+ }),
233
+ );
217
234
  }
218
235
  });
219
236
  this.mux.onClose = () => {
220
237
  if (this.state !== 'closed') {
221
- this.fail(new WebSocketExecError('hwlink websocket closed before completion marker', 1, {
222
- phase: this.state,
223
- }));
238
+ this.fail(
239
+ new WebSocketExecError('hwlink websocket closed before completion marker', 1, {
240
+ phase: this.state,
241
+ }),
242
+ );
224
243
  }
225
244
  };
226
245
  this.mux.onError = (error) => {
227
246
  if (this.state !== 'closed') {
228
- this.fail(new WebSocketExecError(`hwlink websocket error: ${error.message}`, 1, {
229
- cause: error,
230
- phase: this.state,
231
- }));
247
+ this.fail(
248
+ new WebSocketExecError(`hwlink websocket error: ${error.message}`, 1, {
249
+ cause: error,
250
+ phase: this.state,
251
+ }),
252
+ );
232
253
  }
233
254
  };
234
255
 
@@ -259,16 +280,20 @@ class HwlinkTerminalExecSession {
259
280
  this.pending = null;
260
281
 
261
282
  if (wasOpening) {
262
- this.rejectReady(new WebSocketExecError('hwlink terminal session closed before ready', 1, {
263
- phase: 'opening',
264
- }));
283
+ this.rejectReady(
284
+ new WebSocketExecError('hwlink terminal session closed before ready', 1, {
285
+ phase: 'opening',
286
+ }),
287
+ );
265
288
  }
266
289
 
267
290
  if (pending) {
268
291
  clearTimeout(pending.timeout);
269
- pending.reject(new WebSocketExecError('hwlink terminal session closed before completion marker', 1, {
270
- phase: 'running',
271
- }));
292
+ pending.reject(
293
+ new WebSocketExecError('hwlink terminal session closed before completion marker', 1, {
294
+ phase: 'running',
295
+ }),
296
+ );
272
297
  }
273
298
 
274
299
  this.term.close();
@@ -328,9 +353,11 @@ class HwlinkTerminalExecSession {
328
353
 
329
354
  runExec(command, options = {}) {
330
355
  if (this.state !== 'ready') {
331
- return Promise.reject(new WebSocketExecError('hwlink terminal exec session is not ready', 1, {
332
- phase: this.state,
333
- }));
356
+ return Promise.reject(
357
+ new WebSocketExecError('hwlink terminal exec session is not ready', 1, {
358
+ phase: this.state,
359
+ }),
360
+ );
334
361
  }
335
362
 
336
363
  const shellCommand = normalizeCommand(command);
@@ -347,10 +374,12 @@ class HwlinkTerminalExecSession {
347
374
  reject,
348
375
  resolve,
349
376
  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
- }));
377
+ this.fail(
378
+ new WebSocketExecError(`hwlink terminal exec timeout after ${timeoutMs}ms`, 124, {
379
+ partialOutput: this.pending ? this.pending.buffer : '',
380
+ phase: 'running',
381
+ }),
382
+ );
354
383
  }, timeoutMs),
355
384
  };
356
385
 
@@ -363,10 +392,15 @@ class HwlinkTerminalExecSession {
363
392
  const pending = this.pending;
364
393
  if (!pending) return;
365
394
 
366
- const doneMatch = pending.buffer.match(pending.markers.donePattern);
367
- if (!doneMatch || doneMatch.index === undefined) return;
395
+ const doneMarker = pending.markers.doneMarker;
396
+ const markerIndex = pending.buffer.lastIndexOf(doneMarker);
397
+ if (markerIndex === -1) return;
398
+
399
+ const afterMarker = pending.buffer.slice(markerIndex + doneMarker.length);
400
+ const exitMatch = afterMarker.match(/^(\d+)/);
401
+ if (!exitMatch) return;
368
402
 
369
- const rawOutput = pending.buffer.slice(0, doneMatch.index);
403
+ const rawOutput = pending.buffer.slice(0, markerIndex);
370
404
  const stdout = cleanCommandOutput(rawOutput, {
371
405
  inputEchoed: this.inputEchoed,
372
406
  command: pending.command,
@@ -377,7 +411,7 @@ class HwlinkTerminalExecSession {
377
411
  this.pending = null;
378
412
  pending.resolve({
379
413
  stdout,
380
- exitCode: Number(doneMatch[1]),
414
+ exitCode: Number(exitMatch[1]),
381
415
  url: this.url,
382
416
  source: this.source,
383
417
  username: this.username,
@@ -1,3 +1,12 @@
1
+ /*
2
+ * This file contains code derived from hwlink.
3
+ *
4
+ * Source:
5
+ * https://gitcode.com/huawei-developers/hwlink
6
+ *
7
+ * Licensed under the ISC License.
8
+ */
9
+
1
10
  const DEFAULT_HIGH_WATERMARK = 8 * 1024 * 1024;
2
11
  const DEFAULT_LOW_WATERMARK = 2 * 1024 * 1024;
3
12
 
@@ -125,8 +134,4 @@ class FairQueue {
125
134
  }
126
135
  }
127
136
 
128
- export {
129
- DEFAULT_HIGH_WATERMARK,
130
- DEFAULT_LOW_WATERMARK,
131
- FairQueue,
132
- };
137
+ export { DEFAULT_HIGH_WATERMARK, DEFAULT_LOW_WATERMARK, FairQueue };
@@ -1,3 +1,12 @@
1
+ /*
2
+ * This file contains code derived from hwlink.
3
+ *
4
+ * Source:
5
+ * https://gitcode.com/huawei-developers/hwlink
6
+ *
7
+ * Licensed under the ISC License.
8
+ */
9
+
1
10
  import { Buffer } from 'node:buffer';
2
11
 
3
12
  import { FairQueue } from './hwlink-fair-queue.js';
@@ -81,12 +90,7 @@ function sendBinary(ws, data) {
81
90
 
82
91
  class HwlinkWebSocketMultiplexer {
83
92
  constructor(url, source, options = {}) {
84
- const {
85
- WebSocketImpl = globalThis.WebSocket,
86
- protocol = 'devenv',
87
- onFrame,
88
- trace = false,
89
- } = options;
93
+ const { WebSocketImpl = globalThis.WebSocket, protocol = 'devenv', onFrame, trace = false } = options;
90
94
 
91
95
  if (typeof WebSocketImpl !== 'function') {
92
96
  throw new Error('global WebSocket is unavailable; use Node.js 22+ or pass WebSocketImpl');
@@ -122,9 +126,7 @@ class HwlinkWebSocketMultiplexer {
122
126
  });
123
127
 
124
128
  addWsListener(this.ws, 'error', (eventOrError) => {
125
- const error = eventOrError instanceof Error
126
- ? eventOrError
127
- : new Error('hwlink websocket error');
129
+ const error = eventOrError instanceof Error ? eventOrError : new Error('hwlink websocket error');
128
130
  this.handleError(error);
129
131
  });
130
132
 
@@ -219,9 +221,4 @@ class HwlinkWebSocketMultiplexer {
219
221
  }
220
222
  }
221
223
 
222
- export {
223
- HwlinkWebSocketMultiplexer,
224
- addWsListener,
225
- closeQuietly,
226
- eventDataToUint8Array,
227
- };
224
+ export { HwlinkWebSocketMultiplexer, addWsListener, closeQuietly, eventDataToUint8Array };
@@ -1,3 +1,12 @@
1
+ /*
2
+ * This file contains code derived from hwlink.
3
+ *
4
+ * Source:
5
+ * https://gitcode.com/huawei-developers/hwlink
6
+ *
7
+ * Licensed under the ISC License.
8
+ */
9
+
1
10
  const FIXED_HEADER_LEN = 20;
2
11
  const MAX_SEND_CHUNK_SIZE = 8 * 1024;
3
12
 
@@ -42,7 +51,7 @@ function isOpTcpTunnelData(op) {
42
51
  }
43
52
 
44
53
  function isOpFailed(op) {
45
- return (op >> 24) !== 0;
54
+ return op >> 24 !== 0;
46
55
  }
47
56
 
48
57
  function isOpCreateTcpTunnel(op) {
@@ -70,12 +79,7 @@ function hex2(n) {
70
79
  }
71
80
 
72
81
  function operationToString(op) {
73
- return [
74
- hex2((op >> 24) & 0xff),
75
- hex2((op >> 16) & 0xff),
76
- hex2((op >> 8) & 0xff),
77
- hex2(op & 0xff),
78
- ].join(' ');
82
+ return [hex2((op >> 24) & 0xff), hex2((op >> 16) & 0xff), hex2((op >> 8) & 0xff), hex2(op & 0xff)].join(' ');
79
83
  }
80
84
 
81
85
  function formatPacketOneLine(packet) {
@@ -107,15 +111,7 @@ function toUint8Array(bytes) {
107
111
  }
108
112
 
109
113
  function createPacket(opts) {
110
- const {
111
- operation,
112
- reserve = 0,
113
- srcPort = 0,
114
- dstPort = 0,
115
- identifier = 0,
116
- source = 0,
117
- payload = null,
118
- } = opts;
114
+ const { operation, reserve = 0, srcPort = 0, dstPort = 0, identifier = 0, source = 0, payload = null } = opts;
119
115
 
120
116
  const payloadLen = payload ? payload.length : 0;
121
117
  const total = FIXED_HEADER_LEN + payloadLen;
@@ -1,3 +1,12 @@
1
+ /*
2
+ * This file contains code derived from hwlink.
3
+ *
4
+ * Source:
5
+ * https://gitcode.com/huawei-developers/hwlink
6
+ *
7
+ * Licensed under the ISC License.
8
+ */
9
+
1
10
  import {
2
11
  OpCode,
3
12
  ReserveSource,
@@ -54,13 +63,15 @@ class HwlinkTerminalChannel {
54
63
  onopen() {
55
64
  if (this.closed || this.opened) return;
56
65
  this.opened = true;
57
- this.sendRaw(createPacket({
58
- operation: OpCode.OpNewCmdTerminal,
59
- reserve: ReserveSource.Web,
60
- identifier: this.identifier,
61
- source: this.mux ? this.mux.source : 0,
62
- payload: encoder.encode(this.username),
63
- }));
66
+ this.sendRaw(
67
+ createPacket({
68
+ operation: OpCode.OpNewCmdTerminal,
69
+ reserve: ReserveSource.Web,
70
+ identifier: this.identifier,
71
+ source: this.mux ? this.mux.source : 0,
72
+ payload: encoder.encode(this.username),
73
+ }),
74
+ );
64
75
  if (this.onReadyCb) this.onReadyCb();
65
76
  }
66
77
 
@@ -73,13 +84,15 @@ class HwlinkTerminalChannel {
73
84
  }
74
85
 
75
86
  if (isSubStreamPing(packet.operation)) {
76
- this.sendRaw(createPacket({
77
- operation: OpCode.OpCmdTerminalData | OpCode.OpSubStreamPong,
78
- reserve: ReserveSource.Web,
79
- identifier: this.identifier,
80
- source: this.mux ? this.mux.source : 0,
81
- payload: new Uint8Array(0),
82
- }));
87
+ this.sendRaw(
88
+ createPacket({
89
+ operation: OpCode.OpCmdTerminalData | OpCode.OpSubStreamPong,
90
+ reserve: ReserveSource.Web,
91
+ identifier: this.identifier,
92
+ source: this.mux ? this.mux.source : 0,
93
+ payload: new Uint8Array(0),
94
+ }),
95
+ );
83
96
  return;
84
97
  }
85
98
 
@@ -105,13 +118,15 @@ class HwlinkTerminalChannel {
105
118
  }
106
119
 
107
120
  sendTerminalData(payload) {
108
- this.sendRaw(createPacket({
109
- operation: OpCode.OpCmdTerminalData | OpCode.OpSubStreamPong,
110
- reserve: ReserveSource.Web,
111
- identifier: this.identifier,
112
- source: this.mux ? this.mux.source : 0,
113
- payload,
114
- }));
121
+ this.sendRaw(
122
+ createPacket({
123
+ operation: OpCode.OpCmdTerminalData | OpCode.OpSubStreamPong,
124
+ reserve: ReserveSource.Web,
125
+ identifier: this.identifier,
126
+ source: this.mux ? this.mux.source : 0,
127
+ payload,
128
+ }),
129
+ );
115
130
  }
116
131
 
117
132
  sendText(text) {
@@ -124,13 +139,15 @@ class HwlinkTerminalChannel {
124
139
  const view = new DataView(payload.buffer);
125
140
  view.setUint16(0, rows, false);
126
141
  view.setUint16(2, cols, false);
127
- this.sendRaw(createPacket({
128
- operation: OpCode.OpCmdTerminalResize,
129
- reserve: ReserveSource.Web,
130
- identifier: this.identifier,
131
- source: this.mux ? this.mux.source : 0,
132
- payload,
133
- }));
142
+ this.sendRaw(
143
+ createPacket({
144
+ operation: OpCode.OpCmdTerminalResize,
145
+ reserve: ReserveSource.Web,
146
+ identifier: this.identifier,
147
+ source: this.mux ? this.mux.source : 0,
148
+ payload,
149
+ }),
150
+ );
134
151
  }
135
152
 
136
153
  sendRaw(data) {
@@ -153,6 +170,4 @@ class HwlinkTerminalChannel {
153
170
  }
154
171
  }
155
172
 
156
- export {
157
- HwlinkTerminalChannel,
158
- };
173
+ export { HwlinkTerminalChannel };
@@ -0,0 +1,267 @@
1
+ /*
2
+ * This file contains code derived from hwlink.
3
+ *
4
+ * Source:
5
+ * https://gitcode.com/huawei-developers/hwlink
6
+ *
7
+ * Licensed under the ISC License.
8
+ */
9
+
10
+ import { createServer } from 'node:net';
11
+ import {
12
+ OpCode,
13
+ ReserveSource,
14
+ FIXED_HEADER_LEN,
15
+ MAX_SEND_CHUNK_SIZE,
16
+ createPacket,
17
+ isOpTunnelSuccess,
18
+ isOpTcpTunnelData,
19
+ isOpFailed,
20
+ isSubStreamPing,
21
+ nextIdentifier,
22
+ } from './hwlink-packet.js';
23
+
24
+ const MAX_TUNNEL_PAYLOAD_SIZE = MAX_SEND_CHUNK_SIZE - FIXED_HEADER_LEN;
25
+
26
+ class HwlinkTunnelChannel {
27
+ constructor({ localPort = 0, remotePort, onReady, onClose, onError }) {
28
+ this.localPort = localPort;
29
+ this.remotePort = remotePort;
30
+ this.identifier = nextIdentifier();
31
+ this.mux = null;
32
+ this.closed = false;
33
+ this.opened = false;
34
+
35
+ this.subConnections = new Map();
36
+ this.localServer = null;
37
+ this.nextSubId = 1;
38
+
39
+ this.onReadyCb = onReady || null;
40
+ this.onCloseCb = onClose || null;
41
+ this.onErrorCb = onError || null;
42
+
43
+ this._readyResolve = null;
44
+ this._readyReject = null;
45
+ this.ready = new Promise((resolve, reject) => {
46
+ this._readyResolve = resolve;
47
+ this._readyReject = reject;
48
+ });
49
+ }
50
+
51
+ attach(mux) {
52
+ this.mux = mux;
53
+ mux.register(this);
54
+ }
55
+
56
+ onopen() {
57
+ if (this.closed || this.opened) return;
58
+ this.opened = true;
59
+ this.startLocalServer();
60
+ }
61
+
62
+ onmessage(packet) {
63
+ if (this.closed) return;
64
+
65
+ if (isOpFailed(packet.operation)) {
66
+ const subConn = this.subConnections.get(packet.identifier);
67
+ if (subConn && subConn.socket && !subConn.socket.destroyed) {
68
+ try {
69
+ subConn.socket.end();
70
+ } catch {}
71
+ }
72
+ this.subConnections.delete(packet.identifier);
73
+ this._unregisterSubId(packet.identifier);
74
+ return;
75
+ }
76
+
77
+ if (isOpTunnelSuccess(packet.operation)) {
78
+ const subConn = this.subConnections.get(packet.identifier);
79
+ if (subConn && !subConn.ready) {
80
+ subConn.ready = true;
81
+ if (subConn.readyResolve) subConn.readyResolve();
82
+ }
83
+ if (packet.identifier === this.identifier && this._readyResolve) {
84
+ this._readyResolve();
85
+ this._readyResolve = null;
86
+ this._readyReject = null;
87
+ }
88
+ return;
89
+ }
90
+
91
+ if (isOpTcpTunnelData(packet.operation)) {
92
+ const subConn = this.subConnections.get(packet.identifier);
93
+ if (subConn && subConn.socket && !subConn.socket.destroyed) {
94
+ subConn.socket.write(packet.data);
95
+ }
96
+ return;
97
+ }
98
+
99
+ if (isSubStreamPing(packet.operation)) {
100
+ this.sendRaw(
101
+ createPacket({
102
+ operation: OpCode.OpTcpTunnelData | OpCode.OpSubStreamPong,
103
+ reserve: ReserveSource.Web,
104
+ identifier: packet.identifier,
105
+ source: this.mux ? this.mux.source : 0,
106
+ payload: new Uint8Array(0),
107
+ }),
108
+ );
109
+ return;
110
+ }
111
+
112
+ if (packet.operation === OpCode.OpDisconnect) {
113
+ const subConn = this.subConnections.get(packet.identifier);
114
+ if (subConn && subConn.socket && !subConn.socket.destroyed) {
115
+ subConn.socket.end();
116
+ }
117
+ this.subConnections.delete(packet.identifier);
118
+ this._unregisterSubId(packet.identifier);
119
+ return;
120
+ }
121
+ }
122
+
123
+ onerror(error) {
124
+ if (this.onErrorCb) this.onErrorCb(error);
125
+ if (this._readyReject) {
126
+ this._readyReject(error);
127
+ this._readyResolve = null;
128
+ this._readyReject = null;
129
+ }
130
+ }
131
+
132
+ onclose() {
133
+ this.close();
134
+ }
135
+
136
+ startLocalServer() {
137
+ this.localServer = createServer((socket) => {
138
+ this.onIncomingConnection(socket);
139
+ });
140
+
141
+ this.localServer.listen(this.localPort, '127.0.0.1', () => {
142
+ this.localPort = this.localServer.address().port;
143
+ if (this.onReadyCb) this.onReadyCb();
144
+ if (this._readyResolve) {
145
+ this._readyResolve();
146
+ this._readyResolve = null;
147
+ this._readyReject = null;
148
+ }
149
+ });
150
+
151
+ this.localServer.on('error', (err) => {
152
+ if (this.onErrorCb) this.onErrorCb(err);
153
+ if (this._readyReject) {
154
+ this._readyReject(err);
155
+ this._readyResolve = null;
156
+ this._readyReject = null;
157
+ }
158
+ });
159
+ }
160
+
161
+ _registerSubId(subId) {
162
+ if (this.mux && !this.mux.channels.has(subId)) {
163
+ this.mux.channels.set(subId, this);
164
+ this.mux.queue.register({ identifier: subId });
165
+ }
166
+ }
167
+
168
+ _unregisterSubId(subId) {
169
+ if (this.mux) {
170
+ this.mux.channels.delete(subId);
171
+ this.mux.queue.unregister({ identifier: subId });
172
+ }
173
+ }
174
+
175
+ onIncomingConnection(socket) {
176
+ const subId = this.nextSubId;
177
+ this.nextSubId = ((this.nextSubId + 1) & 0xffffffff) >>> 0;
178
+
179
+ let readyResolve;
180
+ const readyPromise = new Promise((resolve) => {
181
+ readyResolve = resolve;
182
+ });
183
+ const subConn = {
184
+ identifier: subId,
185
+ socket,
186
+ ready: false,
187
+ readyResolve,
188
+ readyPromise,
189
+ };
190
+
191
+ this.subConnections.set(subId, subConn);
192
+ this._registerSubId(subId);
193
+
194
+ this.sendRaw(
195
+ createPacket({
196
+ operation: OpCode.OpCreateTcpTunnel,
197
+ reserve: ReserveSource.Web,
198
+ srcPort: this.localPort,
199
+ dstPort: this.remotePort,
200
+ identifier: subId,
201
+ source: this.mux ? this.mux.source : 0,
202
+ payload: new Uint8Array(0),
203
+ }),
204
+ );
205
+
206
+ socket.on('data', (data) => {
207
+ if (this.closed || socket.destroyed) return;
208
+ for (let offset = 0; offset < data.length; offset += MAX_TUNNEL_PAYLOAD_SIZE) {
209
+ const payload = data.subarray(offset, offset + MAX_TUNNEL_PAYLOAD_SIZE);
210
+ this.sendRaw(
211
+ createPacket({
212
+ operation: OpCode.OpTcpTunnelData,
213
+ reserve: ReserveSource.Web,
214
+ srcPort: this.localPort,
215
+ dstPort: this.remotePort,
216
+ identifier: subId,
217
+ source: this.mux ? this.mux.source : 0,
218
+ payload,
219
+ }),
220
+ );
221
+ }
222
+ });
223
+
224
+ socket.on('close', () => {
225
+ this._unregisterSubId(subId);
226
+ this.subConnections.delete(subId);
227
+ });
228
+
229
+ socket.on('error', () => {
230
+ this._unregisterSubId(subId);
231
+ this.subConnections.delete(subId);
232
+ });
233
+ }
234
+
235
+ destroySubConnection(subConn, error) {
236
+ if (subConn.socket && !subConn.socket.destroyed) {
237
+ if (error) subConn.socket.destroy(error);
238
+ else subConn.socket.destroy();
239
+ }
240
+ this._unregisterSubId(subConn.identifier);
241
+ this.subConnections.delete(subConn.identifier);
242
+ }
243
+
244
+ sendRaw(data) {
245
+ if (this.mux) this.mux.sendFairly(this, data);
246
+ }
247
+
248
+ close() {
249
+ if (this.closed) return;
250
+ this.closed = true;
251
+
252
+ for (const subConn of this.subConnections.values()) {
253
+ this.destroySubConnection(subConn);
254
+ }
255
+ this.subConnections.clear();
256
+
257
+ if (this.localServer) {
258
+ this.localServer.close();
259
+ this.localServer = null;
260
+ }
261
+
262
+ if (this.mux) this.mux.unregister(this);
263
+ if (this.onCloseCb) this.onCloseCb();
264
+ }
265
+ }
266
+
267
+ export { HwlinkTunnelChannel };