huaweicloud-devkit 1.1.0-next.15 → 1.1.0-next.17
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 +25 -0
- package/README.zh-CN.md +25 -0
- package/package.json +3 -1
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +29 -12
- package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +11 -11
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +4 -3
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +3 -2
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +19 -12
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +7 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +7 -7
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +5 -4
- package/plugins/huaweicloud-core/src/setup-cli.mjs +221 -18
package/README.md
CHANGED
|
@@ -14,6 +14,14 @@ Supports OpenCode, Codex, CodeArts Agent, WorkBuddy, DeepSeek Harness (DSH), Off
|
|
|
14
14
|
|
|
15
15
|
- Node.js >= 22
|
|
16
16
|
|
|
17
|
+
> **China mainland users**: If you experience slow downloads or connection issues with the default npm registry, configure the Huawei Cloud npm mirror:
|
|
18
|
+
>
|
|
19
|
+
> ```bash
|
|
20
|
+
> npm config set registry https://mirrors.huaweicloud.com/repository/npm/
|
|
21
|
+
> ```
|
|
22
|
+
>
|
|
23
|
+
> Restore the default registry: `npm config delete registry`
|
|
24
|
+
|
|
17
25
|
## Quick Start
|
|
18
26
|
|
|
19
27
|
> If `--target` is omitted, the installer auto-detects agents on your machine. When multiple agents are detected, **all of them** will be installed. Specify `--target` to control which agent receives the install.
|
|
@@ -68,6 +76,23 @@ npx --yes huaweicloud-devkit uninstall --target codearts
|
|
|
68
76
|
|
|
69
77
|
> **Sandbox mode**: CodeArts defaults to sandbox mode which blocks KooCLI. `install-hcloud` detects this and shows how to resolve it — install KooCLI outside the sandbox terminal, or disable sandbox mode in CodeArts settings (Settings → Chats → Agents Terminal Command Running Mode → Auto Running).
|
|
70
78
|
|
|
79
|
+
### CodeArts Work
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npx --yes huaweicloud-devkit install --target codearts-work
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Restart the session** after installation.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx --yes huaweicloud-devkit doctor --target codearts-work
|
|
89
|
+
npx --yes huaweicloud-devkit status --target codearts-work
|
|
90
|
+
npx --yes huaweicloud-devkit update --target codearts-work
|
|
91
|
+
npx --yes huaweicloud-devkit uninstall --target codearts-work
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> **CodeArts Work** (CodeArts Space, appId: `com.codearts.work`) uses user-level config at `%USERPROFILE%\.codeartswork\`. No project-level `.codeartswork` directory is created.
|
|
95
|
+
|
|
71
96
|
### WorkBuddy
|
|
72
97
|
|
|
73
98
|
```bash
|
package/README.zh-CN.md
CHANGED
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
|
|
15
15
|
- Node.js >= 22
|
|
16
16
|
|
|
17
|
+
> **国内用户**:使用默认 npm 镜像时可能遇到下载缓慢或连接失败的问题,建议配置华为云 npm 镜像:
|
|
18
|
+
>
|
|
19
|
+
> ```bash
|
|
20
|
+
> npm config set registry https://mirrors.huaweicloud.com/repository/npm/
|
|
21
|
+
> ```
|
|
22
|
+
>
|
|
23
|
+
> 恢复默认镜像:`npm config delete registry`
|
|
24
|
+
|
|
17
25
|
## 快速开始
|
|
18
26
|
|
|
19
27
|
> 省略 `--target` 时,安装器会自动检测机器上的 agent,检测到多个时**全部安装**。建议始终指定 `--target` 以明确安装目标。
|
|
@@ -68,6 +76,23 @@ npx --yes huaweicloud-devkit uninstall --target codearts
|
|
|
68
76
|
|
|
69
77
|
> **沙箱模式**:码道默认沙箱模式会阻止 KooCLI 运行。`install-hcloud` 自动检测并给出指引——请在码道外终端安装使用 KooCLI,或在码道设置中关闭沙箱模式(设置 → 对话流 → 智能体 终端命令运行模式 → 自动运行)。
|
|
70
78
|
|
|
79
|
+
### CodeArts Work(码道工作空间)
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npx --yes huaweicloud-devkit install --target codearts-work
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
安装后**重启会话**。
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx --yes huaweicloud-devkit doctor --target codearts-work
|
|
89
|
+
npx --yes huaweicloud-devkit status --target codearts-work
|
|
90
|
+
npx --yes huaweicloud-devkit update --target codearts-work
|
|
91
|
+
npx --yes huaweicloud-devkit uninstall --target codearts-work
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> **CodeArts Work**(工作空间,appId: `com.codearts.work`)使用用户级配置 `%USERPROFILE%\.codeartswork\`,不创建项目级目录。
|
|
95
|
+
|
|
71
96
|
### WorkBuddy
|
|
72
97
|
|
|
73
98
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
|
-
"version": "1.1.0-next.
|
|
3
|
+
"version": "1.1.0-next.17",
|
|
4
4
|
"description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
"agent",
|
|
50
50
|
"codex",
|
|
51
51
|
"opencode",
|
|
52
|
+
"codearts",
|
|
53
|
+
"codearts-work",
|
|
52
54
|
"officeace",
|
|
53
55
|
"office-claw",
|
|
54
56
|
"workbuddy",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"mcpServers": "./.mcp.json",
|
|
18
18
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
19
19
|
"skills": "./skills/",
|
|
20
|
-
"version": "1.1.0-next.
|
|
20
|
+
"version": "1.1.0-next.17",
|
|
21
21
|
"author": {
|
|
22
22
|
"name": "HuaweiCloud Mate",
|
|
23
23
|
"url": "https://github.com/huaweicloud"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"mcpServers": "./.mcp.json",
|
|
21
21
|
"description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
|
|
22
22
|
"skills": "./skills/",
|
|
23
|
-
"version": "1.1.0-next.
|
|
23
|
+
"version": "1.1.0-next.17",
|
|
24
24
|
"author": {
|
|
25
25
|
"name": "HuaweiCloud Mate",
|
|
26
26
|
"url": "https://github.com/huaweicloud"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"mcpServers": "./.mcp.json",
|
|
18
18
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
19
19
|
"skills": "./skills/",
|
|
20
|
-
"version": "1.1.0-next.
|
|
20
|
+
"version": "1.1.0-next.17",
|
|
21
21
|
"author": {
|
|
22
22
|
"name": "HuaweiCloud Mate",
|
|
23
23
|
"url": "https://github.com/huaweicloud"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
|
-
"version": "1.1.0-next.
|
|
3
|
+
"version": "1.1.0-next.17",
|
|
4
4
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "HuaweiCloud Mate",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
|
-
"version": "1.1.0-next.
|
|
3
|
+
"version": "1.1.0-next.17",
|
|
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",
|
|
@@ -138,6 +138,31 @@ def first_denied_command_rule(text):
|
|
|
138
138
|
return None
|
|
139
139
|
|
|
140
140
|
|
|
141
|
+
def evaluate(tool_name, tool_input):
|
|
142
|
+
"""Run the full safety check and return a raw block reason, or None to allow.
|
|
143
|
+
|
|
144
|
+
Shared by the CLI shell (``main``) and the Hermes Python-plugin hook, which
|
|
145
|
+
reuses this module via ``importlib`` so a single implementation enforces the
|
|
146
|
+
policy everywhere. The returned reason does NOT include ``DENY_PREFIX`` —
|
|
147
|
+
callers decide the exact output shape.
|
|
148
|
+
"""
|
|
149
|
+
text = command_text(tool_input)
|
|
150
|
+
|
|
151
|
+
if CONFIG_FILE_RE and CONFIG_FILE_RE.search(text):
|
|
152
|
+
return "reading Huawei Cloud credential/profile files can expose AK/SK or tokens. Use redacted toolkit tools."
|
|
153
|
+
if ENV_DUMP_RE.search(text):
|
|
154
|
+
return "dumping cloud credential environment variables is not allowed."
|
|
155
|
+
if SECRET_READ_RE and SECRET_READ_RE.search(text):
|
|
156
|
+
return "direct secret value retrieval would put plaintext secrets into the agent context."
|
|
157
|
+
denied_rule = first_denied_command_rule(text)
|
|
158
|
+
if denied_rule:
|
|
159
|
+
return f"{denied_rule.get('message')} Remediation: {denied_rule.get('remediation')}"
|
|
160
|
+
if tool_name in ("Bash", "terminal") and HCLOUD_RE.search(text) and WRITE_OPERATION_RE.search(text) and not READ_OPERATION_RE.search(text):
|
|
161
|
+
return "unapproved Huawei Cloud write operations must be planned first and explicitly approved by the user."
|
|
162
|
+
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
|
|
141
166
|
def main():
|
|
142
167
|
try:
|
|
143
168
|
data = json.load(sys.stdin)
|
|
@@ -145,20 +170,12 @@ def main():
|
|
|
145
170
|
allow()
|
|
146
171
|
|
|
147
172
|
tool_name = data.get("tool_name", "")
|
|
148
|
-
|
|
173
|
+
tool_input = data.get("tool_input", {})
|
|
149
174
|
hermes = "hook_event_name" in data
|
|
150
175
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
deny("dumping cloud credential environment variables is not allowed.", hermes)
|
|
155
|
-
if SECRET_READ_RE and SECRET_READ_RE.search(text):
|
|
156
|
-
deny("direct secret value retrieval would put plaintext secrets into the agent context.", hermes)
|
|
157
|
-
denied_rule = first_denied_command_rule(text)
|
|
158
|
-
if denied_rule:
|
|
159
|
-
deny(f"{denied_rule.get('message')} Remediation: {denied_rule.get('remediation')}", hermes)
|
|
160
|
-
if tool_name in ("Bash", "terminal") and HCLOUD_RE.search(text) and WRITE_OPERATION_RE.search(text) and not READ_OPERATION_RE.search(text):
|
|
161
|
-
deny("unapproved Huawei Cloud write operations must be planned first and explicitly approved by the user.", hermes)
|
|
176
|
+
reason = evaluate(tool_name, tool_input)
|
|
177
|
+
if reason:
|
|
178
|
+
deny(reason, hermes)
|
|
162
179
|
|
|
163
180
|
allow()
|
|
164
181
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
3
|
"id": "huaweicloud-devkit",
|
|
4
4
|
"displayName": "HuaweiCloud DevKit",
|
|
5
|
-
"version": "1.1.0-next.
|
|
5
|
+
"version": "1.1.0-next.17",
|
|
6
6
|
"family": "bundle-plugin",
|
|
7
7
|
"bundleFormat": "codex",
|
|
8
8
|
"description": "Guide coding agents to use Huawei Cloud safely — KooCLI, APIs, SDKs, 28 MCP tools, skills, and safety guardrails.",
|
|
@@ -42,14 +42,14 @@ Domain expertise for Huawei Cloud Identity and Access Management (IAM). Covers u
|
|
|
42
42
|
|
|
43
43
|
## Common Workflows
|
|
44
44
|
|
|
45
|
-
| Task | Command
|
|
46
|
-
| ------------------------ |
|
|
47
|
-
| List users | hcloud IAM
|
|
48
|
-
| Create group | hcloud IAM
|
|
49
|
-
| Create custom policy | hcloud IAM
|
|
50
|
-
| Create agency | hcloud IAM CreateAgency --agency.name=<n> --agency.domain_id=<id> --agency.
|
|
51
|
-
| Get temporary credential | hcloud STS
|
|
52
|
-
| Attach policy
|
|
45
|
+
| Task | Command | Steps |
|
|
46
|
+
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
|
|
47
|
+
| List users | hcloud IAM KeystoneListUsers --cli-region=<r> | references/iam-ops.md |
|
|
48
|
+
| Create group | hcloud IAM CreateGroupV5 --group_name=<name> | references/iam-ops.md |
|
|
49
|
+
| Create custom policy | hcloud IAM CreateCloudServiceCustomPolicy --role.display_name=<n> --role.type=<type> --role.policy.Statement.1.Effect=Allow | references/policy-examples.md |
|
|
50
|
+
| Create agency | hcloud IAM CreateAgency --agency.name=<n> --agency.domain_id=<id> --agency.trust_domain_name=<domain> | references/agency.md |
|
|
51
|
+
| Get temporary credential | hcloud STS AssumeAgency --agency_urn=<urn> --agency_session_name=<n> --duration_seconds=3600 | references/sts.md |
|
|
52
|
+
| Attach policy to group | hcloud IAM AttachGroupPolicyV5 --group_id=<id> --policy_id=<id> | references/iam-ops.md |
|
|
53
53
|
|
|
54
54
|
## Policy Examples (Least Privilege)
|
|
55
55
|
|
|
@@ -88,10 +88,10 @@ FunctionGraph needs an agency to access VPC resources (RDS, DCS, etc.):
|
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
90
|
# 1. Create agency with FunctionGraph trust
|
|
91
|
-
hcloud IAM CreateAgency --
|
|
91
|
+
hcloud IAM CreateAgency --agency.name=<name> --agency.trust_domain_name=functiongraph
|
|
92
92
|
|
|
93
|
-
# 2.
|
|
94
|
-
hcloud IAM
|
|
93
|
+
# 2. Grant role to agency
|
|
94
|
+
hcloud IAM AssociateAgencyWithDomainPermission --agency_id=<id> --role_id=<role-id>
|
|
95
95
|
|
|
96
96
|
# 3. Use in CreateFunction
|
|
97
97
|
hcloud FunctionGraph CreateFunction ... --app_xrole=<agency-name>
|
|
@@ -55,7 +55,7 @@ hcloud RDS <Operation> --cli-region=<region> [--key=value ...]
|
|
|
55
55
|
hcloud RDS ListInstances --cli-region=<r>
|
|
56
56
|
hcloud RDS ListFlavors --database_name=<engine> --cli-region=<r>
|
|
57
57
|
hcloud RDS ListDatastores --database_name=<engine> --cli-region=<r>
|
|
58
|
-
hcloud RDS ListEngineFlavors --instance_id=<id> --cli-region=<r>
|
|
58
|
+
hcloud RDS ListEngineFlavors --instance_id=<id> --availability_zone_ids=<az> --ha_mode=<mode> --cli-region=<r>
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
### Create Instance
|
|
@@ -150,7 +150,8 @@ hcloud RDS CreateRestoreInstance --instance_id=<id> --backup_id=<id> --name=<new
|
|
|
150
150
|
## Read Replicas
|
|
151
151
|
|
|
152
152
|
```bash
|
|
153
|
-
|
|
153
|
+
# Create a read replica via CreateInstance with --replica_of_id
|
|
154
|
+
hcloud RDS CreateInstance --replica_of_id=<primary-id> --name=<name> --flavor_ref=<id> --volume.type=<vol-type> --volume.size=<size> --cli-region=<r>
|
|
154
155
|
```
|
|
155
156
|
|
|
156
157
|
## Fault Diagnosis
|
|
@@ -159,7 +160,7 @@ hcloud RDS CreateReadReplica --replica_of_id=<primary-id> --name=<name> --flavor
|
|
|
159
160
|
hcloud RDS ListErrorLogs --instance_id=<id> --start_date=2024-01-01T00:00:00Z --end_date=2024-01-31T23:59:59Z --cli-region=<r>
|
|
160
161
|
hcloud RDS ListSlowLogs --instance_id=<id> --start_date=... --end_date=... --cli-region=<r>
|
|
161
162
|
hcloud RDS ShowReplicationStatus --instance_id=<id> --cli-region=<r>
|
|
162
|
-
hcloud RDS ListInstanceDiagnosis --cli-region=<r>
|
|
163
|
+
hcloud RDS ListInstanceDiagnosis --engine=<engine> --cli-region=<r>
|
|
163
164
|
```
|
|
164
165
|
|
|
165
166
|
## Connecting
|
|
@@ -253,7 +253,7 @@ fi
|
|
|
253
253
|
|
|
254
254
|
```bash
|
|
255
255
|
# Step A: List all tunnels (both active and stale)
|
|
256
|
-
devbridge
|
|
256
|
+
devbridge list -j
|
|
257
257
|
# Step B: Remove all stale tunnels
|
|
258
258
|
devbridge delete-all
|
|
259
259
|
# Step C: Retry tunnel creation
|
|
@@ -265,6 +265,7 @@ This eliminates the most common deployment failure — historical tunnels from p
|
|
|
265
265
|
|
|
266
266
|
- The public URL has the form `https://<id>-<port>.cn-north-4-bridge.myhuaweicloud.com` (from the `Tunnel URL:` line).
|
|
267
267
|
- **Return this URL to the developer as the deployment result link.** Keep the host process running (do not close the session before handing over the URL).
|
|
268
|
+
- Tunnel `description` (`-d`) accepts only Chinese characters, letters, and digits (0-64). Symbols such as `-`/`_`/spaces are rejected (`Invalid tunnel description`).
|
|
268
269
|
- Internal docs: https://huaweicloud.github.io/devspace-devbridge/
|
|
269
270
|
|
|
270
271
|
**No local downgrade**: if the tunnel tooling cannot be installed in the sandbox, STOP and report a generic error ("无法生成访问地址") without technical detail. Never install it on the developer's local machine — a local install would defeat the purpose of sandbox deployment.
|
|
@@ -751,7 +752,7 @@ Follow the standard [Expose the deployed app](#expose-the-deployed-app-public-ur
|
|
|
751
752
|
|
|
752
753
|
Use `exec_with_session` to background DevBridge. For SSR, DevBridge tunnels the nginx public port (not the Node port directly).
|
|
753
754
|
|
|
754
|
-
**Pre-flight**: always run `devbridge delete-all` before creating a new tunnel to prevent `10006: quota exceeded` from accumulated stale tunnels. If you still get quota error, list tunnels with `devbridge
|
|
755
|
+
**Pre-flight**: always run `devbridge delete-all` before creating a new tunnel to prevent `10006: quota exceeded` from accumulated stale tunnels. If you still get quota error, list tunnels with `devbridge list -j`, delete stale ones, and retry.
|
|
755
756
|
|
|
756
757
|
Extract the tunnel URL from DevBridge output. The public URL has the form `https://<id>-<port>.cn-north-4-bridge.myhuaweicloud.com`. **Return this URL to the developer as the deployment result.**
|
|
757
758
|
|
|
@@ -8,7 +8,7 @@ version: 1
|
|
|
8
8
|
|
|
9
9
|
**STOP - Do not answer from general knowledge.** Follow the procedure below.
|
|
10
10
|
|
|
11
|
-
Always run `hcloud SMN <Operation> --help` or `hcloud
|
|
11
|
+
Always run `hcloud SMN <Operation> --help`, `hcloud Kafka <Operation> --help`, `hcloud RabbitMQ <Operation> --help`, or `hcloud RocketMQ <Operation> --help` before constructing commands.
|
|
12
12
|
|
|
13
13
|
## Overview
|
|
14
14
|
|
|
@@ -38,21 +38,28 @@ Domain expertise for SMN (Simple Message Notification) and DMS (Distributed Mess
|
|
|
38
38
|
|
|
39
39
|
## DMS
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
> **DMS 是产品统一品牌**,涵盖 Kafka、RabbitMQ、RocketMQ 三种引擎。在 KooCLI 中,三种引擎被拆分为独立服务,**不存在 `hcloud DMS` 命令**。
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
43
|
+
### Engine → KooCLI Service Mapping
|
|
44
|
+
|
|
45
|
+
| Engine | KooCLI Service | Description |
|
|
46
|
+
| -------- | ----------------- | -------------------- |
|
|
47
|
+
| Kafka | `hcloud Kafka` | 高吞吐分布式消息队列 |
|
|
48
|
+
| RabbitMQ | `hcloud RabbitMQ` | AMQP 消息代理 |
|
|
49
|
+
| RocketMQ | `hcloud RocketMQ` | 低延迟金融级消息队列 |
|
|
48
50
|
|
|
49
51
|
### Common Workflows
|
|
50
52
|
|
|
51
|
-
| Task
|
|
52
|
-
|
|
|
53
|
-
| List instances
|
|
54
|
-
|
|
|
55
|
-
| List
|
|
53
|
+
| Task | Service | Operation |
|
|
54
|
+
| ------------------------ | ---------- | ------------------------------------------------------------------ |
|
|
55
|
+
| List Kafka instances | `Kafka` | `ListInstances --cli-region=<r> --project_id=<p>` |
|
|
56
|
+
| List RabbitMQ instances | `RabbitMQ` | `ListInstancesDetails --cli-region=<r> --project_id=<p>` |
|
|
57
|
+
| List RocketMQ instances | `RocketMQ` | `ListInstances --cli-region=<r> --project_id=<p>` |
|
|
58
|
+
| Create Kafka instance | `Kafka` | `CreatePostPaidKafkaInstance --cli-region=<r> --project_id=<p>` |
|
|
59
|
+
| Create RabbitMQ instance | `RabbitMQ` | `CreatePostPaidInstanceByEngine --cli-region=<r> --project_id=<p>` |
|
|
60
|
+
| Create RocketMQ instance | `RocketMQ` | `CreateInstanceByEngine --cli-region=<r> --project_id=<p>` |
|
|
61
|
+
| List Kafka topics | `Kafka` | `ListInstanceTopics --cli-region=<r> --project_id=<p>` |
|
|
62
|
+
| List RocketMQ topics | `RocketMQ` | `ListRocketInstanceTopics --cli-region=<r> --project_id=<p>` |
|
|
56
63
|
|
|
57
64
|
Discover exact parameters with `--help` before executing any command.
|
|
58
65
|
|
|
@@ -29,8 +29,8 @@ Domain expertise for WAF and Anti-DDoS. Covers WAF policy/rules, AAD protection,
|
|
|
29
29
|
|
|
30
30
|
| Task | Operation |
|
|
31
31
|
| ------------------- | --------------------------------------------------------------- |
|
|
32
|
-
| List WAF instances | `
|
|
33
|
-
| List policies | `
|
|
32
|
+
| List WAF instances | `ListCompositeHosts --cli-region=<r> --project_id=<p>` |
|
|
33
|
+
| List policies | `ListPolicy --cli-region=<r> --project_id=<p>` |
|
|
34
34
|
| Create custom rule | `BatchCreateCustomRule --cli-region=<r> --project_id=<p>` |
|
|
35
35
|
| Create IP blacklist | `BatchCreateWhiteblackipRule --cli-region=<r> --project_id=<p>` |
|
|
36
36
|
| Create CC rule | `BatchCreateCcRule --cli-region=<r> --project_id=<p>` |
|
|
@@ -39,8 +39,11 @@ Domain expertise for WAF and Anti-DDoS. Covers WAF policy/rules, AAD protection,
|
|
|
39
39
|
### AAD
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
|
|
43
|
-
hcloud AAD
|
|
42
|
+
# List AAD instances (requires --cli-region=cn-north-4 or ap-southeast-1)
|
|
43
|
+
hcloud AAD ListInstance --cli-region=<r> --project_id=<p>
|
|
44
|
+
|
|
45
|
+
# List DDoS attack events
|
|
46
|
+
hcloud AAD ListDDoSAttackEvent --cli-region=<r> --project_id=<p>
|
|
44
47
|
```
|
|
45
48
|
|
|
46
49
|
## Troubleshooting
|
|
@@ -125,13 +125,13 @@ hcloud <Service> <Op> --cli-debug=true
|
|
|
125
125
|
|
|
126
126
|
Credentials are resolved in this order (highest priority first):
|
|
127
127
|
|
|
128
|
-
| Priority | Source
|
|
129
|
-
| -------- |
|
|
130
|
-
| 1 | Runtime credentials
|
|
131
|
-
| 2 | Environment variables
|
|
132
|
-
| 3 | CodeArts
|
|
133
|
-
| 4 | Global config file
|
|
134
|
-
| 5 | KooCLI profile
|
|
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 / CodeArts Work | `.codeartsdoer/mcp/mcp_settings.json` (project → user) or `.codeartswork/mcp/mcp_settings.json` (user) | File |
|
|
133
|
+
| 4 | Global config file | `~/.config/huaweicloud/credentials.json` | Permanent |
|
|
134
|
+
| 5 | KooCLI profile | `~/.hcloud/config.json` (KooCLI only) | Permanent |
|
|
135
135
|
|
|
136
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
137
|
|
|
@@ -836,7 +836,7 @@ fi`;
|
|
|
836
836
|
try {
|
|
837
837
|
const tunnelCheck = await execOneShot(
|
|
838
838
|
workspaceId,
|
|
839
|
-
'devbridge
|
|
839
|
+
'devbridge list -j 2>/dev/null | grep -q \'"tunnelId"\' && echo "ACTIVE" || echo "INACTIVE"',
|
|
840
840
|
username,
|
|
841
841
|
10000,
|
|
842
842
|
);
|
|
@@ -912,7 +912,7 @@ export async function deployCheck(
|
|
|
912
912
|
`fi`,
|
|
913
913
|
``,
|
|
914
914
|
`TOTAL=$((TOTAL+1))`,
|
|
915
|
-
`if devbridge
|
|
915
|
+
`if devbridge list -j 2>/dev/null | grep -q '"tunnelId"'; then`,
|
|
916
916
|
` echo "devbridge_tunnel:PASS"`,
|
|
917
917
|
` PASS=$((PASS+1))`,
|
|
918
918
|
`else`,
|
|
@@ -920,8 +920,9 @@ export async function deployCheck(
|
|
|
920
920
|
`fi`,
|
|
921
921
|
``,
|
|
922
922
|
`TOTAL=$((TOTAL+1))`,
|
|
923
|
-
`
|
|
924
|
-
`
|
|
923
|
+
`TUNNEL_ID=$(devbridge list -j 2>/dev/null | grep -oP '"tunnelId":\\s*"\\K[^"]+' | head -1)`,
|
|
924
|
+
`TUNNEL_URL="https://\${TUNNEL_ID}-${port}.cn-north-4-bridge.myhuaweicloud.com"`,
|
|
925
|
+
`if [ -n "$TUNNEL_ID" ] && [ -n "$TUNNEL_URL" ]; then`,
|
|
925
926
|
` HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$TUNNEL_URL" 2>/dev/null || echo "000")`,
|
|
926
927
|
` if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "304" ]; then`,
|
|
927
928
|
` echo "tunnel_url_accessible:PASS ($TUNNEL_URL -> $HTTP_CODE)"`,
|
|
@@ -2108,6 +2108,14 @@ function hermesPluginsDir() {
|
|
|
2108
2108
|
return join(hermesHomeDir(), 'huaweicloud-plugins');
|
|
2109
2109
|
}
|
|
2110
2110
|
|
|
2111
|
+
function hermesPythonPluginsDir() {
|
|
2112
|
+
return join(hermesHomeDir(), 'plugins');
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
function hermesSafetyPluginDir() {
|
|
2116
|
+
return join(hermesPythonPluginsDir(), 'huaweicloud-safety');
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2111
2119
|
function hermesConfigFile() {
|
|
2112
2120
|
return join(hermesHomeDir(), 'config.yaml');
|
|
2113
2121
|
}
|
|
@@ -2186,17 +2194,26 @@ function removeHermesMcpConfigBlock() {
|
|
|
2186
2194
|
console.log(' MCP config cleaned');
|
|
2187
2195
|
}
|
|
2188
2196
|
|
|
2197
|
+
function hermesHookScript() {
|
|
2198
|
+
return join(hermesPluginsDir(), 'hooks', 'huaweicloud-safety.py').replace(/\\/g, '/');
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
function hermesHookCommand() {
|
|
2202
|
+
const pythonBin = process.platform === 'win32' ? 'python' : 'python3';
|
|
2203
|
+
return `${pythonBin} ${hermesHookScript()}`;
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2189
2206
|
function ensureHermesHooksConfig() {
|
|
2190
2207
|
const configPath = hermesConfigFile();
|
|
2191
|
-
const
|
|
2192
|
-
const hookScript = join(pluginDest, 'hooks', 'huaweicloud-safety.py').replace(/\\/g, '/');
|
|
2208
|
+
const hookCommand = hermesHookCommand();
|
|
2193
2209
|
|
|
2194
2210
|
const blockLines = [
|
|
2195
2211
|
'hooks:',
|
|
2196
2212
|
' pre_tool_call:',
|
|
2197
2213
|
' - matcher: "terminal"',
|
|
2198
|
-
` command: "
|
|
2214
|
+
` command: "${hookCommand}"`,
|
|
2199
2215
|
' timeout: 5',
|
|
2216
|
+
' fail_closed: true',
|
|
2200
2217
|
];
|
|
2201
2218
|
const block = blockLines.join('\n');
|
|
2202
2219
|
|
|
@@ -2205,10 +2222,21 @@ function ensureHermesHooksConfig() {
|
|
|
2205
2222
|
try {
|
|
2206
2223
|
existing = readFileSync(configPath, 'utf8');
|
|
2207
2224
|
} catch {}
|
|
2208
|
-
if (
|
|
2225
|
+
if (
|
|
2226
|
+
existing.includes('hooks:') &&
|
|
2227
|
+
existing.includes(`command: "${hookCommand}"`) &&
|
|
2228
|
+
existing.includes('fail_closed: true')
|
|
2229
|
+
) {
|
|
2209
2230
|
console.log(` Hooks config unchanged: ${configPath}`);
|
|
2210
2231
|
return false;
|
|
2211
2232
|
}
|
|
2233
|
+
removeHermesHooksConfigBlock();
|
|
2234
|
+
existing = '';
|
|
2235
|
+
if (existsSync(configPath)) {
|
|
2236
|
+
try {
|
|
2237
|
+
existing = readFileSync(configPath, 'utf8');
|
|
2238
|
+
} catch {}
|
|
2239
|
+
}
|
|
2212
2240
|
}
|
|
2213
2241
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
2214
2242
|
const newContent = existing ? `${existing.trimEnd()}\n\n${block}\n` : `${block}\n`;
|
|
@@ -2217,6 +2245,154 @@ function ensureHermesHooksConfig() {
|
|
|
2217
2245
|
return true;
|
|
2218
2246
|
}
|
|
2219
2247
|
|
|
2248
|
+
function hermesHookScriptMtime() {
|
|
2249
|
+
const scriptPath = hermesHookScript();
|
|
2250
|
+
try {
|
|
2251
|
+
return statSync(scriptPath).mtime.toISOString();
|
|
2252
|
+
} catch {
|
|
2253
|
+
return null;
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
function hermesAllowlistPath() {
|
|
2258
|
+
return join(hermesHomeDir(), 'shell-hooks-allowlist.json');
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
function ensureHermesHookAllowlist() {
|
|
2262
|
+
const hookCommand = hermesHookCommand();
|
|
2263
|
+
const allowlistPath = hermesAllowlistPath();
|
|
2264
|
+
|
|
2265
|
+
let data = { approvals: [] };
|
|
2266
|
+
if (existsSync(allowlistPath)) {
|
|
2267
|
+
try {
|
|
2268
|
+
const parsed = JSON.parse(readFileSync(allowlistPath, 'utf8'));
|
|
2269
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
2270
|
+
data = parsed;
|
|
2271
|
+
}
|
|
2272
|
+
} catch {}
|
|
2273
|
+
}
|
|
2274
|
+
const approvals = Array.isArray(data.approvals) ? data.approvals : [];
|
|
2275
|
+
const already = approvals.some(
|
|
2276
|
+
(a) => a && typeof a === 'object' && a.event === 'pre_tool_call' && a.command === hookCommand,
|
|
2277
|
+
);
|
|
2278
|
+
if (already) {
|
|
2279
|
+
console.log(` Hook allowlist unchanged: ${allowlistPath}`);
|
|
2280
|
+
return false;
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
const entry = {
|
|
2284
|
+
event: 'pre_tool_call',
|
|
2285
|
+
command: hookCommand,
|
|
2286
|
+
approved_at: new Date().toISOString(),
|
|
2287
|
+
script_mtime_at_approval: hermesHookScriptMtime(),
|
|
2288
|
+
};
|
|
2289
|
+
data.approvals = [...approvals, entry];
|
|
2290
|
+
mkdirSync(dirname(allowlistPath), { recursive: true });
|
|
2291
|
+
writeFileSync(allowlistPath, JSON.stringify(data, null, 2));
|
|
2292
|
+
console.log(` Hook allowlist updated: ${allowlistPath}`);
|
|
2293
|
+
return true;
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
function hermesHookAllowlisted() {
|
|
2297
|
+
const allowlistPath = hermesAllowlistPath();
|
|
2298
|
+
if (!existsSync(allowlistPath)) return false;
|
|
2299
|
+
try {
|
|
2300
|
+
const data = JSON.parse(readFileSync(allowlistPath, 'utf8'));
|
|
2301
|
+
const cmd = hermesHookCommand();
|
|
2302
|
+
return (
|
|
2303
|
+
Array.isArray(data?.approvals) &&
|
|
2304
|
+
data.approvals.some((a) => a && typeof a === 'object' && a.event === 'pre_tool_call' && a.command === cmd)
|
|
2305
|
+
);
|
|
2306
|
+
} catch {
|
|
2307
|
+
return false;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
function hermesHookPluginInit() {
|
|
2312
|
+
const script = hermesHookScript();
|
|
2313
|
+
return [
|
|
2314
|
+
'import importlib.util',
|
|
2315
|
+
'',
|
|
2316
|
+
`SAFETY_SCRIPT = ${JSON.stringify(script)}`,
|
|
2317
|
+
'',
|
|
2318
|
+
'',
|
|
2319
|
+
'def _load_safety():',
|
|
2320
|
+
' spec = importlib.util.spec_from_file_location("huaweicloud_safety_hook", SAFETY_SCRIPT)',
|
|
2321
|
+
' module = importlib.util.module_from_spec(spec)',
|
|
2322
|
+
' spec.loader.exec_module(module)',
|
|
2323
|
+
' return module',
|
|
2324
|
+
'',
|
|
2325
|
+
'',
|
|
2326
|
+
'_safety = _load_safety()',
|
|
2327
|
+
'',
|
|
2328
|
+
'',
|
|
2329
|
+
'def register(ctx):',
|
|
2330
|
+
' ctx.register_hook("pre_tool_call", _check)',
|
|
2331
|
+
'',
|
|
2332
|
+
'',
|
|
2333
|
+
'def _check(tool_name=None, args=None, **kwargs):',
|
|
2334
|
+
' if tool_name != "terminal":',
|
|
2335
|
+
' return None',
|
|
2336
|
+
' if not (isinstance(args, dict) and "command" in args):',
|
|
2337
|
+
' return None',
|
|
2338
|
+
' reason = _safety.evaluate(tool_name, args)',
|
|
2339
|
+
' if reason:',
|
|
2340
|
+
' return {"action": "block", "message": _safety.DENY_PREFIX + reason}',
|
|
2341
|
+
' return None',
|
|
2342
|
+
'',
|
|
2343
|
+
].join('\n');
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
function ensureHermesHookPlugin() {
|
|
2347
|
+
const pluginDir = hermesSafetyPluginDir();
|
|
2348
|
+
const manifestPath = join(pluginDir, 'plugin.yaml');
|
|
2349
|
+
const initPath = join(pluginDir, '__init__.py');
|
|
2350
|
+
|
|
2351
|
+
const manifest = [
|
|
2352
|
+
'name: huaweicloud-safety',
|
|
2353
|
+
`version: "${pkgVersion}"`,
|
|
2354
|
+
'description: Huawei Cloud safety pre_tool_call hook',
|
|
2355
|
+
'author: HuaweiCloud Mate',
|
|
2356
|
+
'provides_hooks:',
|
|
2357
|
+
' - pre_tool_call',
|
|
2358
|
+
'',
|
|
2359
|
+
].join('\n');
|
|
2360
|
+
const init = hermesHookPluginInit();
|
|
2361
|
+
|
|
2362
|
+
const manifestCurrent = existsSync(manifestPath) ? readFileSync(manifestPath, 'utf8') : '';
|
|
2363
|
+
const initCurrent = existsSync(initPath) ? readFileSync(initPath, 'utf8') : '';
|
|
2364
|
+
if (manifestCurrent === manifest && initCurrent === init) {
|
|
2365
|
+
console.log(` Hook plugin unchanged: ${pluginDir}`);
|
|
2366
|
+
return false;
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
mkdirSync(pluginDir, { recursive: true });
|
|
2370
|
+
writeFileSync(manifestPath, manifest);
|
|
2371
|
+
writeFileSync(initPath, init);
|
|
2372
|
+
console.log(` Hook plugin installed: ${pluginDir}`);
|
|
2373
|
+
return true;
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
function removeHermesHookPlugin() {
|
|
2377
|
+
const pluginDir = hermesSafetyPluginDir();
|
|
2378
|
+
if (!existsSync(pluginDir)) return false;
|
|
2379
|
+
rmSync(pluginDir, { recursive: true, force: true });
|
|
2380
|
+
console.log(` Removed hook plugin: ${pluginDir}`);
|
|
2381
|
+
const parent = hermesPythonPluginsDir();
|
|
2382
|
+
try {
|
|
2383
|
+
if (existsSync(parent) && readdirSync(parent).length === 0) {
|
|
2384
|
+
rmSync(parent, { recursive: true, force: true });
|
|
2385
|
+
}
|
|
2386
|
+
} catch {}
|
|
2387
|
+
return true;
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
function hermesHookPluginInstalled() {
|
|
2391
|
+
return (
|
|
2392
|
+
existsSync(join(hermesSafetyPluginDir(), 'plugin.yaml')) && existsSync(join(hermesSafetyPluginDir(), '__init__.py'))
|
|
2393
|
+
);
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2220
2396
|
function removeHermesHooksConfigBlock() {
|
|
2221
2397
|
const configPath = hermesConfigFile();
|
|
2222
2398
|
if (!existsSync(configPath)) return;
|
|
@@ -2299,6 +2475,8 @@ async function installHermes() {
|
|
|
2299
2475
|
|
|
2300
2476
|
if (!skipMcp) ensureHermesMcpConfig();
|
|
2301
2477
|
ensureHermesHooksConfig();
|
|
2478
|
+
ensureHermesHookAllowlist();
|
|
2479
|
+
ensureHermesHookPlugin();
|
|
2302
2480
|
if (!skipMcp) installRuntimeDeps(pluginDest);
|
|
2303
2481
|
if (!skipMcp) ensureHermesMcpSdk();
|
|
2304
2482
|
}
|
|
@@ -2321,6 +2499,8 @@ async function updateHermes() {
|
|
|
2321
2499
|
console.log(` Safety Hooks updated -> ${join(pluginDest, 'hooks')}`);
|
|
2322
2500
|
ensureHermesMcpConfig();
|
|
2323
2501
|
ensureHermesHooksConfig();
|
|
2502
|
+
ensureHermesHookAllowlist();
|
|
2503
|
+
ensureHermesHookPlugin();
|
|
2324
2504
|
ensureHermesMcpSdk();
|
|
2325
2505
|
mkdirSync(pluginDest, { recursive: true });
|
|
2326
2506
|
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
@@ -2335,6 +2515,9 @@ function uninstallHermes() {
|
|
|
2335
2515
|
removeHermesHooksConfigBlock();
|
|
2336
2516
|
console.log(' Hooks config removed');
|
|
2337
2517
|
|
|
2518
|
+
// 1b. Remove the Python safety hook plugin (v0.9.0+ enforcement path)
|
|
2519
|
+
removeHermesHookPlugin();
|
|
2520
|
+
|
|
2338
2521
|
// 2. Clean shell-hooks-allowlist.json (remove approved hook references)
|
|
2339
2522
|
const hermesHome = hermesHomeDir();
|
|
2340
2523
|
const allowlistPath = join(hermesHome, 'shell-hooks-allowlist.json');
|
|
@@ -2342,9 +2525,10 @@ function uninstallHermes() {
|
|
|
2342
2525
|
try {
|
|
2343
2526
|
const allowlist = JSON.parse(readFileSync(allowlistPath, 'utf8'));
|
|
2344
2527
|
const before = (allowlist.approvals || []).length;
|
|
2345
|
-
allowlist.approvals = (allowlist.approvals || []).filter((a) =>
|
|
2346
|
-
typeof a === 'string' ?
|
|
2347
|
-
|
|
2528
|
+
allowlist.approvals = (allowlist.approvals || []).filter((a) => {
|
|
2529
|
+
const cmd = typeof a === 'string' ? a : a?.command;
|
|
2530
|
+
return !(typeof cmd === 'string' && cmd.includes('huaweicloud-safety.py'));
|
|
2531
|
+
});
|
|
2348
2532
|
if (allowlist.approvals.length < before) {
|
|
2349
2533
|
writeFileSync(allowlistPath, JSON.stringify(allowlist, null, 2));
|
|
2350
2534
|
console.log(` Removed ${before - allowlist.approvals.length} hook approvals from allowlist`);
|
|
@@ -2394,6 +2578,12 @@ function hermesStatus() {
|
|
|
2394
2578
|
console.log(
|
|
2395
2579
|
` Safety Hooks: ${existsSync(join(pluginDir, 'hooks', 'huaweicloud-safety.py')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
2396
2580
|
);
|
|
2581
|
+
console.log(
|
|
2582
|
+
` Hook allowlist: ${hermesHookAllowlisted() ? '\x1b[32mApproved\x1b[0m' : '\x1b[31mNot allowlisted\x1b[0m'}`,
|
|
2583
|
+
);
|
|
2584
|
+
console.log(
|
|
2585
|
+
` Hook plugin: ${hermesHookPluginInstalled() ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
2586
|
+
);
|
|
2397
2587
|
console.log(` MCP Python SDK: ${hermesMcpSdkOk() ? '\x1b[32mReady\x1b[0m' : '\x1b[31mMissing\x1b[0m'}`);
|
|
2398
2588
|
let skillCount = 0;
|
|
2399
2589
|
if (existsSync(skillsDir)) {
|
|
@@ -2867,17 +3057,19 @@ async function cmdDoctor() {
|
|
|
2867
3057
|
? 'Codex Desktop'
|
|
2868
3058
|
: existsSync(join(codeartsPluginDir, 'src', 'mcp-server.mjs'))
|
|
2869
3059
|
? 'CodeArts'
|
|
2870
|
-
: existsSync(join(
|
|
2871
|
-
? '
|
|
2872
|
-
: existsSync(join(
|
|
2873
|
-
? '
|
|
2874
|
-
: existsSync(join(
|
|
2875
|
-
? '
|
|
2876
|
-
: existsSync(join(
|
|
2877
|
-
? '
|
|
2878
|
-
: existsSync(join(
|
|
2879
|
-
? '
|
|
2880
|
-
: ''
|
|
3060
|
+
: existsSync(join(codeartsWorkPluginDir, 'src', 'mcp-server.mjs'))
|
|
3061
|
+
? 'CodeArts Work'
|
|
3062
|
+
: existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs'))
|
|
3063
|
+
? 'WorkBuddy'
|
|
3064
|
+
: existsSync(join(dshPluginDir, 'src', 'mcp-server.mjs'))
|
|
3065
|
+
? 'DSH'
|
|
3066
|
+
: existsSync(join(officeacePluginDir, 'src', 'mcp-server.mjs'))
|
|
3067
|
+
? 'OfficeAce'
|
|
3068
|
+
: existsSync(join(hermesPluginDir, 'src', 'mcp-server.mjs'))
|
|
3069
|
+
? 'Hermes Agent'
|
|
3070
|
+
: existsSync(join(atomcodePluginDir, 'src', 'mcp-server.mjs'))
|
|
3071
|
+
? 'AtomCode'
|
|
3072
|
+
: '';
|
|
2881
3073
|
check('MCP server installed', mcpOk, 'Run: npx huaweicloud-devkit install');
|
|
2882
3074
|
|
|
2883
3075
|
if (mcpOk) {
|
|
@@ -2888,6 +3080,7 @@ async function cmdDoctor() {
|
|
|
2888
3080
|
opencodePluginDir,
|
|
2889
3081
|
codexPluginDir,
|
|
2890
3082
|
codeartsPluginDir,
|
|
3083
|
+
codeartsWorkPluginDir,
|
|
2891
3084
|
workbuddyPluginDir,
|
|
2892
3085
|
dshPluginDir,
|
|
2893
3086
|
officeacePluginDir,
|
|
@@ -2941,6 +3134,16 @@ async function cmdDoctor() {
|
|
|
2941
3134
|
}
|
|
2942
3135
|
} catch {}
|
|
2943
3136
|
}
|
|
3137
|
+
const codeartsWorkCfg = codeartsWorkMcpSettingsFile();
|
|
3138
|
+
if (!mcpConfigured && existsSync(codeartsWorkCfg)) {
|
|
3139
|
+
try {
|
|
3140
|
+
const cfg = JSON.parse(readFileSync(codeartsWorkCfg, 'utf8'));
|
|
3141
|
+
if (cfg.mcpServers && cfg.mcpServers['huaweicloud-devkit']) {
|
|
3142
|
+
mcpConfigured = true;
|
|
3143
|
+
mcpCfgTarget = 'CodeArts Work';
|
|
3144
|
+
}
|
|
3145
|
+
} catch {}
|
|
3146
|
+
}
|
|
2944
3147
|
const workbuddyCfg = workbuddyMcpConfigFile();
|
|
2945
3148
|
if (!mcpConfigured && existsSync(workbuddyCfg)) {
|
|
2946
3149
|
try {
|