huaweicloud-devkit 0.1.26-dev.0 → 1.0.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 +70 -104
- package/README.zh-CN.md +138 -0
- package/package.json +3 -2
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +43 -43
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +42 -42
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +43 -43
- package/plugins/huaweicloud-core/.mcp.json +3 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +44 -0
- package/plugins/huaweicloud-core/safety/policy.json +15 -1
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +21 -0
- package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +22 -2
- package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +17 -0
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +2 -2
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +51 -1
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +23 -3
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +2 -2
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +21 -3
- package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +40 -0
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +48 -5
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +134 -0
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +5 -2
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +7 -6
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +3 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +1 -1
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +87 -0
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +95 -0
- package/plugins/huaweicloud-core/src/auth/service.mjs +63 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +11 -0
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +87 -0
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +153 -0
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +105 -0
- package/plugins/huaweicloud-core/src/setup-cli.mjs +733 -74
- package/plugins/huaweicloud-core/src/tools.mjs +164 -3
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +427 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +132 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +227 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +202 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +158 -0
- package/plugins/huaweicloud-core/src/ws-exec/index.js +19 -0
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +338 -0
|
@@ -29,7 +29,7 @@ hcloud RDS <Operation> --cli-region=<region> [--key=value ...]
|
|
|
29
29
|
| Operation | PascalCase: `ListInstances`, `CreateManualBackup` |
|
|
30
30
|
| Params | `--key=value` format. JSON params: `--key='{"k":"v"}'` |
|
|
31
31
|
| Array params | 1-based: `--instance_ids.1=xxx` |
|
|
32
|
-
| Password param | Conflicts with KooCLI system param;
|
|
32
|
+
| Password param | Conflicts with KooCLI system param; use `--cli-jsonInput` (see Critical Warnings) |
|
|
33
33
|
|
|
34
34
|
## Critical Warnings
|
|
35
35
|
|
|
@@ -38,9 +38,12 @@ hcloud RDS <Operation> --cli-region=<region> [--key=value ...]
|
|
|
38
38
|
| Engine version immutable | Cannot change MySQL to PostgreSQL in-place |
|
|
39
39
|
| Automated backups use OBS | Backup storage incurs separate charges. Set retention period explicitly |
|
|
40
40
|
| Storage auto-scaling off by default | Enable before storage runs out or instance goes read-only |
|
|
41
|
-
| `--password` conflicts with KooCLI |
|
|
41
|
+
| `--password` conflicts with KooCLI | Use `--cli-jsonInput=<file>` with JSON file (see `--cli-jsonInput` section below). The `printf "b\n"` workaround is broken in KooCLI 7.2.12+ |
|
|
42
42
|
| Volume type must match flavor | General→CLOUDSSD; Dedicated→CLOUDSSD\|ESSD; ARM→ULTRAHIGH |
|
|
43
43
|
| Flavor not in region | Always `ListFlavors` first. Spec codes vary by region |
|
|
44
|
+
| `database_name` is case-sensitive | Use `MySQL` / `PostgreSQL` / `SQLServer` / `MariaDB` — NOT lower-case `mysql` |
|
|
45
|
+
| Instance creation takes 3–8 min | Status: BUILD→MODIFYING→ACTIVE. Poll every 15s: `hcloud RDS ListInstances --cli-region=<r> --instance_id=<id> \| jq '.instances[0].status'` |
|
|
46
|
+
| Body `--region` is required | CreateInstance body requires `--region=<r>` (same as `--cli-region`) or `DBS.280243` |
|
|
44
47
|
| Restore creates new instance | No in-place restore. Verify target flavor before restoring |
|
|
45
48
|
|
|
46
49
|
## Instance Management
|
|
@@ -56,6 +59,7 @@ hcloud RDS ListEngineFlavors --instance_id=<id> --cli-region=<r>
|
|
|
56
59
|
### Create Instance
|
|
57
60
|
```bash
|
|
58
61
|
hcloud RDS CreateInstance --cli-region=<r> \
|
|
62
|
+
--region=<r> \
|
|
59
63
|
--name=<name> \
|
|
60
64
|
--datastore.type=<engine> \
|
|
61
65
|
--datastore.version=<version> \
|
|
@@ -66,11 +70,36 @@ hcloud RDS CreateInstance --cli-region=<r> \
|
|
|
66
70
|
--subnet_id=<subnet-id> \
|
|
67
71
|
--security_group_id=<sg-id> \
|
|
68
72
|
--availability_zone=<az> \
|
|
69
|
-
--charge_info.charge_mode=<mode>
|
|
70
|
-
--password=<pw>
|
|
73
|
+
--charge_info.charge_mode=<mode>
|
|
71
74
|
```
|
|
72
75
|
|
|
73
|
-
> `--password` conflicts with a KooCLI system param.
|
|
76
|
+
> `--password` conflicts with a KooCLI system param. Use `--cli-jsonInput` (see below). The `printf "b\n"` pipe workaround is broken in KooCLI 7.2.12+.
|
|
77
|
+
|
|
78
|
+
### `--cli-jsonInput` for CreateInstance
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"path": {
|
|
83
|
+
"project_id": "<project-id>"
|
|
84
|
+
},
|
|
85
|
+
"body": {
|
|
86
|
+
"name": "<name>",
|
|
87
|
+
"region": "<region>",
|
|
88
|
+
"datastore": { "type": "<engine>", "version": "<version>" },
|
|
89
|
+
"ha": { "mode": "single", "replication_mode": "semisync" },
|
|
90
|
+
"flavor_ref": "<flavor-id>",
|
|
91
|
+
"volume": { "type": "<vol-type>", "size": <gb> },
|
|
92
|
+
"vpc_id": "<vpc-id>",
|
|
93
|
+
"subnet_id": "<subnet-id>",
|
|
94
|
+
"security_group_id": "<sg-id>",
|
|
95
|
+
"availability_zone": "<az>",
|
|
96
|
+
"password": "<pw>",
|
|
97
|
+
"charge_info": { "charge_mode": "postPaid" }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
> Save as `rds-create.json` then: `hcloud RDS CreateInstance --cli-jsonInput=rds-create.json`. For `project_id`, run `hcloud IAM KeystoneListProjects`.
|
|
74
103
|
|
|
75
104
|
| Param | Required | Note |
|
|
76
105
|
|-------|----------|------|
|
|
@@ -139,6 +168,20 @@ psql -h <private_ip> -p 5432 -U root -d postgres
|
|
|
139
168
|
> hcloud does NOT support SQL execution. Use a database client.
|
|
140
169
|
> For public access, bind an EIP (see `huawei-vpc`).
|
|
141
170
|
|
|
171
|
+
### Database Client Acquisition
|
|
172
|
+
|
|
173
|
+
If a database client is not installed on the agent's machine, install one:
|
|
174
|
+
|
|
175
|
+
| Platform | MySQL | PostgreSQL |
|
|
176
|
+
|----------|-------|------------|
|
|
177
|
+
| **Linux** | `apt install mysql-client` / `yum install mysql` | `apt install postgresql-client` |
|
|
178
|
+
| **macOS** | `brew install mysql-client` | `brew install libpq` |
|
|
179
|
+
| **Windows** | Download MySQL Workbench or `winget install Oracle.MySQL` | Download pgAdmin or `winget install PostgreSQL.PostgreSQL` |
|
|
180
|
+
| **Python (any)** | `pip install pymysql` then `python -c "import pymysql; ..."` | `pip install psycopg2` then `python -c "import psycopg2; ..."` |
|
|
181
|
+
| **Docker (any)** | `docker run -it --rm mysql:8 mysql -h <ip> -u root -p` | `docker run -it --rm postgres:16 psql -h <ip> -U root` |
|
|
182
|
+
|
|
183
|
+
> If no client can be installed, use the Huawei Cloud **Data Studio** console: https://console.huaweicloud.com/dms/
|
|
184
|
+
|
|
142
185
|
## Mutating Operations (Require Approval)
|
|
143
186
|
|
|
144
187
|
| Operation | Effect |
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: huawei-sandbox
|
|
3
|
+
description: "Use when creating, connecting, or managing Huawei Cloud Sandbox instances and workspace terminals, or when a task needs a temporary runtime to deploy, run, or preview a web application. Covers sandbox lifecycle (check-user, sign-agreement, connect, release), session-based terminal execution, and credential injection. Triggers on: sandbox, workspace, terminal, web app deployment, deploy web app, preview app, deploy github, github repo, deploy repo, website, static site, web application, hwlink, devstation, hdkitservice, remote exec. NOT for: ECS instances (use huawei-ecs), CCE clusters (use huawei-cce)."
|
|
4
|
+
version: 1
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Huawei Cloud Sandbox
|
|
8
|
+
|
|
9
|
+
**STOP - Do not answer from general knowledge.** Follow the procedure below.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace terminal execution. Covers sandbox lifecycle via hdkitservice API and remote terminal command execution via hwlink protocol.
|
|
14
|
+
|
|
15
|
+
## Activation
|
|
16
|
+
|
|
17
|
+
- **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?"
|
|
18
|
+
- **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 应用,建议优先使用沙箱部署(临时运行环境,可预览访问),是否使用?"
|
|
19
|
+
- **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.
|
|
20
|
+
- **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.
|
|
21
|
+
- **Do not intercept a specified target**: if the task already names a deployment target (ECS, CCE, an existing server), follow that target instead of offering the sandbox. Offer the sandbox only when the task needs a temporary runtime or no target is specified.
|
|
22
|
+
- The developer never needs to name or understand the sandbox as a separate service. Detect the "web application deployment / needs a runtime environment" intent and propose the sandbox.
|
|
23
|
+
|
|
24
|
+
## MCP Tools
|
|
25
|
+
|
|
26
|
+
### User Verification (Prerequisites)
|
|
27
|
+
|
|
28
|
+
| Tool | Purpose |
|
|
29
|
+
|------|---------|
|
|
30
|
+
| `huaweicloud_sandbox_check_user` | Check real-name verification and agreement signing status |
|
|
31
|
+
| `huaweicloud_sandbox_sign_agreement` | Sign unsigned/outdated agreements (required before connect) |
|
|
32
|
+
|
|
33
|
+
### Sandbox Lifecycle
|
|
34
|
+
|
|
35
|
+
| Tool | Purpose |
|
|
36
|
+
|------|---------|
|
|
37
|
+
| `huaweicloud_sandbox_connect` | Connect to sandbox (one user one instance, reuses existing if available) |
|
|
38
|
+
| `huaweicloud_sandbox_credentials` | Inject temporary AK/SK into a running sandbox |
|
|
39
|
+
|
|
40
|
+
### Terminal Execution
|
|
41
|
+
|
|
42
|
+
| Tool | Purpose |
|
|
43
|
+
|------|---------|
|
|
44
|
+
| `huaweicloud_sandbox_exec_with_session` | Session-based execution (state persists) |
|
|
45
|
+
| `huaweicloud_sandbox_close_session` | Close a persistent terminal session |
|
|
46
|
+
|
|
47
|
+
## Workflow
|
|
48
|
+
|
|
49
|
+
Setup is a **plugin-side preflight** — the developer should be asked a question only once, when the agreement actually needs signing:
|
|
50
|
+
|
|
51
|
+
1. **Check user** (transparent): `huaweicloud_sandbox_check_user` — verify `realname_verified` and `agreement_signed`
|
|
52
|
+
2. **Real-name verification** (only if `realname_verified=false`): tell the developer once, "Huawei Cloud requires real-name verification before using the sandbox." and stop — do not retry `connect` in a loop
|
|
53
|
+
3. **Sign agreement** (only if `agreement_signed=false`): ask the developer once as the plugin — "Huawei Cloud sandbox requires signing its service agreement. May I sign it on your behalf?" — then call `huaweicloud_sandbox_sign_agreement`. Do not expose the underlying sandbox/DevBridge service as a separate entity the developer must understand or sign up for
|
|
54
|
+
4. **Connect**: `huaweicloud_sandbox_connect` — returns `session_id`, `dev_stage_id`, `connection_id`, `connection_address`
|
|
55
|
+
5. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox
|
|
56
|
+
6. **Execute commands**: `huaweicloud_sandbox_exec_with_session` for interactive work
|
|
57
|
+
7. **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
|
|
58
|
+
|
|
59
|
+
## Expose the deployed app (public URL)
|
|
60
|
+
|
|
61
|
+
Expose a deployed web app to a public URL and return that URL to the developer.
|
|
62
|
+
|
|
63
|
+
**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.
|
|
64
|
+
|
|
65
|
+
**Install** (internal tooling; skip if `devbridge version` already works):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
curl -fsSL https://res-hd.hc-cdn.cn/sharedata/hdspace/devbridge/install.sh | bash
|
|
69
|
+
export PATH=$PATH:$HOME/.huawei/bin # installer only writes ~/.bashrc; session shells do not re-source it
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Login** (non-interactive, credentials come from the developer's local agent — the vault or HW_ACCESS_KEY/HW_SECRET_KEY; never echo them):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
devbridge auth login --huaweicloud --access-key "$AK" --secret-key "$SK"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- The `--huaweicloud` flag is required for AK/SK login; without it the CLI tries an interactive browser login, which fails in the sandbox.
|
|
79
|
+
- Write the AK/SK to temp files with `umask 077` (or shell vars) and delete them right after login. Verify with `devbridge auth status`.
|
|
80
|
+
|
|
81
|
+
**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>`):
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
cd /workspace/<repo-name> && nohup python3 -m http.server 8080 > /tmp/http.log 2>&1 &
|
|
85
|
+
nohup devbridge host -p 8080 -e 8 > /tmp/host.log 2>&1 &
|
|
86
|
+
sleep 10 && cat /tmp/host.log
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- The public URL has the form `https://<id>-<port>.cn-north-4-bridge.myhuaweicloud.com` (from the `Tunnel URL:` line).
|
|
90
|
+
- **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).
|
|
91
|
+
- Internal docs: https://huaweicloud.github.io/devspace-devbridge/
|
|
92
|
+
|
|
93
|
+
**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.
|
|
94
|
+
|
|
95
|
+
## Critical Warnings
|
|
96
|
+
|
|
97
|
+
| Trap | Why |
|
|
98
|
+
|------|-----|
|
|
99
|
+
| 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 |
|
|
100
|
+
| Real-name required | `sandbox_connect` fails if `realname_verified=false`; tell the developer once and stop, don't loop on connect |
|
|
101
|
+
| Never expose tunnel details | Do not mention "DevBridge"/"tunnel"/"devbridge" to the developer — say "正在生成访问地址..." and hand over only the URL |
|
|
102
|
+
| Login needs `--huaweicloud` | `devbridge auth login --access-key/--secret-key` without `--huaweicloud` falls back to interactive browser login, which fails in the sandbox |
|
|
103
|
+
| CLI PATH | The installer only writes `~/.bashrc`; run `export PATH=$PATH:$HOME/.huawei/bin` in the session before using `devbridge` |
|
|
104
|
+
| 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 |
|
|
105
|
+
| Return the deployment URL | Always hand the public URL from the host log to the developer as the final result |
|
|
106
|
+
| Session state persists | `exec_with_session` preserves `cd`, env vars, aliases between calls |
|
|
107
|
+
| Destructive commands blocked | `rm -rf /`, `mkfs`, `dd if=`, fork bombs are denied by safety policy |
|
|
108
|
+
| Workspace ID = dev_stage_id | Use `dev_stage_id` from `sandbox_connect` as `workspace_id` for terminal exec |
|
|
109
|
+
| 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 |
|
|
110
|
+
| 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") |
|
|
111
|
+
|
|
112
|
+
## Node.js in the sandbox
|
|
113
|
+
|
|
114
|
+
If the sandbox has no Node.js, download it from the Huawei Cloud mirror. Pick the tarball matching the sandbox arch (`uname -m`: `aarch64` -> arm64, `x86_64` -> x64):
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# aarch64 sandbox:
|
|
118
|
+
curl -fsSL https://mirrors.huaweicloud.com/nodejs/v24.19.0/node-v24.19.0-linux-arm64.tar.gz -o node.tar.gz
|
|
119
|
+
# x86_64 sandbox:
|
|
120
|
+
curl -fsSL https://mirrors.huaweicloud.com/nodejs/v24.19.0/node-v24.19.0-linux-x64.tar.gz -o node.tar.gz
|
|
121
|
+
sudo tar -xzf node.tar.gz -C /usr/local --strip-components=1
|
|
122
|
+
node --version
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Environment Variables
|
|
126
|
+
|
|
127
|
+
| Variable | Required | Description |
|
|
128
|
+
|----------|----------|-------------|
|
|
129
|
+
| `HW_ACCESS_KEY` | Yes | Huawei Cloud AK |
|
|
130
|
+
| `HW_SECRET_KEY` | Yes | Huawei Cloud SK |
|
|
131
|
+
| `HW_SECURITY_TOKEN` | No | STS security token |
|
|
132
|
+
| `HW_WORKSPACE_ID` | No | Default workspace ID |
|
|
133
|
+
| `HDKITSERVICE_ENDPOINT` | No | hdkitservice API endpoint |
|
|
134
|
+
| `HWLINK_ENDPOINT` | No | DevStation API endpoint |
|
|
@@ -28,15 +28,17 @@ Domain expertise for Huawei Cloud Virtual Private Cloud (VPC). Covers VPC/subnet
|
|
|
28
28
|
| EIP PER type needs `--bandwidth.name` | PER bandwidth requires explicit name; `--help` marks it optional but it's required |
|
|
29
29
|
| **VPC params need nested prefix** | KooCLI 7.x VPC API uses `--vpc.<param>`, `--subnet.<param>`, `--security_group.<param>`. Example: `--vpc.name=xxx` NOT `--name=xxx` |
|
|
30
30
|
| **Security group needs no vpc_id** | VPC v3 API `CreateSecurityGroup` does NOT accept `vpc_id`. Security groups are region-level, not VPC-bound |
|
|
31
|
-
| Subnet DNS empty → ECS no DNS | DNS params (`--subnet.primary_dns`, `--subnet.secondary_dns`) marked optional but empty default breaks cloud-init domain resolution — `yum`/`apt` installs fail silently. Always set both.
|
|
31
|
+
| Subnet DNS empty → ECS no DNS | DNS params (`--subnet.primary_dns`, `--subnet.secondary_dns`) marked optional but empty default breaks cloud-init domain resolution — `yum`/`apt` installs fail silently. Always set both. Common DNS IPs: cn-north-4 (100.125.1.250 / 100.125.1.251), cn-north-1 (100.125.1.250 / 100.125.129.250), cn-east-3 (100.125.1.250 / 100.125.129.250), ap-southeast-3 (100.125.1.250 / 100.125.128.250) |
|
|
32
32
|
| SCP blocks 0.0.0.0/0 SG rules | If `CreateSecurityGroupRule` with `--remote_ip_prefix=0.0.0.0/0` fails with `SYS.0403`, an org-level SCP policy is denying wide-open rules. Narrow to a specific CIDR range (e.g., your office IP) instead |
|
|
33
|
+
| **VPC tags use `*` separator** | VPC tag format: `--vpc.tags.1=env*test` (asterisk between key and value). NOT `--vpc.tags.1.key=env` (ECS-style). This is different from ECS `--server.metadata.key=value` |
|
|
33
34
|
|
|
34
35
|
## Common Workflows
|
|
35
36
|
|
|
36
37
|
| Task | Command | Steps |
|
|
37
38
|
|------|---------|-------|
|
|
38
39
|
| Create VPC | hcloud VPC CreateVpc --vpc.name=<name> --vpc.cidr=<cidr> | CIDR must not conflict with existing VPCs. Run `hcloud VPC ListVpcs` first |
|
|
39
|
-
| Create subnet | hcloud VPC CreateSubnet --subnet.name=<name> --subnet.vpc_id=<id> --subnet.cidr=<cidr> --subnet.gateway_ip=<gw> --subnet.primary_dns=<dns1> --subnet.secondary_dns=<dns2> --subnet.availability_zone=<az> | Subnet CIDR must be a subset of the VPC CIDR. DNS addresses vary by region — see
|
|
40
|
+
| Create subnet | hcloud VPC CreateSubnet --subnet.name=<name> --subnet.vpc_id=<id> --subnet.cidr=<cidr> --subnet.gateway_ip=<gw> --subnet.primary_dns=<dns1> --subnet.secondary_dns=<dns2> --subnet.availability_zone=<az> | Subnet CIDR must be a subset of the VPC CIDR. DNS addresses vary by region — see Critical Warnings for common values |
|
|
41
|
+
| Update subnet | hcloud VPC UpdateSubnet --subnet_id=<id> --subnet.dnsList.1=<dns1> --subnet.dnsList.2=<dns2> | Fix DNS after creation. Restart ECS after updating DNS for cloud-init to pick up changes |
|
|
40
42
|
| Security group | hcloud VPC CreateSecurityGroup --security_group.name=<name> | references/security-group.md |
|
|
41
43
|
| SG rule | hcloud VPC CreateSecurityGroupRule --security_group_rule.security_group_id=<id> --security_group_rule.direction=<direction> --security_group_rule.protocol=<protocol> --security_group_rule.multiport=<port> --security_group_rule.remote_ip_prefix=<cidr> | references/security-group.md |
|
|
42
44
|
| Create EIP | hcloud EIP CreatePublicip --publicip.type=<type> --bandwidth.size=<size> --bandwidth.share_type=<share-type> --bandwidth.name=<name> | Run `hcloud EIP CreatePublicip --help` to confirm valid type values per region |
|
|
@@ -58,6 +60,7 @@ Domain expertise for Huawei Cloud Virtual Private Cloud (VPC). Covers VPC/subnet
|
|
|
58
60
|
| VPC.0301: Bandwidth name invalid | PER type requires `--bandwidth.name`, even though `--help` marks it optional |
|
|
59
61
|
| EIP has no public IP after binding | May need AddIngressEipV2 for ELB-type resources (see huawei-apig) |
|
|
60
62
|
| ECS cloud-init fails silently (port 80/443 closed) | Subnet likely has no DNS. Check `hcloud VPC ShowSubnet --subnet_id=<id>` → `dnsList` empty? Rebuild subnet with `--subnet.primary_dns=<dns1> --subnet.secondary_dns=<dns2>`. DNS addresses per region: `hcloud VPC CreateSubnet --help` |
|
|
63
|
+
| VPC.0209: Subnet still used | Subnet has dependent resources (ECS/RDS) — delete instances first, then subnet |
|
|
61
64
|
| SYS.0403 / SCP deny | Service Control Policy explicitly denies this operation — contact org admin to adjust SCP, or use an account/region without the restriction. If SSH is blocked, bootstrap via cloud-init user_data instead: see `huawei-ecs` — no SSH needed |
|
|
62
65
|
|
|
63
66
|
## Security Considerations
|
|
@@ -60,7 +60,8 @@ Agent processes find executables through `PATH`. If OpenCode/Codex cannot find `
|
|
|
60
60
|
**NEVER let AK/SK enter shell history. This is the #1 credential leak vector.**
|
|
61
61
|
|
|
62
62
|
- Create AK/SK in the Huawei Cloud console under `My Credentials -> Access Keys`.
|
|
63
|
-
- **
|
|
63
|
+
- **Unified credentials** (preferred): `npx huaweicloud-devkit auth init`. This is the DevKit's primary auth path; `hcloud configure init` only covers KooCLI.
|
|
64
|
+
- **KooCLI only, interactive** (SAFE): `hcloud configure init` — prompts for AK/SK via terminal input. Values do NOT enter shell history.
|
|
64
65
|
- **Non-interactive** (DANGEROUS — AK/SK in shell history): `hcloud configure set --cli-access-key=<AK> --cli-secret-key=<SK> --cli-region=<region>`. Only use in ephemeral CI/CD shells. User must execute outside agent chat.
|
|
65
66
|
- If MCP is available, use `huaweicloud_show_profile_redacted` to check status without ever seeing credentials.
|
|
66
67
|
- Never paste AK/SK, passwords, tokens, or profile files into the agent conversation.
|
|
@@ -72,14 +73,14 @@ Agent processes find executables through `PATH`. If OpenCode/Codex cannot find `
|
|
|
72
73
|
2. **KooCLI first-run privacy agreement**: On a fresh KooCLI install, `hcloud` blocks with `同意并继续使用(y)/不同意并退出(N)` and fails with `[USE_ERROR]您输入的是无效字符` in non-interactive mode. Detection: check command output for these strings. Ask the user: "KooCLI needs to accept its privacy agreement. May I accept it on your behalf?" If the user agrees, run `huaweicloud_run_readonly_command` with `args=["version"]` and `stdin="y\n"`. This accepts the agreement once, after which hcloud works normally.
|
|
73
74
|
3. Ask the user to configure credentials outside the agent conversation when setup is needed.
|
|
74
75
|
4. Inspect profile and region only through redacted tooling.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
5. Discover exact operation names with `hcloud <Service> --help` before guessing. Example: ECS instance listing is commonly `ECS ListServersDetails`; ECS creation is commonly `ECS CreateServers`; image lookup may be under `IMS GlanceShowImage`.
|
|
77
|
+
6. Use `--cli-output=json` for machine-readable responses when supported.
|
|
78
|
+
7. For resource operations, include `--cli-region`, `--cli-profile`, and service-specific project information when required.
|
|
79
|
+
8. Classify every command before running it:
|
|
79
80
|
- Read-only: `List*`, `Show*`, `Get*`, `Describe*`.
|
|
80
81
|
- Write: `Create*`, `Delete*`, `Update*`, `Resize*`, `Start*`, `Stop*`, `Authorize*`, and similar.
|
|
81
82
|
- Secret: any operation returning secret string, binary secret, token, or password.
|
|
82
|
-
|
|
83
|
+
9. For write operations, show the exact command and ask for explicit approval.
|
|
83
84
|
|
|
84
85
|
## KooCLI Syntax Notes
|
|
85
86
|
|
|
@@ -36,7 +36,8 @@ Do not rely on training data for facts. Huawei Cloud services, pricing, quotas,
|
|
|
36
36
|
| observability | Observability Routing | monitor, alarm, log, audit, trace, Cloud Eye | Handoff to huawei-cloud-eye or huawei-cts |
|
|
37
37
|
| billing | Billing Routing | cost, bill, budget, spending, invoice | Handoff to huawei-billing |
|
|
38
38
|
| iam | IAM Routing | permission, policy, role, user, group, AK/SK | Handoff to huawei-iam |
|
|
39
|
-
| deployment | Deployment Routing | deploy, CI/CD, pipeline, release |
|
|
39
|
+
| deployment | Deployment Routing | deploy, CI/CD, pipeline, release | CI/CD pipeline -> huawei-deployment; deploy a web app or a GitHub repo (no CI/CD target) -> huawei-sandbox |
|
|
40
|
+
| sandbox | Sandbox Routing | sandbox, DevStation, workspace, terminal, preview, temporary runtime | Handoff to huawei-sandbox |
|
|
40
41
|
| cli | CLI and Auth Routing | install hcloud, configure KooCLI, AK/SK setup | Handoff to huaweicloud-cli-and-auth |
|
|
41
42
|
| safety | Safety Routing | is this safe, approve command, risk review | Handoff to huaweicloud-safety |
|
|
42
43
|
| troubleshoot | Troubleshooting Routing | error, bug, failed, AccessDenied, quota | Handoff to huaweicloud-troubleshooting |
|
|
@@ -67,6 +68,7 @@ Do not rely on training data for facts. Huawei Cloud services, pricing, quotas,
|
|
|
67
68
|
| Audit trails | CTS | huawei-cts |
|
|
68
69
|
| Backup / disaster recovery | CBR | huawei-cbr |
|
|
69
70
|
| CI/CD pipeline | CloudDeploy | huawei-deployment |
|
|
71
|
+
| Temporary runtime / web app preview | Sandbox (DevStation) | huawei-sandbox |
|
|
70
72
|
| Getting started | Account setup | huaweicloud-cli-and-auth |
|
|
71
73
|
|
|
72
74
|
## Capability Sources
|
|
@@ -50,7 +50,7 @@ Use evidence before fixes. Do not guess service behavior when request IDs, regio
|
|
|
50
50
|
|
|
51
51
|
| Error | Likely Cause | Fix |
|
|
52
52
|
|-------|-------------|-----|
|
|
53
|
-
| AuthFailure / 401 | AK/SK invalid or expired | Regenerate AK/SK, re-run `
|
|
53
|
+
| AuthFailure / 401 | AK/SK invalid or expired | Regenerate AK/SK, re-run `npx huaweicloud-devkit auth init` |
|
|
54
54
|
| AccessDenied / 403 | IAM permission missing | Check `huawei-iam` skill, add required policy action |
|
|
55
55
|
| NoSuchKey / 404 | Resource not found | Verify resource ID, region, and project_id |
|
|
56
56
|
| QuotaExceeded | Account limit reached | Request quota increase in console |
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
export const SUPPORTED_AGENT_TARGETS = ['opencode', 'codex', 'codex-desktop', 'codearts', 'workbuddy'];
|
|
7
|
+
|
|
8
|
+
function baseHome() {
|
|
9
|
+
return process.env.HUAWEICLOUD_HOME || homedir();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function opencodeConfigFile() {
|
|
13
|
+
const jsonc = join(baseHome(), '.config', 'opencode', 'opencode.jsonc');
|
|
14
|
+
if (existsSync(jsonc)) return jsonc;
|
|
15
|
+
return join(baseHome(), '.config', 'opencode', 'opencode.json');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function readJsonSafe(path) {
|
|
19
|
+
if (!existsSync(path)) return null;
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function opencodeRegistered() {
|
|
28
|
+
const path = opencodeConfigFile();
|
|
29
|
+
const cfg = readJsonSafe(path);
|
|
30
|
+
return Boolean(cfg?.mcp?.['huaweicloud-devkit']);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function codexDesktopRegistered() {
|
|
34
|
+
const path = join(baseHome(), '.codex', 'config.toml');
|
|
35
|
+
if (!existsSync(path)) return false;
|
|
36
|
+
try {
|
|
37
|
+
return readFileSync(path, 'utf8').includes('[mcp_servers.huaweicloud-devkit]');
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function codexCliRegistered() {
|
|
44
|
+
try {
|
|
45
|
+
const r = spawnSync('codex', ['plugin', 'list'], {
|
|
46
|
+
shell: false,
|
|
47
|
+
windowsHide: true,
|
|
48
|
+
stdio: 'pipe',
|
|
49
|
+
timeout: 10000,
|
|
50
|
+
});
|
|
51
|
+
const out = `${r.stdout || ''}${r.stderr || ''}`;
|
|
52
|
+
return out.includes('huaweicloud-core');
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function codeartsRegistered() {
|
|
59
|
+
const paths = [
|
|
60
|
+
join(baseHome(), '.codeartsdoer', 'mcp', 'mcp_settings.json'),
|
|
61
|
+
join(process.cwd(), '.codeartsdoer', 'mcp', 'mcp_settings.json'),
|
|
62
|
+
];
|
|
63
|
+
return paths.some((path) => {
|
|
64
|
+
const cfg = readJsonSafe(path);
|
|
65
|
+
return Boolean(cfg?.mcpServers?.['huaweicloud-devkit']);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function workbuddyRegistered() {
|
|
70
|
+
const cfg = readJsonSafe(join(baseHome(), '.workbuddy', 'mcp.json'));
|
|
71
|
+
return Boolean(cfg?.mcpServers?.['huaweicloud-devkit']);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getAgentRegistrationStatuses(target = 'all') {
|
|
75
|
+
const requested = target === 'all' ? SUPPORTED_AGENT_TARGETS : [target];
|
|
76
|
+
const result = { target, agents: {} };
|
|
77
|
+
for (const agent of requested) {
|
|
78
|
+
let configured = false;
|
|
79
|
+
if (agent === 'opencode') configured = opencodeRegistered();
|
|
80
|
+
if (agent === 'codex-desktop') configured = codexDesktopRegistered();
|
|
81
|
+
if (agent === 'codex') configured = codexCliRegistered();
|
|
82
|
+
if (agent === 'codearts') configured = codeartsRegistered();
|
|
83
|
+
if (agent === 'workbuddy') configured = workbuddyRegistered();
|
|
84
|
+
result.agents[agent] = { configured };
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
|
|
5
|
+
// Verify a credential file ended up with 0600. On Windows-mounted drives inside WSL
|
|
6
|
+
// (drvfs/9p) chmod is silently ignored, so the file can be world-readable (0777).
|
|
7
|
+
// Native Windows has no POSIX modes (statSync always reports 0666), so skip the check there.
|
|
8
|
+
function ensurePrivateMode(path) {
|
|
9
|
+
if (process.platform === 'win32') return;
|
|
10
|
+
try { chmodSync(path, 0o600); } catch {}
|
|
11
|
+
try {
|
|
12
|
+
const mode = statSync(path).mode & 0o777;
|
|
13
|
+
if (mode !== 0o600) {
|
|
14
|
+
console.warn(`\x1b[33m[WARN]\x1b[0m Could not set 0600 on ${path} (current mode ${mode.toString(8)}). Credentials may be readable by other users.`);
|
|
15
|
+
console.warn(`\x1b[33m If running under WSL, move the credential home to the Linux filesystem:\x1b[0m`);
|
|
16
|
+
console.warn(`\x1b[33m export HUAWEICLOUD_HOME=$HOME (then re-run auth init)\x1b[0m`);
|
|
17
|
+
console.warn(`\x1b[33m Or skip file storage entirely with HW_ACCESS_KEY/HW_SECRET_KEY environment variables.\x1b[0m`);
|
|
18
|
+
}
|
|
19
|
+
} catch {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function baseHome() {
|
|
23
|
+
return process.env.HUAWEICLOUD_HOME || homedir();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function globalCredentialsPath() {
|
|
27
|
+
return join(baseHome(), '.config', 'huaweicloud', 'credentials.json');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function obsConfigPath() {
|
|
31
|
+
return join(baseHome(), '.obsutilconfig');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function readGlobalCredentials() {
|
|
35
|
+
const path = globalCredentialsPath();
|
|
36
|
+
if (!existsSync(path)) return null;
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function writeGlobalCredentials(credentials = {}) {
|
|
45
|
+
const path = globalCredentialsPath();
|
|
46
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
47
|
+
const payload = {
|
|
48
|
+
ak: String(credentials.ak || ''),
|
|
49
|
+
sk: String(credentials.sk || ''),
|
|
50
|
+
securityToken: String(credentials.securityToken || ''),
|
|
51
|
+
region: String(credentials.region || ''),
|
|
52
|
+
};
|
|
53
|
+
writeFileSync(path, JSON.stringify(payload, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
54
|
+
ensurePrivateMode(path);
|
|
55
|
+
return path;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function writeObsConfig(credentials = {}) {
|
|
59
|
+
const region = String(credentials.region || '');
|
|
60
|
+
const ak = String(credentials.ak || '');
|
|
61
|
+
const sk = String(credentials.sk || '');
|
|
62
|
+
const securityToken = String(credentials.securityToken || '');
|
|
63
|
+
if (!region || !ak || !sk) {
|
|
64
|
+
throw new Error('region, ak, and sk are required to write OBS config');
|
|
65
|
+
}
|
|
66
|
+
const path = obsConfigPath();
|
|
67
|
+
const endpoint = credentials.endpoint || `https://obs.${region}.myhuaweicloud.com`;
|
|
68
|
+
// Flat key=value format (no [default] section) as written by KooCLI 7.x `hcloud OBS config`.
|
|
69
|
+
const content = `endpoint=${endpoint}\nak=${ak}\nsk=${sk}${securityToken ? `\ntoken=${securityToken}` : ''}\n`;
|
|
70
|
+
writeFileSync(path, content, { encoding: 'utf8', mode: 0o600 });
|
|
71
|
+
ensurePrivateMode(path);
|
|
72
|
+
return { path, endpoint };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function resolveCredentials(options = {}) {
|
|
76
|
+
let ak = process.env.HW_ACCESS_KEY;
|
|
77
|
+
let sk = process.env.HW_SECRET_KEY;
|
|
78
|
+
let securityToken = process.env.HW_SECURITY_TOKEN;
|
|
79
|
+
let region = process.env.HW_REGION || process.env.HUAWEICLOUD_REGION || '';
|
|
80
|
+
|
|
81
|
+
const stored = readGlobalCredentials();
|
|
82
|
+
if (stored) {
|
|
83
|
+
if (!ak && stored.ak) ak = stored.ak;
|
|
84
|
+
if (!sk && stored.sk) sk = stored.sk;
|
|
85
|
+
if (!securityToken && stored.securityToken) securityToken = stored.securityToken;
|
|
86
|
+
if (!region && stored.region) region = stored.region;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!ak || !sk) {
|
|
90
|
+
if (options.allowMissing) return null;
|
|
91
|
+
throw new Error('Huawei Cloud credentials are not configured. Run "npx huaweicloud-devkit auth init" or set HW_ACCESS_KEY/HW_SECRET_KEY.');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { ak, sk, securityToken, region };
|
|
95
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { getAgentRegistrationStatuses } from './agent-registration.mjs';
|
|
4
|
+
import { globalCredentialsPath, obsConfigPath, readGlobalCredentials, writeObsConfig } from './credentials.mjs';
|
|
5
|
+
|
|
6
|
+
function hcloudInstalled() {
|
|
7
|
+
const bin = process.env.HCLOUD_BIN || 'hcloud';
|
|
8
|
+
try {
|
|
9
|
+
const r = spawnSync(`"${bin}" version`, [], {
|
|
10
|
+
shell: true,
|
|
11
|
+
windowsHide: true,
|
|
12
|
+
stdio: 'pipe',
|
|
13
|
+
timeout: 5000,
|
|
14
|
+
});
|
|
15
|
+
const out = `${r.stdout || ''}${r.stderr || ''}`;
|
|
16
|
+
return r.status === 0 && /KooCLI|Current.*version|当前KooCLI/i.test(out);
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getAuthStatus(target = 'all') {
|
|
23
|
+
const credentials = readGlobalCredentials();
|
|
24
|
+
return {
|
|
25
|
+
target,
|
|
26
|
+
credentialsConfigured: Boolean(credentials?.ak && credentials?.sk),
|
|
27
|
+
credentialsPath: globalCredentialsPath(),
|
|
28
|
+
obsConfigured: existsSync(obsConfigPath()),
|
|
29
|
+
obsConfigPath: obsConfigPath(),
|
|
30
|
+
kooCliInstalled: hcloudInstalled(),
|
|
31
|
+
agents: getAgentRegistrationStatuses(target).agents,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function syncAuth(target = 'all') {
|
|
36
|
+
const credentials = readGlobalCredentials();
|
|
37
|
+
if (!credentials?.ak || !credentials?.sk) {
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
error: 'Global credentials are not configured.',
|
|
41
|
+
nextStep: 'Run "npx huaweicloud-devkit auth init" first.',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let obs = null;
|
|
46
|
+
try {
|
|
47
|
+
obs = writeObsConfig(credentials);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
error: error.message,
|
|
52
|
+
nextStep: 'Run "npx huaweicloud-devkit auth init" to refresh credentials and region.',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
ok: true,
|
|
58
|
+
obs: { configured: true, path: obs.path, endpoint: obs.endpoint },
|
|
59
|
+
credentialsConfigured: true,
|
|
60
|
+
agents: getAgentRegistrationStatuses(target).agents,
|
|
61
|
+
note: 'OBS credentials were synced from the global credential vault. Agent MCP registration is managed by "npx huaweicloud-devkit install --target <agent>".',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { stdin, stdout } from 'node:process';
|
|
3
|
+
import { rmSync, existsSync } from 'node:fs';
|
|
4
|
+
import { resolve, dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
3
6
|
import { TOOL_DEFINITIONS, callTool } from './tools.mjs';
|
|
4
7
|
|
|
8
|
+
// The MCP server is now loaded by a live agent session. Clear the install marker
|
|
9
|
+
// in this plugin dir so `doctor` no longer reports "restart needed".
|
|
10
|
+
try {
|
|
11
|
+
const pluginDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
12
|
+
const marker = resolve(pluginDir, '.installed');
|
|
13
|
+
if (existsSync(marker)) rmSync(marker, { force: true });
|
|
14
|
+
} catch {}
|
|
15
|
+
|
|
5
16
|
let buffer = Buffer.alloc(0);
|
|
6
17
|
let useContentLengthFraming = true;
|
|
7
18
|
|