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
@@ -0,0 +1,383 @@
1
+ // Version-update detection & auto-upgrade for huaweicloud-devkit (session-level).
2
+ // Spec: docs/superpowers/specs/2026-09-07-version-upgrade-design.md (internal, not committed).
3
+ import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync, rmSync } from 'node:fs';
4
+ import { spawn, spawnSync } from 'node:child_process';
5
+ import { join, dirname, resolve } from 'node:path';
6
+ import { homedir } from 'node:os';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const IS_WINDOWS = process.platform === 'win32';
10
+ const NPM_BIN = IS_WINDOWS ? 'npm.cmd' : 'npm';
11
+ const NPX_BIN = IS_WINDOWS ? 'npx.cmd' : 'npx';
12
+ const TTL_MS = 60 * 60 * 1000;
13
+ const FAIL_THROTTLE_MS = 5 * 60 * 1000;
14
+ const COOLDOWN_DAYS = 3;
15
+
16
+ export function semverParse(input) {
17
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(String(input).trim());
18
+ if (!m) return null;
19
+ const pre = m[4] ? m[4].split('.') : null;
20
+ return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]), pre, raw: String(input).trim() };
21
+ }
22
+
23
+ function comparePre(a, b) {
24
+ if (a === null && b === null) return 0;
25
+ if (a === null) return 1; // 无 prerelease(正式版)更大
26
+ if (b === null) return -1;
27
+ const length = Math.max(a.length, b.length);
28
+ for (let i = 0; i < length; i++) {
29
+ const x = a[i];
30
+ const y = b[i];
31
+ if (x === undefined) return -1;
32
+ if (y === undefined) return 1;
33
+ const xNum = /^\d+$/.test(x);
34
+ const yNum = /^\d+$/.test(y);
35
+ if (xNum && yNum) {
36
+ const dx = Number(x);
37
+ const dy = Number(y);
38
+ if (dx !== dy) return dx < dy ? -1 : 1;
39
+ } else if (xNum) {
40
+ return -1; // 数字标识符 < 字母标识符
41
+ } else if (yNum) {
42
+ return 1;
43
+ } else if (x !== y) {
44
+ return x < y ? -1 : 1;
45
+ }
46
+ }
47
+ return 0;
48
+ }
49
+
50
+ export function semverCompare(a, b) {
51
+ const A = semverParse(a);
52
+ const B = semverParse(b);
53
+ if (!A || !B) {
54
+ const sa = String(a);
55
+ const sb = String(b);
56
+ return sa === sb ? 0 : sa < sb ? -1 : 1;
57
+ }
58
+ if (A.major !== B.major) return A.major < B.major ? -1 : 1;
59
+ if (A.minor !== B.minor) return A.minor < B.minor ? -1 : 1;
60
+ if (A.patch !== B.patch) return A.patch < B.patch ? -1 : 1;
61
+ return comparePre(A.pre, B.pre);
62
+ }
63
+
64
+ export function hasPrerelease(v) {
65
+ const parsed = semverParse(v);
66
+ return Boolean(parsed && parsed.pre);
67
+ }
68
+
69
+ export function determineTarget(current, distTags = {}) {
70
+ const isPre = hasPrerelease(current);
71
+ const candidates = [];
72
+ if (distTags.latest) candidates.push(distTags.latest);
73
+ if (isPre && distTags.next) candidates.push(distTags.next);
74
+ if (!candidates.length) return null;
75
+ return candidates.slice().sort(semverCompare).pop();
76
+ }
77
+
78
+ export function parseDistTagsOutput(stdout) {
79
+ try {
80
+ const text = String(stdout || '').trim();
81
+ if (!text) return null; // 空输出视为 npm view 失败
82
+ const parsed = JSON.parse(text);
83
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return null;
84
+ return {
85
+ latest: typeof parsed.latest === 'string' ? parsed.latest : null,
86
+ next: typeof parsed.next === 'string' ? parsed.next : null,
87
+ };
88
+ } catch {
89
+ return null;
90
+ }
91
+ }
92
+
93
+ function buildResult(current, distTags, extra = {}) {
94
+ const result = {
95
+ currentVersion: current,
96
+ latestStable: distTags?.latest ?? null,
97
+ latestNext: distTags?.next ?? null,
98
+ targetVersion: extra.target ?? null,
99
+ updateAvailable: extra.result === 'update_available',
100
+ dismissed: extra.result === 'dismissed',
101
+ dismissExpiresAt: extra.dismissExpiresAt ?? null,
102
+ result: extra.result ?? 'up_to_date',
103
+ };
104
+ if (extra.note) result.note = extra.note;
105
+ return result;
106
+ }
107
+
108
+ export function judgeUpdate(current, distTags, skipState, now = Date.now()) {
109
+ if (process.env.HUAWEICLOUD_DEVKIT_SKIP_UPDATE === '1') {
110
+ return buildResult(current, distTags ?? null);
111
+ }
112
+ if (!distTags) {
113
+ return buildResult(current, null, { result: 'check_failed', note: '检测失败,不影响使用' });
114
+ }
115
+ const target = determineTarget(current, distTags);
116
+ if (!target || semverCompare(target, current) <= 0) {
117
+ return buildResult(current, distTags, { result: 'up_to_date', target: target ?? null });
118
+ }
119
+ const expiresAt = skipState?.expireAt ? new Date(skipState.expireAt).getTime() : 0;
120
+ const inCooldown =
121
+ Boolean(skipState) && now < expiresAt && semverCompare(target, String(skipState.dismissedVersion)) <= 0;
122
+ if (inCooldown) {
123
+ return buildResult(current, distTags, { result: 'dismissed', target, dismissExpiresAt: skipState.expireAt });
124
+ }
125
+ return buildResult(current, distTags, { result: 'update_available', target });
126
+ }
127
+
128
+ function selfDir() {
129
+ return dirname(fileURLToPath(import.meta.url));
130
+ }
131
+
132
+ export function readInstalledVersion() {
133
+ const pluginRoot = resolve(selfDir(), '..');
134
+ const packageRoot = resolve(pluginRoot, '..', '..');
135
+ for (const base of [pluginRoot, packageRoot]) {
136
+ try {
137
+ const version = JSON.parse(readFileSync(join(base, 'package.json'), 'utf8')).version;
138
+ if (typeof version === 'string' && version) return version;
139
+ } catch {}
140
+ }
141
+ return null;
142
+ }
143
+
144
+ export function skipFilePath() {
145
+ return join(resolve(selfDir(), '..'), '.update-skip.json');
146
+ }
147
+
148
+ export function fallbackSkipFilePath() {
149
+ const base = process.env.HUAWEICLOUD_HOME || homedir();
150
+ return join(base, '.config', 'huaweicloud', 'devkit-skip.json');
151
+ }
152
+
153
+ // A1 定稿: 标准 agent 用插件目录副本(有 package.json); codex 等无副本时回退共享文件
154
+ export function resolveSkipFilePath() {
155
+ try {
156
+ if (existsSync(join(dirname(skipFilePath()), 'package.json'))) return skipFilePath();
157
+ } catch {}
158
+ return fallbackSkipFilePath();
159
+ }
160
+
161
+ export function readSkipState(file) {
162
+ if (!existsSync(file)) return null;
163
+ try {
164
+ const parsed = JSON.parse(readFileSync(file, 'utf8'));
165
+ if (!parsed || typeof parsed.dismissedVersion !== 'string' || !parsed.dismissedAt || !parsed.expireAt) return null;
166
+ return parsed;
167
+ } catch {
168
+ return null;
169
+ }
170
+ }
171
+
172
+ export function writeSkipState(file, dismissedVersion, { at = Date.now(), days = COOLDOWN_DAYS } = {}) {
173
+ const state = {
174
+ dismissedVersion: String(dismissedVersion),
175
+ dismissedAt: new Date(at).toISOString(),
176
+ expireAt: new Date(at + days * 24 * 60 * 60 * 1000).toISOString(),
177
+ };
178
+ mkdirSync(dirname(file), { recursive: true });
179
+ const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
180
+ writeFileSync(tmp, JSON.stringify(state, null, 2), 'utf8');
181
+ try {
182
+ renameSync(tmp, file);
183
+ } catch (error) {
184
+ rmSync(tmp, { force: true });
185
+ throw error;
186
+ }
187
+ return state;
188
+ }
189
+
190
+ export function queryDistTagsSync({ timeoutMs = 5000, cwd } = {}) {
191
+ try {
192
+ const result = spawnSync(NPM_BIN, ['view', 'huaweicloud-devkit', 'dist-tags', '--json'], {
193
+ encoding: 'utf8',
194
+ timeout: timeoutMs,
195
+ windowsHide: true,
196
+ cwd,
197
+ });
198
+ if (result.status !== 0) return null;
199
+ return parseDistTagsOutput(result.stdout);
200
+ } catch {
201
+ return null;
202
+ }
203
+ }
204
+
205
+ export function queryDistTags({ timeoutMs = 5000, cwd } = {}) {
206
+ return new Promise((resolve) => {
207
+ let child;
208
+ try {
209
+ child = spawn(NPM_BIN, ['view', 'huaweicloud-devkit', 'dist-tags', '--json'], {
210
+ windowsHide: true,
211
+ cwd,
212
+ });
213
+ } catch {
214
+ resolve(null);
215
+ return;
216
+ }
217
+ const timer = setTimeout(() => {
218
+ try {
219
+ child.kill();
220
+ } catch {}
221
+ resolve(null);
222
+ }, timeoutMs);
223
+ let stdout = '';
224
+ child.stdout.on('data', (d) => {
225
+ stdout += String(d);
226
+ });
227
+ child.on('error', () => {
228
+ clearTimeout(timer);
229
+ resolve(null);
230
+ });
231
+ child.on('close', (code) => {
232
+ clearTimeout(timer);
233
+ if (code === 0) resolve(parseDistTagsOutput(stdout));
234
+ else resolve(null);
235
+ });
236
+ });
237
+ }
238
+
239
+ let cachedDistTags = null;
240
+ let cachedAt = 0;
241
+ let failedAt = 0;
242
+ let inflightQuery = null;
243
+ let lastHint = null;
244
+
245
+ export function invalidateUpdateCache() {
246
+ cachedDistTags = null;
247
+ cachedAt = 0;
248
+ failedAt = 0;
249
+ inflightQuery = null;
250
+ lastHint = null;
251
+ }
252
+
253
+ function cacheValid() {
254
+ return Boolean(cachedDistTags) && Date.now() - cachedAt <= TTL_MS;
255
+ }
256
+
257
+ export async function getCachedUpdateInfo(current, { doQuery = queryDistTags } = {}) {
258
+ if (process.env.HUAWEICLOUD_DEVKIT_SKIP_UPDATE === '1') {
259
+ lastHint = judgeUpdate(current, null);
260
+ return lastHint;
261
+ }
262
+ const skipState = readSkipState(resolveSkipFilePath());
263
+ if (!cacheValid()) {
264
+ if (!cachedDistTags && Date.now() - failedAt < FAIL_THROTTLE_MS) {
265
+ lastHint = judgeUpdate(current, null, skipState);
266
+ return lastHint;
267
+ }
268
+ if (!inflightQuery) {
269
+ inflightQuery = doQuery()
270
+ .then((distTags) => {
271
+ if (distTags) {
272
+ cachedDistTags = distTags;
273
+ cachedAt = Date.now();
274
+ } else {
275
+ failedAt = Date.now();
276
+ }
277
+ return distTags;
278
+ })
279
+ .finally(() => {
280
+ inflightQuery = null;
281
+ });
282
+ }
283
+ const distTags = await inflightQuery;
284
+ lastHint = judgeUpdate(current, distTags, skipState);
285
+ return lastHint;
286
+ }
287
+ lastHint = judgeUpdate(current, cachedDistTags, skipState);
288
+ return lastHint;
289
+ }
290
+
291
+ export function peekCachedUpdateInfo() {
292
+ return lastHint && lastHint.updateAvailable && lastHint.targetVersion ? lastHint : null;
293
+ }
294
+
295
+ export async function getUpdateDistTags(current) {
296
+ const result = await getCachedUpdateInfo(current);
297
+ if (!result) return null;
298
+ return { latest: result.latestStable ?? null, next: result.latestNext ?? null };
299
+ }
300
+
301
+ export function applyUpdateHint(result, name, hint) {
302
+ if (!hint || !hint.updateAvailable || !hint.targetVersion) return result;
303
+ if (name === 'huaweicloud_check_update' || name === 'huaweicloud_upgrade') return result;
304
+ return {
305
+ ...result,
306
+ _updateInfo: { currentVersion: hint.currentVersion, latestVersion: hint.targetVersion },
307
+ };
308
+ }
309
+
310
+ function defaultSpawn(command, args, options) {
311
+ return spawnSync(command, args, options);
312
+ }
313
+
314
+ function restartMessage(target) {
315
+ if (target === 'officeace') {
316
+ return '升级完成,请打开连接器 → 我的连接器 → huaweicloud-devkit → 重新连接后使用新版本。';
317
+ }
318
+ return '升级完成,请重启当前会话使新版本生效。';
319
+ }
320
+
321
+ export async function upgradePackage({ target = 'all', version = 'latest' } = {}, options = {}) {
322
+ if (version !== 'latest') {
323
+ return { success: false, error: 'version 参数仅支持 latest。目标版本由插件自动判定。' };
324
+ }
325
+ const { doQuery = queryDistTags, spawnFn = defaultSpawn } = options;
326
+ const previousVersion = readInstalledVersion();
327
+ let distTags;
328
+ try {
329
+ distTags = await doQuery();
330
+ } catch (error) {
331
+ return {
332
+ success: false,
333
+ error: error?.message || '无法确认最新版本(registry 查询失败)。',
334
+ manual: `npx --yes huaweicloud-devkit@latest update --target ${target}`,
335
+ };
336
+ }
337
+ const targetVersion = determineTarget(previousVersion, distTags ?? {});
338
+ if (distTags === null) {
339
+ return {
340
+ success: false,
341
+ error: '无法确认最新版本(registry 查询失败)。',
342
+ manual: `npx --yes huaweicloud-devkit@latest update --target ${target}`,
343
+ };
344
+ }
345
+ if (!targetVersion) {
346
+ return { success: false, message: '已是最新版本,无需升级。', requiresRestart: false };
347
+ }
348
+ const isNextTarget = Boolean(distTags.next && semverCompare(targetVersion, distTags.next) === 0);
349
+ const tag = isNextTarget ? 'next' : 'latest';
350
+ const command = ['--yes', `huaweicloud-devkit@${tag}`, 'update', '--target', String(target)];
351
+ let execResult;
352
+ try {
353
+ execResult = spawnFn(NPX_BIN, command, { encoding: 'utf8', timeout: 300000, windowsHide: true });
354
+ } catch (error) {
355
+ return {
356
+ success: false,
357
+ error: error?.message || '升级命令执行失败。',
358
+ manual: `npx --yes huaweicloud-devkit@${tag} update --target ${target}`,
359
+ };
360
+ }
361
+ if (execResult.status === 0) {
362
+ invalidateUpdateCache();
363
+ return {
364
+ success: true,
365
+ previousVersion,
366
+ installedVersion: targetVersion,
367
+ requiresRestart: true,
368
+ message: restartMessage(target),
369
+ };
370
+ }
371
+ const errObj = execResult.error;
372
+ const stderr = String(execResult.stderr || '').trim();
373
+ const reason = errObj?.message
374
+ ? errObj.message
375
+ : stderr
376
+ ? stderr.split(/\r?\n/).filter(Boolean).slice(-2).join(' ')
377
+ : `exit ${execResult.status}`;
378
+ return {
379
+ success: false,
380
+ error: reason,
381
+ manual: `npx --yes huaweicloud-devkit@${tag} update --target ${target}`,
382
+ };
383
+ }
@@ -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,15 +90,10 @@ 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
- throw new Error('global WebSocket is unavailable; use Node.js 22+ or pass WebSocketImpl');
96
+ throw new TypeError('global WebSocket is unavailable; use Node.js 22+ or pass WebSocketImpl');
93
97
  }
94
98
  if (!Number.isInteger(source) || source < -0x80000000 || source > 0xffffffff) {
95
99
  throw new Error('hwlink source must be an int32 or uint32 number');
@@ -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;