huaweicloud-devkit 1.1.5 → 1.1.6-next.1
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
- package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +19 -14
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +119 -55
- package/plugins/huaweicloud-core/skills/huawei-sandbox/references/framework-commands.md +5 -5
- package/plugins/huaweicloud-core/src/mcp-protocol.mjs +20 -18
- package/plugins/huaweicloud-core/src/mcp-server.mjs +30 -2
- package/plugins/huaweicloud-core/src/officeace-paths.mjs +51 -0
- package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +43 -1
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +83 -30
- package/plugins/huaweicloud-core/src/setup-cli.mjs +88 -56
- package/plugins/huaweicloud-core/src/tools.mjs +43 -4
- package/plugins/huaweicloud-core/src/update-check.mjs +32 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://github.com/huaweicloud/huaweicloud-devkit/actions/workflows/ci.yml)
|
|
6
6
|
[](https://www.npmjs.com/package/huaweicloud-devkit)
|
|
7
|
-
[](https://github.com/huaweicloud/huaweicloud-devkit)
|
|
8
8
|
|
|
9
9
|
**[中文](README.zh-CN.md) | English**
|
|
10
10
|
|
package/README.zh-CN.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://github.com/huaweicloud/huaweicloud-devkit/actions/workflows/ci.yml)
|
|
6
6
|
[](https://www.npmjs.com/package/huaweicloud-devkit)
|
|
7
|
-
[](https://github.com/huaweicloud/huaweicloud-devkit)
|
|
8
8
|
|
|
9
9
|
**中文 | [English](README.md)**
|
|
10
10
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
3
|
"mcpName": "io.github.huaweicloud/huaweicloud-devkit",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.6-next.1",
|
|
5
5
|
"kooCliVersion": "7.2.12",
|
|
6
6
|
"description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
|
|
7
7
|
"type": "module",
|
|
@@ -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.
|
|
20
|
+
"version": "1.1.6-next.1",
|
|
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.
|
|
23
|
+
"version": "1.1.6-next.1",
|
|
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.
|
|
20
|
+
"version": "1.1.6-next.1",
|
|
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.
|
|
3
|
+
"version": "1.1.6-next.1",
|
|
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.
|
|
3
|
+
"version": "1.1.6-next.1",
|
|
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.
|
|
5
|
+
"version": "1.1.6-next.1",
|
|
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.",
|
|
@@ -18,8 +18,10 @@ Domain expertise for Huawei Cloud CloudDeploy. Covers application creation, depl
|
|
|
18
18
|
|
|
19
19
|
| Trap | Why |
|
|
20
20
|
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
21
|
+
| Service name is `CodeArtsDeploy` | `hcloud CloudDeploy` returns `[USE_ERROR]不支持的服务名称`. The KooCLI service name is **CodeArtsDeploy** (product name CloudDeploy). Run `hcloud --help` to verify. |
|
|
22
|
+
| Wrong operation names fail | The per-action names historically assumed (Start/List/Create/Delete + `Task`/`Tasks`) do NOT exist — KooCLI returns `[USE_ERROR]不支持的operation`. Use the verified names in Common Workflows. |
|
|
23
|
+
| `project_id` is the CodeArts project ID | Not the IAM project. If CodeArts is not enabled for the account, calls fail with `Deploy.00016902 项目不存在`. |
|
|
21
24
|
| **Flyway SQL dialect mismatch (H2 dev → MySQL prod)** | Spring Boot apps commonly develop with H2 in-memory DB, then deploy to RDS MySQL. Flyway migrations using H2-specific syntax (e.g. `DATEADD`, `CHARACTER_LENGTH`, `BOOLEAN`) silently succeed on H2 but fail on MySQL. Before deploying, audit `V*__*.sql` migration files: replace `DATEADD` with `DATE_ADD`, `BOOLEAN` with `TINYINT(1)`, remove `characterEncoding=utf8mb4` from Spring Boot datasource URL (KooCLI RDS CreateInstance sets charset at the instance level). Use `Flyway.validate-on-migrate=true` in CI to catch dialect issues early. |
|
|
22
|
-
| Service name may be `CodeArtsDeploy` | hcloud service name for deployment may be `CodeArtsDeploy` instead of `CloudDeploy`. Run `hcloud --help` to verify |
|
|
23
25
|
| Deployment hosts need agent | Install CloudDeploy agent on target hosts first |
|
|
24
26
|
| Task must reference application first | Create application before task |
|
|
25
27
|
| Artifact source defaults to OBS | Most deployment tasks pull artifacts from OBS. Ensure bucket and object exist |
|
|
@@ -27,22 +29,25 @@ Domain expertise for Huawei Cloud CloudDeploy. Covers application creation, depl
|
|
|
27
29
|
|
|
28
30
|
## Common Workflows
|
|
29
31
|
|
|
30
|
-
| Task | Operation
|
|
31
|
-
| ---------------------- |
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
32
|
+
| Task | Operation(省略参数用 `<placeholder>`,参数以 `hcloud CodeArtsDeploy <Op> --help` 为准) |
|
|
33
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| List applications | `ListAllApp --cli-region=<r> --project_id=<id> --page=1 --size=10`(`ListDeployTasks` 已由云侧于 2024-09-30 弃用,推荐本接口) |
|
|
35
|
+
| Show app detail | `ShowAppDetailById --cli-region=<r> --app_id=<id>` |
|
|
36
|
+
| Create application | `CreateApp --cli-region=<r> --name=<n> --create_type=template --project_id=<id> --is_draft=false`(`--create_type` 仅有 `template` 一个值) |
|
|
37
|
+
| Create deployment task | `CreateDeployTaskByTemplate --cli-region=<r> --template_id=<id> --task_name=<n>`(deprecated since 2024-09-30 — `--help` recommends `CreateApp`) |
|
|
38
|
+
| Start deployment | `StartDeployTask --cli-region=<r> --task_id=<id>` |
|
|
39
|
+
| Delete task | `DeleteDeployTask --cli-region=<r> --task_id=<id>`(deprecated since 2024-09-30 — `--help` recommends `DeleteApplication --app_id=<id>`) |
|
|
37
40
|
|
|
38
41
|
## Troubleshooting
|
|
39
42
|
|
|
40
|
-
| Error
|
|
41
|
-
|
|
|
42
|
-
| Agent offline
|
|
43
|
-
| Deployment timeout
|
|
44
|
-
| Artifact not found
|
|
45
|
-
| Permission denied
|
|
43
|
+
| Error | Fix |
|
|
44
|
+
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
45
|
+
| Agent offline | Check agent service on target host, network connectivity, firewall rules |
|
|
46
|
+
| Deployment timeout | Check artifact size, increase task timeout, verify target host resources |
|
|
47
|
+
| Artifact not found | Verify OBS bucket and object path, check artifact permissions |
|
|
48
|
+
| Permission denied | Verify IAM roles for deployment: `CodeArtsDeploy FullAccess` or custom policy |
|
|
49
|
+
| `Deploy.00016902` 项目不存在 | CodeArts not enabled or wrong project type: use the **CodeArts project ID** (found in CodeArts console), not the IAM project ID; enable CodeArts first |
|
|
50
|
+
| `APIGW.0301` Incorrect IAM authentication information | Credentials lack CodeArtsDeploy access — use AK/SK with CodeArtsDeploy permissions (STS scoped credentials may be rejected) |
|
|
46
51
|
|
|
47
52
|
## Security
|
|
48
53
|
|
|
@@ -222,29 +222,84 @@ Expose a deployed web app to a public URL and return that URL to the developer.
|
|
|
222
222
|
|
|
223
223
|
**User-facing language rule**: the tunnel mechanism below is an internal implementation detail. Never mention "DevBridge", "tunnel", or the tunnel CLI to the developer. Tell the developer only: "正在生成访问地址..." while exposing, then hand over the final URL.
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
### Step 0: Ensure DevBridge CLI 0.2.x (install or upgrade in place)
|
|
226
|
+
|
|
227
|
+
**Check the version first.** Sandboxes created before Sep 2026 ship devbridge 0.1.13, whose hardcoded gateway (`cn-north-4-bridge.myhuaweicloud.com`) was migrated and now serves a 「服务已迁移」 placeholder page — 0.1.x can no longer connect:
|
|
226
228
|
|
|
227
229
|
```bash
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
230
|
+
export PATH="$HOME/.huawei/bin:$PATH" # PREPEND — appending lets a stale image binary in /usr/local/bin win
|
|
231
|
+
devbridge version 2>/dev/null || echo "not installed"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
- `0.2.x-release` → continue to Step 1.
|
|
235
|
+
- `0.1.x-release` or not installed → upgrade in place (replaces the binary — keep NO second copy):
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# Official installer (GitHub can be slow/flaky from the sandbox — force HTTP/1.1 and retry all errors)
|
|
239
|
+
curl -fsSL --http1.1 --retry 3 --retry-all-errors --max-time 120 \
|
|
240
|
+
https://github.com/huaweicloud/devspace-devbridge/releases/latest/download/install.sh \
|
|
241
|
+
-o /tmp/devbridge-install.sh
|
|
242
|
+
|
|
243
|
+
# Preferred: install via the GitCode mirror (domestic CDN, much faster).
|
|
244
|
+
# Extract the version baked into the installer, then point -u at the matching GitCode tag.
|
|
245
|
+
# Note: GitCode repo is named "devbrige" (missing 'd') — this is the actual repo name, not a typo.
|
|
246
|
+
# GitHub repo is "devspace-devbridge" (correct spelling). Do NOT "fix" the GitCode URL.
|
|
247
|
+
DB_TARGET=$(grep -m1 'DEFAULT_VERSION=' /tmp/devbridge-install.sh | cut -d'"' -f2)
|
|
248
|
+
if ! bash /tmp/devbridge-install.sh -s -u "https://gitcode.com/CloudDeveloperDepartment/devbrige/releases/download/${DB_TARGET}" -v "${DB_TARGET}"; then
|
|
249
|
+
bash /tmp/devbridge-install.sh -s # fallback: GitHub (baked-in URL)
|
|
250
|
+
fi
|
|
251
|
+
export PATH="$HOME/.huawei/bin:$PATH" # PREPEND (session shells do not re-source ~/.bashrc)
|
|
252
|
+
devbridge version # must print 0.2.x-release
|
|
253
|
+
|
|
254
|
+
# Remove the stale image-installed binary so only ONE version remains.
|
|
255
|
+
# (Image sandboxes ship 0.1.x at /usr/local/bin/devbridge; the installer writes ~/.huawei/bin.
|
|
256
|
+
# Compare against the explicit ~/.huawei/bin path — never `command -v`, which can resolve
|
|
257
|
+
# to the stale binary itself when PATH order differs.)
|
|
258
|
+
if [ -x "$HOME/.huawei/bin/devbridge" ] && [ -f /usr/local/bin/devbridge ] && [ "$(readlink -f /usr/local/bin/devbridge)" != "$(readlink -f "$HOME/.huawei/bin/devbridge")" ]; then
|
|
259
|
+
rm -f /usr/local/bin/devbridge && echo "stale /usr/local/bin/devbridge removed"
|
|
232
260
|
fi
|
|
233
|
-
export PATH=$PATH:$HOME/.huawei/bin # installer only writes ~/.bashrc; session shells do not re-source it
|
|
234
261
|
```
|
|
235
262
|
|
|
236
|
-
|
|
263
|
+
- `-s` (silent) is **required** in the sandbox — without it the installer blocks reading `/dev/tty`.
|
|
264
|
+
- **Old tunnels do not survive the upgrade**: 0.1.x-created tunnels are not registered on the s2 gateway — always rebuild the tunnel after upgrading (the expose flow below does this via pre-cleanup).
|
|
265
|
+
- If `10006: quota exceeded` appears after upgrading, stale pre-upgrade tunnels still count against the quota — `devbridge delete-all` and retry.
|
|
266
|
+
|
|
267
|
+
### Step 1: Authenticate with an API Key (0.2.x removed AK/SK login)
|
|
268
|
+
|
|
269
|
+
devbridge 0.2.x removed `--access-key/--secret-key/--huaweicloud`. The only non-interactive auth is a DevBridge API Key. AK/SK (`/tmp/hw_creds.sh`) remains valid for hcloud, but not for devbridge.
|
|
270
|
+
|
|
271
|
+
**Check, then login if the key exists** (the API Key is a long-lived account-level credential stored in its own file `/tmp/hw_api_key`, separate from the temporary AK/SK in `/tmp/hw_creds.sh` — never echo its value):
|
|
237
272
|
|
|
238
273
|
```bash
|
|
239
274
|
source /tmp/hw_creds.sh 2>/dev/null
|
|
240
|
-
|
|
275
|
+
source /tmp/hw_api_key 2>/dev/null
|
|
276
|
+
if [ -n "$HW_API_KEY" ]; then
|
|
277
|
+
devbridge auth login --api-key "$HW_API_KEY" && devbridge auth status
|
|
278
|
+
else
|
|
279
|
+
echo "NO_API_KEY"
|
|
280
|
+
fi
|
|
241
281
|
```
|
|
242
282
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
283
|
+
**If `NO_API_KEY`** — STOP and guide the developer through creating one (wait for the key before continuing):
|
|
284
|
+
|
|
285
|
+
1. **Why (one sentence)**: "沙箱的地址生成服务已升级到 0.2.x,新版本仅支持 API Key 登录(官方变更)。API Key 按账号管理,创建一次长期可用、所有沙箱通用。"
|
|
286
|
+
2. **Where (exact steps)**: open https://devstation.connect.huaweicloud.com/space/devbridge/apikey → 登录控制台 → 选择 DevBridge 场景 → 点击"创建"。**完整值仅在创建时展示一次,立即复制**(`devbridge_` 开头)。
|
|
287
|
+
3. **How to hand it over** (present both, recommend the first):
|
|
288
|
+
- **Recommended**: 在本地终端执行 `export HW_API_KEY=<粘贴Key>`(或写入 shell profile),然后告知"已设置" — Key 不经过对话记录;随后 agent 重新调用 `huaweicloud_sandbox_credentials`(本地 `HW_API_KEY` 会被自动透传注入沙箱)。
|
|
289
|
+
- **Alternative**: 直接把 Key 粘贴在对话中 — agent 通过 `huaweicloud_sandbox_credentials` 的 `api_key` 参数注入,**绝不回显、不复述、不写入日志**。
|
|
290
|
+
4. **Security**: 不要提交到代码库或分享;怀疑泄露时在同页面删除并重建(1 分钟)。
|
|
246
291
|
|
|
247
|
-
**
|
|
292
|
+
**Failure paths**:
|
|
293
|
+
|
|
294
|
+
| Symptom | Guidance |
|
|
295
|
+
| -------------------------------------- | ---------------------------------------------------- |
|
|
296
|
+
| `auth login` returns 401/403 | Key 已被删除或禁用 — 引导在同页面删除旧 Key 重新创建 |
|
|
297
|
+
| 无法访问 API Key 管理页面 | 账号无该页面权限 — 引导联系账号管理员处理 |
|
|
298
|
+
| Key 带空格/换行/缺失 `devbridge_` 前缀 | 引导重新完整复制粘贴 |
|
|
299
|
+
|
|
300
|
+
### Step 2: Expose
|
|
301
|
+
|
|
302
|
+
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>`:
|
|
248
303
|
|
|
249
304
|
```bash
|
|
250
305
|
# 0. Pre-cleanup: kill old processes and stale tunnels
|
|
@@ -273,13 +328,15 @@ else
|
|
|
273
328
|
fi
|
|
274
329
|
```
|
|
275
330
|
|
|
276
|
-
**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.
|
|
331
|
+
**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. Note: a migrated gateway serves its placeholder page with **HTTP 200** — a bare status check is not enough when diagnosing "200 but wrong content".
|
|
277
332
|
|
|
278
333
|
**Quota recovery**: if the tunnel creation fails with `10006: quota exceeded`:
|
|
279
334
|
|
|
280
335
|
```bash
|
|
281
336
|
# Step A: List all tunnels (both active and stale)
|
|
282
|
-
devbridge list -
|
|
337
|
+
# 0.2.x: `devbridge list` (plain table; data rows start with the 8-char tunnel ID)
|
|
338
|
+
# 0.1.x (legacy): `devbridge list -j` (JSON)
|
|
339
|
+
devbridge list
|
|
283
340
|
# Step B: Remove all stale tunnels
|
|
284
341
|
devbridge delete-all
|
|
285
342
|
# Step C: Retry tunnel creation
|
|
@@ -289,10 +346,10 @@ sleep 10 && cat /tmp/host.log
|
|
|
289
346
|
|
|
290
347
|
This eliminates the most common deployment failure — historical tunnels from previous sessions accumulating past the max=10 quota.
|
|
291
348
|
|
|
292
|
-
- The public URL has the form `https://<id>-<port>.
|
|
349
|
+
- The public URL has the form `https://<id>-<port>.devbridge-s2.hwtunnel.com` (from the `Tunnel URL:` line; 8-char lowercase base32 id). The pre-migration bridge domain `cn-north-4-bridge.myhuaweicloud.com` is dead — treat any URL on it as broken and never hand one to the developer.
|
|
293
350
|
- **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).
|
|
294
351
|
- Tunnel `description` (`-d`) accepts only Chinese characters, letters, and digits (0-64). Symbols such as `-`/`_`/spaces are rejected (`Invalid tunnel description`).
|
|
295
|
-
-
|
|
352
|
+
- Official docs: https://huaweicloud.github.io/devspace-devbridge/
|
|
296
353
|
|
|
297
354
|
**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.
|
|
298
355
|
|
|
@@ -446,7 +503,7 @@ For each check, parse the output: if stdout contains `MISSING:` or the tool wasn
|
|
|
446
503
|
| pnpm | `npm i -g pnpm` | Same |
|
|
447
504
|
| yarn | `npm i -g yarn` | Same |
|
|
448
505
|
| Hugo | `curl -fsSL https://github.com/gohugoio/hugo/releases/download/v0.140.0/hugo_extended_0.140.0_linux-amd64.tar.gz -o /tmp/hugo.tar.gz && sudo tar -xzf /tmp/hugo.tar.gz -C /usr/local/bin hugo && rm /tmp/hugo.tar.gz` | Same |
|
|
449
|
-
| DevBridge |
|
|
506
|
+
| DevBridge | Follow [Step 0 of "Expose the deployed app"](#step-0-ensure-devbridge-cli-02x-install-or-upgrade-in-place) — checks version, installs or upgrades in place via the official installer | Same |
|
|
450
507
|
|
|
451
508
|
**If Node.js is missing**, install it first — all build workflows depend on it. Stop and report to the developer if Node.js installation fails.
|
|
452
509
|
|
|
@@ -782,13 +839,15 @@ If the status code is not 2xx/3xx:
|
|
|
782
839
|
|
|
783
840
|
### Step 7: Expose via DevBridge [REQUIRED — deployment incomplete without this]
|
|
784
841
|
|
|
842
|
+
When `deploy_check` returns `nextStep: expose_via_devbridge`, it also returns an executable `remediation` string — run those devbridge commands in the sandbox (after `source /tmp/hw_creds.sh`). Until a tunnel exists, `deploy_check` returns no `publicUrl`; do not fabricate a URL.
|
|
843
|
+
|
|
785
844
|
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.
|
|
786
845
|
|
|
787
846
|
Use `exec_with_session` to background DevBridge. For SSR, DevBridge tunnels the nginx public port (not the Node port directly).
|
|
788
847
|
|
|
789
|
-
**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
|
|
848
|
+
**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` (0.2.x plain table; 0.1.x legacy `-j` JSON), delete stale ones, and retry.
|
|
790
849
|
|
|
791
|
-
Extract the tunnel URL from DevBridge output. The public URL has the form `https://<id>-<port>.
|
|
850
|
+
Extract the tunnel URL from DevBridge output. The public URL has the form `https://<id>-<port>.devbridge-s2.hwtunnel.com`. **Return this URL to the developer as the deployment result.**
|
|
792
851
|
|
|
793
852
|
#### Cross-platform H5 QR code
|
|
794
853
|
|
|
@@ -830,8 +889,8 @@ The tool checks:
|
|
|
830
889
|
|
|
831
890
|
- **nginx_serving** — nginx responds with 2xx/3xx on the app port
|
|
832
891
|
- **output_dir** — build output directory exists and is non-empty
|
|
833
|
-
- **devbridge_tunnel** — DevBridge tunnel is active
|
|
834
|
-
- **tunnel_url_accessible** — tunnel URL returns 200/304
|
|
892
|
+
- **devbridge_tunnel** — DevBridge tunnel is active (version-aware: 0.1.x JSON / 0.2.x table)
|
|
893
|
+
- **tunnel_url_accessible** — tunnel URL returns 200/304 with real content; a gateway migration placeholder page (「服务已迁移」 with HTTP 200) counts as FAIL
|
|
835
894
|
- **qr_code** (cross-platform only) — QR image exists in output dir
|
|
836
895
|
|
|
837
896
|
Returns `complete: true/false`, `score`, and `nextStep` to fix missing items.
|
|
@@ -854,32 +913,36 @@ Returns `complete: true/false`, `score`, and `nextStep` to fix missing items.
|
|
|
854
913
|
|
|
855
914
|
## Critical Warnings
|
|
856
915
|
|
|
857
|
-
| Trap
|
|
858
|
-
|
|
|
859
|
-
| Target not confirmed
|
|
860
|
-
| Agreement required first
|
|
861
|
-
| Real-name required
|
|
862
|
-
| Never expose tunnel details
|
|
863
|
-
|
|
|
864
|
-
|
|
|
865
|
-
|
|
|
866
|
-
|
|
|
867
|
-
|
|
|
868
|
-
|
|
|
869
|
-
|
|
|
870
|
-
|
|
|
871
|
-
|
|
|
872
|
-
|
|
|
873
|
-
|
|
|
874
|
-
|
|
|
875
|
-
|
|
|
876
|
-
|
|
|
877
|
-
| Upload
|
|
878
|
-
|
|
|
879
|
-
|
|
|
880
|
-
|
|
|
881
|
-
| Cross-platform
|
|
882
|
-
|
|
|
916
|
+
| Trap | Why |
|
|
917
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
918
|
+
| Target not confirmed | "部署到华为云" without a named target is NOT a go signal. You MUST run the Target-Selection Gate and get an explicit choice before calling any sandbox lifecycle tool. Skipping it and defaulting to the sandbox is a violation. |
|
|
919
|
+
| 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 |
|
|
920
|
+
| Real-name required | `sandbox_connect` fails if `realnameVerified=false`; tell the developer once and stop, don't loop on connect |
|
|
921
|
+
| Never expose tunnel details | Do not mention "DevBridge"/"tunnel"/"devbridge" to the developer — say "正在生成访问地址..." and hand over only the URL |
|
|
922
|
+
| devbridge 0.2.x needs an API Key | 0.2.x removed AK/SK login (`--access-key/--secret-key/--huaweicloud` are gone). Login with `--api-key "$HW_API_KEY"` from `/tmp/hw_api_key` (long-lived credential, stored separately from `/tmp/hw_creds.sh`). If missing, guide the developer to create one (see Step 1 of "Expose the deployed app") |
|
|
923
|
+
| devbridge 0.1.x is dead | Sandboxes created before Sep 2026 ship 0.1.13, which connects to a migrated gateway serving a 「服务已迁移」 placeholder with HTTP 200. Check `devbridge version` first and upgrade in place (Step 0) — old tunnels never survive the upgrade |
|
|
924
|
+
| CLI PATH | The installer only writes `~/.bashrc`; run `export PATH="$HOME/.huawei/bin:$PATH"` (prepend) in the session before using `devbridge` — appending lets a stale image binary in `/usr/local/bin` win |
|
|
925
|
+
| 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 |
|
|
926
|
+
| Return the deployment URL | Always hand the public URL from the host log to the developer as the final result |
|
|
927
|
+
| 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. |
|
|
928
|
+
| 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. |
|
|
929
|
+
| Session state persists | `exec_with_session` preserves `cd`, env vars, aliases between calls |
|
|
930
|
+
| 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). |
|
|
931
|
+
| 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. |
|
|
932
|
+
| 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. |
|
|
933
|
+
| Destructive commands blocked | `rm -rf /`, `mkfs`, `dd if=`, fork bombs are denied by safety policy |
|
|
934
|
+
| Workspace ID = dev_stage_id | Use `dev_stage_id` from `sandbox_connect` as `workspace_id` for terminal exec |
|
|
935
|
+
| 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 |
|
|
936
|
+
| 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 |
|
|
937
|
+
| Upload file for single files | Use `sandbox_upload_file` for individual files — base64 chunked, reliable for small files (<1MB) |
|
|
938
|
+
| 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") |
|
|
939
|
+
| 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. |
|
|
940
|
+
| 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. |
|
|
941
|
+
| 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. |
|
|
942
|
+
| 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. |
|
|
943
|
+
| Tunnel description: no spaces/hyphens | `devbridge create <name> -d 'hello world'` fails with `Invalid tunnel description: only Chinese characters, digits, letters, length 0-64`. The message is misleading — the real rule is no spaces/hyphens. Omit `-d` or use bare letters/digits. |
|
|
944
|
+
| Use `host`, never `connect` | `devbridge connect` is the sender side (for on-prem machines dialing out). For public preview it loops `Connection failed, retrying...` forever with no hint. Always use `devbridge host <tunnelId> -p <port>`. |
|
|
945
|
+
| Port drift detaches the tunnel | When deploy_nginx auto-increments to a free port, an existing tunnel stays bound to the old port — re-bind: `devbridge port create <tunnelId> -p <newPort> --protocol http -a` and restart `devbridge host`. deploy_nginx emits a warning when this happens. |
|
|
883
946
|
|
|
884
947
|
## Node.js in the sandbox
|
|
885
948
|
|
|
@@ -896,11 +959,12 @@ node --version
|
|
|
896
959
|
|
|
897
960
|
## Environment Variables
|
|
898
961
|
|
|
899
|
-
| Variable | Required | Description
|
|
900
|
-
| ----------------------- | -------- |
|
|
901
|
-
| `HW_ACCESS_KEY` | Yes | Huawei Cloud AK
|
|
902
|
-
| `HW_SECRET_KEY` | Yes | Huawei Cloud SK
|
|
903
|
-
| `HW_SECURITY_TOKEN` | No | STS security token
|
|
904
|
-
| `
|
|
905
|
-
| `
|
|
906
|
-
| `
|
|
962
|
+
| Variable | Required | Description |
|
|
963
|
+
| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
964
|
+
| `HW_ACCESS_KEY` | Yes | Huawei Cloud AK |
|
|
965
|
+
| `HW_SECRET_KEY` | Yes | Huawei Cloud SK |
|
|
966
|
+
| `HW_SECURITY_TOKEN` | No | STS security token |
|
|
967
|
+
| `HW_API_KEY` | No | DevBridge API Key (`devbridge_...`) — required for devbridge 0.2.x tunnel login; injected into the sandbox via `huaweicloud_sandbox_credentials` (`api_key` param or local env) |
|
|
968
|
+
| `HW_WORKSPACE_ID` | No | Default workspace ID |
|
|
969
|
+
| `HDKITSERVICE_ENDPOINT` | No | hdkitservice API endpoint (default: devkit.huaweicloud.com) |
|
|
970
|
+
| `HWLINK_ENDPOINT` | No | DevStation API endpoint (default: devstation.myhuaweicloud.com) |
|
|
@@ -75,10 +75,10 @@ These tools should be available in the sandbox image. Verify with the pre-flight
|
|
|
75
75
|
|
|
76
76
|
Tools that may need installation in the sandbox before building:
|
|
77
77
|
|
|
78
|
-
| Tool | Trigger | Install Command
|
|
79
|
-
| --------- | ------------------------------------ |
|
|
80
|
-
| yarn | `yarn.lock` detected | `npm i -g yarn`
|
|
81
|
-
| Hugo | `hugo.toml` / `config.toml` detected | Download Hugo extended binary (see below)
|
|
82
|
-
| DevBridge | Tunnel exposure step |
|
|
78
|
+
| Tool | Trigger | Install Command |
|
|
79
|
+
| --------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------- |
|
|
80
|
+
| yarn | `yarn.lock` detected | `npm i -g yarn` |
|
|
81
|
+
| Hugo | `hugo.toml` / `config.toml` detected | Download Hugo extended binary (see below) |
|
|
82
|
+
| DevBridge | Tunnel exposure step | Official installer with version check + in-place upgrade (see SKILL.md "Expose the deployed app" Step 0) |
|
|
83
83
|
|
|
84
84
|
> Hugo download: `curl -fsSL https://github.com/gohugoio/hugo/releases/download/v0.140.0/hugo_extended_0.140.0_linux-amd64.tar.gz -o /tmp/hugo.tar.gz && sudo tar -xzf /tmp/hugo.tar.gz -C /usr/local/bin hugo && rm /tmp/hugo.tar.gz`
|
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { resolve, dirname, join } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
|
|
5
1
|
import { TOOL_DEFINITIONS, callTool } from './tools.mjs';
|
|
6
|
-
import { peekCachedUpdateInfo, applyUpdateHint } from './update-check.mjs';
|
|
2
|
+
import { peekCachedUpdateInfo, applyUpdateHint, readInstalledVersion } from './update-check.mjs';
|
|
7
3
|
import { initTelemetry } from './telemetry/telemetry.mjs';
|
|
8
4
|
import { detectAgent } from './telemetry/agent-detect.mjs';
|
|
9
5
|
|
|
10
|
-
const
|
|
11
|
-
const pluginRoot = resolve(__dirname, '..');
|
|
12
|
-
const packageRoot = resolve(pluginRoot, '..', '..');
|
|
13
|
-
let pkgVersion = '0.0.0';
|
|
14
|
-
for (const base of [pluginRoot, packageRoot]) {
|
|
15
|
-
try {
|
|
16
|
-
const version = JSON.parse(readFileSync(join(base, 'package.json'), 'utf8')).version;
|
|
17
|
-
if (version) {
|
|
18
|
-
pkgVersion = version;
|
|
19
|
-
break;
|
|
20
|
-
}
|
|
21
|
-
} catch {}
|
|
22
|
-
}
|
|
6
|
+
const pkgVersion = readInstalledVersion() || '0.0.0';
|
|
23
7
|
|
|
24
8
|
// 会话内首个非 check/upgrade 工具调用附加 _updateInfo,只消费一次。
|
|
25
9
|
// 按会话隔离:同进程内不同会话(A/B)各自首次提示;stdio 用固定 'stdin'。
|
|
@@ -75,6 +59,24 @@ export async function dispatch(method, params, opts = {}) {
|
|
|
75
59
|
}
|
|
76
60
|
|
|
77
61
|
if (method === 'tools/call') {
|
|
62
|
+
const tool = TOOL_DEFINITIONS.find((t) => t.name === params.name);
|
|
63
|
+
if (!tool) {
|
|
64
|
+
// JSON-RPC 2.0: an unknown tool name is a client-side parameter error,
|
|
65
|
+
// not a server fault (#704 D9-2).
|
|
66
|
+
const unknownToolError = new Error(`Unknown tool: ${params.name}`);
|
|
67
|
+
unknownToolError.code = -32602;
|
|
68
|
+
throw unknownToolError;
|
|
69
|
+
}
|
|
70
|
+
const missing = (tool.inputSchema?.required || []).filter(
|
|
71
|
+
(key) => !params.arguments || !Object.hasOwn(params.arguments, key),
|
|
72
|
+
);
|
|
73
|
+
if (missing.length > 0) {
|
|
74
|
+
const invalidParamsError = new Error(
|
|
75
|
+
`Invalid params: missing required field(s) ${missing.map((key) => JSON.stringify(key)).join(', ')} for tool "${params.name}".`,
|
|
76
|
+
);
|
|
77
|
+
invalidParamsError.code = -32602;
|
|
78
|
+
throw invalidParamsError;
|
|
79
|
+
}
|
|
78
80
|
const result = await callTool(params.name, params.arguments || {});
|
|
79
81
|
const decorated = _decorateResult(sessionId, params.name, result);
|
|
80
82
|
return {
|
|
@@ -128,7 +128,13 @@ function runStdioServer() {
|
|
|
128
128
|
useContentLengthFraming = false;
|
|
129
129
|
const line = buffer.subarray(0, lf).toString('utf8').trim();
|
|
130
130
|
buffer = buffer.subarray(lf + 1);
|
|
131
|
-
if (line)
|
|
131
|
+
if (line) {
|
|
132
|
+
try {
|
|
133
|
+
void handleMessage(JSON.parse(line));
|
|
134
|
+
} catch {
|
|
135
|
+
writeParseError();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
132
138
|
continue;
|
|
133
139
|
}
|
|
134
140
|
|
|
@@ -149,11 +155,21 @@ function runStdioServer() {
|
|
|
149
155
|
if (buffer.length < bodyEnd) return false;
|
|
150
156
|
const body = buffer.subarray(bodyStart, bodyEnd).toString('utf8');
|
|
151
157
|
buffer = buffer.subarray(bodyEnd);
|
|
152
|
-
|
|
158
|
+
try {
|
|
159
|
+
void handleMessage(JSON.parse(body));
|
|
160
|
+
} catch {
|
|
161
|
+
writeParseError();
|
|
162
|
+
}
|
|
153
163
|
return true;
|
|
154
164
|
}
|
|
155
165
|
|
|
156
166
|
async function handleMessage(message) {
|
|
167
|
+
// Valid JSON that is not an object (null / array / string) is an invalid
|
|
168
|
+
// request per JSON-RPC 2.0 — reply -32600 rather than silently dropping it.
|
|
169
|
+
if (!message || typeof message !== 'object' || Array.isArray(message)) {
|
|
170
|
+
writeJsonRpcError(-32600, 'Invalid Request');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
157
173
|
if (!Object.hasOwn(message, 'id')) {
|
|
158
174
|
if (message.method === 'notifications/initialized') return;
|
|
159
175
|
return;
|
|
@@ -181,4 +197,16 @@ function runStdioServer() {
|
|
|
181
197
|
stdout.write(json + '\n');
|
|
182
198
|
}
|
|
183
199
|
}
|
|
200
|
+
|
|
201
|
+
function writeJsonRpcError(code, message) {
|
|
202
|
+
writeMessage({
|
|
203
|
+
jsonrpc: '2.0',
|
|
204
|
+
id: null,
|
|
205
|
+
error: { code, message },
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function writeParseError() {
|
|
210
|
+
writeJsonRpcError(-32700, 'Parse error');
|
|
211
|
+
}
|
|
184
212
|
}
|