huaweicloud-devkit 1.1.0-next.4 → 1.1.0-next.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.0-next.4",
3
+ "version": "1.1.0-next.5",
4
4
  "description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
5
5
  "type": "module",
6
6
  "files": [
@@ -17,7 +17,7 @@
17
17
  "mcpServers": "./.mcp.json",
18
18
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
19
19
  "skills": "./skills/",
20
- "version": "1.1.0-next.4",
20
+ "version": "1.1.0-next.5",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
@@ -20,7 +20,7 @@
20
20
  "mcpServers": "./.mcp.json",
21
21
  "description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
22
22
  "skills": "./skills/",
23
- "version": "1.1.0-next.4",
23
+ "version": "1.1.0-next.5",
24
24
  "author": {
25
25
  "name": "HuaweiCloud Mate",
26
26
  "url": "https://github.com/huaweicloud"
@@ -17,7 +17,7 @@
17
17
  "mcpServers": "./.mcp.json",
18
18
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
19
19
  "skills": "./skills/",
20
- "version": "1.1.0-next.4",
20
+ "version": "1.1.0-next.5",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.0-next.4",
3
+ "version": "1.1.0-next.5",
4
4
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
5
  "author": {
6
6
  "name": "HuaweiCloud Mate",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.0-next.4",
3
+ "version": "1.1.0-next.5",
4
4
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
5
  "author": {
6
6
  "name": "HuaweiCloud Mate",
@@ -2,7 +2,7 @@
2
2
  "name": "huaweicloud-devkit",
3
3
  "id": "huaweicloud-devkit",
4
4
  "displayName": "HuaweiCloud DevKit",
5
- "version": "1.1.0-next.4",
5
+ "version": "1.1.0-next.5",
6
6
  "family": "bundle-plugin",
7
7
  "bundleFormat": "codex",
8
8
  "description": "Guide coding agents to use Huawei Cloud safely — KooCLI, APIs, SDKs, 28 MCP tools, skills, and safety guardrails.",
@@ -335,23 +335,25 @@ Extract the tunnel URL from DevBridge output and return it to the developer. For
335
335
 
336
336
  ## Critical Warnings
337
337
 
338
- | Trap | Why |
339
- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
340
- | Agreement required first | `sandbox_connect` fails if the agreement isn't signed; the `sandbox_check_user` preflight detects this, so surface it to the developer only when signing is needed |
341
- | Real-name required | `sandbox_connect` fails if `realnameVerified=false`; tell the developer once and stop, don't loop on connect |
342
- | Never expose tunnel details | Do not mention "DevBridge"/"tunnel"/"devbridge" to the developer — say "正在生成访问地址..." and hand over only the URL |
343
- | Login needs `--huaweicloud` | `devbridge auth login --access-key/--secret-key` without `--huaweicloud` falls back to interactive browser login, which fails in the sandbox |
344
- | CLI PATH | The installer only writes `~/.bashrc`; run `export PATH=$PATH:$HOME/.huawei/bin` in the session before using `devbridge` |
345
- | Never install tunnel tooling locally | If the sandbox cannot install it, report a generic error and stop — installing on the developer's machine defeats sandbox deployment |
346
- | Return the deployment URL | Always hand the public URL from the host log to the developer as the final result |
347
- | Session state persists | `exec_with_session` preserves `cd`, env vars, aliases between calls |
348
- | Long commands prefer one-shot | `exec_one_shot` creates a fresh connection per call — more stable for builds, installs, and scripts >30s. See [Tool Selection Guide](#tool-selection-guide). |
349
- | Destructive commands blocked | `rm -rf /`, `mkfs`, `dd if=`, fork bombs are denied by safety policy |
350
- | Workspace ID = dev_stage_id | Use `dev_stage_id` from `sandbox_connect` as `workspace_id` for terminal exec |
351
- | Projects live in `/workspace` | Clone/install project code under `/workspace/<repo-name>` (filesystem-root workspace mount, not `$HOME/workspace`), never in `/tmp` — ephemeral locations lose the project when the sandbox session restarts |
352
- | Upload project for local code | Use `sandbox_upload_project` to transfer local projects — packages as tar.gz, uploads via HTTP tunnel, extracts on sandbox. Much faster than base64 for multi-file projects |
353
- | Upload file for single files | Use `sandbox_upload_file` for individual files — base64 chunked, reliable for small files (<1MB) |
354
- | Node.js >= 22 required | Sandbox terminal uses built-in WebSocket (globalThis.WebSocket); if Node.js is missing, install it from the Huawei Cloud mirror (see "Node.js in the sandbox") |
338
+ | Trap | Why |
339
+ | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
340
+ | Agreement required first | `sandbox_connect` fails if the agreement isn't signed; the `sandbox_check_user` preflight detects this, so surface it to the developer only when signing is needed |
341
+ | Real-name required | `sandbox_connect` fails if `realnameVerified=false`; tell the developer once and stop, don't loop on connect |
342
+ | Never expose tunnel details | Do not mention "DevBridge"/"tunnel"/"devbridge" to the developer — say "正在生成访问地址..." and hand over only the URL |
343
+ | Login needs `--huaweicloud` | `devbridge auth login --access-key/--secret-key` without `--huaweicloud` falls back to interactive browser login, which fails in the sandbox |
344
+ | CLI PATH | The installer only writes `~/.bashrc`; run `export PATH=$PATH:$HOME/.huawei/bin` in the session before using `devbridge` |
345
+ | Never install tunnel tooling locally | If the sandbox cannot install it, report a generic error and stop — installing on the developer's machine defeats sandbox deployment |
346
+ | Return the deployment URL | Always hand the public URL from the host log to the developer as the final result |
347
+ | Session state persists | `exec_with_session` preserves `cd`, env vars, aliases between calls |
348
+ | Long commands prefer one-shot | `exec_one_shot` creates a fresh connection per call — more stable for builds, installs, and scripts >30s. See [Tool Selection Guide](#tool-selection-guide). |
349
+ | Destructive commands blocked | `rm -rf /`, `mkfs`, `dd if=`, fork bombs are denied by safety policy |
350
+ | Workspace ID = dev_stage_id | Use `dev_stage_id` from `sandbox_connect` as `workspace_id` for terminal exec |
351
+ | Projects live in `/workspace` | Clone/install project code under `/workspace/<repo-name>` (filesystem-root workspace mount, not `$HOME/workspace`), never in `/tmp` — ephemeral locations lose the project when the sandbox session restarts |
352
+ | Upload project for local code | Use `sandbox_upload_project` to transfer local projects — packages as tar.gz, uploads via HTTP tunnel, extracts on sandbox. Much faster than base64 for multi-file projects |
353
+ | Upload file for single files | Use `sandbox_upload_file` for individual files — base64 chunked, reliable for small files (<1MB) |
354
+ | Node.js >= 22 required | Sandbox terminal uses built-in WebSocket (globalThis.WebSocket); if Node.js is missing, install it from the Huawei Cloud mirror (see "Node.js in the sandbox") |
355
+ | Sandbox restart kills processes | After sandbox restarts, all user processes (nginx, Node.js, Python servers) are stopped. Re-run startup commands and verify ports are listening before proceeding. |
356
+ | Cross-platform binaries incompatible | The sandbox runs Linux. Native binaries built on Windows/macOS (e.g., Prisma client, `node_modules/.prisma/`, platform-specific native addons) will not execute. Always install and build dependencies inside the sandbox, not locally. |
355
357
 
356
358
  ## Node.js in the sandbox
357
359
 
@@ -155,7 +155,9 @@ export async function execWithSession(workspaceId, command, username, timeoutMs)
155
155
 
156
156
  export const UPLOAD_CHUNK_SIZE = 30000;
157
157
 
158
- export const UPLOAD_BATCH_SIZE = 5;
158
+ export const UPLOAD_BATCH_SIZE = 2;
159
+
160
+ export const UPLOAD_MAX_RETRIES = 3;
159
161
 
160
162
  export function splitBase64Chunks(base64, chunkSize = UPLOAD_CHUNK_SIZE) {
161
163
  const chunks = [];
@@ -189,11 +191,21 @@ export async function uploadFileWithSession(workspaceId, localPath, remotePath,
189
191
  const batchNum = Math.floor(batchStart / UPLOAD_BATCH_SIZE) + 1;
190
192
  const totalBatches = Math.ceil(chunks.length / UPLOAD_BATCH_SIZE);
191
193
  const cmd = `printf '%s' '${combinedChunk}' >> "${tmp}"`;
192
- const res = await execWithSession(workspaceId, cmd, username, timeoutMs);
193
- if (res.exitCode !== 0) {
194
- throw new Error(
195
- `sandbox upload: failed writing batch ${batchNum}/${totalBatches}: ${res.stdout || res.error || res.exitCode}`,
196
- );
194
+
195
+ let batchOk = false;
196
+ let lastError;
197
+ for (let retry = 0; retry < UPLOAD_MAX_RETRIES; retry++) {
198
+ const res = await execWithSession(workspaceId, cmd, username, timeoutMs);
199
+ if (res.exitCode === 0) {
200
+ batchOk = true;
201
+ break;
202
+ }
203
+ lastError = res.stdout || res.error || res.exitCode;
204
+ console.error(` upload retry ${retry + 1}/${UPLOAD_MAX_RETRIES} for batch ${batchNum}/${totalBatches}`);
205
+ await new Promise((r) => setTimeout(r, 2000));
206
+ }
207
+ if (!batchOk) {
208
+ throw new Error(`sandbox upload: failed writing batch ${batchNum}/${totalBatches}: ${lastError}`);
197
209
  }
198
210
  if (batchNum % 10 === 0 || batchNum === totalBatches) {
199
211
  console.error(` upload progress: batch ${batchNum}/${totalBatches}`);
@@ -556,14 +568,25 @@ export async function uploadProjectWithSession(
556
568
  uploadLog(`uploadProject: ${localDir} -> ${archiveRemotePath} (archive=${archiveSize} bytes, md5=${expectedMd5})`);
557
569
 
558
570
  let result;
559
- try {
560
- result = await uploadViaHttpTunnel(workspaceId, archivePath, archiveRemotePath, username, timeoutMs, options);
561
- } catch (tunnelError) {
562
- uploadLog(`uploadProject: HTTP tunnel upload failed: ${tunnelError.message}`);
571
+ let tunnelError;
572
+ for (let attempt = 0; attempt < UPLOAD_MAX_RETRIES; attempt++) {
573
+ try {
574
+ await cleanupFileServer(workspaceId, username).catch(() => {});
575
+ result = await uploadViaHttpTunnel(workspaceId, archivePath, archiveRemotePath, username, timeoutMs, options);
576
+ tunnelError = null;
577
+ break;
578
+ } catch (error) {
579
+ tunnelError = error;
580
+ uploadLog(`uploadProject: attempt ${attempt + 1}/${UPLOAD_MAX_RETRIES} failed: ${error.message}`);
581
+ await new Promise((r) => setTimeout(r, 5000));
582
+ }
583
+ }
584
+ if (tunnelError) {
585
+ uploadLog(`uploadProject: all ${UPLOAD_MAX_RETRIES} attempts failed: ${tunnelError.message}`);
563
586
  uploadLog(`uploadProject: NOT falling back to base64 (removed). Rethrowing with diagnostics.`);
564
587
  cleanupLocalArchive(archivePath);
565
588
  throw new Error(
566
- `sandbox upload failed: HTTP tunnel could not transfer the project archive. ` +
589
+ `sandbox upload failed after ${UPLOAD_MAX_RETRIES} attempts: HTTP tunnel could not transfer the project archive. ` +
567
590
  `Archive size: ${(archiveSize / 1024).toFixed(1)}KB. ` +
568
591
  `Root cause: ${tunnelError.message}. ` +
569
592
  `Diagnostic log: ${UPLOAD_LOG_PATH}`,
@@ -648,21 +648,27 @@ export const TOOL_DEFINITIONS = [
648
648
  },
649
649
  {
650
650
  name: 'huaweicloud_voucher_status',
651
- description: '查询激励金代金券领取状态。',
651
+ description: 'Query the Huawei Cloud incentive voucher claim status.',
652
652
  inputSchema: {
653
653
  type: 'object',
654
654
  properties: {
655
- domain_id: { type: 'string' },
655
+ domain_id: {
656
+ type: 'string',
657
+ description: 'Optional. Leave empty in production — account is resolved from IAM automatically.',
658
+ },
656
659
  },
657
660
  },
658
661
  },
659
662
  {
660
663
  name: 'huaweicloud_voucher_claim',
661
- description: '领取激励金代金券。领取前会自动检查是否已领取。',
664
+ description: 'Claim the Huawei Cloud incentive voucher. Automatically checks if already claimed.',
662
665
  inputSchema: {
663
666
  type: 'object',
664
667
  properties: {
665
- domain_id: { type: 'string' },
668
+ domain_id: {
669
+ type: 'string',
670
+ description: 'Optional. Leave empty in production — account is resolved from IAM automatically.',
671
+ },
666
672
  },
667
673
  },
668
674
  },