huaweicloud-devkit 1.1.8 → 1.1.9-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/integrations/opencode/package-lock.json +24 -0
- package/integrations/opencode/package.json +10 -0
- package/integrations/opencode/plugins/huaweicloud-devkit.js +194 -0
- package/package.json +2 -2
- 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-ecs/SKILL.md +5 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +16 -2
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +22 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/windows-ssh.md +118 -0
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +3 -3
- package/plugins/huaweicloud-core/skills/huawei-iac/SKILL.md +36 -36
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +2 -2
- package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +30 -3
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +35 -6
- package/plugins/huaweicloud-core/skills/huawei-voucher/SKILL.md +1 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +2 -2
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +6 -6
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +16 -9
- package/plugins/huaweicloud-core/src/auth/credential-validator.mjs +37 -2
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +14 -27
- package/plugins/huaweicloud-core/src/auth/guidance.mjs +112 -0
- package/plugins/huaweicloud-core/src/auth/reconcile.mjs +36 -2
- package/plugins/huaweicloud-core/src/auth/service.mjs +54 -46
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +32 -4
- package/plugins/huaweicloud-core/src/hcloud-probe.mjs +26 -6
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +120 -4
- package/plugins/huaweicloud-core/src/setup-cli.mjs +111 -8
- package/plugins/huaweicloud-core/src/tools.mjs +23 -3
|
@@ -21,16 +21,16 @@ Always run `hcloud <Service> <Operation> --help` before constructing commands to
|
|
|
21
21
|
|
|
22
22
|
**Proactive by design**: any deployment intent ("部署一个博客", "搭一套环境", "买个服务器") automatically triggers stages 1-5 BEFORE anything is created. The user NEVER needs to ask for an architecture proposal, cost estimate, or risk warnings - they are built into the flow. Only stages 6-8 (create / verify / manage) wait for explicit user confirmation at the approval gates.
|
|
23
23
|
|
|
24
|
-
| Stage | What happens | Hard rule
|
|
25
|
-
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
-
| 1. Scale assessment | **FIRST inspect the project code** (clone remote repos to a temp dir, or read the local path): tech stack, data services (Redis/MySQL/SQLite...), build artifacts, existing Dockerfile/k8s manifests → then classify or ask scale | **NEVER present deployment options (sandbox/ECS/OBS...) before the codebase has been inspected** - options without code analysis are guesses. If scale is not stated or clearly inferable, ASK - present the 3 tiers as options with what each includes and its monthly cost band (see `references/architectures.md`), and let the user choose. Never assume a tier. The scale question and the hosting-target question are SEPARATE questions on separate axes. FunctionGraph filesystem is ephemeral - never recommend SQLite/local files for persistent data
|
|
27
|
-
| 2. Frontend hosting choice | Present BOTH options: (a) OBS+CDN+DNS custom domain, (b) ECS+EIP | For SPA/static frontends (Vue/React dist), **OBS hosting must always appear as an option** with cost comparison against same-host Nginx - do not silently merge the frontend into a compute node. For option (a) three prerequisites: domain ownership verification (CDN requires a DNS record or file upload first - `CDN.00010185` otherwise), ICP filing check, DNS hosting check; domain registration link: https://www.huaweicloud.com/product/domain.html
|
|
28
|
-
| 3. Architecture proposal | ASCII topology tree with dependencies | User confirms before continuing. **Architecture decision points must be surfaced as explicit choices, never silently made**: (a) app depends on Redis/MySQL/Kafka etc. → managed service (DCS/RDS/DMS, billed) vs same-host install (cheap, ops burden) - state the trade-off and ask; (b) repo ships a Dockerfile / SWR image / k8s manifests → offer container path (Docker on ECS or CCE) alongside source-build path; (c) frontend hosting layer (Nginx same-host vs OBS+CDN). Silent defaults here are a correctness failure
|
|
29
|
-
| 4. Parameter discovery | `ListFlavors` / `ListImages` / `huaweicloud_list_regions` | Never hardcode flavor/image names
|
|
30
|
-
| 5. Cost + balance gate | Price every resource via BSS `ListOnDemandResourceRatings`, then query balance |
|
|
31
|
-
| 6. Provision | deployment_id, session state, batch approval, ordered creation | See "Provisioning Rules" below
|
|
32
|
-
| 7. Verify | `curl -I` the site / API; expect 200 | DNS CNAME may take minutes to propagate - not an error
|
|
33
|
-
| 8. Manage | Session-scoped status query and destroy | See "Destroy Rules" below
|
|
24
|
+
| Stage | What happens | Hard rule |
|
|
25
|
+
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| 1. Scale assessment | **FIRST inspect the project code** (clone remote repos to a temp dir, or read the local path): tech stack, data services (Redis/MySQL/SQLite...), build artifacts, existing Dockerfile/k8s manifests → then classify or ask scale | **NEVER present deployment options (sandbox/ECS/OBS...) before the codebase has been inspected** - options without code analysis are guesses. If scale is not stated or clearly inferable, ASK - present the 3 tiers as options with what each includes and its monthly cost band (see `references/architectures.md`), and let the user choose. Never assume a tier. The scale question and the hosting-target question are SEPARATE questions on separate axes. FunctionGraph filesystem is ephemeral - never recommend SQLite/local files for persistent data |
|
|
27
|
+
| 2. Frontend hosting choice | Present BOTH options: (a) OBS+CDN+DNS custom domain, (b) ECS+EIP | For SPA/static frontends (Vue/React dist), **OBS hosting must always appear as an option** with cost comparison against same-host Nginx - do not silently merge the frontend into a compute node. For option (a) three prerequisites: domain ownership verification (CDN requires a DNS record or file upload first - `CDN.00010185` otherwise), ICP filing check, DNS hosting check; domain registration link: https://www.huaweicloud.com/product/domain.html |
|
|
28
|
+
| 3. Architecture proposal | ASCII topology tree with dependencies | User confirms before continuing. **Architecture decision points must be surfaced as explicit choices, never silently made**: (a) app depends on Redis/MySQL/Kafka etc. → managed service (DCS/RDS/DMS, billed) vs same-host install (cheap, ops burden) - state the trade-off and ask; (b) repo ships a Dockerfile / SWR image / k8s manifests → offer container path (Docker on ECS or CCE) alongside source-build path; (c) frontend hosting layer (Nginx same-host vs OBS+CDN). Silent defaults here are a correctness failure |
|
|
29
|
+
| 4. Parameter discovery | `ListFlavors` / `ListImages` / `huaweicloud_list_regions` | Never hardcode flavor/image names |
|
|
30
|
+
| 5. Cost + balance gate | Price every resource via BSS `ListOnDemandResourceRatings`, then query balance | **前置拦截(实测 #922):估算成本 > 余额(含余额为 0)时 STOP — 不要带着一份注定失败的下单继续 stage 6**。第一条消息必须说明:"您的余额不足以覆盖本次部署的按时长计费资源(预计首月 <估算>,当前余额 <余额>),下单将失败(Ecs.7000 Insufficient account balance)——已暂停,请先充值或领券,完成后回复"已充值/继续"即可从本阶段续跑原配置"。充值入口:https://account.huaweicloud.com/usercenter/?region=cn-north-4#/userindex/balanceRecharge 。Remedy priority: (1) FIRST claim the one-time incentive voucher (huaweicloud_voucher_status → huaweicloud_voucher_claim) to offset usage fees; (2) then instruct the user to top up balance (via the link above) — pause the flow and resume from stage 5 with the SAME configuration after the top-up, never walk a known-doomed order; (3) free-tier resources (FunctionGraph, small OBS) proceed without any balance. Balance >= 估算成本 → report how long it can sustain (balance ÷ monthly estimate). **权限降级(实测)**:IAM 用户无 `billing:balance:view` 时余额查询报 `CBC.0151` → 不阻塞流程,明确告知"余额无法程序化查询,请自行核对;若下单报 Ecs.7000 即余额不足",领券后继续。定价编码逐服务查证,见 `references/resource-catalog.md` § Pricing |
|
|
31
|
+
| 6. Provision | deployment_id, session state, batch approval, ordered creation | See "Provisioning Rules" below |
|
|
32
|
+
| 7. Verify | `curl -I` the site / API; expect 200 | DNS CNAME may take minutes to propagate - not an error |
|
|
33
|
+
| 8. Manage | Session-scoped status query and destroy | See "Destroy Rules" below |
|
|
34
34
|
|
|
35
35
|
## Provisioning Rules (Stage 6)
|
|
36
36
|
|
|
@@ -52,32 +52,32 @@ Execution: show the to-be-deleted list for final confirmation → delete in **re
|
|
|
52
52
|
|
|
53
53
|
## Critical Warnings
|
|
54
54
|
|
|
55
|
-
| Trap | Why / Rule
|
|
56
|
-
| ---------------------------------------------------- |
|
|
57
|
-
| Stale `securityToken` poisons every hcloud call | Symptom: every API returns `APIGW.0301 Incorrect IAM authentication information` while OBS/obsutil still works. `configure set` refuses empty values and auth-sync never clears the field. Fix: `hcloud configure delete --cli-profile=default`, then re-run auth init with AK/SK only (no token)
|
|
58
|
-
| OBS writes bypass the REST write-prefix policy | `mb` / `cp` / `rm` / `chattri` are obsutil-style commands. ALWAYS route them through `huaweicloud_plan_cli_command` → approval → `huaweicloud_run_approved_command`. Never treat them as read-only
|
|
59
|
-
| `OBS mb` flag rules differ from cp/rm | `mb` does NOT accept `-f` (flag parse error) and REQUIRES explicit `-location=<region>` even with a regional endpoint (else `IllegalLocationConstraintException`)
|
|
60
|
-
| Duplicate `OBS mb` is idempotent success | Same-account same-name bucket creation returns success, not an error - never treat a second create as failure. OBS deletes are also idempotent (deleting a nonexistent object succeeds)
|
|
61
|
-
| Bucket ACL does not cascade to objects | Every uploaded object needs `-acl=public-read` at upload time (`-f` IS valid on cp/rm)
|
|
62
|
-
| OBS static website bucket name must equal the domain | e.g. bucket `www.example.com` for domain `www.example.com`
|
|
63
|
-
| VPC v3 API differs from older docs | v3 `CreateSecurityGroup` has NO `vpc_id` (account-scoped); `CreateSecurityGroupRule` uses `multiport=80` not `port_range_min/max`; `DeleteSubnet` requires `vpc_id` too. Always `--help` first
|
|
64
|
-
| Public exposure is auto-blocked | Risk rules deny `0.0.0.0/0` port rules at plan time. For test/dev use a narrower `remote_ip_prefix` (e.g. the VPC CIDR) or get user consent for a documented exception
|
|
65
|
-
| ECS CreateServers hidden required params | Besides flavor/image/nics/az it also needs `--server.vpcid` and `--server.root_volume.volumetype` - order submission fails without them
|
|
66
|
-
| FunctionGraph runtime/code enums are strict | runtime must be exact (`Node.js18.15`, `Python3.9`, `Go1.x`, ...); inline code is the PAIR `--package=default --code_type=inline` (`--package=inline` → `FSS.1006`); TIMER triggers use `--event_data.schedule_type=Rate --event_data.schedule=1m` (`@every` style → `FSS.1109`); DeleteFunction takes `--function_urn` WITHOUT `:latest`
|
|
67
|
-
| Keypairs: `NovaCreateKeypair`, not CreateKeypair | `ECS CreateKeypair` does NOT exist - use `hcloud ECS NovaCreateKeypair`; delete takes `--keypair_name` (not `--keypair.name`). The API-generated `private_key` is MASKED in tool output - for SSH access create the keypair in the console or import the user's own public key, then bind with `--server.key_name=<n>`
|
|
68
|
-
| Long inline args break MCP JSON | Values >~2KB inline (e.g. `--server.user_data=<base64 cloud-init>`) fail JSON parsing silently. Write a local JSON file and pass `--cli-jsonInput=<file>` (see `references/resource-catalog.md` header note)
|
|
69
|
-
| EIP quota fails at RUN stage, not at plan | `EIP.7905 Quota exceeded` appears only after plan+approval - pre-check quota during stage 5 (and count precisely: `ListPublicips --limit=50` truncates, a "50/50" read on a 59-EIP account is wrong). Released quotas may lag minutes before `EIP.7905` clears
|
|
70
|
-
| Zero balance blocks order submission | ECS creation fails with `Ecs.7000 Insufficient account balance` when cash + voucher accounts are 0. This is the stage-5 gate materialized
|
|
71
|
-
| CDN mainland acceleration requires ICP filing | Unregistered domains cannot go live on CDN - check before stage 3. CDN create ALSO requires domain ownership verification first (`CDN.00010185 Verify domain ownership failed` - user must add a DNS record or upload a verification file)
|
|
72
|
-
| CDN is a global service in KooCLI | CDN operations only accept `--cli-region=cn-north-1` or `ap-southeast-1` - never the resource region
|
|
73
|
-
| OBS static website hosting has NO CLI path | `chattri` only sets ACL/storage-class; obsutil has no website command. Options: console setup, or CDN OBS-origin with `--domain.sources.1.enable_obs_web_hosting=1`. Without website hosting, verify via direct object URLs (still public HTTP 200)
|
|
74
|
-
| DNS hosting location | `hcloud DNS ListPublicZones` decides: hosted in Huawei Cloud → auto-create CNAME record set; hosted elsewhere → output the CNAME value for manual setup at the user's DNS provider
|
|
75
|
-
| EIP bills when idle; stopped ECS still bills | Include in cost estimate; destroy unattached EIPs
|
|
76
|
-
| ECS delete defaults leak resources | `--delete_publicip` and `--delete_volume` are false by default - set `true` when tearing down
|
|
77
|
-
| EIP binding type is non-obvious | `--publicip.associate_instance_type=PORT` (not INSTANCE)
|
|
78
|
-
| State is session-only | Temp-dir file, dies with the session. Never promise cross-session deploy management
|
|
79
|
-
| State file must be UTF-8 WITHOUT BOM | PowerShell `Set-Content -Encoding utf8` adds a BOM that breaks JSON parsers - write state with Node or another BOM-less writer
|
|
80
|
-
| HTTPS needs a certificate | CDN serves plain HTTP by default; SSL cert (upload or SCM) required for HTTPS
|
|
55
|
+
| Trap | Why / Rule |
|
|
56
|
+
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
57
|
+
| Stale `securityToken` poisons every hcloud call | Symptom: every API returns `APIGW.0301 Incorrect IAM authentication information` while OBS/obsutil still works. `configure set` refuses empty values and auth-sync never clears the field. Fix: `hcloud configure delete --cli-profile=default`, then re-run auth init with AK/SK only (no token) |
|
|
58
|
+
| OBS writes bypass the REST write-prefix policy | `mb` / `cp` / `rm` / `chattri` are obsutil-style commands. ALWAYS route them through `huaweicloud_plan_cli_command` → approval → `huaweicloud_run_approved_command`. Never treat them as read-only |
|
|
59
|
+
| `OBS mb` flag rules differ from cp/rm | `mb` does NOT accept `-f` (flag parse error) and REQUIRES explicit `-location=<region>` even with a regional endpoint (else `IllegalLocationConstraintException`) |
|
|
60
|
+
| Duplicate `OBS mb` is idempotent success | Same-account same-name bucket creation returns success, not an error - never treat a second create as failure. OBS deletes are also idempotent (deleting a nonexistent object succeeds) |
|
|
61
|
+
| Bucket ACL does not cascade to objects | Every uploaded object needs `-acl=public-read` at upload time (`-f` IS valid on cp/rm) |
|
|
62
|
+
| OBS static website bucket name must equal the domain | e.g. bucket `www.example.com` for domain `www.example.com` |
|
|
63
|
+
| VPC v3 API differs from older docs | v3 `CreateSecurityGroup` has NO `vpc_id` (account-scoped); `CreateSecurityGroupRule` uses `multiport=80` not `port_range_min/max`; `DeleteSubnet` requires `vpc_id` too. Always `--help` first |
|
|
64
|
+
| Public exposure is auto-blocked | Risk rules deny `0.0.0.0/0` port rules at plan time. For test/dev use a narrower `remote_ip_prefix` (e.g. the VPC CIDR) or get user consent for a documented exception |
|
|
65
|
+
| ECS CreateServers hidden required params | Besides flavor/image/nics/az it also needs `--server.vpcid` and `--server.root_volume.volumetype` - order submission fails without them |
|
|
66
|
+
| FunctionGraph runtime/code enums are strict | runtime must be exact (`Node.js18.15`, `Python3.9`, `Go1.x`, ...); inline code is the PAIR `--package=default --code_type=inline` (`--package=inline` → `FSS.1006`); TIMER triggers use `--event_data.schedule_type=Rate --event_data.schedule=1m` (`@every` style → `FSS.1109`); DeleteFunction takes `--function_urn` WITHOUT `:latest` |
|
|
67
|
+
| Keypairs: `NovaCreateKeypair`, not CreateKeypair | `ECS CreateKeypair` does NOT exist - use `hcloud ECS NovaCreateKeypair`; delete takes `--keypair_name` (not `--keypair.name`). The API-generated `private_key` is MASKED in tool output - for SSH access create the keypair in the console or import the user's own public key, then bind with `--server.key_name=<n>` |
|
|
68
|
+
| Long inline args break MCP JSON | Values >~2KB inline (e.g. `--server.user_data=<base64 cloud-init>`) fail JSON parsing silently. Write a local JSON file and pass `--cli-jsonInput=<file>` (see `references/resource-catalog.md` header note) |
|
|
69
|
+
| EIP quota fails at RUN stage, not at plan | `EIP.7905 Quota exceeded` appears only after plan+approval - pre-check quota during stage 5 (and count precisely: `ListPublicips --limit=50` truncates, a "50/50" read on a 59-EIP account is wrong). Released quotas may lag minutes before `EIP.7905` clears |
|
|
70
|
+
| Zero balance blocks order submission | ECS creation fails with `Ecs.7000 Insufficient account balance` when cash + voucher accounts are 0. This is the stage-5 gate materialized — and since #922 the gate INTERCEPTS before the order: when the estimate exceeds the balance, STOP at stage 5 (recharge entry + resume after top-up) instead of walking a known-doomed `CreateServers`. Free-tier resources (FunctionGraph, small OBS) DO work at 0 balance - the gate applies to order-submission resources only |
|
|
71
|
+
| CDN mainland acceleration requires ICP filing | Unregistered domains cannot go live on CDN - check before stage 3. CDN create ALSO requires domain ownership verification first (`CDN.00010185 Verify domain ownership failed` - user must add a DNS record or upload a verification file) |
|
|
72
|
+
| CDN is a global service in KooCLI | CDN operations only accept `--cli-region=cn-north-1` or `ap-southeast-1` - never the resource region |
|
|
73
|
+
| OBS static website hosting has NO CLI path | `chattri` only sets ACL/storage-class; obsutil has no website command. Options: console setup, or CDN OBS-origin with `--domain.sources.1.enable_obs_web_hosting=1`. Without website hosting, verify via direct object URLs (still public HTTP 200) |
|
|
74
|
+
| DNS hosting location | `hcloud DNS ListPublicZones` decides: hosted in Huawei Cloud → auto-create CNAME record set; hosted elsewhere → output the CNAME value for manual setup at the user's DNS provider |
|
|
75
|
+
| EIP bills when idle; stopped ECS still bills | Include in cost estimate; destroy unattached EIPs |
|
|
76
|
+
| ECS delete defaults leak resources | `--delete_publicip` and `--delete_volume` are false by default - set `true` when tearing down |
|
|
77
|
+
| EIP binding type is non-obvious | `--publicip.associate_instance_type=PORT` (not INSTANCE) |
|
|
78
|
+
| State is session-only | Temp-dir file, dies with the session. Never promise cross-session deploy management |
|
|
79
|
+
| State file must be UTF-8 WITHOUT BOM | PowerShell `Set-Content -Encoding utf8` adds a BOM that breaks JSON parsers - write state with Node or another BOM-less writer |
|
|
80
|
+
| HTTPS needs a certificate | CDN serves plain HTTP by default; SSL cert (upload or SCM) required for HTTPS |
|
|
81
81
|
|
|
82
82
|
## MCP Tools
|
|
83
83
|
|
|
@@ -10,8 +10,8 @@ version: 1
|
|
|
10
10
|
|
|
11
11
|
## Routing Guard: Deploy vs Store
|
|
12
12
|
|
|
13
|
-
- If the developer's goal is to **deploy, host, or preview a web app / static website** (temporary hosting, quick preview), do NOT default to OBS.
|
|
14
|
-
- Proceed with OBS only when the developer
|
|
13
|
+
- If the developer's goal is to **deploy, host, or preview a web app / static website** (temporary hosting, quick preview), do NOT default to OBS. You MUST present deployment-target options via the `question` tool with the sandbox first: ① huawei-sandbox (recommended), ② OBS static hosting (需提前准备好自定义域名 — the default OBS endpoint does not support HTTPS for static hosting), ③ ECS (需搭配弹性公网IP/EIP 才能公网访问), ④ CCE. Ask: "建议优先部署到沙箱(临时运行环境,可预览访问),也可选 OBS 静态托管(需提前准备好自定义域名)、ECS(需搭配弹性公网IP/EIP)或 CCE,你想部署到哪里?"
|
|
14
|
+
- Proceed with OBS only when the developer explicitly names OBS, or selects OBS through the `question` gate after being asked. Long-term static hosting / CDN / file storage intent alone is NOT a named target and cannot bypass the gate — still present the options via the `question` tool first. **NEVER infer or default the target** when it is unstated. **Deployment intent proceeding on OBS is a resource purchase: load `huawei-iac` for orchestration (cost+balance gate, batch approval, state tracking, ordered destroy) - this skill then supplies the bucket/object commands.** Sandbox choice (huawei-sandbox) bypasses iac - no purchase involved.
|
|
15
15
|
- OBS is a storage service; it is not a general web-hosting default.
|
|
16
16
|
|
|
17
17
|
## Critical: OBS Command Syntax
|
|
@@ -54,10 +54,37 @@ hcloud OBS config -i
|
|
|
54
54
|
|
|
55
55
|
KooCLI OBS **不支持** `SetBucketWebsite` 命令。配置静态网站托管需要:
|
|
56
56
|
|
|
57
|
-
1. **
|
|
58
|
-
2.
|
|
57
|
+
1. **MCP 工具**: `huaweicloud_obs_set_website_config`(插件内置,AWS4 签名处理,action=set/get/delete,实测可用)
|
|
58
|
+
2. **REST API**: `PUT /?website` 到桶的 endpoint
|
|
59
|
+
3. **控制台**: OBS 控制台 → 桶 → 静态网站托管 → 开启
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
## Content-Disposition 强制下载陷阱(2022-01-01 后创建的桶)
|
|
62
|
+
|
|
63
|
+
**实测发现(2026-10,cn-north-4)**:2022 年 1 月 1 日之后创建的 OBS 桶,对象 **GET 响应被 OBS 强制注入 `Content-Disposition: attachment`**——浏览器访问 HTML 一律下载而不是渲染,静态网站托管端点(`<bucket>.obs-website.<region>.myhuaweicloud.com`)也不例外,与对象元数据无关。
|
|
64
|
+
|
|
65
|
+
| 请求方式 | 返回的 Content-Disposition | 说明 |
|
|
66
|
+
| ----------------------- | ---------------------------- | -------------------------- |
|
|
67
|
+
| GET(浏览器、在线预览) | `attachment`(OBS 强制注入) | 网页被下载,无法渲染 |
|
|
68
|
+
| HEAD | 元数据中的值(如 inline) | 会误导诊断,不是浏览器行为 |
|
|
69
|
+
|
|
70
|
+
**避坑方法**:
|
|
71
|
+
|
|
72
|
+
1. **验证网页行为必须用 GET 直连**(`curl -s -D - -o NUL <url>` 或浏览器)——HEAD 和代理层会给出假象
|
|
73
|
+
2. **官方解法**:绑定自定义域名接入 **CDN**,在"CDN 控制台 > 域名管理 > 高级设置 > HTTP header 配置"设置 `Content-Disposition: inline`(参见华为云 CDN 文档《如何在浏览器中在线预览 OBS 中的对象?》)
|
|
74
|
+
3. 跨云对照:阿里云 OSS 同样策略(Bucket 域名访问 HTML 强制下载,绑定自定义域名即可在线预览)
|
|
75
|
+
|
|
76
|
+
**对静态托管的结论**:2022 年后创建的桶,裸 OBS(含 obs-website 端点)**无法交付可渲染的 web 站点**;自定义域名 + CDN(+ header 配置 inline)是**必要条件**,不是增强项。
|
|
77
|
+
|
|
78
|
+
## obsutil 元数据大小写陷阱
|
|
79
|
+
|
|
80
|
+
`cp -meta=content-disposition:inline`(小写)会被存为用户自定义元数据 `x-amz-meta-content-disposition`,**不命中标准头**;标准 HTTP 头(Content-Type、Content-Disposition、Cache-Control 等)必须按标准大小写书写:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# 错误:存为 x-amz-meta-content-disposition,浏览器不识别
|
|
84
|
+
hcloud OBS cp index.html obs://<bucket>/ -meta=content-disposition:inline
|
|
85
|
+
# 正确:命中标准 Content-Disposition 头
|
|
86
|
+
hcloud OBS cp index.html obs://<bucket>/ -meta=Content-Disposition:inline
|
|
87
|
+
```
|
|
61
88
|
|
|
62
89
|
## 权限模型(三层)
|
|
63
90
|
|
|
@@ -15,8 +15,8 @@ When the developer asks to **deploy / host / publish / 上线 / 部署 / 发布
|
|
|
15
15
|
Use the `question` tool and present, in this exact order:
|
|
16
16
|
|
|
17
17
|
1. **沙箱 (Sandbox)** — temporary runtime, instant preview URL (recommended for quick preview)
|
|
18
|
-
2. **OBS 静态托管** — long-term static hosting + CDN + custom domain
|
|
19
|
-
3. **ECS** — server-based hosting
|
|
18
|
+
2. **OBS 静态托管** — long-term static hosting + CDN + custom domain. **Remind the developer: a custom domain (域名) must be prepared in advance — the default OBS endpoint does not support HTTPS for static hosting**
|
|
19
|
+
3. **ECS** — server-based hosting. **Remind the developer: ECS must be paired with an EIP (弹性公网IP) for public access — extra billing applies**
|
|
20
20
|
4. **CCE** — container-based hosting
|
|
21
21
|
|
|
22
22
|
Hard rules:
|
|
@@ -24,6 +24,7 @@ Hard rules:
|
|
|
24
24
|
- **NEVER infer or default the target** when it is unstated. Calling `huaweicloud_sandbox_connect` before this choice is made is a violation.
|
|
25
25
|
- Only skip the question when: (a) the developer already named a target (e.g. "部署到 ECS", "用 OBS"), or (b) the developer explicitly asked for a temporary / sandbox / preview runtime.
|
|
26
26
|
- "部署到华为云" / "deploy to Huawei Cloud" / "上线" alone is NOT a named target — still run the gate.
|
|
27
|
+
- Include the OBS domain reminder and the ECS EIP pairing reminder in the options as presented.
|
|
27
28
|
- After the choice: sandbox → continue with this skill; OBS → `huawei-obs`; ECS → `huawei-ecs` / `huawei-iac`; CCE → `huawei-cce`.
|
|
28
29
|
|
|
29
30
|
## Overview
|
|
@@ -33,7 +34,7 @@ Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace t
|
|
|
33
34
|
## Activation
|
|
34
35
|
|
|
35
36
|
- **Proactive offering**: when the developer's task needs a temporary runtime (e.g. "deploy this web app", "run this app and preview it"), offer the sandbox proactively — the developer never has to say "use sandbox". Prompt: "This task can be satisfied by a sandbox — use it?"
|
|
36
|
-
- **Static website hosting — present options, sandbox first (MANDATORY)**: when the developer asks to deploy/host/publish a static website or web app without naming a target, do NOT silently pick a service and do NOT default to the sandbox. Run the [Target-Selection Gate](#target-selection-gate-mandatory--do-not-skip) via the `question` tool first. Present, in order: ① 沙箱 (recommended — temporary runtime, instant preview URL) ② OBS 静态托管 (long-term static hosting/CDN) ③ ECS ④ CCE. Recommended prompt: "建议优先部署到沙箱(临时运行环境,可预览访问),也可选择 OBS
|
|
37
|
+
- **Static website hosting — present options, sandbox first (MANDATORY)**: when the developer asks to deploy/host/publish a static website or web app without naming a target, do NOT silently pick a service and do NOT default to the sandbox. Run the [Target-Selection Gate](#target-selection-gate-mandatory--do-not-skip) via the `question` tool first. Present, in order: ① 沙箱 (recommended — temporary runtime, instant preview URL) ② OBS 静态托管 (long-term static hosting/CDN; 需提前准备好自定义域名) ③ ECS (需搭配弹性公网IP/EIP 才能公网访问) ④ CCE. Recommended prompt: "建议优先部署到沙箱(临时运行环境,可预览访问),也可选择 OBS 静态托管(需提前准备好自定义域名)、ECS(需搭配弹性公网IP/EIP)或 CCE,你想部署到哪里?" Only if the developer, after being asked, expresses no preference may you proceed with the sandbox.
|
|
37
38
|
- **Detect web apps in deploy-a-repo prompts**: prompts like "部署 GitHub - <owner>/<repo>" or "deploy <github repo>" do not say "web app". Clone or inspect the repository first (package.json with dev/serve/build scripts, index.html, frontend framework dependencies such as vue/react/angular/vite, static sites, Dockerfile running a web server). If it is a web application, offer the sandbox first: "检测到该项目是 Web 应用,建议优先使用沙箱部署(临时运行环境,可预览访问),是否使用?"
|
|
38
39
|
- **Clone into the sandbox workspace directory**: always put project code under `/workspace/<repo-name>` (create the directory if missing) — `/workspace` is the sandbox's dedicated workspace mount at the filesystem root, not `$HOME/workspace`. Never use `/tmp` or other ephemeral locations. This keeps the project with the sandbox session, is easy to reference for serving/exposing, and survives session-level restarts of the shell.
|
|
39
40
|
- **Deployment must end with a public URL**: after deploying and exposing the app with DevBridge, always return the tunnel URL to the developer as the final result — a deployment without an accessible link is incomplete.
|
|
@@ -147,6 +148,8 @@ Setup is a **plugin-side preflight** — the developer should be asked a questio
|
|
|
147
148
|
|
|
148
149
|
If `source /tmp/hw_creds.sh` returns empty, the credentials injection may not have completed — re-run `huaweicloud_sandbox_credentials`.
|
|
149
150
|
|
|
151
|
+
**DevBridge API Key 前置引导(before expose, not mid-expose)**: the address-generation service (DevBridge 0.2.x) requires an API Key. Recent sandbox images auto-acquire one from the platform keyring — but if the sandbox was created on an older image (cannot auto-acquire), prepare the key at THIS stage instead of being interrupted halfway through the expose flow: create it once at https://devstation.connect.huaweicloud.com/space/devbridge/apikey (value shown only once, `devbridge_` prefix), hand it over via `export HW_API_KEY=<key>` locally (preferred) or paste it in the conversation for the `api_key` param, then run `huaweicloud_sandbox_credentials`. Full why/where/how guide: [Step 1, NO_CREDENTIAL branch](#step-1-authenticate--verified-waterfall-keyring-first).
|
|
152
|
+
|
|
150
153
|
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).
|
|
151
154
|
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.
|
|
152
155
|
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
|
|
@@ -220,6 +223,11 @@ curl -s -o /workspace/<repo>/<file> https://<bucket>.obs.<region>.myhuaweicloud.
|
|
|
220
223
|
|
|
221
224
|
Expose a deployed web app to a public URL and return that URL to the developer.
|
|
222
225
|
|
|
226
|
+
### Workspace URL vs public share link (401 trap — do not confuse them)
|
|
227
|
+
|
|
228
|
+
- The sandbox **workspace URL** (`https://<port>-<container_id>.workspace.developer.huaweicloud.com/`) is **authenticated**: opening it directly (or sharing it with someone who is not logged in) returns **401 Unauthorized**. A 401 here does NOT mean the port failed to expose — never debug it as an exposure failure.
|
|
229
|
+
- A **publicly shareable address is ONLY the DevBridge tunnel URL** (`https://<id>-<port>.devbridge-s2.hwtunnel.com`, produced in Step 2 below). Public sharing must go through the DevBridge tunnel — never hand the workspace URL to the developer as the deployment result link.
|
|
230
|
+
|
|
223
231
|
**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
232
|
|
|
225
233
|
### Step 0: Ensure DevBridge CLI 0.2.x (install or upgrade in place)
|
|
@@ -249,6 +257,23 @@ if ! bash /tmp/devbridge-install.sh -s -u "https://gitcode.com/CloudDeveloperDep
|
|
|
249
257
|
bash /tmp/devbridge-install.sh -s # fallback: GitHub (baked-in URL)
|
|
250
258
|
fi
|
|
251
259
|
export PATH="$HOME/.huawei/bin:$PATH" # PREPEND (session shells do not re-source ~/.bashrc)
|
|
260
|
+
|
|
261
|
+
# Layer 3 (both installer paths failed): direct GitHub release tar.gz.
|
|
262
|
+
# Asset naming is fixed: devbridge_linux_arm64_<version>-release.tar.gz (the sandbox
|
|
263
|
+
# is Linux aarch64). If DB_TARGET is empty the installer download itself failed —
|
|
264
|
+
# resolve the latest tag from the GitHub releases API first.
|
|
265
|
+
if ! command -v devbridge >/dev/null 2>&1; then
|
|
266
|
+
[ -z "$DB_TARGET" ] && DB_TARGET=$(curl -fsSL --http1.1 --retry 2 --max-time 60 \
|
|
267
|
+
https://api.github.com/repos/huaweicloud/devspace-devbridge/releases/latest | grep -m1 '"tag_name"' | cut -d'"' -f4)
|
|
268
|
+
mkdir -p "$HOME/.huawei/bin"
|
|
269
|
+
curl -fsSL --http1.1 --retry 3 --retry-all-errors --max-time 180 \
|
|
270
|
+
"https://github.com/huaweicloud/devspace-devbridge/releases/download/${DB_TARGET}/devbridge_linux_arm64_${DB_TARGET}.tar.gz" \
|
|
271
|
+
-o /tmp/devbridge.tar.gz \
|
|
272
|
+
&& rm -rf /tmp/devbridge-pkg && mkdir -p /tmp/devbridge-pkg \
|
|
273
|
+
&& tar -xzf /tmp/devbridge.tar.gz -C /tmp/devbridge-pkg \
|
|
274
|
+
&& install -m 0755 /tmp/devbridge-pkg/devbridge "$HOME/.huawei/bin/devbridge"
|
|
275
|
+
export PATH="$HOME/.huawei/bin:$PATH"
|
|
276
|
+
fi
|
|
252
277
|
devbridge version # must print 0.2.x-release
|
|
253
278
|
|
|
254
279
|
# Remove the stale image-installed binary so only ONE version remains.
|
|
@@ -325,7 +350,8 @@ sleep 2
|
|
|
325
350
|
devbridge delete-all 2>/dev/null || true
|
|
326
351
|
|
|
327
352
|
# 1. Start tunnel
|
|
328
|
-
nohup devbridge host -p <port> -e
|
|
353
|
+
if command -v setsid > /dev/null 2>&1; then setsid nohup devbridge host -p <port> -e 72 > /tmp/host.log 2>&1 < /dev/null & else nohup devbridge host -p <port> -e 72 > /tmp/host.log 2>&1 < /dev/null & fi
|
|
354
|
+
disown 2>/dev/null || true
|
|
329
355
|
sleep 10 && cat /tmp/host.log
|
|
330
356
|
|
|
331
357
|
# 2. Extract tunnel URL and health-check before returning
|
|
@@ -337,7 +363,8 @@ else
|
|
|
337
363
|
echo "WARN: Tunnel URL unreachable (HTTP $HTTP_CODE). Rebuilding tunnel..."
|
|
338
364
|
pkill -f "devbridge host" && sleep 2
|
|
339
365
|
devbridge delete-all 2>/dev/null || true
|
|
340
|
-
nohup devbridge host -p <port> -e
|
|
366
|
+
if command -v setsid > /dev/null 2>&1; then setsid nohup devbridge host -p <port> -e 72 > /tmp/host.log 2>&1 < /dev/null & else nohup devbridge host -p <port> -e 72 > /tmp/host.log 2>&1 < /dev/null & fi
|
|
367
|
+
disown 2>/dev/null || true
|
|
341
368
|
sleep 10
|
|
342
369
|
TUNNEL_URL=$(grep -oP 'Tunnel URL: \K.*' /tmp/host.log)
|
|
343
370
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$TUNNEL_URL" 2>/dev/null || echo "000")
|
|
@@ -357,13 +384,15 @@ devbridge list
|
|
|
357
384
|
# Step B: Remove all stale tunnels
|
|
358
385
|
devbridge delete-all
|
|
359
386
|
# Step C: Retry tunnel creation
|
|
360
|
-
nohup devbridge host -p <port> -e
|
|
387
|
+
if command -v setsid > /dev/null 2>&1; then setsid nohup devbridge host -p <port> -e 72 > /tmp/host.log 2>&1 < /dev/null & else nohup devbridge host -p <port> -e 72 > /tmp/host.log 2>&1 < /dev/null & fi
|
|
388
|
+
disown 2>/dev/null || true
|
|
361
389
|
sleep 10 && cat /tmp/host.log
|
|
362
390
|
```
|
|
363
391
|
|
|
364
392
|
This eliminates the most common deployment failure — historical tunnels from previous sessions accumulating past the max=10 quota.
|
|
365
393
|
|
|
366
394
|
- 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.
|
|
395
|
+
- **Start the host so it survives the exec session**: `setsid nohup ... < /dev/null & disown` (all three: setsid detaches the process group, `< /dev/null` drops the controlling tty, disown removes it from the shell's job table). On images without `setsid`, fall back to a bare `nohup ... &` launch (`if command -v setsid ...; else nohup ... ; fi`) and guard `disown` with `2>/dev/null || true` — a missing `setsid` must not leave the host unstarted. A bare `nohup ... &` (when `setsid` IS available) is reaped when the one-shot session ends — the tunnel dies right after a green in-session health check (the "200 but tunnel not found" trap). If the independent re-verify fails or answers 200 with a "tunnel not found" body, the URL is NOT persistent — always tell the developer 隧道将在会话结束后失效,请勿分享. `-e 72` sets the tunnel lifetime in **hours** — surface it to the developer with the URL ("链接有效期 72 小时").
|
|
367
396
|
- **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).
|
|
368
397
|
- Tunnel `description` (`-d`) accepts only Chinese characters, letters, and digits (0-64). Symbols such as `-`/`_`/spaces are rejected (`Invalid tunnel description`).
|
|
369
398
|
- Official docs: https://huaweicloud.github.io/devspace-devbridge/
|
|
@@ -74,6 +74,7 @@ version: 2
|
|
|
74
74
|
| `code=HDKIT_NOT_SIGNED_UP`(您尚未报名…) | 未报名该赛事 | 告知需先完成赛事报名后再来领取 |
|
|
75
75
|
| `已领取过` / `已领取` | 该账号已领过赛事券 | 告知已领取,不打扰 |
|
|
76
76
|
| `code=HDKIT_COMPETITION_NOT_FOUND`(赛事不存在或已下线) | 赛事配置异常 | 告知赛事暂不可用,稍后再试 |
|
|
77
|
+
| `code=HDKIT_COMPETITION_NOT_CONFIGURED`(赛事代金券服务配置缺失…) | 服务端活动参数未配置 | 告知赛事代金券暂未开放,稍后再试 |
|
|
77
78
|
| `本月代金券总额度已用完,所有账号均无法领取` | 全局额度用尽(HD.60630042) | 告知下月再试 |
|
|
78
79
|
| `请先完成实名认证` | 账号未实名(HD.60630022) | 引导去控制台实名认证 |
|
|
79
80
|
| `code=HDKIT_CRED_*` / 含「未配置 / not configured / AK/SK 无效 / 限额」 | 凭证未配置、无效或达限额 | 引导用户检查并配置 AK/SK |
|
|
@@ -41,9 +41,9 @@ Route to the sandbox when the developer signals free/quick preview intent ("免
|
|
|
41
41
|
- Use `huaweicloud_get_service_icon` with the service name, alias, or Chinese name (e.g. `ecs`, `obs`, `modelarts`, `对象存储`). It returns the official CDN logo URL (`logo.source_url`), category, and product page link from https://open.huaweicloud.com/openplatform/icons.html.
|
|
42
42
|
- Prefer `logo.source_url` for web deliverables and the Icons library search page for browsing; do not scrape logos from third-party sites.
|
|
43
43
|
|
|
44
|
-
## Deployment Target Options
|
|
44
|
+
## Deployment Target Options (MANDATORY — use the `question` tool)
|
|
45
45
|
|
|
46
|
-
When the intent is to **deploy, host, or preview a web app or static website** and the developer has not named a target, do NOT default to OBS or any single service.
|
|
46
|
+
When the intent is to **deploy, host, or preview a web app or static website** and the developer has not named a target, do NOT default to OBS or any single service. **NEVER infer or default the target** when it is unstated. You MUST present the options via the `question` tool and obtain an explicit choice before calling any deploy tool, recommending the sandbox first: ① huawei-sandbox (recommended — temporary runtime, instant preview URL) ② huawei-obs (long-term static hosting/CDN; 需提前准备好自定义域名) ③ huawei-ecs (需搭配弹性公网IP/EIP 才能公网访问) ④ huawei-cce. Only follow an explicit target when the developer names one; "部署到华为云" alone is NOT a named target — still ask.
|
|
47
47
|
|
|
48
48
|
## Region Intent
|
|
49
49
|
|
|
@@ -13,11 +13,11 @@ Use KooCLI `hcloud` for local inspection and reviewed operations. Never ask the
|
|
|
13
13
|
|
|
14
14
|
Official guide: `https://support.huaweicloud.com/qs-hcli/hcli_02_003.html`.
|
|
15
15
|
|
|
16
|
-
> **Version pairing**: this plugin is paired with **KooCLI v7.
|
|
16
|
+
> **Version pairing**: this plugin is paired with **KooCLI v7.3.12** (`kooCliVersion` in `package.json`). Prefer the pinned download below. The one-liner script `hcloud_install.sh` always installs the `latest` version and cannot be pinned — if you use it, `check_cli`/`doctor` will warn when the installed version does not match v7.3.12.
|
|
17
17
|
|
|
18
18
|
### Windows
|
|
19
19
|
|
|
20
|
-
1. Download and unzip: `https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.
|
|
20
|
+
1. Download and unzip: `https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.3.12/huaweicloud-cli-windows-amd64.zip`
|
|
21
21
|
2. Extract to `%USERPROFILE%\hcloud`, add to user `PATH`
|
|
22
22
|
3. Verify: `hcloud version`
|
|
23
23
|
|
|
@@ -27,10 +27,10 @@ Fixed download (recommended):
|
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
# amd64
|
|
30
|
-
curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.
|
|
30
|
+
curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.3.12/huaweicloud-cli-linux-amd64.tar.gz"
|
|
31
31
|
tar -zxvf huaweicloud-cli-linux-amd64.tar.gz
|
|
32
32
|
# arm64
|
|
33
|
-
curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.
|
|
33
|
+
curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.3.12/huaweicloud-cli-linux-arm64.tar.gz"
|
|
34
34
|
tar -zxvf huaweicloud-cli-linux-arm64.tar.gz
|
|
35
35
|
```
|
|
36
36
|
|
|
@@ -49,10 +49,10 @@ Fixed download (recommended):
|
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
# amd64
|
|
52
|
-
curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.
|
|
52
|
+
curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.3.12/huaweicloud-cli-mac-amd64.tar.gz"
|
|
53
53
|
tar -zxvf huaweicloud-cli-mac-amd64.tar.gz
|
|
54
54
|
# arm64 (Apple Silicon)
|
|
55
|
-
curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.
|
|
55
|
+
curl -LO "https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/7.3.12/huaweicloud-cli-mac-arm64.tar.gz"
|
|
56
56
|
tar -zxvf huaweicloud-cli-mac-arm64.tar.gz
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -85,18 +85,25 @@ Do not rely on training data for facts. Huawei Cloud services, pricing, quotas,
|
|
|
85
85
|
|
|
86
86
|
**Web app scenario layering**: for "deploy a web app", prefer `huawei-sandbox` for free/quick try (hello world, prototype, demo, temporary preview — temporary runtime + public URL (runtime lifetime not exposed via API); credential expiry see `expiresAt` from `connect`/`credentials`, zero billed resources); route to `huawei-functiongraph` / `huawei-ecs` (or `huawei-cce`) for production / long-term / custom-domain / high-availability hosting. When the chosen target requires **multiple purchased resources** (e.g. VPC + subnet + ECS + EIP + RDS, or OBS + CDN + DNS) or the user asks for an architecture/cost proposal first, hand off to `huawei-iac` for orchestrated provisioning with a mandatory cost gate.
|
|
87
87
|
|
|
88
|
-
## Deployment Target Options
|
|
88
|
+
## Deployment Target Options (MANDATORY GATE — do not skip)
|
|
89
89
|
|
|
90
|
-
When the intent is to **deploy, host, or preview a web app or static website** (no explicit target named), NEVER default to a single service such as OBS.
|
|
90
|
+
When the intent is to **deploy, host, or preview a web app or static website** (no explicit target named), NEVER default to a single service such as OBS. You MUST present the target options via the `question` tool and obtain an explicit choice BEFORE calling any deployment tool. Present the options in this order:
|
|
91
91
|
|
|
92
|
-
| # | Option | When to choose
|
|
93
|
-
| --- | -------------------------------------- |
|
|
94
|
-
| 1 | **Sandbox (DevStation) — recommended** | Temporary runtime, quick preview, demo; instant public URL
|
|
95
|
-
| 2 | OBS static website hosting | Long-term static hosting / CDN, no backend
|
|
96
|
-
| 3 | ECS | Needs a server runtime (backend, custom server)
|
|
97
|
-
| 4 | CCE | Containerized workloads, scaling
|
|
92
|
+
| # | Option | When to choose | Skill |
|
|
93
|
+
| --- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
|
|
94
|
+
| 1 | **Sandbox (DevStation) — recommended** | Temporary runtime, quick preview, demo; instant public URL | huawei-sandbox |
|
|
95
|
+
| 2 | OBS static website hosting | Long-term static hosting / CDN, no backend. **Remind: a custom domain (域名) must be prepared — the default OBS endpoint does not support HTTPS for static hosting** | huawei-obs |
|
|
96
|
+
| 3 | ECS | Needs a server runtime (backend, custom server). **Remind: ECS must be paired with an EIP (弹性公网IP) for public access — extra billing** | huawei-ecs |
|
|
97
|
+
| 4 | CCE | Containerized workloads, scaling | huawei-cce |
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
Hard rules:
|
|
100
|
+
|
|
101
|
+
- **NEVER infer or default the target** when it is unstated. Calling any deploy tool (`sandbox_connect`, `OBS mb`, `ECS CreateServers`, ...) before the choice is made is a violation.
|
|
102
|
+
- Only skip the question when: (a) the developer already named a target (e.g. "部署到 ECS", "用 OBS"), or (b) the developer explicitly asked for a temporary / sandbox / preview runtime.
|
|
103
|
+
- "部署到华为云" / "deploy to Huawei Cloud" / "上线" alone is NOT a named target — still run the gate.
|
|
104
|
+
- Include the OBS domain reminder and the ECS EIP pairing reminder in the options as presented.
|
|
105
|
+
|
|
106
|
+
- Recommended prompt: "这个项目是 Web 应用/静态网站,建议优先部署到沙箱(临时运行环境,立即可预览)。也可选择 OBS 静态托管(需提前准备好自定义域名)、ECS(需搭配弹性公网IP/EIP 才能公网访问)或 CCE,你想部署到哪里?"
|
|
100
107
|
- If the developer explicitly names a target (e.g. "deploy to OBS"), the SANDBOX target hands off directly to `huawei-sandbox`; every other target (OBS / ECS / CCE / FunctionGraph / ...) hands off to `huawei-iac` for orchestration - iac loads the corresponding service skill for commands and adds the mandatory cost+balance gate, batch approval, state tracking, and ordered destroy. Direct handoff to the bare service skill skips these safety stages and must not happen for deployment intent. **Existing network resources (a reusable VPC) do NOT downgrade this to a single-service task - the purchase itself (compute, EIP, disk, rules) is what triggers iac.**
|
|
101
108
|
- If the developer has no preference, recommend the sandbox first.
|
|
102
109
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
|
|
3
3
|
import { getProxyDispatcher } from '../proxy/proxy-agent.mjs';
|
|
4
|
+
import { errorFromTable } from './guidance.mjs';
|
|
4
5
|
|
|
5
6
|
function iamBaseUrl() {
|
|
6
7
|
return process.env.HW_IAM_ENDPOINT || 'https://iam.myhuaweicloud.com';
|
|
@@ -81,15 +82,28 @@ function projectForRegion(projects, region) {
|
|
|
81
82
|
* SDK-HMAC-SHA256 request signing. A wrong SK produces an invalid signature,
|
|
82
83
|
* which IAM rejects with HTTP 401 before any project data is returned.
|
|
83
84
|
*
|
|
84
|
-
* Returns { valid, projectId, error, warning }:
|
|
85
|
+
* Returns { valid, projectId, error, warning, code, messageZh, action, nextStep }:
|
|
85
86
|
* - valid: true when IAM verified the signature (or the rejection is not
|
|
86
87
|
* authentication-related), false when the credentials are unusable.
|
|
87
88
|
* - projectId: first project matching `region`, else the first visible project.
|
|
88
89
|
* - warning: set when credentials passed but project discovery was denied.
|
|
90
|
+
* - code/messageZh/action/nextStep: unified #900 shape from the shared
|
|
91
|
+
* ERROR_TABLE (guidance.mjs). The English `error`/`warning` detail text is
|
|
92
|
+
* intentionally kept — existing consumers (tools.mjs sandbox_credentials,
|
|
93
|
+
* external string-matching tests) rely on it.
|
|
89
94
|
*/
|
|
90
95
|
export async function validateIamCredentials({ ak, sk, securityToken, region, timeoutMs = 15000 } = {}) {
|
|
91
96
|
if (!ak || !sk) {
|
|
92
|
-
|
|
97
|
+
const e = errorFromTable('HDKIT_CRED_MISSING');
|
|
98
|
+
return {
|
|
99
|
+
valid: false,
|
|
100
|
+
projectId: null,
|
|
101
|
+
error: 'AK and SK are both required for credential validation.',
|
|
102
|
+
code: e.code,
|
|
103
|
+
messageZh: e.messageZh,
|
|
104
|
+
action: e.action,
|
|
105
|
+
nextStep: e.nextStep,
|
|
106
|
+
};
|
|
93
107
|
}
|
|
94
108
|
|
|
95
109
|
const base = iamBaseUrl();
|
|
@@ -115,11 +129,16 @@ export async function validateIamCredentials({ ak, sk, securityToken, region, ti
|
|
|
115
129
|
resp = await fetch(url, fetchOpts);
|
|
116
130
|
}
|
|
117
131
|
} catch (error) {
|
|
132
|
+
const e = errorFromTable('HDKIT_CRED_UNEXPECTED', error.message);
|
|
118
133
|
return {
|
|
119
134
|
valid: false,
|
|
120
135
|
projectId: null,
|
|
121
136
|
skipped: true,
|
|
122
137
|
error: `IAM validation request failed (treating credentials as unverified): ${error.message}`,
|
|
138
|
+
code: e.code,
|
|
139
|
+
messageZh: e.messageZh,
|
|
140
|
+
action: e.action,
|
|
141
|
+
nextStep: e.nextStep,
|
|
123
142
|
};
|
|
124
143
|
} finally {
|
|
125
144
|
clearTimeout(timer);
|
|
@@ -138,26 +157,42 @@ export async function validateIamCredentials({ ak, sk, securityToken, region, ti
|
|
|
138
157
|
|
|
139
158
|
if (resp.status === 401) {
|
|
140
159
|
const msg = data?.error?.message || text.slice(0, 200);
|
|
160
|
+
const e = errorFromTable('HDKIT_CRED_INVALID', `HTTP 401: ${msg}`);
|
|
141
161
|
return {
|
|
142
162
|
valid: false,
|
|
143
163
|
projectId: null,
|
|
144
164
|
error: `IAM rejected the credentials (HTTP 401: ${msg}). The AK/SK is invalid - check the SK for typos or expired security tokens.`,
|
|
165
|
+
code: e.code,
|
|
166
|
+
messageZh: e.messageZh,
|
|
167
|
+
action: e.action,
|
|
168
|
+
nextStep: e.nextStep,
|
|
145
169
|
};
|
|
146
170
|
}
|
|
147
171
|
|
|
148
172
|
if (resp.status === 403) {
|
|
173
|
+
const msg = data?.error?.message || text.slice(0, 200);
|
|
174
|
+
const e = errorFromTable('HDKIT_CRED_FORBIDDEN', `HTTP 403: ${msg}`);
|
|
149
175
|
return {
|
|
150
176
|
valid: true,
|
|
151
177
|
projectId: null,
|
|
152
178
|
error: null,
|
|
153
179
|
warning: `Credentials signed successfully but project listing was denied (HTTP 403). Continuing without project_id.`,
|
|
180
|
+
code: e.code,
|
|
181
|
+
messageZh: e.messageZh,
|
|
182
|
+
action: e.action,
|
|
183
|
+
nextStep: e.nextStep,
|
|
154
184
|
};
|
|
155
185
|
}
|
|
156
186
|
|
|
187
|
+
const e = errorFromTable('HDKIT_CRED_UNEXPECTED', `HTTP ${resp.status}`);
|
|
157
188
|
return {
|
|
158
189
|
valid: false,
|
|
159
190
|
projectId: null,
|
|
160
191
|
skipped: true,
|
|
161
192
|
error: `Unexpected IAM response (HTTP ${resp.status}): ${text.slice(0, 200)}`,
|
|
193
|
+
code: e.code,
|
|
194
|
+
messageZh: e.messageZh,
|
|
195
|
+
action: e.action,
|
|
196
|
+
nextStep: e.nextStep,
|
|
162
197
|
};
|
|
163
198
|
}
|