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
@@ -17,9 +17,13 @@ function regexFrom(pattern) {
17
17
  }
18
18
 
19
19
  function isSecretKeyName(key, policy = DEFAULT_POLICY) {
20
- const normalized = String(key).toLowerCase().replace(/[^a-z0-9]/g, '');
20
+ const normalized = String(key)
21
+ .toLowerCase()
22
+ .replace(/[^a-z0-9]/g, '');
21
23
  if (
22
- /access.*key|secret.*key|security.*token|xauth.*token|authorization|password|passwd|adminpass|credential|private.*key/.test(normalized)
24
+ /access.*key|secret.*key|security.*token|xauth.*token|authorization|password|passwd|adminpass|credential|private.*key/.test(
25
+ normalized,
26
+ )
23
27
  ) {
24
28
  return true;
25
29
  }
@@ -28,7 +32,10 @@ function isSecretKeyName(key, policy = DEFAULT_POLICY) {
28
32
 
29
33
  function redactString(text) {
30
34
  return String(text)
31
- .replace(/((?:access[_-]?key|secret[_-]?key|security[_-]?token|x[_-]?auth[_-]?token|authorization|password|passwd|adminPass|credential)\s*[:=]\s*)("[^"]*"|'[^']*'|[^\s,;]+)/gi, '$1<redacted>')
35
+ .replace(
36
+ /((?:access[_-]?key|secret[_-]?key|security[_-]?token|x[_-]?auth[_-]?token|authorization|password|passwd|adminPass|credential)\s*[:=]\s*)("[^"]*"|'[^']*'|[^\s,;]+)/gi,
37
+ '$1<redacted>',
38
+ )
32
39
  .replace(/(AK|SK)\s*[:=]\s*("[^"]*"|'[^']*'|[^\s,;]+)/g, '$1=<redacted>');
33
40
  }
34
41
 
@@ -78,15 +85,11 @@ function matchesAny(value, patterns) {
78
85
 
79
86
  function hasWritePrefix(operation, policy) {
80
87
  const normalized = String(operation);
81
- return policy.writeOperationPrefixes.some((prefix) =>
82
- new RegExp(`(^|[A-Za-z0-9])${prefix}`, 'i').test(normalized),
83
- );
88
+ return policy.writeOperationPrefixes.some((prefix) => new RegExp(`(^|[A-Za-z0-9])${prefix}`, 'i').test(normalized));
84
89
  }
85
90
 
86
91
  function hasReadPrefix(operation, policy) {
87
- return policy.readOperationPrefixes.some((prefix) =>
88
- new RegExp(`^${prefix}`, 'i').test(operation),
89
- );
92
+ return policy.readOperationPrefixes.some((prefix) => new RegExp(`^${prefix}`, 'i').test(operation));
90
93
  }
91
94
 
92
95
  function isLocalMetadataCommand(args) {
@@ -119,26 +122,31 @@ export function classifyHcloudArgs(args, options = {}) {
119
122
  }
120
123
 
121
124
  if (isLocalMetadataCommand(normalizedArgs)) {
122
- return applyCommandRiskRules({
123
- decision: 'allow',
124
- risk: 'local_metadata',
125
- reason: 'KooCLI local help and version commands are read-only and do not call Huawei Cloud resource APIs.',
126
- service,
127
- operation,
128
- args: normalizedArgs,
129
- }, normalizedArgs, options);
125
+ return applyCommandRiskRules(
126
+ {
127
+ decision: 'allow',
128
+ risk: 'local_metadata',
129
+ reason: 'KooCLI local help and version commands are read-only and do not call Huawei Cloud resource APIs.',
130
+ service,
131
+ operation,
132
+ args: normalizedArgs,
133
+ },
134
+ normalizedArgs,
135
+ options,
136
+ );
130
137
  }
131
138
 
132
139
  if (service.toLowerCase() === 'configure') {
133
140
  const subcommand = operation.toLowerCase();
134
141
  if (
135
- policy.blockedConfigureSubcommands.map((cmd) => cmd.toLowerCase()).includes(subcommand)
136
- && options.allowCredentialRead !== true
142
+ policy.blockedConfigureSubcommands.map((cmd) => cmd.toLowerCase()).includes(subcommand) &&
143
+ options.allowCredentialRead !== true
137
144
  ) {
138
145
  return {
139
146
  decision: 'deny',
140
147
  risk: 'credential',
141
- reason: 'Direct hcloud configure inspection may expose profile credentials. Use the redacted toolkit tools instead.',
148
+ reason:
149
+ 'Direct hcloud configure inspection may expose profile credentials. Use the redacted toolkit tools instead.',
142
150
  };
143
151
  }
144
152
  }
@@ -160,7 +168,8 @@ export function classifyHcloudArgs(args, options = {}) {
160
168
  }
161
169
 
162
170
  const readOnly = hasReadPrefix(operation, policy);
163
- const executionOps = /(^|\.)(Invoke|SyncInvoke|AsyncInvoke|Send|Trigger|Execute|Start|Reboot|Restart|Stop|Publish|Deploy)/i;
171
+ const executionOps =
172
+ /(^|\.)(Invoke|SyncInvoke|AsyncInvoke|Send|Trigger|Execute|Start|Reboot|Restart|Stop|Publish|Deploy)/i;
164
173
  const isExecution = executionOps.test(operation) && !readOnly;
165
174
  const isWrite = !readOnly && hasWritePrefix(operation, policy);
166
175
 
@@ -176,11 +185,30 @@ export function classifyHcloudArgs(args, options = {}) {
176
185
  return {
177
186
  decision: 'deny',
178
187
  risk: 'write',
179
- reason: 'Huawei Cloud write operation blocked until the agent presents a plan and receives explicit user approval.',
188
+ reason:
189
+ 'Huawei Cloud write operation blocked until the agent presents a plan and receives explicit user approval.',
180
190
  };
181
191
  }
182
192
 
183
- const obsutilWrites = ['mb', 'cp', 'mv', 'rm', 'delete', 'mkdir', 'sync', 'restore', 'chattri', 'bucketpolicy', 'lifecycle', 'cors', 'website', 'sign', 'share-add', 'share-update', 'share-rm'];
193
+ const obsutilWrites = [
194
+ 'mb',
195
+ 'cp',
196
+ 'mv',
197
+ 'rm',
198
+ 'delete',
199
+ 'mkdir',
200
+ 'sync',
201
+ 'restore',
202
+ 'chattri',
203
+ 'bucketpolicy',
204
+ 'lifecycle',
205
+ 'cors',
206
+ 'website',
207
+ 'sign',
208
+ 'share-add',
209
+ 'share-update',
210
+ 'share-rm',
211
+ ];
184
212
  const obsutilReads = ['ls', 'stat', 'cat', 'help', 'version'];
185
213
  const isObs = service.toLowerCase() === 'obs' || service.toLowerCase() === 'hcloud obs';
186
214
  const isObsWrite = isObs && obsutilWrites.includes(operation);
@@ -193,64 +221,86 @@ export function classifyHcloudArgs(args, options = {}) {
193
221
  };
194
222
  }
195
223
  if (isObsRead) {
196
- return applyCommandRiskRules({
197
- decision: 'allow',
198
- risk: 'read_only',
199
- reason: 'OBS read-only operation.',
200
- service,
201
- operation,
202
- args: normalizedArgs,
203
- }, normalizedArgs, options);
224
+ return applyCommandRiskRules(
225
+ {
226
+ decision: 'allow',
227
+ risk: 'read_only',
228
+ reason: 'OBS read-only operation.',
229
+ service,
230
+ operation,
231
+ args: normalizedArgs,
232
+ },
233
+ normalizedArgs,
234
+ options,
235
+ );
204
236
  }
205
237
  if (isObsWrite && options.allowWrites) {
206
- return applyCommandRiskRules({
207
- decision: 'allow',
208
- risk: 'write',
209
- reason: 'OBS write operation approved by user.',
210
- service,
211
- operation,
212
- args: normalizedArgs,
213
- }, normalizedArgs, options);
238
+ return applyCommandRiskRules(
239
+ {
240
+ decision: 'allow',
241
+ risk: 'write',
242
+ reason: 'OBS write operation approved by user.',
243
+ service,
244
+ operation,
245
+ args: normalizedArgs,
246
+ },
247
+ normalizedArgs,
248
+ options,
249
+ );
214
250
  }
215
251
 
216
252
  if (isExecution && options.allowWrites) {
217
- return applyCommandRiskRules({
218
- decision: 'allow',
219
- risk: 'execution',
220
- reason: 'Huawei Cloud execution/trigger operation approved by user.',
221
- service,
222
- operation,
223
- args: normalizedArgs,
224
- }, normalizedArgs, options);
253
+ return applyCommandRiskRules(
254
+ {
255
+ decision: 'allow',
256
+ risk: 'execution',
257
+ reason: 'Huawei Cloud execution/trigger operation approved by user.',
258
+ service,
259
+ operation,
260
+ args: normalizedArgs,
261
+ },
262
+ normalizedArgs,
263
+ options,
264
+ );
225
265
  }
226
266
 
227
267
  if (isWrite && options.allowWrites) {
228
- return applyCommandRiskRules({
268
+ return applyCommandRiskRules(
269
+ {
270
+ decision: 'allow',
271
+ risk: 'write',
272
+ reason: 'Huawei Cloud write operation approved by user.',
273
+ service,
274
+ operation,
275
+ args: normalizedArgs,
276
+ },
277
+ normalizedArgs,
278
+ options,
279
+ );
280
+ }
281
+
282
+ return applyCommandRiskRules(
283
+ {
229
284
  decision: 'allow',
230
- risk: 'write',
231
- reason: 'Huawei Cloud write operation approved by user.',
285
+ risk: readOnly ? 'read_only' : 'unknown_read',
286
+ reason: readOnly
287
+ ? 'Command appears to be a read-only Huawei Cloud operation.'
288
+ : 'Command does not match a known write or secret operation; treat output as untrusted and redact it.',
232
289
  service,
233
290
  operation,
234
291
  args: normalizedArgs,
235
- }, normalizedArgs, options);
236
- }
237
-
238
- return applyCommandRiskRules({
239
- decision: 'allow',
240
- risk: readOnly ? 'read_only' : 'unknown_read',
241
- reason: readOnly
242
- ? 'Command appears to be a read-only Huawei Cloud operation.'
243
- : 'Command does not match a known write or secret operation; treat output as untrusted and redact it.',
244
- service,
245
- operation,
246
- args: normalizedArgs,
247
- }, normalizedArgs, options);
292
+ },
293
+ normalizedArgs,
294
+ options,
295
+ );
248
296
  }
249
297
 
250
298
  function splitSimpleCommand(command) {
251
- return String(command)
252
- .match(/"[^"]*"|'[^']*'|\S+/g)
253
- ?.map((part) => part.replace(/^['"]|['"]$/g, '')) || [];
299
+ return (
300
+ String(command)
301
+ .match(/"[^"]*"|'[^']*'|\S+/g)
302
+ ?.map((part) => part.replace(/^['"]|['"]$/g, '')) || []
303
+ );
254
304
  }
255
305
 
256
306
  export function classifyTextCommand(command, options = {}) {
@@ -261,11 +311,15 @@ export function classifyTextCommand(command, options = {}) {
261
311
  return {
262
312
  decision: 'deny',
263
313
  risk: 'credential',
264
- reason: 'Reading Huawei Cloud credential or profile files is blocked. Use redacted profile inspection tools instead.',
314
+ reason:
315
+ 'Reading Huawei Cloud credential or profile files is blocked. Use redacted profile inspection tools instead.',
265
316
  };
266
317
  }
267
318
 
268
- if (/(^|\s)(env|printenv|Get-ChildItem\s+Env:|gci\s+Env:|dir\s+Env:)/i.test(text) && /HUAWEICLOUD|HWC_|HCLOUD|OS_/i.test(text)) {
319
+ if (
320
+ /(^|\s)(env|printenv|Get-ChildItem\s+Env:|gci\s+Env:|dir\s+Env:)/i.test(text) &&
321
+ /HUAWEICLOUD|HWC_|HCLOUD|OS_/i.test(text)
322
+ ) {
269
323
  return {
270
324
  decision: 'deny',
271
325
  risk: 'credential',
@@ -2,8 +2,7 @@ import { getCredentials } from './hwlink-api.mjs';
2
2
  import { getProxyDispatcher } from '../proxy/proxy-agent.mjs';
3
3
 
4
4
  const HDKIT_BASE_URL =
5
- process.env.HDKITSERVICE_ENDPOINT ||
6
- 'https://devkit.huaweicloud.com/rest/developer/server/hdkitservice/';
5
+ process.env.HDKITSERVICE_ENDPOINT || 'https://devkit.huaweicloud.com/rest/developer/server/hdkitservice/';
7
6
 
8
7
  async function hdkitRequest(method, path, body, timeoutMs = 300000) {
9
8
  const { ak, sk, securitytoken } = getCredentials();
@@ -30,8 +29,13 @@ async function hdkitRequest(method, path, body, timeoutMs = 300000) {
30
29
  body: body ? JSON.stringify(body) : undefined,
31
30
  signal: controller.signal,
32
31
  };
33
- if (dispatcher) fetchOpts.dispatcher = dispatcher;
34
- resp = await fetch(url, fetchOpts);
32
+ if (dispatcher) {
33
+ fetchOpts.dispatcher = dispatcher;
34
+ const { fetch: undiciFetch } = await import('undici');
35
+ resp = await undiciFetch(url, fetchOpts);
36
+ } else {
37
+ resp = await fetch(url, fetchOpts);
38
+ }
35
39
  } finally {
36
40
  clearTimeout(timer);
37
41
  }
@@ -45,9 +49,7 @@ async function hdkitRequest(method, path, body, timeoutMs = 300000) {
45
49
  }
46
50
 
47
51
  if (!resp.ok) {
48
- const err = new Error(
49
- data.message || `hdkitservice error: ${data.code || resp.status}`
50
- );
52
+ const err = new Error(data.message || `hdkitservice error: ${data.code || resp.status}`);
51
53
  err.code = data.code;
52
54
  err.status = resp.status;
53
55
  err.traceId = data.traceId; // 后端实际返回驼峰 traceId
@@ -87,5 +89,3 @@ export async function hdkitCredentials(sessionId, devStageId, enableSts = true)
87
89
 
88
90
  return await hdkitRequest('POST', 'credentials', body);
89
91
  }
90
-
91
-
@@ -1,5 +1,5 @@
1
1
  import crypto from 'node:crypto';
2
- import { resolveCredentials } from '../auth/credentials.mjs';
2
+ import { resolveCredentialsWithRuntime } from '../auth/credentials.mjs';
3
3
  import { getProxyDispatcher } from '../proxy/proxy-agent.mjs';
4
4
 
5
5
  const BASE_URL = process.env.HWLINK_ENDPOINT || 'https://devstation.myhuaweicloud.com';
@@ -14,9 +14,7 @@ function hmacSha256(key, data) {
14
14
 
15
15
  function urlEncode(str) {
16
16
  const hex = (c) => '%' + (c < 16 ? '0' : '') + c.toString(16).toUpperCase();
17
- const noEscape = new Set(
18
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~'.split('')
19
- );
17
+ const noEscape = new Set('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~'.split(''));
20
18
  let out = '';
21
19
  for (const ch of str) {
22
20
  const c = ch.codePointAt(0);
@@ -54,9 +52,7 @@ function signRequest(method, path, query, body, ak, sk, securitytoken) {
54
52
  .join('/') +
55
53
  '/';
56
54
 
57
- const signedHeaders = securitytoken
58
- ? 'host;x-sdk-date;x-security-token'
59
- : 'host;x-sdk-date';
55
+ const signedHeaders = securitytoken ? 'host;x-sdk-date;x-security-token' : 'host;x-sdk-date';
60
56
  const canonicalHeaders = securitytoken
61
57
  ? `host:${host}\nx-sdk-date:${ts}\nx-security-token:${securitytoken}\n`
62
58
  : `host:${host}\nx-sdk-date:${ts}\n`;
@@ -64,14 +60,7 @@ function signRequest(method, path, query, body, ak, sk, securitytoken) {
64
60
  const bodyStr = body ? JSON.stringify(body) : '';
65
61
  const payloadHash = sha256Hex(bodyStr);
66
62
 
67
- const canonicalRequest = [
68
- method,
69
- curi,
70
- cqs,
71
- canonicalHeaders,
72
- signedHeaders,
73
- payloadHash,
74
- ].join('\n');
63
+ const canonicalRequest = [method, curi, cqs, canonicalHeaders, signedHeaders, payloadHash].join('\n');
75
64
 
76
65
  const stringToSign = `SDK-HMAC-SHA256\n${ts}\n${sha256Hex(canonicalRequest)}`;
77
66
  const signature = hmacSha256(sk, stringToSign);
@@ -88,7 +77,7 @@ function signRequest(method, path, query, body, ak, sk, securitytoken) {
88
77
  }
89
78
 
90
79
  export function getCredentials() {
91
- const credentials = resolveCredentials();
80
+ const credentials = resolveCredentialsWithRuntime();
92
81
  return { ak: credentials.ak, sk: credentials.sk, securitytoken: credentials.securityToken };
93
82
  }
94
83
 
@@ -100,7 +89,12 @@ async function apiGet(path, query, ak, sk, securitytoken) {
100
89
  const url = `${BASE_URL}${fullPath}`;
101
90
  const dispatcher = await getProxyDispatcher(url);
102
91
  const fetchOpts = { headers };
103
- if (dispatcher) fetchOpts.dispatcher = dispatcher;
92
+ if (dispatcher) {
93
+ fetchOpts.dispatcher = dispatcher;
94
+ const { fetch: undiciFetch } = await import('undici');
95
+ const resp = await undiciFetch(url, fetchOpts);
96
+ return { status: resp.status, data: await resp.json() };
97
+ }
104
98
  const resp = await fetch(url, fetchOpts);
105
99
  return { status: resp.status, data: await resp.json() };
106
100
  }
@@ -114,7 +108,12 @@ async function apiPost(path, body, ak, sk, securitytoken) {
114
108
  headers: { ...headers, 'Content-Type': 'application/json' },
115
109
  body: JSON.stringify(body),
116
110
  };
117
- if (dispatcher) fetchOpts.dispatcher = dispatcher;
111
+ if (dispatcher) {
112
+ fetchOpts.dispatcher = dispatcher;
113
+ const { fetch: undiciFetch } = await import('undici');
114
+ const resp = await undiciFetch(url, fetchOpts);
115
+ return { status: resp.status, data: await resp.json() };
116
+ }
118
117
  const resp = await fetch(url, fetchOpts);
119
118
  return { status: resp.status, data: await resp.json() };
120
119
  }
@@ -123,7 +122,9 @@ export async function createConnection(envId, ak, sk, securitytoken) {
123
122
  const { status, data } = await apiPost(
124
123
  `/open-api-public/v1/devenvs/${envId}/connections`,
125
124
  { source: 'CLI' },
126
- ak, sk, securitytoken
125
+ ak,
126
+ sk,
127
+ securitytoken,
127
128
  );
128
129
 
129
130
  if (status !== 200 || data?.error_code !== '0000' || !data?.result?.connection_id) {
@@ -138,13 +139,12 @@ export async function createConnection(envId, ak, sk, securitytoken) {
138
139
  const { data: getData } = await apiGet(
139
140
  `/open-api-public/v1/devenvs/${envId}/connections/${connectionId}`,
140
141
  {},
141
- ak, sk, securitytoken
142
+ ak,
143
+ sk,
144
+ securitytoken,
142
145
  );
143
146
 
144
- if (
145
- getData?.result?.connection_info?.url &&
146
- getData.result.connection_info.extensions?.source != null
147
- ) {
147
+ if (getData?.result?.connection_info?.url && getData.result.connection_info.extensions?.source != null) {
148
148
  const u = new URL(getData.result.connection_info.url);
149
149
  u.searchParams.set('source', String(getData.result.connection_info.extensions.source));
150
150
  process.stderr.write(`\rConnection ${connectionId} established (${i}s).\n`);
@@ -158,5 +158,3 @@ export async function createConnection(envId, ak, sk, securitytoken) {
158
158
 
159
159
  throw new Error(`Timed out waiting for connection ${connectionId}`);
160
160
  }
161
-
162
-
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env python3
2
+ import hashlib, http.server, json, os, sys
3
+ class UploadHandler(http.server.BaseHTTPRequestHandler):
4
+ def do_POST(self):
5
+ if self.path != '/upload':
6
+ self.send_error(404)
7
+ return
8
+ target_path = self.headers.get('X-Target-Path', '/workspace/upload.tar.gz')
9
+ content_length = int(self.headers.get('Content-Length', 0))
10
+ os.makedirs(os.path.dirname(target_path), exist_ok=True)
11
+ md5 = hashlib.md5()
12
+ bytes_written = 0
13
+ with open(target_path, 'wb') as f:
14
+ remaining = content_length
15
+ while remaining > 0:
16
+ chunk = self.rfile.read(min(remaining, 65536))
17
+ if not chunk:
18
+ break
19
+ f.write(chunk)
20
+ md5.update(chunk)
21
+ bytes_written += len(chunk)
22
+ remaining -= len(chunk)
23
+ result = json.dumps({'ok': True, 'path': target_path, 'bytes': bytes_written, 'md5': md5.hexdigest()})
24
+ self.send_response(200)
25
+ self.send_header('Content-Type', 'application/json')
26
+ self.send_header('Content-Length', str(len(result)))
27
+ self.end_headers()
28
+ self.wfile.write(result.encode())
29
+ def do_GET(self):
30
+ if self.path == '/health':
31
+ body = b'ok'
32
+ self.send_response(200)
33
+ self.send_header('Content-Length', str(len(body)))
34
+ self.end_headers()
35
+ self.wfile.write(body)
36
+ else:
37
+ self.send_error(404)
38
+ def log_message(self, format, *args):
39
+ pass
40
+ if __name__ == '__main__':
41
+ port = int(sys.argv[1]) if len(sys.argv) > 1 else 8888
42
+ token = sys.argv[2] if len(sys.argv) > 2 else ''
43
+ server = http.server.HTTPServer(('127.0.0.1', port), UploadHandler)
44
+ server.upload_token = token
45
+ server.serve_forever()