huaweicloud-devkit 1.0.2-next.9 → 1.0.2
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/.agents/plugins/marketplace.json +1 -1
- package/README.md +85 -51
- package/README.zh-CN.md +85 -49
- package/package.json +11 -5
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
- package/plugins/huaweicloud-core/.mcp.json +1 -2
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -18
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +32 -9
- package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +67 -20
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +11 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +16 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +27 -24
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +12 -0
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +103 -2
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +65 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +31 -4
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +6 -1
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +7 -2
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +14 -4
- package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +432 -10
- package/plugins/huaweicloud-core/src/setup-cli.mjs +823 -22
- package/plugins/huaweicloud-core/src/tools.mjs +239 -27
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +18 -4
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +267 -0
- package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +9 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "huaweicloud-
|
|
3
|
-
"version": "1.0.2
|
|
2
|
+
"name": "huaweicloud-devkit",
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "HuaweiCloud Mate",
|
|
@@ -9,30 +9,16 @@
|
|
|
9
9
|
"homepage": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
|
|
10
10
|
"repository": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
|
|
11
11
|
"license": "Apache-2.0",
|
|
12
|
-
"keywords": [
|
|
13
|
-
"huaweicloud",
|
|
14
|
-
"huawei-cloud",
|
|
15
|
-
"koocli",
|
|
16
|
-
"hcloud",
|
|
17
|
-
"agent",
|
|
18
|
-
"mcp",
|
|
19
|
-
"api",
|
|
20
|
-
"sdk",
|
|
21
|
-
"skills"
|
|
22
|
-
],
|
|
12
|
+
"keywords": ["huaweicloud", "huawei-cloud", "koocli", "hcloud", "agent", "mcp", "api", "sdk", "skills"],
|
|
23
13
|
"skills": "./skills/",
|
|
24
14
|
"mcpServers": "./.mcp.json",
|
|
25
|
-
"hooks": "./hooks/",
|
|
26
15
|
"interface": {
|
|
27
16
|
"displayName": "HuaweiCloud Devkit",
|
|
28
17
|
"shortDescription": "HuaweiCloud Devkit - Huawei Cloud guidance, CLI/API/SDK routing, MCP tools, and safety for coding agents.",
|
|
29
18
|
"longDescription": "HuaweiCloud Devkit helps coding agents choose and use Huawei Cloud Skills, KooCLI, APIs, SDKs, and MCP tools with less context, safer command execution, and more accurate cloud implementation decisions.",
|
|
30
19
|
"developerName": "HuaweiCloud Mate",
|
|
31
20
|
"category": "Cloud",
|
|
32
|
-
"capabilities": [
|
|
33
|
-
"Read",
|
|
34
|
-
"Interactive"
|
|
35
|
-
],
|
|
21
|
+
"capabilities": ["Read", "Interactive"],
|
|
36
22
|
"websiteURL": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
|
|
37
23
|
"brandColor": "#C7000B",
|
|
38
24
|
"defaultPrompt": [
|
|
@@ -18,15 +18,38 @@ from pathlib import Path
|
|
|
18
18
|
|
|
19
19
|
DENY_PREFIX = "Huawei Cloud safety hook blocked this action: "
|
|
20
20
|
RULES_PATH = Path(__file__).resolve().parents[1] / "safety" / "rules" / "cloud-risk-rules.json"
|
|
21
|
+
POLICY_PATH = Path(__file__).resolve().parents[1] / "safety" / "policy.json"
|
|
21
22
|
|
|
22
|
-
CONFIG_FILE_RE =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
CONFIG_FILE_RE = None
|
|
24
|
+
SECRET_READ_RE = None
|
|
25
|
+
WRITE_OPERATION_RE = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load_policy():
|
|
29
|
+
global CONFIG_FILE_RE, SECRET_READ_RE, WRITE_OPERATION_RE
|
|
30
|
+
try:
|
|
31
|
+
with POLICY_PATH.open("r", encoding="utf-8") as file_obj:
|
|
32
|
+
policy = json.load(file_obj)
|
|
33
|
+
cred_patterns = policy.get("credentialFilePatterns", [])
|
|
34
|
+
if cred_patterns:
|
|
35
|
+
CONFIG_FILE_RE = re.compile("|".join(cred_patterns), re.I)
|
|
36
|
+
blocked_secrets = policy.get("blockedSecretOperations", [])
|
|
37
|
+
if blocked_secrets:
|
|
38
|
+
SECRET_READ_RE = re.compile("|".join(re.escape(op) for op in blocked_secrets), re.I)
|
|
39
|
+
write_prefixes = policy.get("writeOperationPrefixes", [])
|
|
40
|
+
if write_prefixes:
|
|
41
|
+
WRITE_OPERATION_RE = re.compile(r"\b(" + "|".join(write_prefixes) + r")\w*", re.I)
|
|
42
|
+
except Exception:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
load_policy()
|
|
47
|
+
|
|
48
|
+
ENV_DUMP_RE = re.compile(
|
|
49
|
+
r"(env|printenv|Get-ChildItem\s+Env:|gci\s+Env:|dir\s+Env:).*(HUAWEICLOUD|HWC_|HCLOUD|OS_)", re.I,
|
|
29
50
|
)
|
|
51
|
+
HCLOUD_RE = re.compile(r"(^|\s)hcloud(\.exe)?\s+", re.I)
|
|
52
|
+
READ_OPERATION_RE = re.compile(r"\b(List|Show|Get|Describe|NovaList|NovaShow)\w*", re.I)
|
|
30
53
|
READ_OPERATION_RE = re.compile(r"\b(List|Show|Get|Describe|NovaList|NovaShow)\w*", re.I)
|
|
31
54
|
|
|
32
55
|
|
|
@@ -117,11 +140,11 @@ def main():
|
|
|
117
140
|
tool_name = data.get("tool_name", "")
|
|
118
141
|
text = command_text(data.get("tool_input", {}))
|
|
119
142
|
|
|
120
|
-
if CONFIG_FILE_RE.search(text):
|
|
143
|
+
if CONFIG_FILE_RE and CONFIG_FILE_RE.search(text):
|
|
121
144
|
deny("reading Huawei Cloud credential/profile files can expose AK/SK or tokens. Use redacted toolkit tools.")
|
|
122
145
|
if ENV_DUMP_RE.search(text):
|
|
123
146
|
deny("dumping cloud credential environment variables is not allowed.")
|
|
124
|
-
if SECRET_READ_RE.search(text):
|
|
147
|
+
if SECRET_READ_RE and SECRET_READ_RE.search(text):
|
|
125
148
|
deny("direct secret value retrieval would put plaintext secrets into the agent context.")
|
|
126
149
|
denied_rule = first_denied_command_rule(text)
|
|
127
150
|
if denied_rule:
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "huaweicloud-devkit",
|
|
3
|
+
"id": "huaweicloud-devkit",
|
|
4
|
+
"displayName": "HuaweiCloud DevKit",
|
|
5
|
+
"version": "1.0.2",
|
|
6
|
+
"family": "bundle-plugin",
|
|
7
|
+
"bundleFormat": "codex",
|
|
8
|
+
"description": "Guide coding agents to use Huawei Cloud safely — KooCLI, APIs, SDKs, 28 MCP tools, skills, and safety guardrails.",
|
|
9
|
+
"author": {
|
|
10
|
+
"name": "HuaweiCloud Mate",
|
|
11
|
+
"url": "https://github.com/huaweicloud"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/huaweicloud/huaweicloud-devkit",
|
|
14
|
+
"repository": "https://github.com/huaweicloud/huaweicloud-devkit",
|
|
15
|
+
"license": "Apache-2.0",
|
|
16
|
+
"keywords": ["huaweicloud", "huawei-cloud", "koocli", "hcloud", "agent", "mcp", "api", "sdk", "skills", "cloud"],
|
|
17
|
+
"category": "Cloud",
|
|
18
|
+
"capabilities": ["Read", "Interactive"],
|
|
19
|
+
"brandColor": "#C7000B"
|
|
20
|
+
}
|
|
@@ -37,7 +37,7 @@ This skill enables users to:
|
|
|
37
37
|
|
|
38
38
|
## Prerequisites
|
|
39
39
|
|
|
40
|
-
- **Node.js >=
|
|
40
|
+
- **Node.js >= 22** must be installed
|
|
41
41
|
- **Network access** to `gitcode.com` (API v5 for index) and `github.com` / `raw.githubusercontent.com` (for skill details)
|
|
42
42
|
|
|
43
43
|
### Step 0: Check Python Environment
|
|
@@ -40,11 +40,31 @@ Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace t
|
|
|
40
40
|
|
|
41
41
|
### Terminal Execution
|
|
42
42
|
|
|
43
|
-
| Tool | Purpose
|
|
44
|
-
| --------------------------------------- |
|
|
45
|
-
| `huaweicloud_sandbox_exec_with_session` | Session-based execution (state persists)
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
43
|
+
| Tool | Purpose |
|
|
44
|
+
| --------------------------------------- | --------------------------------------------------------------------------- |
|
|
45
|
+
| `huaweicloud_sandbox_exec_with_session` | Session-based execution (state persists; best for interactive work) |
|
|
46
|
+
| `huaweicloud_sandbox_exec_one_shot` | One-shot execution (fresh connection; best for long/heavy commands) |
|
|
47
|
+
| `huaweicloud_sandbox_upload_file` | Upload a local file into the sandbox (chunked base64 write + md5 verify) |
|
|
48
|
+
| `huaweicloud_sandbox_upload_project` | Upload a local project directory to sandbox (HTTP tunnel, tar.gz + extract) |
|
|
49
|
+
| `huaweicloud_sandbox_close_session` | Close a persistent terminal session |
|
|
50
|
+
|
|
51
|
+
### Tool Selection Guide
|
|
52
|
+
|
|
53
|
+
| Scenario | Use | Why |
|
|
54
|
+
| ---------------------------------- | ---------------------------------- | ------------------------------------------------------------ |
|
|
55
|
+
| `cd`, env setup, command chains | `exec_with_session` | Needs shared shell state across calls |
|
|
56
|
+
| `npm install`, `apt-get`, builds | `exec_one_shot` | Long-running (>30s), no state needed, more stable |
|
|
57
|
+
| `curl`, health checks, quick tests | Either — `exec_one_shot` preferred | Stateless, fast |
|
|
58
|
+
| Server startup (background) | `exec_with_session` | Need to `nohup ... &` then check output in same session |
|
|
59
|
+
| Deployment scripts | `exec_one_shot+shot` | Long script, fresh connection avoids session timeouts |
|
|
60
|
+
| Single file upload (<1MB) | `upload_file` | Base64 chunked, reliable for small files |
|
|
61
|
+
| Project directory upload (>1MB) | `upload_project` | HTTP tunnel, much faster than base64 for multi-file projects |
|
|
62
|
+
|
|
63
|
+
**Timeout tuning**: default is 120s. For commands expected to run longer (e.g. large builds), pass `timeout_ms` explicitly:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{ "timeout_ms": 300000 }
|
|
67
|
+
```
|
|
48
68
|
|
|
49
69
|
## Workflow
|
|
50
70
|
|
|
@@ -59,20 +79,40 @@ Setup is a **plugin-side preflight** — the developer should be asked a questio
|
|
|
59
79
|
4. **Both missing** (`HDKIT_NOT_REALNAME_AND_AGREEMENT`): present **both** requirements together in one message — the real-name verification steps (console, step 2) **and** the agreement-signing request (step 3, wait for explicit consent) — so the developer can complete both at once
|
|
60
80
|
5. **Connect**: `huaweicloud_sandbox_connect` — returns `session_id`, `dev_stage_id`, `connection_id`, `connection_address`
|
|
61
81
|
6. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox
|
|
62
|
-
7. **Execute commands**: `huaweicloud_sandbox_exec_with_session` for interactive work
|
|
63
|
-
8. **
|
|
82
|
+
7. **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).
|
|
83
|
+
8. **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.
|
|
84
|
+
9. **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
|
|
64
85
|
|
|
65
86
|
## File Transfer (local → sandbox)
|
|
66
87
|
|
|
67
|
-
|
|
88
|
+
### upload_project (preferred for directories)
|
|
89
|
+
|
|
90
|
+
`huaweicloud_sandbox_upload_project` packages a local directory as tar.gz, uploads it to the sandbox via HTTP tunnel, and extracts it. This is the fastest way to transfer multi-file projects.
|
|
91
|
+
|
|
92
|
+
**Prerequisites**: sandbox must already be connected (`sandbox_connect` called first). The tool handles everything internally — no manual setup needed.
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"local_dir": "/path/to/local/project",
|
|
97
|
+
"remote_dir": "/workspace",
|
|
98
|
+
"extract": true,
|
|
99
|
+
"exclude": ["node_modules", ".git", "__pycache__"]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- `local_dir` (required): local project directory
|
|
104
|
+
- `remote_dir` (optional, default `/workspace`): parent directory on sandbox
|
|
105
|
+
- `extract` (optional, default `true`): extract tar.gz after upload
|
|
106
|
+
- `exclude` (optional): patterns to exclude from archive
|
|
107
|
+
- Result includes `md5` and `md5Verified` for integrity check
|
|
68
108
|
|
|
69
|
-
###
|
|
109
|
+
### upload_file (for single files)
|
|
70
110
|
|
|
71
|
-
`huaweicloud_sandbox_upload_file` (local_path → remote_path) uploads a local file by base64-encoding it and writing it in small chunks through the terminal session, then decoding and verifying the md5 checksum. Use it for
|
|
111
|
+
`huaweicloud_sandbox_upload_file` (local_path → remote_path) uploads a local file by base64-encoding it and writing it in small chunks through the terminal session, then decoding and verifying the md5 checksum. Use it for individual files, especially small ones (<1MB).
|
|
72
112
|
|
|
73
113
|
### Exec channel size limit
|
|
74
114
|
|
|
75
|
-
`huaweicloud_sandbox_exec_with_session`
|
|
115
|
+
`huaweicloud_sandbox_exec_with_session` and `huaweicloud_sandbox_exec_one_shot` are fragile for long commands and large inline content. Do NOT paste a file's base64 directly into a single command:
|
|
76
116
|
|
|
77
117
|
| Payload | Result |
|
|
78
118
|
| --------------------------- | -------------------------------------- |
|
|
@@ -104,7 +144,11 @@ Expose a deployed web app to a public URL and return that URL to the developer.
|
|
|
104
144
|
**Install** (internal tooling; skip if `devbridge version` already works):
|
|
105
145
|
|
|
106
146
|
```bash
|
|
107
|
-
|
|
147
|
+
if command -v devbridge >/dev/null 2>&1; then
|
|
148
|
+
echo "devbridge already installed"
|
|
149
|
+
else
|
|
150
|
+
curl -fsSL https://res-hd.hc-cdn.cn/sharedata/hdspace/devbridge/install.sh | bash
|
|
151
|
+
fi
|
|
108
152
|
export PATH=$PATH:$HOME/.huawei/bin # installer only writes ~/.bashrc; session shells do not re-source it
|
|
109
153
|
```
|
|
110
154
|
|
|
@@ -143,9 +187,12 @@ sleep 10 && cat /tmp/host.log
|
|
|
143
187
|
| 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 |
|
|
144
188
|
| Return the deployment URL | Always hand the public URL from the host log to the developer as the final result |
|
|
145
189
|
| Session state persists | `exec_with_session` preserves `cd`, env vars, aliases between calls |
|
|
190
|
+
| Long commands prefer one-shot | `exec_one_shot` creates a fresh connection per call — more stable for builds, installs, and scripts >30s. See [Tool Selection Guide](#tool-selection-guide). |
|
|
146
191
|
| Destructive commands blocked | `rm -rf /`, `mkfs`, `dd if=`, fork bombs are denied by safety policy |
|
|
147
192
|
| Workspace ID = dev_stage_id | Use `dev_stage_id` from `sandbox_connect` as `workspace_id` for terminal exec |
|
|
148
193
|
| 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 |
|
|
194
|
+
| Upload project for local code | Use `sandbox_upload_project` to transfer local projects — packages as tar.gz, uploads via HTTP tunnel, extracts on sandbox. Much faster than base64 for multi-file projects |
|
|
195
|
+
| Upload file for single files | Use `sandbox_upload_file` for individual files — base64 chunked, reliable for small files (<1MB) |
|
|
149
196
|
| 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") |
|
|
150
197
|
|
|
151
198
|
## Node.js in the sandbox
|
|
@@ -163,11 +210,11 @@ node --version
|
|
|
163
210
|
|
|
164
211
|
## Environment Variables
|
|
165
212
|
|
|
166
|
-
| Variable | Required | Description
|
|
167
|
-
| ----------------------- | -------- |
|
|
168
|
-
| `HW_ACCESS_KEY` | Yes | Huawei Cloud AK
|
|
169
|
-
| `HW_SECRET_KEY` | Yes | Huawei Cloud SK
|
|
170
|
-
| `HW_SECURITY_TOKEN` | No | STS security token
|
|
171
|
-
| `HW_WORKSPACE_ID` | No | Default workspace ID
|
|
172
|
-
| `HDKITSERVICE_ENDPOINT` | No | hdkitservice API endpoint |
|
|
173
|
-
| `HWLINK_ENDPOINT` | No | DevStation API endpoint
|
|
213
|
+
| Variable | Required | Description |
|
|
214
|
+
| ----------------------- | -------- | --------------------------------------------------------------- |
|
|
215
|
+
| `HW_ACCESS_KEY` | Yes | Huawei Cloud AK |
|
|
216
|
+
| `HW_SECRET_KEY` | Yes | Huawei Cloud SK |
|
|
217
|
+
| `HW_SECURITY_TOKEN` | No | STS security token |
|
|
218
|
+
| `HW_WORKSPACE_ID` | No | Default workspace ID |
|
|
219
|
+
| `HDKITSERVICE_ENDPOINT` | No | hdkitservice API endpoint (default: devkit.huaweicloud.com) |
|
|
220
|
+
| `HWLINK_ENDPOINT` | No | DevStation API endpoint (default: devstation.myhuaweicloud.com) |
|
|
@@ -22,6 +22,17 @@ Use this skill to turn vague developer intent into a precise Huawei Cloud capabi
|
|
|
22
22
|
9. When no built-in devkit skill matches, browse the community skill marketplace at https://github.com/huaweicloud/huaweicloud-skills. Fetch the index from https://raw.githubusercontent.com/huaweicloud/huaweicloud-skills/master/skills-index/index.json.
|
|
23
23
|
10. When the deliverable is a PPT, architecture diagram (draw.io), or frontend page that needs official Huawei Cloud service logos, use the `huaweicloud_get_service_icon` MCP tool to get logo URLs from the official Icons library instead of guessing or hotlinking unofficial images.
|
|
24
24
|
|
|
25
|
+
## Scenario Routing
|
|
26
|
+
|
|
27
|
+
Match the scenario before picking a service. For "deploy a web app" requests, layer the recommendation instead of defaulting to a production service:
|
|
28
|
+
|
|
29
|
+
| Scenario | Recommended path |
|
|
30
|
+
| -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
31
|
+
| hello world / prototype / demo / temporary preview (free, quick try) | `huawei-sandbox` first — temporary runtime + public URL, ~8h validity, zero billed resources |
|
|
32
|
+
| production / long-term / custom domain / high availability | `huawei-functiongraph` / `huawei-ecs` (or `huawei-cce` for containers) |
|
|
33
|
+
|
|
34
|
+
Route to the sandbox when the developer signals free/quick preview intent ("免费", "快速", "预览", "hello world", "原型", "演示"); route to production services only when production-grade hosting is explicitly required.
|
|
35
|
+
|
|
25
36
|
## Official Service Logos
|
|
26
37
|
|
|
27
38
|
- 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.
|
|
@@ -121,8 +121,24 @@ hcloud <Service> <ListOp> --cli-output=json --cli-query "items[?status=='ACTIVE'
|
|
|
121
121
|
hcloud <Service> <Op> --cli-debug=true
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
## Credential Resolution Priority
|
|
125
|
+
|
|
126
|
+
Credentials are resolved in this order (highest priority first):
|
|
127
|
+
|
|
128
|
+
| Priority | Source | Mechanism | Persistence |
|
|
129
|
+
| -------- | ----------------------- | ---------------------------------------------------------- | ------------------------------- |
|
|
130
|
+
| 1 | Runtime credentials | `huaweicloud_auth_init` tool | Memory (cleared on MCP restart) |
|
|
131
|
+
| 2 | Environment variables | `HW_ACCESS_KEY` / `HW_SECRET_KEY` | MCP process lifetime |
|
|
132
|
+
| 3 | CodeArts project config | `.codeartsdoer/mcp/mcp_settings.json` (project, then user) | File |
|
|
133
|
+
| 4 | Global config file | `~/.config/huaweicloud/credentials.json` | Permanent |
|
|
134
|
+
| 5 | KooCLI profile | `~/.hcloud/config.json` (KooCLI only) | Permanent |
|
|
135
|
+
|
|
136
|
+
When switching accounts within the same Agent session, use `huaweicloud_auth_init` to set runtime credentials. This overrides all other sources for the current MCP process.
|
|
137
|
+
|
|
124
138
|
## Preferred Toolkit Tools
|
|
125
139
|
|
|
140
|
+
- `huaweicloud_auth_init`
|
|
141
|
+
- `huaweicloud_auth_status`
|
|
126
142
|
- `huaweicloud_check_cli`
|
|
127
143
|
- `huaweicloud_show_profile_redacted`
|
|
128
144
|
- `huaweicloud_plan_cli_command`
|
|
@@ -46,30 +46,32 @@ Do not rely on training data for facts. Huawei Cloud services, pricing, quotas,
|
|
|
46
46
|
|
|
47
47
|
## Service Map
|
|
48
48
|
|
|
49
|
-
| Workload
|
|
50
|
-
|
|
|
51
|
-
| Web application hosting
|
|
52
|
-
| Containerized microservices
|
|
53
|
-
| Static file storage / CDN (long-term)
|
|
54
|
-
| Relational database (MySQL/PG)
|
|
55
|
-
| Distributed SQL database
|
|
56
|
-
| Document database (MongoDB API)
|
|
57
|
-
| In-memory cache
|
|
58
|
-
| Serverless functions
|
|
59
|
-
| API management
|
|
60
|
-
| AI model training/inference
|
|
61
|
-
| Message queuing
|
|
62
|
-
| Push notifications
|
|
63
|
-
| Secret management
|
|
64
|
-
| Key management
|
|
65
|
-
| DDoS protection
|
|
66
|
-
| Web firewall
|
|
67
|
-
| Monitoring dashboards
|
|
68
|
-
| Audit trails
|
|
69
|
-
| Backup / disaster recovery
|
|
70
|
-
| CI/CD pipeline
|
|
71
|
-
| Temporary runtime / web app preview
|
|
72
|
-
| Getting started
|
|
49
|
+
| Workload | Primary Service | Skill |
|
|
50
|
+
| ------------------------------------------------ | -------------------- | ------------------------ |
|
|
51
|
+
| Web application hosting (production / long-term) | ECS | huawei-ecs |
|
|
52
|
+
| Containerized microservices | CCE | huawei-cce |
|
|
53
|
+
| Static file storage / CDN (long-term) | OBS | huawei-obs |
|
|
54
|
+
| Relational database (MySQL/PG) | RDS | huawei-rds |
|
|
55
|
+
| Distributed SQL database | GaussDB | huawei-gaussdb |
|
|
56
|
+
| Document database (MongoDB API) | DDS | huawei-dds-dcs |
|
|
57
|
+
| In-memory cache | DCS (Redis) | huawei-dds-dcs |
|
|
58
|
+
| Serverless functions | FunctionGraph | huawei-functiongraph |
|
|
59
|
+
| API management | APIG | huawei-apig |
|
|
60
|
+
| AI model training/inference | ModelArts | huawei-modelarts |
|
|
61
|
+
| Message queuing | DMS (Kafka/RabbitMQ) | huawei-smn-dms |
|
|
62
|
+
| Push notifications | SMN | huawei-smn-dms |
|
|
63
|
+
| Secret management | DEW (CSMS) | huawei-dew |
|
|
64
|
+
| Key management | DEW (KMS) | huawei-dew |
|
|
65
|
+
| DDoS protection | AAD | huawei-waf-aad |
|
|
66
|
+
| Web firewall | WAF | huawei-waf-aad |
|
|
67
|
+
| Monitoring dashboards | Cloud Eye (CES) | huawei-cloud-eye |
|
|
68
|
+
| Audit trails | CTS | huawei-cts |
|
|
69
|
+
| Backup / disaster recovery | CBR | huawei-cbr |
|
|
70
|
+
| CI/CD pipeline | CloudDeploy | huawei-deployment |
|
|
71
|
+
| Temporary runtime / web app preview | Sandbox (DevStation) | huawei-sandbox |
|
|
72
|
+
| Getting started | Account setup | huaweicloud-cli-and-auth |
|
|
73
|
+
|
|
74
|
+
**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, ~8h validity, zero billed resources); route to `huawei-functiongraph` / `huawei-ecs` (or `huawei-cce`) for production / long-term / custom-domain / high-availability hosting.
|
|
73
75
|
|
|
74
76
|
## Deployment Target Options
|
|
75
77
|
|
|
@@ -100,6 +102,7 @@ When the intent is to **deploy, host, or preview a web app or static website** (
|
|
|
100
102
|
|
|
101
103
|
- Use huaweicloud_search_docs to discover skills covering a topic
|
|
102
104
|
- Use huaweicloud_retrieve_skill to load a full SKILL.md after routing
|
|
105
|
+
- Use huaweicloud_auth_init to switch credentials at runtime when different accounts co-exist in the same session
|
|
103
106
|
- If no built-in skill matches, consult huaweicloud-capability-discovery for external marketplace options
|
|
104
107
|
- Use huaweicloud_list_regions before creating regional resources
|
|
105
108
|
- Use huaweicloud_get_regional_availability when unsure about service-region pairs
|
|
@@ -28,3 +28,15 @@ Route based on answers:
|
|
|
28
28
|
- SQL + massive scale / distributed -> huawei-gaussdb
|
|
29
29
|
- Document model (MongoDB-compatible) -> huawei-dds-dcs
|
|
30
30
|
- Cache / key-value -> huawei-dds-dcs (DCS)
|
|
31
|
+
|
|
32
|
+
## Example: I need to deploy a web app
|
|
33
|
+
|
|
34
|
+
Ask:
|
|
35
|
+
|
|
36
|
+
- Is this a quick try / prototype / demo / temporary preview (free)?
|
|
37
|
+
- Or production (long-term, custom domain, high availability)?
|
|
38
|
+
|
|
39
|
+
Route by scenario:
|
|
40
|
+
|
|
41
|
+
- Free / quick try / prototype / demo / temporary preview -> huawei-sandbox (temporary runtime + public URL, ~8h validity, zero billed resources)
|
|
42
|
+
- Production / long-term / custom domain / high availability -> huawei-functiongraph / huawei-ecs (or huawei-cce for containers)
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
5
6
|
|
|
6
|
-
export const SUPPORTED_AGENT_TARGETS = [
|
|
7
|
+
export const SUPPORTED_AGENT_TARGETS = [
|
|
8
|
+
'opencode',
|
|
9
|
+
'codex',
|
|
10
|
+
'codex-desktop',
|
|
11
|
+
'codearts',
|
|
12
|
+
'workbuddy',
|
|
13
|
+
'dsh',
|
|
14
|
+
'officeace',
|
|
15
|
+
'hermes',
|
|
16
|
+
'openclaw',
|
|
17
|
+
];
|
|
7
18
|
|
|
8
19
|
function baseHome() {
|
|
9
20
|
return process.env.HUAWEICLOUD_HOME || homedir();
|
|
@@ -90,6 +101,94 @@ function dshRegistered() {
|
|
|
90
101
|
}
|
|
91
102
|
}
|
|
92
103
|
|
|
104
|
+
function readOfficeaceRegistryInstallDir() {
|
|
105
|
+
if (process.platform !== 'win32') return null;
|
|
106
|
+
try {
|
|
107
|
+
const r = spawnSync('reg', ['query', 'HKCU\\SOFTWARE\\OfficeAce\\OfficeAce', '/v', 'InstallDir'], {
|
|
108
|
+
encoding: 'utf8',
|
|
109
|
+
windowsHide: true,
|
|
110
|
+
timeout: 5000,
|
|
111
|
+
});
|
|
112
|
+
if (r.status === 0) {
|
|
113
|
+
const m = r.stdout.match(/InstallDir\s+REG_SZ\s+(.+)/);
|
|
114
|
+
if (m) return m[1].trim();
|
|
115
|
+
}
|
|
116
|
+
} catch {}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function officeaceCapabilitiesDir() {
|
|
121
|
+
const configRoot = process.env.OFFICE_CLAW_CONFIG_ROOT;
|
|
122
|
+
if (configRoot && existsSync(join(configRoot, 'capabilities.json'))) return configRoot;
|
|
123
|
+
const regDir = readOfficeaceRegistryInstallDir();
|
|
124
|
+
if (regDir) {
|
|
125
|
+
const dir = join(regDir, '.office-claw');
|
|
126
|
+
if (existsSync(join(dir, 'capabilities.json'))) return dir;
|
|
127
|
+
}
|
|
128
|
+
if (process.platform === 'win32') {
|
|
129
|
+
const bases = [process.env.ProgramFiles, 'C:\\Program Files', 'D:\\Program Files'];
|
|
130
|
+
if (process.env.LOCALAPPDATA) bases.push(join(process.env.LOCALAPPDATA, 'Programs'));
|
|
131
|
+
for (const base of bases) {
|
|
132
|
+
if (!base) continue;
|
|
133
|
+
const dir = join(base, 'OfficeAce', '.office-claw');
|
|
134
|
+
if (existsSync(join(dir, 'capabilities.json'))) return dir;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function officeaceCapabilitiesDirSafe() {
|
|
141
|
+
return officeaceCapabilitiesDir() || join(baseHome(), '.office-claw');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function officeaceSqlitePath() {
|
|
145
|
+
const capDir = officeaceCapabilitiesDirSafe();
|
|
146
|
+
return join(resolve(capDir, '..'), 'data', 'mcp-connectors.sqlite');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function officeaceRegistered() {
|
|
150
|
+
let hasMcp = false;
|
|
151
|
+
const dbPath = officeaceSqlitePath();
|
|
152
|
+
if (existsSync(dbPath)) {
|
|
153
|
+
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
|
154
|
+
if (nodeMajor >= 22) {
|
|
155
|
+
try {
|
|
156
|
+
const { DatabaseSync } = createRequire(import.meta.url)('node:sqlite');
|
|
157
|
+
const db = new DatabaseSync(dbPath, { readonly: true });
|
|
158
|
+
const row = db.prepare("SELECT enabled FROM mcp_connectors WHERE name = 'huaweicloud-devkit'").get();
|
|
159
|
+
db.close();
|
|
160
|
+
hasMcp = Boolean(row?.enabled);
|
|
161
|
+
} catch {}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const capFile = join(officeaceCapabilitiesDirSafe(), 'capabilities.json');
|
|
166
|
+
const cfg = readJsonSafe(capFile);
|
|
167
|
+
const hasSkills = cfg?.capabilities?.some((c) => c.id === 'huaweicloud-core' && c.type === 'skill') ?? false;
|
|
168
|
+
|
|
169
|
+
return hasMcp || hasSkills;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function hermesHome() {
|
|
173
|
+
if (process.env.HERMES_HOME) return process.env.HERMES_HOME;
|
|
174
|
+
// Hermes on Windows stores under LOCALAPPDATA, not ~/.hermes
|
|
175
|
+
if (process.platform === 'win32' && process.env.LOCALAPPDATA) {
|
|
176
|
+
return join(process.env.LOCALAPPDATA, 'hermes');
|
|
177
|
+
}
|
|
178
|
+
return join(baseHome(), '.hermes');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function hermesRegistered() {
|
|
182
|
+
const configPath = join(hermesHome(), 'config.yaml');
|
|
183
|
+
if (!existsSync(configPath)) return false;
|
|
184
|
+
try {
|
|
185
|
+
const content = readFileSync(configPath, 'utf8');
|
|
186
|
+
return content.includes('mcp_servers:') && content.includes('huaweicloud-devkit');
|
|
187
|
+
} catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
93
192
|
export function getAgentRegistrationStatuses(target = 'all') {
|
|
94
193
|
const requested = target === 'all' ? SUPPORTED_AGENT_TARGETS : [target];
|
|
95
194
|
const result = { target, agents: {} };
|
|
@@ -101,6 +200,8 @@ export function getAgentRegistrationStatuses(target = 'all') {
|
|
|
101
200
|
if (agent === 'codearts') configured = codeartsRegistered();
|
|
102
201
|
if (agent === 'workbuddy') configured = workbuddyRegistered();
|
|
103
202
|
if (agent === 'dsh') configured = dshRegistered();
|
|
203
|
+
if (agent === 'officeace') configured = officeaceRegistered();
|
|
204
|
+
if (agent === 'hermes') configured = hermesRegistered();
|
|
104
205
|
result.agents[agent] = { configured };
|
|
105
206
|
}
|
|
106
207
|
return result;
|
|
@@ -84,6 +84,13 @@ export function resolveCredentials(options = {}) {
|
|
|
84
84
|
let securityToken = process.env.HW_SECURITY_TOKEN;
|
|
85
85
|
let region = process.env.HW_REGION || process.env.HUAWEICLOUD_REGION || '';
|
|
86
86
|
|
|
87
|
+
const codeartsCreds = isCodeArtsContext() ? readCodeArtsCredentials() : null;
|
|
88
|
+
if (codeartsCreds) {
|
|
89
|
+
if (!ak && codeartsCreds.ak) ak = codeartsCreds.ak;
|
|
90
|
+
if (!sk && codeartsCreds.sk) sk = codeartsCreds.sk;
|
|
91
|
+
if (!region && codeartsCreds.region) region = codeartsCreds.region;
|
|
92
|
+
}
|
|
93
|
+
|
|
87
94
|
const stored = readGlobalCredentials();
|
|
88
95
|
if (stored) {
|
|
89
96
|
if (!ak && stored.ak) ak = stored.ak;
|
|
@@ -101,3 +108,61 @@ export function resolveCredentials(options = {}) {
|
|
|
101
108
|
|
|
102
109
|
return { ak, sk, securityToken, region };
|
|
103
110
|
}
|
|
111
|
+
|
|
112
|
+
function isCodeArtsContext() {
|
|
113
|
+
return existsSync(join(process.cwd(), '.codeartsdoer')) || existsSync(join(homedir(), '.codeartsdoer'));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function readCodeArtsCredentials() {
|
|
117
|
+
const searchPaths = [
|
|
118
|
+
join(process.cwd(), '.codeartsdoer', 'mcp', 'mcp_settings.json'),
|
|
119
|
+
join(homedir(), '.codeartsdoer', 'mcp', 'mcp_settings.json'),
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
for (const path of searchPaths) {
|
|
123
|
+
try {
|
|
124
|
+
if (!existsSync(path)) continue;
|
|
125
|
+
const config = JSON.parse(readFileSync(path, 'utf8'));
|
|
126
|
+
const server = config?.mcpServers?.['huaweicloud-devkit'];
|
|
127
|
+
if (!server?.env) continue;
|
|
128
|
+
|
|
129
|
+
const ak = server.env.HW_ACCESS_KEY;
|
|
130
|
+
const sk = server.env.HW_SECRET_KEY;
|
|
131
|
+
if (ak && sk) {
|
|
132
|
+
return {
|
|
133
|
+
ak,
|
|
134
|
+
sk,
|
|
135
|
+
securityToken: server.env.HW_SECURITY_TOKEN || '',
|
|
136
|
+
region: server.env.HW_REGION || server.env.HUAWEICLOUD_REGION || '',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
// mcp_settings.json missing or invalid — skip
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let runtimeCredentials = null;
|
|
148
|
+
|
|
149
|
+
export function setRuntimeCredentials(ak, sk, securityToken, region) {
|
|
150
|
+
runtimeCredentials = { ak, sk, securityToken: securityToken || '', region: region || '' };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function clearRuntimeCredentials() {
|
|
154
|
+
runtimeCredentials = null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function resolveCredentialsWithRuntime(options = {}) {
|
|
158
|
+
if (runtimeCredentials) {
|
|
159
|
+
return {
|
|
160
|
+
ak: runtimeCredentials.ak,
|
|
161
|
+
sk: runtimeCredentials.sk,
|
|
162
|
+
securityToken: runtimeCredentials.securityToken,
|
|
163
|
+
region: runtimeCredentials.region,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return resolveCredentials(options);
|
|
168
|
+
}
|