huaweicloud-devkit 1.1.0-next.8 → 1.1.0

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 (35) hide show
  1. package/README.md +46 -2
  2. package/README.zh-CN.md +45 -2
  3. package/package.json +3 -1
  4. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
  5. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
  6. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
  7. package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +1 -1
  8. package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
  9. package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +29 -12
  10. package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
  11. package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +136 -24
  12. package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +15 -14
  13. package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +1 -1
  14. package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +5 -1
  15. package/plugins/huaweicloud-core/skills/huawei-ecs/references/hc-activity.md +27 -0
  16. package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +11 -11
  17. package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +6 -5
  18. package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +291 -53
  19. package/plugins/huaweicloud-core/skills/huawei-sandbox/references/nginx-templates.md +4 -0
  20. package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +19 -12
  21. package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +26 -26
  22. package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +12 -6
  23. package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +7 -4
  24. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +7 -7
  25. package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +14 -0
  26. package/plugins/huaweicloud-core/src/auth/credentials.mjs +30 -1
  27. package/plugins/huaweicloud-core/src/auth/service.mjs +33 -0
  28. package/plugins/huaweicloud-core/src/detect-framework.mjs +25 -3
  29. package/plugins/huaweicloud-core/src/hcloud-cli.mjs +67 -5
  30. package/plugins/huaweicloud-core/src/mcp-server.mjs +23 -0
  31. package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +11 -3
  32. package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +365 -11
  33. package/plugins/huaweicloud-core/src/search-market.mjs +1 -0
  34. package/plugins/huaweicloud-core/src/setup-cli.mjs +491 -64
  35. package/plugins/huaweicloud-core/src/tools.mjs +184 -14
@@ -52,6 +52,8 @@ Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace t
52
52
  | `huaweicloud_sandbox_exec_one_shot` | One-shot execution (fresh connection; best for long/heavy commands) |
53
53
  | `huaweicloud_sandbox_upload_file` | Upload a local file into the sandbox (chunked base64 write + md5 verify) |
54
54
  | `huaweicloud_sandbox_upload_project` | Upload a local project directory to sandbox (HTTP tunnel, tar.gz + extract) |
55
+ | `huaweicloud_sandbox_deploy_nginx` | Deploy nginx config with permissions fix and reload in one call |
56
+ | `huaweicloud_sandbox_deploy_check` | Run deployment completeness check (nginx, DevBridge, URL, QR if needed) |
55
57
  | `huaweicloud_sandbox_close_session` | Close a persistent terminal session |
56
58
 
57
59
  ### Tool Selection Guide
@@ -63,6 +65,8 @@ Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace t
63
65
  | `curl`, health checks, quick tests | Either — `exec_one_shot` preferred | Stateless, fast |
64
66
  | Server startup (background) | `exec_with_session` | Need to `nohup ... &` then check output in same session |
65
67
  | Deployment scripts | `exec_one_shot+shot` | Long script, fresh connection avoids session timeouts |
68
+ | nginx configuration | `deploy_nginx` | Auto-generates correct template + permissions + reload |
69
+ | Deployment completeness check | `deploy_check` | Verifies nginx, DevBridge, URL, QR before reporting success |
66
70
  | Single file upload (<1MB) | `upload_file` | Base64 chunked, reliable for small files |
67
71
  | Project directory upload (>1MB) | `upload_project` | HTTP tunnel, much faster than base64 for multi-file projects |
68
72
 
@@ -74,6 +78,12 @@ Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace t
74
78
 
75
79
  **Session recovery**: if `exec_with_session` returns `session is not ready`, the WebSocket connection has dropped. Do NOT retry the same session — fall back to `exec_one_shot` for that command instead. To recover state (cd, env vars), reconstruct them explicitly in the one-shot command.
76
80
 
81
+ **Timeout recovery**: if `exec_one_shot` returns a timeout error, check whether partial output is available before declaring failure:
82
+
83
+ - For build commands: check `tail -30 /tmp/build.log` — the build may have completed but the tee pipe didn't flush before timeout
84
+ - For long scripts: split into independent `exec_one_shot` calls (max 5 sub-commands per call, 15s timeout per call)
85
+ - Do NOT retry the same composite command — split and retry individual steps
86
+
77
87
  ## Workflow
78
88
 
79
89
  Setup is a **plugin-side preflight** — the developer should be asked a question only once, when the agreement actually needs signing:
@@ -85,19 +95,32 @@ Setup is a **plugin-side preflight** — the developer should be asked a questio
85
95
  2. **Real-name verification only** (`HDKIT_NOT_REALNAME`): tell the developer once, "Huawei Cloud requires real-name verification before using the sandbox — please complete it in the Huawei Cloud console (实名认证)." and stop — do not retry `connect` in a loop
86
96
  3. **Sign agreement only** (`HDKIT_NOT_AGREEMENT`): **STOP and do NOT sign on your own.** Ask the developer: "Huawei Cloud sandbox requires signing the latest developer service agreement. May I sign it for you?" Then **wait for the developer to explicitly agree** (e.g. "签署" / "确认" / "sign it"). Only after explicit consent call `huaweicloud_sandbox_sign_agreement` and return its result (`signed`/`signedCount`) to the developer. **Never sign a legal agreement on the developer's behalf without their explicit, unambiguous consent.** Do not expose the underlying sandbox/DevBridge service as a separate entity the developer must understand or sign up for
87
97
  4. **Both missing** (`HDKIT_NOT_REALNAME_AND_AGREEMENT`): present **both** requirements together in one message — the real-name verification steps (console, step 2) **and** the agreement-signing request (step 3, wait for explicit consent) — so the developer can complete both at once
88
- 5. **Connect**: `huaweicloud_sandbox_connect` — returns `session_id`, `dev_stage_id`, `connection_id`, `connection_address`
89
- 6. **Cleanup previous deployments** (after first connect to a sandbox): nginx configs and DevBridge tunnels from previous deployments can cause port conflicts and quota errors. Run cleanup immediately after connect:
98
+ 5. **Connect**: `huaweicloud_sandbox_connect` — returns `session_id`, `dev_stage_id`, `connection_id`, `connection_address`. The `source` parameter identifies the calling agent (valid values: `CLI`, `WEB`, `VSCODE`, `WEBVNC`, `WEBPTY`, `WEBIDE`, `CURSOR`, etc. — case-sensitive, all uppercase). The `git` parameter (with `repo_url`, `repo_name`, `target_path`) is accepted but does NOT auto-clone the repository — always clone manually.
99
+ 6. **Cleanup previous deployments** (after first connect to a sandbox): nginx configs, DevBridge tunnels, and stale web processes from previous deployments can cause port conflicts and quota errors. Run cleanup immediately after connect:
90
100
 
91
101
  ```bash
102
+ # Kill stale Node.js web processes from previous deployments
103
+ pkill -9 -f "next-server" 2>/dev/null || true
104
+ pkill -9 -f "next start" 2>/dev/null || true
105
+ pkill -9 -f "nuxt" 2>/dev/null || true
106
+ sleep 1
92
107
  # Remove stale nginx configs from previous deployments
93
- sudo rm -f /etc/nginx/conf.d/app.conf /etc/nginx/conf.d/*.conf.bak 2>/dev/null
108
+ sudo rm -f /etc/nginx/conf.d/*.conf /etc/nginx/conf.d/*.conf.bak 2>/dev/null
94
109
  # Remove stale DevBridge tunnels
95
110
  devbridge delete-all 2>/dev/null || true
96
111
  # Reload nginx to apply
97
112
  sudo nginx -s reload 2>/dev/null || true
98
113
  ```
99
114
 
100
- 7. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox
115
+ 7. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox. After injection, credentials are also written to `/tmp/hw_creds.sh` for shell access:
116
+
117
+ ```bash
118
+ # Source credentials in any sandbox shell before using hcloud/devbridge:
119
+ source /tmp/hw_creds.sh 2>/dev/null && echo "Credentials loaded"
120
+ ```
121
+
122
+ If `source /tmp/hw_creds.sh` returns empty, the credentials injection may not have completed — re-run `huaweicloud_sandbox_credentials`.
123
+
101
124
  8. **Execute commands**: use `huaweicloud_sandbox_exec_with_session` for interactive work (cd, env setup), or `huaweicloud_sandbox_exec_one_shot` for long-running builds/installs. See [Tool Selection Guide](#tool-selection-guide).
102
125
  9. **Upload project** (if deploying local code): `huaweicloud_sandbox_upload_project` to transfer a local project directory into `/workspace/<name>` — uses HTTP tunnel, much faster than base64 for multi-file projects. Use `huaweicloud_sandbox_upload_file` for single files.
103
126
  10. **Expose the deployed app and return the public URL**: set up a public access link for the app port and give the URL to the developer as the final result (see "Expose the deployed app (public URL)") — this step uses an internal tunnel mechanism that must never be mentioned to the developer
@@ -184,31 +207,53 @@ fi
184
207
  export PATH=$PATH:$HOME/.huawei/bin # installer only writes ~/.bashrc; session shells do not re-source it
185
208
  ```
186
209
 
187
- **Login** (non-interactive, credentials come from the developer's local agent — the vault or HW_ACCESS_KEY/HW_SECRET_KEY; never echo them):
210
+ **Login** (non-interactive; credentials from `huaweicloud_sandbox_credentials` are available via `/tmp/hw_creds.sh`). If `source /tmp/hw_creds.sh` returns empty, the credentials injection has expired (sandbox session reconnection resets them) — re-run `huaweicloud_sandbox_credentials` first:
188
211
 
189
212
  ```bash
190
- devbridge auth login --huaweicloud --access-key "$AK" --secret-key "$SK"
213
+ source /tmp/hw_creds.sh 2>/dev/null
214
+ devbridge auth login --huaweicloud --access-key "$HW_ACCESS_KEY" --secret-key "$HW_SECRET_KEY"
191
215
  ```
192
216
 
193
217
  - The `--huaweicloud` flag is required for AK/SK login; without it the CLI tries an interactive browser login, which fails in the sandbox.
194
- - Write the AK/SK to temp files with `umask 077` (or shell vars) and delete them right after login. Verify with `devbridge auth status`.
218
+ - Credentials are stored in `/tmp/hw_creds.sh` (chmod 600) — source it before login, never echo the values.
219
+ - Verify with `devbridge auth status`. If `$HW_ACCESS_KEY` is empty, ensure `huaweicloud_sandbox_credentials` was called first.
195
220
 
196
221
  **Expose** (run the web server and the tunnel in the background, then read the URL from the log; the app lives in the workspace mount, e.g. `/workspace/<repo-name>`):
197
222
 
198
223
  ```bash
199
- # 0. Pre-cleanup: remove stale tunnels to avoid quota exceeded
224
+ # 0. Pre-cleanup: kill old processes and stale tunnels
225
+ pkill -f "devbridge host" 2>/dev/null || true
226
+ sleep 2
200
227
  devbridge delete-all 2>/dev/null || true
201
228
 
202
229
  # 1. Start tunnel
203
230
  nohup devbridge host -p <port> -e 8 > /tmp/host.log 2>&1 &
204
231
  sleep 10 && cat /tmp/host.log
232
+
233
+ # 2. Extract tunnel URL and health-check before returning
234
+ TUNNEL_URL=$(grep -oP 'Tunnel URL: \K.*' /tmp/host.log)
235
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$TUNNEL_URL" 2>/dev/null || echo "000")
236
+ if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "304" ]; then
237
+ echo "Tunnel verified: $TUNNEL_URL (HTTP $HTTP_CODE)"
238
+ else
239
+ echo "WARN: Tunnel URL unreachable (HTTP $HTTP_CODE). Rebuilding tunnel..."
240
+ pkill -f "devbridge host" && sleep 2
241
+ devbridge delete-all 2>/dev/null || true
242
+ nohup devbridge host -p <port> -e 8 > /tmp/host.log 2>&1 &
243
+ sleep 10
244
+ TUNNEL_URL=$(grep -oP 'Tunnel URL: \K.*' /tmp/host.log)
245
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$TUNNEL_URL" 2>/dev/null || echo "000")
246
+ echo "Retry tunnel: $TUNNEL_URL (HTTP $HTTP_CODE)"
247
+ fi
205
248
  ```
206
249
 
250
+ **Never return a tunnel URL without verifying it first** — a stale URL (from a killed tunnel process) will silently fail. Always curl-check before giving the URL to the developer.
251
+
207
252
  **Quota recovery**: if the tunnel creation fails with `10006: quota exceeded`:
208
253
 
209
254
  ```bash
210
255
  # Step A: List all tunnels (both active and stale)
211
- devbridge ls --all
256
+ devbridge list -j
212
257
  # Step B: Remove all stale tunnels
213
258
  devbridge delete-all
214
259
  # Step C: Retry tunnel creation
@@ -220,6 +265,7 @@ This eliminates the most common deployment failure — historical tunnels from p
220
265
 
221
266
  - The public URL has the form `https://<id>-<port>.cn-north-4-bridge.myhuaweicloud.com` (from the `Tunnel URL:` line).
222
267
  - **Return this URL to the developer as the deployment result link.** Keep the host process running (do not close the session before handing over the URL).
268
+ - Tunnel `description` (`-d`) accepts only Chinese characters, letters, and digits (0-64). Symbols such as `-`/`_`/spaces are rejected (`Invalid tunnel description`).
223
269
  - Internal docs: https://huaweicloud.github.io/devspace-devbridge/
224
270
 
225
271
  **No local downgrade**: if the tunnel tooling cannot be installed in the sandbox, STOP and report a generic error ("无法生成访问地址") without technical detail. Never install it on the developer's local machine — a local install would defeat the purpose of sandbox deployment.
@@ -309,6 +355,16 @@ if command -v apt-get >/dev/null 2>&1; then echo "PKG_MGR=apt"; elif command -v
309
355
 
310
356
  Use the detected `PKG_MGR` for all package installations below.
311
357
 
358
+ **Architecture awareness**: the sandbox runs Linux aarch64 (ARM64). Native binaries built on x64 (Windows/macOS Intel) will not execute. Always install dependencies and build inside the sandbox. For projects with native addons (Taro `@swc/core`, Prisma, `esbuild`, `node-gyp`), local x64 pre-build + upload of `dist/` output is a viable alternative when sandbox builds fail.
359
+
360
+ **GitCode SSL**: if `git clone` from GitCode fails with SSL certificate errors, use a one-shot override (do NOT set it globally — that would disable cert verification for every repo):
361
+
362
+ ```bash
363
+ git -c http.sslVerify=false clone <repo-url>
364
+ ```
365
+
366
+ Then retry the clone. This bypasses SSL verification only for this single clone.
367
+
312
368
  #### 3b: Install nginx (before project upload)
313
369
 
314
370
  ```bash
@@ -325,27 +381,24 @@ If nginx cannot be installed, skip to Python HTTP server fallback (see `referenc
325
381
 
326
382
  #### 3c: Verify remaining tools
327
383
 
328
- Before installing project dependencies, verify the sandbox has the required runtime tools. Run this pre-flight check via `exec_one_shot`:
384
+ Before installing project dependencies, verify the sandbox has the required runtime tools. **Run each check as a separate `exec_one_shot` call with 15s timeout** — do not bundle all checks into one command. A single hung subcommand (e.g., `make --version` or `hugo version`) will timeout the entire check, blocking deployment:
329
385
 
330
- ```bash
331
- # Core tools (expected pre-installed in sandbox image)
332
- echo "=== Checking core tools ==="
333
- if command -v node >/dev/null 2>&1; then node --version; else echo "MISSING: node"; fi
334
- if command -v npm >/dev/null 2>&1; then npm --version; else echo "MISSING: npm"; fi
335
- if command -v nginx >/dev/null 2>&1; then nginx -v 2>&1; else echo "MISSING: nginx"; fi
336
- if command -v git >/dev/null 2>&1; then git --version; else echo "MISSING: git"; fi
337
- if command -v python3 >/dev/null 2>&1; then python3 --version; else echo "MISSING: python3"; fi
338
- if command -v curl >/dev/null 2>&1; then curl --version | head -1; else echo "MISSING: curl"; fi
339
- if command -v wget >/dev/null 2>&1; then wget --version | head -1; else echo "MISSING: wget"; fi
340
- if command -v make >/dev/null 2>&1; then make --version | head -1; else echo "MISSING: make"; fi
341
-
342
- # Framework-specific tools (install on demand)
343
- echo "=== Checking framework tools ==="
344
- if command -v pnpm >/dev/null 2>&1; then pnpm --version; else echo "MISSING: pnpm"; fi
345
- if command -v yarn >/dev/null 2>&1; then yarn --version; else echo "MISSING: yarn"; fi
346
- if command -v hugo >/dev/null 2>&1; then hugo version; else echo "MISSING: hugo"; fi
347
- if command -v devbridge >/dev/null 2>&1; then devbridge version; else echo "MISSING: devbridge"; fi
348
386
  ```
387
+ Check 1: node --version (timeout: 15s)
388
+ Check 2: npm --version (timeout: 15s)
389
+ Check 3: nginx -v 2>&1 (timeout: 15s)
390
+ Check 4: git --version (timeout: 15s)
391
+ Check 5: python3 --version (timeout: 15s)
392
+ Check 6: curl --version | head -1 (timeout: 15s)
393
+ Check 7: wget --version | head -1 (timeout: 15s)
394
+ Check 8: make --version | head -1 (timeout: 15s)
395
+ Check 9: pnpm --version (timeout: 15s)
396
+ Check 10: yarn --version (timeout: 15s)
397
+ Check 11: hugo version (timeout: 15s)
398
+ Check 12: devbridge version (timeout: 15s)
399
+ ```
400
+
401
+ For each check, parse the output: if stdout contains `MISSING:` or the tool wasn't found, install it. **Skip framework-specific tools not needed for the current project** (e.g., skip Hugo for React apps).
349
402
 
350
403
  **Install only missing tools** — parse the pre-flight output and install only tools reported as `MISSING`. Use OS-aware commands:
351
404
 
@@ -385,6 +438,15 @@ echo "NODE_ENV=${NODE_ENV:-development}"
385
438
 
386
439
  This must run via `exec_with_session` so the exported variables persist for subsequent build commands in the same session.
387
440
 
441
+ **Prisma / ORM compatibility**: Prisma CLI (`prisma generate`, `prisma db push`, `prisma migrate`) only reads `.env` by default, NOT `.env.local` or `.env.development`. If the project uses `.env.local` for `DATABASE_URL`, link it before any Prisma command:
442
+
443
+ ```bash
444
+ # Prisma requires .env (not .env.local) — symlink if needed
445
+ if [ -f .env.local ] && [ ! -f .env ]; then ln -sf .env.local .env 2>/dev/null || cp .env.local .env; fi
446
+ # Then re-source
447
+ set -a && source .env 2>/dev/null; set +a
448
+ ```
449
+
388
450
  #### 4b: Install Dependencies
389
451
 
390
452
  Use `exec_one_shot` for install (no shared state needed). Skip if `node_modules` already exists:
@@ -395,6 +457,30 @@ cd /workspace/<dirname> && [ -d node_modules ] && echo "SKIP: node_modules exist
395
457
 
396
458
  Wait for install to complete. For large projects on aarch64 sandboxes (1000+ packages), set `timeout_ms` to 180000 (3 min).
397
459
 
460
+ **Node version compatibility**: if `npm install` fails with native module errors (e.g., `rollup 4`, `@esbuild`, `node-gyp`), check the Node version:
461
+
462
+ ```bash
463
+ node -v
464
+ ```
465
+
466
+ Node v24+ uses musl-based binaries on some sandbox images, which may break native addons built for glibc. If native modules fail:
467
+
468
+ - Try `npm install --force` or `npm install --legacy-peer-deps`
469
+ - For rollup 4 projects, consider `npm install rollup@3` as fallback
470
+ - If webpack/rollup native addon errors persist, add `--ignore-scripts` then manually rebuild: `npm rebuild`
471
+
472
+ **Prisma / database initialization**: if `prisma/schema.prisma` exists in the project, initialize the database after install and before build. Prisma Client generation (`prisma generate`) is usually handled by `postinstall`, but `prisma db push` (SQLite) or `prisma migrate deploy` (PostgreSQL/MySQL) must be run manually:
473
+
474
+ ```bash
475
+ cd /workspace/<dirname>
476
+ if [ -f prisma/schema.prisma ]; then
477
+ echo "Prisma schema detected — initializing database..."
478
+ npx prisma db push --skip-generate 2>/dev/null || npx prisma migrate deploy 2>/dev/null || echo "WARN: skip db init"
479
+ fi
480
+ ```
481
+
482
+ > `--skip-generate` avoids redundant generation when `postinstall` already ran `prisma generate`. For SQLite, `DATABASE_URL="file:./dev.db"` must be set in `.env`/`.env.local` before this step.
483
+
398
484
  #### 4c: Build
399
485
 
400
486
  **Timeout strategy by framework type:**
@@ -424,6 +510,20 @@ grep -r "outDir\|outputDir\|dest\|distDir" /workspace/<dirname>/.vitepress/confi
424
510
 
425
511
  If a custom outDir is found, use that instead of the framework-detected default for all subsequent checks.
426
512
 
513
+ **Post-build output verification**: after a successful build, verify the actual `index.html` location. Framework-returned `outputDir` may be inaccurate (e.g., uni-app v3 framework: `dist`, actual: `dist/build/h5`):
514
+
515
+ ```bash
516
+ # Find the real index.html after build
517
+ REAL_INDEX=$(find /workspace/<dirname>/<outputDir> -name "index.html" -type f 2>/dev/null | head -1)
518
+ if [ -n "$REAL_INDEX" ] && [ -f "$REAL_INDEX" ]; then
519
+ REAL_OUTDIR=$(dirname "$REAL_INDEX")
520
+ echo "Actual output dir: $REAL_OUTDIR"
521
+ # Use REAL_OUTDIR for nginx config instead of framework-reported outputDir
522
+ fi
523
+ ```
524
+
525
+ If `REAL_OUTDIR` differs from `<outputDir>`, use `REAL_OUTDIR` for all subsequent steps (nginx config, port check, etc.).
526
+
427
527
  **Post-timeout recovery**: if `exec_one_shot` returns a timeout error (Request timed out), do NOT fail immediately. First dump any captured build log, then check the output directory:
428
528
 
429
529
  ```bash
@@ -468,6 +568,73 @@ tail -5 /tmp/build.log 2>/dev/null
468
568
  - For Hugo/static sites where `installCmd` is `null`, skip install entirely.
469
569
  - For static sites where `buildCmd` is `null`, skip build entirely.
470
570
 
571
+ #### 4c-aux: Build Failure Response
572
+
573
+ Build failures fall into two categories. Handle them differently:
574
+
575
+ | Failure Type | Behavior |
576
+ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
577
+ | **Timeout** (timed out) | Check `/tmp/build.log` tail and output directory — build may have completed but `tee` pipe didn't flush. See Post-timeout recovery above. |
578
+ | **Non-zero exit code** | **STOP immediately.** The build engine explicitly rejected the output. Do NOT retry, modify source, or tweak env vars. Follow the procedure below. |
579
+
580
+ **When a build exits with non-zero exit code:**
581
+
582
+ 1. **STOP** — do NOT retry, do NOT modify source code, do NOT change environment variables
583
+ 2. **Extract the error** from `/tmp/build.log`:
584
+ ```bash
585
+ tail -30 /tmp/build.log
586
+ ```
587
+ 3. **Present the failure to the developer** with:
588
+ - The app name and build command that failed
589
+ - The key error message (last meaningful lines from the build log)
590
+ - A brief diagnosis of the likely cause
591
+ 4. **Offer fix options** (1-3 choices) and **wait for the developer to choose** before applying any fix:
592
+ - Never modify project source files (configs, scripts, etc.) without explicit approval
593
+ - If the fix requires editing source code, tell the developer what to change and where
594
+ 5. **After the developer selects a fix**, apply it, then restart only the failed build — do NOT rebuild already-succeeded apps
595
+
596
+ | Rule | Rationale |
597
+ | ---------------------- | -------------------------------------------------------------- |
598
+ | No blind retry | Retrying without diagnosis wastes time and obscures real error |
599
+ | No silent source edits | Modifying project files without consent destroys trust |
600
+ | Developer decides fix | Different projects have different fix preferences |
601
+ | Only rebuild failed | Avoid redundant work in monorepo deployments |
602
+
603
+ **Monorepo-specific**: if one sub-app build fails while others succeed, report the failure immediately — do NOT delay until all builds complete. Continue other builds in parallel if possible, but notify the developer as soon as a failure is detected.
604
+
605
+ #### 4d: Fix Build Output Permissions
606
+
607
+ After a successful build, fix directory traverse permissions on the build output. Build tools (webpack, vite, uni-app) may create directories with restrictive permissions that block nginx from traversing to `index.html`. **Always resolve symlinks first** — `chmod` does not follow symlinks on Linux:
608
+
609
+ ```bash
610
+ # Fix directory execute permissions for nginx traverse
611
+ REAL_OUTDIR="<outputDir>" # use actual output dir from post-build verification
612
+ PROJECT_ROOT="/workspace/<dirname>"
613
+
614
+ # Resolve symlinks — monorepo sub-apps may be symlinked
615
+ REAL_ROOT=$(readlink -f "$PROJECT_ROOT" 2>/dev/null || echo "$PROJECT_ROOT")
616
+ REL_OUTDIR=$(echo "$REAL_OUTDIR" | sed "s|$PROJECT_ROOT/||")
617
+ REAL_OUTDIR="${REAL_ROOT}/${REL_OUTDIR}"
618
+
619
+ # Fix permissions on resolved real paths
620
+ find "$REAL_ROOT" -path "*/${REAL_OUTDIR}" -prune -o -type d -exec chmod o+x {} \; 2>/dev/null || true
621
+ chmod -R o+rX "$REAL_ROOT" 2>/dev/null || true
622
+ chmod -R +x "$REAL_ROOT/node_modules/.bin" 2>/dev/null || true
623
+ ```
624
+
625
+ This prevents the most common deployment failure: nginx 500 with `stat() ... Permission denied` caused by missing `o+x` on intermediate directories.
626
+
627
+ #### 4e: Write Deployment Fingerprint
628
+
629
+ After a successful build, write a deployment fingerprint into the output directory. This enables `deploy_check` to verify the nginx-served content belongs to the current deployment (not a stale process from an earlier session):
630
+
631
+ ```bash
632
+ echo "deployed-$(date +%s)-<dirname>" > /workspace/<dirname>/<outputDir>/.deploy_fingerprint
633
+ chmod o+r /workspace/<dirname>/<outputDir>/.deploy_fingerprint 2>/dev/null || true
634
+ ```
635
+
636
+ > For SSR proxy type, nginx does not serve static files from `.next` — the fingerprint check will gracefully SKIP in `deploy_check`. The fingerprint is still written as a deployment marker for debugging.
637
+
471
638
  ### Step 5: Port Availability Check
472
639
 
473
640
  **Before configuring nginx or starting the app**, verify the target ports are free. Port conflicts from previous deployments cause silent failures:
@@ -518,17 +685,33 @@ If the port cannot be freed (different user/process), increment to the next avai
518
685
 
519
686
  #### Configure Nginx
520
687
 
521
- Check `references/nginx-templates.md` for the correct template based on `nginxType`:
688
+ Use `huaweicloud_sandbox_deploy_nginx` to write the correct template, fix directory permissions, and reload nginx — all in one call:
689
+
690
+ ```json
691
+ {
692
+ "nginx_type": "<nginxType>",
693
+ "port": <port>,
694
+ "project": "<dirname>",
695
+ "output_dir": "<outputDir>",
696
+ "node_port": <nodePort>,
697
+ "public_port": <publicPort>
698
+ }
699
+ ```
700
+
701
+ - `nginx_type` — `spa` (SPA/SSG/cross-platform), `proxy` (SSR), or `static` (Hugo/Hexo) from `detect_framework`
702
+ - `port` — listen port from framework detection
703
+ - `project` — project dir name under `/workspace`
704
+ - `output_dir` — build output dir relative to `/workspace/<project>`
705
+ - `node_port` — Node.js app port for SSR proxy. Defaults to `<port> + 1` if omitted, and the result includes `nodePort` so you know which port to bind the Node process to.
706
+ - `public_port` — public listen port for SSR proxy (optional, defaults to `port`)
522
707
 
523
- | nginxType | Template | When |
524
- | --------- | ----------------------- | --------------------------- |
525
- | `spa` | Template 1 (try_files) | SPA, SSG, cross-platform H5 |
526
- | `proxy` | Template 2 (proxy_pass) | SSR (Next.js, Nuxt) |
527
- | `static` | Template 3 (plain root) | Hugo, Hexo, static sites |
708
+ The tool automatically handles:
528
709
 
529
- Replace `<port>`, `<project>`, `<outputDir>` (and `<nodePort>`/`<publicPort>` for SSR) with detected values.
710
+ - Writing the correct nginx template (SPA try_files, SSR reverse proxy, or static)
711
+ - Fixing `o+x` directory traverse permissions on the project path
712
+ - Reloading nginx
530
713
 
531
- Write the config with `sudo tee`, then reload nginx. If nginx fails, fall back to Python HTTP server (see `references/nginx-templates.md`).
714
+ If the tool returns `ok: false`, nginx may not be installed — fall back to Python HTTP server (see `references/nginx-templates.md`).
532
715
 
533
716
  **Verify nginx is serving** — curl-check the app before proceeding to DevBridge:
534
717
 
@@ -538,48 +721,97 @@ curl -s -o /dev/null -w "nginx status: %{http_code}\n" http://localhost:<port>
538
721
 
539
722
  If the status code is not 2xx/3xx:
540
723
 
541
- - **403** — likely file permissions: run `chmod -R o+rX /workspace/<project>/<outputDir>` and re-test
724
+ - **403** — run `chmod -R o+rX /workspace/<project>/<outputDir>` and re-test
542
725
  - **000 (connection refused)** — nginx not listening: check `sudo nginx -t` for config errors
543
726
  - **Other** — check nginx error log: `sudo tail -20 /var/log/nginx/error.log`
544
727
 
545
728
  > If `curl` is unavailable, check port via `lsof -i :<port>` or `netstat -tlnp | grep :<port>`
546
729
 
547
- ### Step 6: Start the App
730
+ ### Step 6: Start the App [REQUIRED]
548
731
 
549
732
  - **Static (SPA/SSG/cross-platform)**: nginx is already serving. Skip.
550
- - **SSR**: run `<serveCmd>` via `exec_with_session` to start the Node process in background.
733
+ - **SSR**: `PORT=<nodePort>` prefix is REQUIRED before `<serveCmd>`. nginx `proxy_pass` targets `<nodePort>`, not `<port>` — the two must differ. `deployNginx` returns `nodePort` in its result (defaults to `<port> + 1` for proxy type).
734
+
735
+ **Runtime environment variables**: SSR apps often need `DATABASE_URL`, `NEXTAUTH_URL`, `NEXTAUTH_SECRET`, etc. at runtime. Before starting, verify env vars from Step 4a are still loaded, and re-source `.env` if the shell session was reset:
736
+
737
+ ```bash
738
+ cd /workspace/<dirname>
739
+ # Re-load env vars (SSR apps need them at runtime, not just build time)
740
+ if [ -f .env ]; then set -a && source .env 2>/dev/null; set +a; echo "Loaded .env"; fi
741
+ if [ -f .env.local ]; then set -a && source .env.local 2>/dev/null; set +a; echo "Loaded .env.local"; fi
742
+ # Verify critical vars
743
+ echo "DATABASE_URL=${DATABASE_URL:-<NOT SET>}"
744
+ echo "NEXTAUTH_URL=${NEXTAUTH_URL:-<NOT SET>}"
745
+ ```
551
746
 
552
- ### Step 7: Expose via DevBridge
747
+ Then start with `PORT=<nodePort> <serveCmd>` via `exec_with_session` to run the Node process in background.
748
+
749
+ ### Step 7: Expose via DevBridge [REQUIRED — deployment incomplete without this]
553
750
 
554
751
  Follow the standard [Expose the deployed app](#expose-the-deployed-app-public-url) procedure. The app is already running on the detected port — only DevBridge tunnel setup is needed.
555
752
 
556
753
  Use `exec_with_session` to background DevBridge. For SSR, DevBridge tunnels the nginx public port (not the Node port directly).
557
754
 
558
- **Pre-flight**: always run `devbridge delete-all` before creating a new tunnel to prevent `10006: quota exceeded` from accumulated stale tunnels. If you still get quota error, list tunnels with `devbridge ls --all`, delete stale ones, and retry.
755
+ **Pre-flight**: always run `devbridge delete-all` before creating a new tunnel to prevent `10006: quota exceeded` from accumulated stale tunnels. If you still get quota error, list tunnels with `devbridge list -j`, delete stale ones, and retry.
559
756
 
560
- ### Step 8: Return URL
757
+ Extract the tunnel URL from DevBridge output. The public URL has the form `https://<id>-<port>.cn-north-4-bridge.myhuaweicloud.com`. **Return this URL to the developer as the deployment result.**
561
758
 
562
- Extract the tunnel URL from DevBridge output and return it to the developer.
759
+ #### Cross-platform H5 QR code
563
760
 
564
- **For cross-platform H5 apps** (Taro, uni-app), also generate a QR code for mobile scanning:
761
+ **If `detect_framework` returned `type: "cross-platform"` (Taro, uni-app)**, after exposing the tunnel, generate a QR code for mobile scanning:
565
762
 
566
763
  ```bash
567
764
  TUNNEL_URL="<extracted-tunnel-url>"
568
765
 
569
- # Method 1 (preferred): PNG via curl API (works on all terminals)
570
- curl -s "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$TUNNEL_URL")" -o /workspace/qr.png
571
- chmod o+r /workspace/qr.png
572
- echo "QR code saved. Scan QR to access on mobile."
766
+ # Generate QR inside nginx-served output directory so it is accessible via the tunnel URL
767
+ curl -s "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$TUNNEL_URL")" -o /workspace/<dirname>/<outputDir>/qr.png
768
+ chmod o+r /workspace/<dirname>/<outputDir>/qr.png
769
+ echo "QR code URL: ${TUNNEL_URL}/qr.png"
573
770
  echo "Desktop URL: $TUNNEL_URL"
771
+ ```
772
+
773
+ **Do NOT use `qrencode -t ANSI256`** — terminal ANSI/ASCII QR codes have low precision and phone cameras cannot scan them. Also, never save the QR image outside the nginx root (e.g., `/workspace/qr.png`) — it must be inside the output directory so it is served by nginx alongside the app.
774
+
775
+ **After generating the QR code**, return both URLs to the developer:
776
+
777
+ ```
778
+ 桌面访问: <tunnel-url>
779
+ 手机扫码: <tunnel-url>/qr.png
780
+ ```
781
+
782
+ #### Deployment Completion Check [REQUIRED]
574
783
 
575
- # Method 2 (fallback): terminal ANSI QR (requires qrencode, may not render on all terminals)
576
- # apt-get install -y qrencode || yum install -y qrencode
577
- # qrencode -t ANSI256 -m 1 -s 2 "$TUNNEL_URL"
784
+ **Before reporting success, call `huaweicloud_sandbox_deploy_check`** to verify the deployment is complete:
785
+
786
+ ```json
787
+ {
788
+ "port": <port>,
789
+ "project": "<dirname>",
790
+ "output_dir": "<outputDir>",
791
+ "framework_type": "<type>"
792
+ }
578
793
  ```
579
794
 
580
- If the sandbox cannot reach `api.qrserver.com`, fall back to installing `qrencode` for terminal QR output. Always `chmod o+r` the generated QR image file.
795
+ The tool checks:
796
+
797
+ - **nginx_serving** — nginx responds with 2xx/3xx on the app port
798
+ - **output_dir** — build output directory exists and is non-empty
799
+ - **devbridge_tunnel** — DevBridge tunnel is active
800
+ - **tunnel_url_accessible** — tunnel URL returns 200/304
801
+ - **qr_code** (cross-platform only) — QR image exists in output dir
802
+
803
+ Returns `complete: true/false`, `score`, and `nextStep` to fix missing items.
804
+
805
+ **If `complete` is false, follow `nextStep` to resolve before reporting success.** Do not return a deployment URL until `complete: true`.
806
+
807
+ | Framework Type | Must Return |
808
+ | --------------------------- | ------------------------- |
809
+ | `cross-platform` | Desktop URL + QR code URL |
810
+ | `spa` / `ssg` / `static` | Desktop URL |
811
+ | `ssr` | Desktop URL |
812
+ | `monorepo` (cross-platform) | Desktop URL + QR code URL |
581
813
 
582
- Return both the QR code and the URL to the developer. For cross-platform apps, mention: "手机扫描二维码即可访问".
814
+ **If the framework is cross-platform and the QR code was not generated, the deployment is incomplete — go back and generate it before reporting success.**
583
815
 
584
816
  ## References
585
817
 
@@ -597,8 +829,12 @@ Return both the QR code and the URL to the developer. For cross-platform apps, m
597
829
  | CLI PATH | The installer only writes `~/.bashrc`; run `export PATH=$PATH:$HOME/.huawei/bin` in the session before using `devbridge` |
598
830
  | 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 |
599
831
  | Return the deployment URL | Always hand the public URL from the host log to the developer as the final result |
832
+ | Deploy is not just nginx | Configuring nginx does NOT complete the deployment. Steps 7 (DevBridge expose) and deploy_check are REQUIRED — `deploy_nginx` returns `nextStep: expose_via_devbridge` as a reminder. Do not stop after nginx. |
833
+ | Call deploy_check before success | Always call `huaweicloud_sandbox_deploy_check` before reporting deployment success. A green nginx status does not mean the tunnel is accessible — verify end-to-end with the tool. |
600
834
  | Session state persists | `exec_with_session` preserves `cd`, env vars, aliases between calls |
601
835
  | 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). |
836
+ | SSR nginx/Node ports must differ | nginx `proxy_pass` targets `<nodePort>`, not `<port>`. `deploy_nginx` auto-defaults `nodePort` to `<port>+1` — always start the Node process with `PORT=<nodePort>` to match. Same-port = EADDRINUSE. |
837
+ | HTTP 200 ≠ correct content | A green HTTP check does not guarantee the right project is serving — old processes from a previous session bound to the same port will still return 200. `deploy_check` verifies the deployment fingerprint to catch this. |
602
838
  | Destructive commands blocked | `rm -rf /`, `mkfs`, `dd if=`, fork bombs are denied by safety policy |
603
839
  | Workspace ID = dev_stage_id | Use `dev_stage_id` from `sandbox_connect` as `workspace_id` for terminal exec |
604
840
  | 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 |
@@ -607,6 +843,8 @@ Return both the QR code and the URL to the developer. For cross-platform apps, m
607
843
  | 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") |
608
844
  | 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. |
609
845
  | 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. |
846
+ | Cross-platform needs QR code | When `detect_framework` returns `type: "cross-platform"` (Taro, uni-app), generating a QR code image is **mandatory** — the deployment is incomplete without it. Check the Deployment Completion Check table in Step 7. |
847
+ | Build fails do NOT auto-fix | When a build exits with non-zero exit code, STOP and present the error + fix options to the developer. Do not silently retry, modify configs, or change source files without explicit approval. See 4c-aux. |
610
848
 
611
849
  ## Node.js in the sandbox
612
850
 
@@ -42,6 +42,7 @@ sudo tee /etc/nginx/conf.d/app.conf > /dev/null << 'NGINX_EOF'
42
42
  server {
43
43
  listen <publicPort>;
44
44
  server_name _;
45
+ large_client_header_buffers 4 32k;
45
46
 
46
47
  location / {
47
48
  proxy_pass http://127.0.0.1:<nodePort>;
@@ -54,6 +55,9 @@ server {
54
55
  proxy_set_header X-Forwarded-Proto $scheme;
55
56
  proxy_cache_bypass $http_upgrade;
56
57
  proxy_read_timeout 60s;
58
+ proxy_buffer_size 128k;
59
+ proxy_buffers 4 256k;
60
+ proxy_busy_buffers_size 256k;
57
61
  }
58
62
  }
59
63
  NGINX_EOF
@@ -8,7 +8,7 @@ version: 1
8
8
 
9
9
  **STOP - Do not answer from general knowledge.** Follow the procedure below.
10
10
 
11
- Always run `hcloud SMN <Operation> --help` or `hcloud DMS <Operation> --help` before constructing commands.
11
+ Always run `hcloud SMN <Operation> --help`, `hcloud Kafka <Operation> --help`, `hcloud RabbitMQ <Operation> --help`, or `hcloud RocketMQ <Operation> --help` before constructing commands.
12
12
 
13
13
  ## Overview
14
14
 
@@ -38,21 +38,28 @@ Domain expertise for SMN (Simple Message Notification) and DMS (Distributed Mess
38
38
 
39
39
  ## DMS
40
40
 
41
- ### Service Types
41
+ > **DMS 是产品统一品牌**,涵盖 Kafka、RabbitMQ、RocketMQ 三种引擎。在 KooCLI 中,三种引擎被拆分为独立服务,**不存在 `hcloud DMS` 命令**。
42
42
 
43
- | Type | KooCLI Service |
44
- | -------- | -------------- |
45
- | Kafka | `hcloud DMS` |
46
- | RabbitMQ | `hcloud DMS` |
47
- | RocketMQ | `hcloud DMS` |
43
+ ### Engine → KooCLI Service Mapping
44
+
45
+ | Engine | KooCLI Service | Description |
46
+ | -------- | ----------------- | -------------------- |
47
+ | Kafka | `hcloud Kafka` | 高吞吐分布式消息队列 |
48
+ | RabbitMQ | `hcloud RabbitMQ` | AMQP 消息代理 |
49
+ | RocketMQ | `hcloud RocketMQ` | 低延迟金融级消息队列 |
48
50
 
49
51
  ### Common Workflows
50
52
 
51
- | Task | Operation |
52
- | --------------------- | -------------------------------------------------- |
53
- | List instances | `ListInstances --cli-region=<r> --project_id=<p>` |
54
- | Create Kafka instance | `CreateInstance --cli-region=<r> --project_id=<p>` |
55
- | List Kafka topics | `ListTopics --cli-region=<r> --project_id=<p>` |
53
+ | Task | Service | Operation |
54
+ | ------------------------ | ---------- | ------------------------------------------------------------------ |
55
+ | List Kafka instances | `Kafka` | `ListInstances --cli-region=<r> --project_id=<p>` |
56
+ | List RabbitMQ instances | `RabbitMQ` | `ListInstancesDetails --cli-region=<r> --project_id=<p>` |
57
+ | List RocketMQ instances | `RocketMQ` | `ListInstances --cli-region=<r> --project_id=<p>` |
58
+ | Create Kafka instance | `Kafka` | `CreatePostPaidKafkaInstance --cli-region=<r> --project_id=<p>` |
59
+ | Create RabbitMQ instance | `RabbitMQ` | `CreatePostPaidInstanceByEngine --cli-region=<r> --project_id=<p>` |
60
+ | Create RocketMQ instance | `RocketMQ` | `CreateInstanceByEngine --cli-region=<r> --project_id=<p>` |
61
+ | List Kafka topics | `Kafka` | `ListInstanceTopics --cli-region=<r> --project_id=<p>` |
62
+ | List RocketMQ topics | `RocketMQ` | `ListRocketInstanceTopics --cli-region=<r> --project_id=<p>` |
56
63
 
57
64
  Discover exact parameters with `--help` before executing any command.
58
65