huaweicloud-devkit 1.1.2-next.0 → 1.1.2-next.2

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 (125) hide show
  1. package/.agents/plugins/marketplace.json +18 -18
  2. package/README.md +182 -38
  3. package/README.zh-CN.md +180 -36
  4. package/bin/dsh-postinstall.cjs +77 -0
  5. package/bin/setup.cjs +7 -4
  6. package/cordis.patch.yml +13 -0
  7. package/integrations/atomcode/hooks/huaweicloud-telemetry.js +181 -0
  8. package/integrations/atomcode/hooks.json +9 -0
  9. package/integrations/dsh/hook-plugin.mjs +148 -0
  10. package/integrations/hermes/hooks/huaweicloud-telemetry.py +238 -0
  11. package/integrations/hermes/manifest.yaml +43 -0
  12. package/integrations/opencode/hooks/skill-tracker.js +115 -0
  13. package/integrations/opencode/opencode.json +1 -4
  14. package/integrations/workbuddy/hooks/telemetry-tracker.py +226 -0
  15. package/package.json +74 -43
  16. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -17
  17. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +23 -33
  18. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -17
  19. package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
  20. package/plugins/huaweicloud-core/.mcp.json +2 -5
  21. package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -18
  22. package/plugins/huaweicloud-core/assets/icon.png +0 -0
  23. package/plugins/huaweicloud-core/assets/logo-dark.png +0 -0
  24. package/plugins/huaweicloud-core/assets/logo.png +0 -0
  25. package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +104 -28
  26. package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
  27. package/plugins/huaweicloud-core/safety/policy.json +75 -99
  28. package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +196 -35
  29. package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +38 -34
  30. package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +21 -20
  31. package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +19 -19
  32. package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +38 -32
  33. package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +30 -30
  34. package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +20 -20
  35. package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +31 -31
  36. package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +21 -20
  37. package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +40 -31
  38. package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +8 -1
  39. package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +6 -0
  40. package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +45 -37
  41. package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +57 -6
  42. package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +11 -9
  43. package/plugins/huaweicloud-core/skills/huawei-ecs/references/hc-activity.md +27 -0
  44. package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +13 -13
  45. package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +44 -39
  46. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +12 -10
  47. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +21 -20
  48. package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +19 -19
  49. package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +20 -20
  50. package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +27 -23
  51. package/plugins/huaweicloud-core/skills/huawei-iac/SKILL.md +93 -0
  52. package/plugins/huaweicloud-core/skills/huawei-iac/references/architectures.md +83 -0
  53. package/plugins/huaweicloud-core/skills/huawei-iac/references/resource-catalog.md +107 -0
  54. package/plugins/huaweicloud-core/skills/huawei-iac/references/state-file.md +104 -0
  55. package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +30 -30
  56. package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +65 -39
  57. package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +9 -9
  58. package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +33 -25
  59. package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +46 -45
  60. package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +19 -19
  61. package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +12 -7
  62. package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +13 -12
  63. package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +4 -4
  64. package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +71 -61
  65. package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +776 -44
  66. package/plugins/huaweicloud-core/skills/huawei-sandbox/references/framework-commands.md +84 -0
  67. package/plugins/huaweicloud-core/skills/huawei-sandbox/references/nginx-templates.md +100 -0
  68. package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +36 -29
  69. package/plugins/huaweicloud-core/skills/huawei-voucher/SKILL.md +57 -0
  70. package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +39 -39
  71. package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +22 -15
  72. package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +20 -19
  73. package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +15 -15
  74. package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +23 -20
  75. package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +13 -13
  76. package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +3 -3
  77. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +21 -2
  78. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +3 -3
  79. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +30 -4
  80. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +3 -3
  81. package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +68 -54
  82. package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +3 -3
  83. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +6 -6
  84. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +15 -1
  85. package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +0 -1
  86. package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +3 -3
  87. package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +36 -34
  88. package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +3 -3
  89. package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +142 -5
  90. package/plugins/huaweicloud-core/src/auth/credentials.mjs +217 -5
  91. package/plugins/huaweicloud-core/src/auth/reconcile.mjs +174 -0
  92. package/plugins/huaweicloud-core/src/auth/service.mjs +57 -2
  93. package/plugins/huaweicloud-core/src/data/icons-manifest.v1.json +3517 -0
  94. package/plugins/huaweicloud-core/src/detect-framework.mjs +366 -0
  95. package/plugins/huaweicloud-core/src/hcloud-cli.mjs +203 -16
  96. package/plugins/huaweicloud-core/src/icon-library.mjs +176 -0
  97. package/plugins/huaweicloud-core/src/mcp-protocol.mjs +88 -0
  98. package/plugins/huaweicloud-core/src/mcp-server-remote.mjs +107 -0
  99. package/plugins/huaweicloud-core/src/mcp-server.mjs +154 -99
  100. package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +20 -102
  101. package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +11 -4
  102. package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +4 -1
  103. package/plugins/huaweicloud-core/src/safety-policy.mjs +123 -68
  104. package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +48 -12
  105. package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +25 -26
  106. package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
  107. package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +943 -10
  108. package/plugins/huaweicloud-core/src/sandbox/uninstall-cleanup.mjs +50 -0
  109. package/plugins/huaweicloud-core/src/search-market.mjs +49 -14
  110. package/plugins/huaweicloud-core/src/setup-cli.mjs +3091 -498
  111. package/plugins/huaweicloud-core/src/telemetry/agent-detect.mjs +40 -0
  112. package/plugins/huaweicloud-core/src/telemetry/agent-registry.mjs +328 -0
  113. package/plugins/huaweicloud-core/src/telemetry/telemetry.mjs +378 -0
  114. package/plugins/huaweicloud-core/src/tools.mjs +1272 -146
  115. package/plugins/huaweicloud-core/src/update-check.mjs +383 -0
  116. package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +72 -38
  117. package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +10 -5
  118. package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +13 -16
  119. package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +12 -16
  120. package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +46 -31
  121. package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +268 -0
  122. package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
  123. package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +40 -13
  124. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +0 -190
  125. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +0 -124
@@ -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,268 @@
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
+
12
+ import {
13
+ OpCode,
14
+ ReserveSource,
15
+ FIXED_HEADER_LEN,
16
+ MAX_SEND_CHUNK_SIZE,
17
+ createPacket,
18
+ isOpTunnelSuccess,
19
+ isOpTcpTunnelData,
20
+ isOpFailed,
21
+ isSubStreamPing,
22
+ nextIdentifier,
23
+ } from './hwlink-packet.js';
24
+
25
+ const MAX_TUNNEL_PAYLOAD_SIZE = MAX_SEND_CHUNK_SIZE - FIXED_HEADER_LEN;
26
+
27
+ class HwlinkTunnelChannel {
28
+ constructor({ localPort = 0, remotePort, onReady, onClose, onError }) {
29
+ this.localPort = localPort;
30
+ this.remotePort = remotePort;
31
+ this.identifier = nextIdentifier();
32
+ this.mux = null;
33
+ this.closed = false;
34
+ this.opened = false;
35
+
36
+ this.subConnections = new Map();
37
+ this.localServer = null;
38
+ this.nextSubId = 1;
39
+
40
+ this.onReadyCb = onReady || null;
41
+ this.onCloseCb = onClose || null;
42
+ this.onErrorCb = onError || null;
43
+
44
+ this._readyResolve = null;
45
+ this._readyReject = null;
46
+ this.ready = new Promise((resolve, reject) => {
47
+ this._readyResolve = resolve;
48
+ this._readyReject = reject;
49
+ });
50
+ }
51
+
52
+ attach(mux) {
53
+ this.mux = mux;
54
+ mux.register(this);
55
+ }
56
+
57
+ onopen() {
58
+ if (this.closed || this.opened) return;
59
+ this.opened = true;
60
+ this.startLocalServer();
61
+ }
62
+
63
+ onmessage(packet) {
64
+ if (this.closed) return;
65
+
66
+ if (isOpFailed(packet.operation)) {
67
+ const subConn = this.subConnections.get(packet.identifier);
68
+ if (subConn && subConn.socket && !subConn.socket.destroyed) {
69
+ try {
70
+ subConn.socket.end();
71
+ } catch {}
72
+ }
73
+ this.subConnections.delete(packet.identifier);
74
+ this._unregisterSubId(packet.identifier);
75
+ return;
76
+ }
77
+
78
+ if (isOpTunnelSuccess(packet.operation)) {
79
+ const subConn = this.subConnections.get(packet.identifier);
80
+ if (subConn && !subConn.ready) {
81
+ subConn.ready = true;
82
+ if (subConn.readyResolve) subConn.readyResolve();
83
+ }
84
+ if (packet.identifier === this.identifier && this._readyResolve) {
85
+ this._readyResolve();
86
+ this._readyResolve = null;
87
+ this._readyReject = null;
88
+ }
89
+ return;
90
+ }
91
+
92
+ if (isOpTcpTunnelData(packet.operation)) {
93
+ const subConn = this.subConnections.get(packet.identifier);
94
+ if (subConn && subConn.socket && !subConn.socket.destroyed) {
95
+ subConn.socket.write(packet.data);
96
+ }
97
+ return;
98
+ }
99
+
100
+ if (isSubStreamPing(packet.operation)) {
101
+ this.sendRaw(
102
+ createPacket({
103
+ operation: OpCode.OpTcpTunnelData | OpCode.OpSubStreamPong,
104
+ reserve: ReserveSource.Web,
105
+ identifier: packet.identifier,
106
+ source: this.mux ? this.mux.source : 0,
107
+ payload: new Uint8Array(0),
108
+ }),
109
+ );
110
+ return;
111
+ }
112
+
113
+ if (packet.operation === OpCode.OpDisconnect) {
114
+ const subConn = this.subConnections.get(packet.identifier);
115
+ if (subConn && subConn.socket && !subConn.socket.destroyed) {
116
+ subConn.socket.end();
117
+ }
118
+ this.subConnections.delete(packet.identifier);
119
+ this._unregisterSubId(packet.identifier);
120
+ return;
121
+ }
122
+ }
123
+
124
+ onerror(error) {
125
+ if (this.onErrorCb) this.onErrorCb(error);
126
+ if (this._readyReject) {
127
+ this._readyReject(error);
128
+ this._readyResolve = null;
129
+ this._readyReject = null;
130
+ }
131
+ }
132
+
133
+ onclose() {
134
+ this.close();
135
+ }
136
+
137
+ startLocalServer() {
138
+ this.localServer = createServer((socket) => {
139
+ this.onIncomingConnection(socket);
140
+ });
141
+
142
+ this.localServer.listen(this.localPort, '127.0.0.1', () => {
143
+ this.localPort = this.localServer.address().port;
144
+ if (this.onReadyCb) this.onReadyCb();
145
+ if (this._readyResolve) {
146
+ this._readyResolve();
147
+ this._readyResolve = null;
148
+ this._readyReject = null;
149
+ }
150
+ });
151
+
152
+ this.localServer.on('error', (err) => {
153
+ if (this.onErrorCb) this.onErrorCb(err);
154
+ if (this._readyReject) {
155
+ this._readyReject(err);
156
+ this._readyResolve = null;
157
+ this._readyReject = null;
158
+ }
159
+ });
160
+ }
161
+
162
+ _registerSubId(subId) {
163
+ if (this.mux && !this.mux.channels.has(subId)) {
164
+ this.mux.channels.set(subId, this);
165
+ this.mux.queue.register({ identifier: subId });
166
+ }
167
+ }
168
+
169
+ _unregisterSubId(subId) {
170
+ if (this.mux) {
171
+ this.mux.channels.delete(subId);
172
+ this.mux.queue.unregister({ identifier: subId });
173
+ }
174
+ }
175
+
176
+ onIncomingConnection(socket) {
177
+ const subId = this.nextSubId;
178
+ this.nextSubId = ((this.nextSubId + 1) & 0xffffffff) >>> 0;
179
+
180
+ let readyResolve;
181
+ const readyPromise = new Promise((resolve) => {
182
+ readyResolve = resolve;
183
+ });
184
+ const subConn = {
185
+ identifier: subId,
186
+ socket,
187
+ ready: false,
188
+ readyResolve,
189
+ readyPromise,
190
+ };
191
+
192
+ this.subConnections.set(subId, subConn);
193
+ this._registerSubId(subId);
194
+
195
+ this.sendRaw(
196
+ createPacket({
197
+ operation: OpCode.OpCreateTcpTunnel,
198
+ reserve: ReserveSource.Web,
199
+ srcPort: this.localPort,
200
+ dstPort: this.remotePort,
201
+ identifier: subId,
202
+ source: this.mux ? this.mux.source : 0,
203
+ payload: new Uint8Array(0),
204
+ }),
205
+ );
206
+
207
+ socket.on('data', (data) => {
208
+ if (this.closed || socket.destroyed) return;
209
+ for (let offset = 0; offset < data.length; offset += MAX_TUNNEL_PAYLOAD_SIZE) {
210
+ const payload = data.subarray(offset, offset + MAX_TUNNEL_PAYLOAD_SIZE);
211
+ this.sendRaw(
212
+ createPacket({
213
+ operation: OpCode.OpTcpTunnelData,
214
+ reserve: ReserveSource.Web,
215
+ srcPort: this.localPort,
216
+ dstPort: this.remotePort,
217
+ identifier: subId,
218
+ source: this.mux ? this.mux.source : 0,
219
+ payload,
220
+ }),
221
+ );
222
+ }
223
+ });
224
+
225
+ socket.on('close', () => {
226
+ this._unregisterSubId(subId);
227
+ this.subConnections.delete(subId);
228
+ });
229
+
230
+ socket.on('error', () => {
231
+ this._unregisterSubId(subId);
232
+ this.subConnections.delete(subId);
233
+ });
234
+ }
235
+
236
+ destroySubConnection(subConn, error) {
237
+ if (subConn.socket && !subConn.socket.destroyed) {
238
+ if (error) subConn.socket.destroy(error);
239
+ else subConn.socket.destroy();
240
+ }
241
+ this._unregisterSubId(subConn.identifier);
242
+ this.subConnections.delete(subConn.identifier);
243
+ }
244
+
245
+ sendRaw(data) {
246
+ if (this.mux) this.mux.sendFairly(this, data);
247
+ }
248
+
249
+ close() {
250
+ if (this.closed) return;
251
+ this.closed = true;
252
+
253
+ for (const subConn of this.subConnections.values()) {
254
+ this.destroySubConnection(subConn);
255
+ }
256
+ this.subConnections.clear();
257
+
258
+ if (this.localServer) {
259
+ this.localServer.close();
260
+ this.localServer = null;
261
+ }
262
+
263
+ if (this.mux) this.mux.unregister(this);
264
+ if (this.onCloseCb) this.onCloseCb();
265
+ }
266
+ }
267
+
268
+ export { HwlinkTunnelChannel };
@@ -3,10 +3,12 @@ import * as hwlinkPacket from './hwlink-packet.js';
3
3
  import * as wsExec from './ws-exec-client.js';
4
4
  import { HwlinkWebSocketMultiplexer } from './hwlink-multiplexer.js';
5
5
  import { HwlinkTerminalChannel } from './hwlink-terminal-channel.js';
6
+ import { HwlinkTunnelChannel } from './hwlink-tunnel-channel.mjs';
6
7
 
7
8
  export * from './ws-exec-client.js';
8
9
  export * from './hwlink-exec-client.js';
9
10
  export { HwlinkTerminalChannel } from './hwlink-terminal-channel.js';
11
+ export { HwlinkTunnelChannel } from './hwlink-tunnel-channel.mjs';
10
12
  export { HwlinkWebSocketMultiplexer } from './hwlink-multiplexer.js';
11
13
  export { hwlinkPacket };
12
14
 
@@ -14,6 +16,7 @@ export default {
14
16
  ...wsExec,
15
17
  ...hwlinkExec,
16
18
  HwlinkTerminalChannel,
19
+ HwlinkTunnelChannel,
17
20
  HwlinkWebSocketMultiplexer,
18
21
  hwlinkPacket,
19
22
  };
@@ -38,15 +38,36 @@ function closeQuietly(ws) {
38
38
  }
39
39
  }
40
40
 
41
- function escapeRegExp(text) {
42
- return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
43
- }
44
-
45
41
  function stripEchoedLine(text, line, options = {}) {
46
- const escaped = escapeRegExp(line);
47
- const stripped = text.replace(new RegExp(`(^|\\r?\\n)${escaped}\\r?\\n?`), '$1');
48
- if (!options.allowAttached || stripped !== text) return stripped;
49
- return text.replace(new RegExp(`${escaped}(?:\\r?\\n){0,2}$`), '');
42
+ if (text.startsWith(line)) {
43
+ text = text.slice(line.length);
44
+ text = text.replace(/^\r?\n/, '');
45
+ if (!options.allowAttached) return text;
46
+ }
47
+
48
+ for (const nl of ['\r\n', '\n']) {
49
+ const idx = text.indexOf(nl + line);
50
+ if (idx !== -1) {
51
+ const before = text.slice(0, idx + nl.length);
52
+ const after = text.slice(idx + nl.length + line.length);
53
+ text = before + after.replace(/^\r?\n/, '');
54
+ if (!options.allowAttached) return text;
55
+ break;
56
+ }
57
+ }
58
+
59
+ if (options.allowAttached) {
60
+ for (let trail = 2; trail >= 0; trail--) {
61
+ for (const nl of ['\r\n', '\n']) {
62
+ const suffix = nl.repeat(trail);
63
+ if (text.endsWith(line + suffix)) {
64
+ return text.slice(0, text.length - line.length - suffix.length);
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ return text;
50
71
  }
51
72
 
52
73
  function buildMarkers(nonce = randomBytes(8).toString('hex')) {
@@ -65,7 +86,6 @@ function buildMarkers(nonce = randomBytes(8).toString('hex')) {
65
86
  doneSuffix,
66
87
  readyMarker,
67
88
  doneMarker,
68
- donePattern: new RegExp(`${escapeRegExp(doneMarker)}(\\d+)`),
69
89
  };
70
90
  }
71
91
 
@@ -284,10 +304,17 @@ class WebSocketShellSession {
284
304
  const pending = this.pending;
285
305
  if (!pending) return;
286
306
 
287
- const doneMatch = pending.buffer.match(pending.markers.donePattern);
288
- if (!doneMatch || doneMatch.index === undefined) return;
307
+ const doneMarker = pending.markers.doneMarker;
308
+ const markerIndex = pending.buffer.lastIndexOf(doneMarker);
309
+ if (markerIndex === -1) return;
310
+
311
+ // eslint-disable-next-line unicorn/no-unsafe-buffer-conversion -- pending.buffer is a String, not a Buffer
312
+ const afterMarker = pending.buffer.slice(markerIndex + doneMarker.length);
313
+ const exitMatch = afterMarker.match(/^(\d+)/);
314
+ if (!exitMatch) return;
289
315
 
290
- const rawOutput = pending.buffer.slice(0, doneMatch.index);
316
+ // eslint-disable-next-line unicorn/no-unsafe-buffer-conversion -- pending.buffer is a String, not a Buffer
317
+ const rawOutput = pending.buffer.slice(0, markerIndex);
291
318
  const stdout = cleanCommandOutput(rawOutput, {
292
319
  inputEchoed: this.inputEchoed,
293
320
  command: pending.command,
@@ -298,7 +325,7 @@ class WebSocketShellSession {
298
325
  this.pending = null;
299
326
  pending.resolve({
300
327
  stdout,
301
- exitCode: Number(doneMatch[1]),
328
+ exitCode: Number(exitMatch[1]),
302
329
  url: this.url,
303
330
  command: pending.command,
304
331
  });
@@ -1,190 +0,0 @@
1
- ---
2
- name: huawei-cloud-find-skills
3
- description: |
4
- Invoke this skill to search, discover, browse, find and install any Huawei Cloud (华为云) agent skill.Triggers include: "华为云","华为云有什么skill","华为云相关skill","华为云agent skill 市场","华为云skill类目","explore Huawei Cloud skills","show Huawei Cloud skill categories","does a Huawei Cloud skill exist for...","which Huawei Cloud skills exist","搜索华为云技能","有没有管理ECS/OBS/RDS的skill","帮我找 XX 华为云skill","介绍 XX Skill 内容","华为云 XX Skill 具体做什么","安装华为云Skill".
5
- ---
6
-
7
- > [!IMPORTANT]
8
- > **For any Huawei Cloud query or management task:**
9
- > 1. **Search** — use this skill (`huawei-cloud-find-skills`) to find the relevant Skill.
10
- > 2. **Install** — install the matched Skill (see [Step 3](#step-3-install-skill)).
11
- > 3. **Execute** — follow the installed Skill's instructions to fulfill the request.
12
-
13
- # Huawei Cloud Agent Skills Search and Discovery
14
-
15
- This skill enables users to efficiently search, discover, and install Huawei Cloud skills.
16
-
17
- ## Scenario Description
18
-
19
- This skill enables users to:
20
-
21
- - **Search Skills**: Find skills by keyword, category, or both (matched against name, description, and triggers)
22
- - **Browse Categories**: Explore available skill categories
23
- - **View Skill Details**: Fetch full SKILL.md content from GitHub for specific skills
24
- - **Install Skills**: Guide users through skill installation via `npx skills add` (GitCode default), `npx clawhub install`, or fallback GitHub method
25
-
26
- **Architecture**: GitCode API v5 (`index.json` + `cn-en-map.json`) → HTTP GET (base64 decode) → In-memory search → GitHub raw fetch for details → Install
27
-
28
- ### Use Cases
29
-
30
- - "Find a skill for managing ECS instances"
31
- - "What Huawei Cloud skills are available for OBS?"
32
- - "华为云有哪些 VPC 相关的 skill?"
33
- - "Browse all available Huawei Cloud skills"
34
- - "Install a skill for RDS management"
35
- - "帮我找一个华为云网络相关的skill"
36
-
37
- ## Prerequisites
38
-
39
- - **Node.js >= 20** must be installed
40
- - **Network access** to `gitcode.com` (API v5 for index) and `github.com` / `raw.githubusercontent.com` (for skill details)
41
-
42
- ### Step 0: Check Python Environment
43
-
44
- > **MANDATORY**: Before running any script command, verify Python is available.
45
-
46
- ```bash
47
- # Check Python availability
48
- python --version # or: python3 --version
49
- ```
50
-
51
- If the command fails or returns Python 2.x:
52
-
53
- 1. **Install Python 3**: Download from [python.org](https://www.python.org/downloads/) or use a package manager:
54
- ```bash
55
- # macOS
56
- brew install python3
57
- # Ubuntu/Debian
58
- sudo apt-get install python3
59
- # Windows — download installer from python.org, check "Add Python to PATH"
60
- ```
61
- 2. **Verify after install**: Run `python --version` again to confirm Python 3.6+ is available
62
- 3. **If `python` points to Python 2**: Use `python3` instead of `python` in all commands below
63
-
64
- ## Repository Info
65
-
66
- ```
67
- INDEX_REPO=2501_91318609/skills-for-index
68
- INDEX_BRANCH=main
69
- SKILLS_REPO=huaweicloud/huaweicloud-skills
70
- SKILLS_BRANCH=master
71
- RAW_BASE=https://raw.githubusercontent.com/$SKILLS_REPO/$SKILLS_BRANCH
72
- ```
73
-
74
- ## Index Source
75
-
76
- The search script fetches the skill index from GitCode API v5 via HTTP GET (base64 auto-decoded):
77
-
78
- ```
79
- SKILLS_INDEX_URL=https://gitcode.com/api/v5/repos/2501_91318609/skills-for-index/contents/skills-index/index.json?ref=main
80
- SKILLS_CN_EN_MAP_URL=https://gitcode.com/api/v5/repos/2501_91318609/skills-for-index/contents/skills-index/cn-en-map.json?ref=main
81
- ```
82
-
83
-
84
- ## Core Workflow
85
-
86
- ### Step 1: Search Skills
87
-
88
- > **MANDATORY**: The agent MUST execute the search script to search the skill index. Do NOT read the JSON file directly — always use the script.
89
-
90
- Given `keyword` (from AI-understood user intent) and optional `category`, run the search script:
91
-
92
- ```bash
93
- node scripts/search-skills.mjs <keyword>
94
- node scripts/search-skills.mjs <keyword> -c <category>
95
- node scripts/search-skills.mjs -c <category>
96
- ```
97
-
98
- → [scripts/search-skills.mjs](scripts/search-skills.mjs) (Node.js — cross-platform)
99
-
100
- **What the script does**:
101
- 1. Fetches `index.json` and `cn-en-map.json` via HTTP GET from GitCode API v5 (auto-decodes base64 content)
102
- 2. Expands keywords via `cn-en-map.json` (bidirectional CN↔EN, e.g., "ECS" → "ECS, 弹性云服务器, 云服务器")
103
- 3. Scores each skill: name match **+10**, trigger match **+8**, description match **+5**, service match **+3**
104
- 4. Sorts by score descending, outputs formatted results with matched keywords
105
-
106
- **Fallback iteration** (if no results): 1) Switch CN↔EN keywords 2) Expand keywords 3) Remove category filter 4) Try synonyms 5) List all skills
107
-
108
- The process should persist until the skill is found or its absence is confirmed. In the event of total failure, notify the user of the specific steps that were attempted.
109
-
110
- ### Step 2: View Skill Details (optional)
111
-
112
- Fetch the full SKILL.md content from GitHub for intent validation. Skip this step if the search results from Step 1 are sufficiently informative.
113
-
114
- ```bash
115
- # URL pattern — use the skill's category, service, and name from index
116
- DETAIL_URL="https://raw.githubusercontent.com/huaweicloud/huaweicloud-skills/master/skills/${category}/${service}/${name}/SKILL.md"
117
- ```
118
-
119
- The agent can fetch this URL using `curl` or its web-fetch tool, then present the skill's full documentation to the user.
120
-
121
- ### Step 3: Install Skill
122
-
123
- > **MANDATORY**: Use one of the commands below. Option A is the default; Option C is a fallback when Option A is unavailable.
124
-
125
- ```bash
126
- # Option A: npx skills add from GitCode (default)
127
- npx skills add https://gitcode.com/huaweicloud/huaweicloud-skills.git#master --skill <skill-name> -y
128
-
129
- # Option B: npx clawhub install (OpenClaw ecosystem)
130
- npx clawhub install <skill-name> -y
131
-
132
- # Option C (fallback): npx skills add from GitHub
133
- npx skills add huaweicloud/huaweicloud-skills --skill <skill-name> -y
134
- ```
135
-
136
- If all installation attempts fail, report the error message to the user. Do NOT attempt any method outside the commands above.
137
-
138
- ## Parameters
139
-
140
- | Parameter | Required/Optional | Description | Default |
141
- |-----------|-------------------|-------------|---------|
142
- | `Keyword` | Optional | Search keyword (matched against name, description, triggers, service) | None |
143
- | `Category` | Optional | Category code for filtering (e.g., "computing", "storage", "network") | None |
144
- | `skill-name` | Required (Step 3) | Exact skill name for installing | None |
145
-
146
-
147
- ## Reference Documentation
148
-
149
- | Document | Description |
150
- |----------|-------------|
151
- | GitCode API v5 `index.json` | Skill index fetched via HTTP GET (base64 decoded) |
152
- | GitCode API v5 `cn-en-map.json` | Chinese-English keyword mapping fetched via HTTP GET (base64 decoded) |
153
- | [scripts/search-skills.py](scripts/search-skills.py) | Search script (Python) — fetches from GitCode API v5, expands keywords, scores, sorts |
154
-
155
- ## Search Heuristics
156
-
157
- > Optional reference for keyword-to-category hints. The agent can infer categories from `index.json` without these.
158
-
159
- - Cloud infrastructure keywords (ecs, bms, vpc, obs, rds, ...) → likely `computing`, `network`, `storage`, etc.
160
- - Tool keywords (cli, terraform, koo) → likely `devtools`
161
- - Management keywords (monitoring, alarm, log) → likely `monitoring`
162
-
163
- ## Troubleshooting
164
-
165
- ### Issue: Script fails with "Failed to fetch index.json"
166
-
167
- **Cause**: GitCode API v5 URL unreachable
168
- **Solution**: Verify network connectivity to `gitcode.com`
169
-
170
- ### Issue: GitCode API v5 returns 404
171
-
172
- **Cause**: File path incorrect or default branch is not `main`
173
- **Solution**: Verify the skill's `category`, `service`, and `name` from search results
174
-
175
- ### Issue: Search returns no results
176
-
177
- **Cause**: Keywords don't match any skill
178
- **Solution**:
179
- 1. Try broader keywords
180
- 2. Switch between Chinese and English keywords (e.g., "对象存储" → "obs")
181
- 3. List all skills: `python scripts/search-skills.py -c "computing"`
182
-
183
- ## Notes
184
-
185
- - This skill is **read-only** and does not create any cloud resources
186
- - **No cache management needed** — index is fetched fresh from GitCode API v5 each run
187
- - **Network required** — index data is hosted on GitCode, fetched via HTTP GET (base64 decoded)
188
- - **MUST use script to search** — do not read index.json directly
189
- - Index repo: `https://gitcode.com/2501_91318609/skills-for-index` (branch: `main`)
190
- - Skills repo: `https://github.com/huaweicloud/huaweicloud-skills` (branch: `master`)