huaweicloud-devkit 1.0.2-next.8 → 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.
Files changed (38) hide show
  1. package/.agents/plugins/marketplace.json +1 -1
  2. package/LICENSE +201 -201
  3. package/README.md +71 -52
  4. package/README.zh-CN.md +71 -50
  5. package/bin/setup.cjs +0 -0
  6. package/package.json +9 -5
  7. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +2 -2
  8. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +2 -2
  9. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +2 -2
  10. package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
  11. package/plugins/huaweicloud-core/.mcp.json +1 -2
  12. package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +2 -3
  13. package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +159 -136
  14. package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
  15. package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +1 -1
  16. package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +67 -20
  17. package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +11 -0
  18. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +16 -0
  19. package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +27 -24
  20. package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +12 -0
  21. package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +79 -11
  22. package/plugins/huaweicloud-core/src/auth/credentials.mjs +65 -0
  23. package/plugins/huaweicloud-core/src/mcp-server.mjs +31 -4
  24. package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +6 -1
  25. package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +7 -2
  26. package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +14 -4
  27. package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
  28. package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +432 -10
  29. package/plugins/huaweicloud-core/src/setup-cli.mjs +581 -87
  30. package/plugins/huaweicloud-core/src/tools.mjs +225 -33
  31. package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +18 -4
  32. package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +9 -0
  33. package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +9 -0
  34. package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +9 -0
  35. package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +9 -0
  36. package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +267 -0
  37. package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
  38. package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +9 -5
package/README.zh-CN.md CHANGED
@@ -8,46 +8,32 @@
8
8
 
9
9
  帮助 AI 编码助手安全、准确地使用华为云——一站式集成云知识、CLI 工具和安全护栏。
10
10
 
11
- 支持 OpenCode、Codex、码道(CodeArts Agent)、WorkBuddy、DeepSeek Harness(DSH)、OfficeAce。
11
+ 支持 OpenCode、码道(CodeArts Agent)、WorkBuddy、DeepSeek Harness(DSH)、OfficeAce、OpenClaw
12
12
 
13
13
  ## 前置条件
14
14
 
15
- - Node.js >= 20
15
+ - Node.js >= 22
16
16
 
17
17
  ## 快速开始
18
18
 
19
- ### OpenCode
20
-
21
- ```bash
22
- npx --yes huaweicloud-devkit install
23
- ```
24
-
25
- 自动安装 DevKit。安装后**重启会话**。
19
+ > 省略 `--target` 时,安装器会自动检测机器上的 agent,检测到多个时**全部安装**。建议始终指定 `--target` 以明确安装目标。
26
20
 
27
- ```bash
28
- npx --yes huaweicloud-devkit doctor # 自检
29
- npx --yes huaweicloud-devkit status # 查看状态
30
- npx --yes huaweicloud-devkit update # 更新
31
- npx --yes huaweicloud-devkit uninstall # 卸载
32
- ```
33
-
34
- ### Codex
21
+ ### OpenCode
35
22
 
36
23
  ```bash
37
- npx --yes huaweicloud-devkit install --target codex
24
+ npx --yes huaweicloud-devkit install --target opencode
38
25
  ```
39
26
 
40
- > 需要先安装 Codex CLI。
27
+ 安装后**重启会话**。
41
28
 
42
29
  ```bash
43
- npx --yes huaweicloud-devkit doctor # 自检
44
- npx --yes huaweicloud-devkit status --target codex
45
- npx --yes huaweicloud-devkit update --target codex # 更新
46
- npx --yes huaweicloud-devkit uninstall --target codex
30
+ npx --yes huaweicloud-devkit doctor --target opencode
31
+ npx --yes huaweicloud-devkit status --target opencode
32
+ npx --yes huaweicloud-devkit update --target opencode
33
+ npx --yes huaweicloud-devkit uninstall --target opencode
34
+ rm -rf ~/.npm/_npx/ # 仅 Linux/macOS;Windows 路径待确认
47
35
  ```
48
36
 
49
- > **Codex Desktop**(Windows):将 `--target codex` 换成 `--target codex-desktop`,命令相同。
50
-
51
37
  ### CodeArts Agent(码道)
52
38
 
53
39
  ```bash
@@ -57,16 +43,13 @@ npx --yes huaweicloud-devkit install --target codearts
57
43
  安装后**重启会话**。
58
44
 
59
45
  ```bash
60
- npx --yes huaweicloud-devkit install-hcloud # 安装 KooCLI
61
- npx --yes huaweicloud-devkit doctor # 自检
46
+ npx --yes huaweicloud-devkit doctor --target codearts
62
47
  npx --yes huaweicloud-devkit status --target codearts
63
48
  npx --yes huaweicloud-devkit update --target codearts
64
49
  npx --yes huaweicloud-devkit uninstall --target codearts
65
50
  ```
66
51
 
67
- > **沙箱模式**:码道默认沙箱模式会阻止 KooCLI 运行。`install-hcloud` 自动检测并给出指引——请在码道外终端安装使用 KooCLI,或在码道设置中关闭沙箱模式(设置 → 权限Bash 模式)。
68
- >
69
- > **认证**:执行 `npx huaweicloud-devkit auth init`,统一配置 KooCLI、OBS 和沙箱接口所需的 AK/SK。
52
+ > **沙箱模式**:码道默认沙箱模式会阻止 KooCLI 运行。`install-hcloud` 自动检测并给出指引——请在码道外终端安装使用 KooCLI,或在码道设置中关闭沙箱模式(设置 → 对话流智能体 终端命令运行模式 → 自动运行)。
70
53
 
71
54
  ### WorkBuddy
72
55
 
@@ -77,34 +60,28 @@ npx --yes huaweicloud-devkit install --target workbuddy
77
60
  安装后**重启会话**。
78
61
 
79
62
  ```bash
80
- npx --yes huaweicloud-devkit doctor
63
+ npx --yes huaweicloud-devkit doctor --target workbuddy
81
64
  npx --yes huaweicloud-devkit status --target workbuddy
82
- npx --yes huaweicloud-devkit update --target workbuddy # 更新
65
+ npx --yes huaweicloud-devkit update --target workbuddy
83
66
  npx --yes huaweicloud-devkit uninstall --target workbuddy
84
67
  ```
85
68
 
86
- > **更新机制**:`update` 按 agent 增量更新,只刷新必要文件、不动你的配置文件。`update --target all` 可一次更新所有已安装的 agent。
87
-
88
69
  ### DeepSeek Harness(DSH)
89
70
 
90
71
  ```bash
91
72
  npx --yes huaweicloud-devkit install --target dsh
92
73
  ```
93
74
 
94
- 安装器会把 Skills 写入 `$DSH_HOME/skills`,把 MCP Server 和安全策略写入
95
- `$DSH_HOME/huaweicloud-plugins`,并自动维护
96
- `$DSH_HOME/profiles/web/cordis.patch.yml`。安装后**重启 DSH 会话**。
75
+ 安装后**重启 DSH 会话**。
97
76
 
98
77
  ```bash
99
- npx --yes huaweicloud-devkit doctor
78
+ npx --yes huaweicloud-devkit doctor --target dsh
100
79
  npx --yes huaweicloud-devkit status --target dsh
101
80
  npx --yes huaweicloud-devkit update --target dsh
102
81
  npx --yes huaweicloud-devkit uninstall --target dsh
103
82
  ```
104
83
 
105
- > DSH V1 通过 `@deepseek-ai/dsh-mcp-client` 复用现有 MCP Server
106
- > 如果安装器提示 DSH MCP 客户端未检测到,请按提示执行
107
- > `npx @deepseek-ai/dsh plugin --profile web add @deepseek-ai/dsh-mcp-client`。
84
+ > DSH V1 通过 `@deepseek-ai/dsh-mcp-client` 复用现有 MCP Server。如果安装器提示客户端未检测到,请执行:`npx @deepseek-ai/dsh plugin --profile web add @deepseek-ai/dsh-mcp-client`。
108
85
 
109
86
  ### OfficeAce
110
87
 
@@ -115,35 +92,78 @@ npx --yes huaweicloud-devkit install --target officeace
115
92
  安装后**重启 OfficeAce**。
116
93
 
117
94
  ```bash
118
- npx --yes huaweicloud-devkit doctor
95
+ npx --yes huaweicloud-devkit doctor --target officeace
119
96
  npx --yes huaweicloud-devkit status --target officeace
120
97
  npx --yes huaweicloud-devkit update --target officeace
121
98
  npx --yes huaweicloud-devkit uninstall --target officeace
122
99
  ```
123
100
 
101
+ ### OpenClaw
102
+
103
+ ```bash
104
+ # 推荐方式 (ClawHub)
105
+ openclaw plugins install clawhub:huaweicloud-devkit
106
+ openclaw plugins uninstall huaweicloud-devkit
107
+ openclaw plugins update huaweicloud-devkit
108
+ ```
109
+
110
+ 安装后**重启 OpenClaw**。如提示安全风险确认,加 `--acknowledge-clawhub-risk`。
111
+
112
+ ```bash
113
+ # 或通过 npx
114
+ npx --yes huaweicloud-devkit install --target openclaw
115
+ npx --yes huaweicloud-devkit status --target openclaw
116
+ npx --yes huaweicloud-devkit update --target openclaw
117
+ npx --yes huaweicloud-devkit uninstall --target openclaw
118
+ rm -rf ~/.npm/_npx/ # 仅 Linux/macOS;Windows 路径待确认
119
+ ```
120
+
124
121
  ### 其他 Agent
125
122
 
126
- 支持 MCP 协议的 Agent,手动配置:
123
+ 任何支持 MCP 协议的 Agent,直接使用标准 MCP 配置:
127
124
 
128
125
  ```json
129
126
  {
130
- "mcp": {
127
+ "mcpServers": {
131
128
  "huaweicloud-devkit": {
132
- "type": "local",
133
- "command": ["node", "<路径>/plugins/huaweicloud-core/src/mcp-server.mjs"],
134
- "enabled": true
129
+ "command": "npx",
130
+ "args": ["-y", "-p", "huaweicloud-devkit", "huaweicloud-devkit-mcp"]
135
131
  }
136
132
  }
137
133
  }
138
134
  ```
139
135
 
140
- 然后安装:
136
+ 无需预安装 — `npx` 自动处理一切。
137
+
138
+ > 项目级 AK/SK 可通过 MCP 配置的 `env` 字段设置 `HW_ACCESS_KEY`/`HW_SECRET_KEY`。
139
+
140
+ ### 安装 KooCLI
141
+
142
+ ```bash
143
+ npx --yes huaweicloud-devkit install-hcloud
144
+ ```
145
+
146
+ ### 配置凭据
147
+
148
+ ```bash
149
+ npx --yes huaweicloud-devkit auth init
150
+ ```
151
+
152
+ 一步同步 AK/SK 到 KooCLI、OBS 和沙箱接口。
153
+
154
+ ### 安装所有 Agent
155
+
156
+ ```bash
157
+ npx --yes huaweicloud-devkit install --target all
158
+ ```
159
+
160
+ ### 更新所有 Agent
141
161
 
142
162
  ```bash
143
- npx --yes huaweicloud-devkit install
163
+ npx --yes huaweicloud-devkit update --target all
144
164
  ```
145
165
 
146
- > **前置条件:** Node.js >= 20。执行 `npx huaweicloud-devkit auth init` 完成统一认证。
166
+ `update` 是增量更新——只刷新已安装的文件,不动配置文件。
147
167
 
148
168
  ## 功能特性
149
169
 
@@ -151,6 +171,7 @@ npx --yes huaweicloud-devkit install
151
171
  - **安全优先执行** — 所有写操作需用户明确批准;凭证和密钥自动脱敏
152
172
  - **执行前风险检查** — 公网暴露、凭证泄露、破坏性操作在执行前即被拦截
153
173
  - **区域感知** — 自动发现可用区域,创建资源前检查服务可用性
174
+ - **沙箱(DevStation)** — 临时云端运行环境,部署 Web 应用并即刻获得公网预览地址
154
175
 
155
176
  ## 支持的服务
156
177
 
package/bin/setup.cjs CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.0.2-next.8",
3
+ "version": "1.0.2",
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": [
@@ -10,11 +10,12 @@
10
10
  "integrations/opencode"
11
11
  ],
12
12
  "bin": {
13
- "huaweicloud-devkit": "./bin/setup.cjs"
13
+ "huaweicloud-devkit": "./bin/setup.cjs",
14
+ "huaweicloud-devkit-mcp": "./plugins/huaweicloud-core/src/mcp-server.mjs"
14
15
  },
15
16
  "repository": {
16
17
  "type": "git",
17
- "url": "https://github.com/huaweicloud/HuaweiCloud-Devkit.git"
18
+ "url": "https://github.com/huaweicloud/huaweicloud-devkit.git"
18
19
  },
19
20
  "scripts": {
20
21
  "test": "node --test",
@@ -27,7 +28,10 @@
27
28
  "pack:verify": "node ./scripts/pack-verify.mjs",
28
29
  "postinstall": "node -e \"console.log('\\nHuaweiCloud DevKit installed. Run: npx huaweicloud-devkit install\\n')\""
29
30
  },
30
- "dependencies": {},
31
+ "dependencies": {
32
+ "better-sqlite3": "^13.0.3",
33
+ "undici": "^8.10.0"
34
+ },
31
35
  "devDependencies": {
32
36
  "@eslint/js": "^10.0.1",
33
37
  "eslint": "^10.8.1",
@@ -54,6 +58,6 @@
54
58
  ],
55
59
  "license": "Apache-2.0",
56
60
  "engines": {
57
- "node": ">=20"
61
+ "node": ">=22"
58
62
  }
59
63
  }
@@ -17,13 +17,13 @@
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.0.2-next.8",
20
+ "version": "1.0.2",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
24
24
  },
25
25
  "hooks": "./hooks/",
26
- "name": "huaweicloud-core",
26
+ "name": "huaweicloud-devkit",
27
27
  "homepage": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
28
28
  "repository": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
29
29
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
- "name": "huaweicloud-core",
3
- "version": "1.0.2-next.8",
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",
@@ -17,13 +17,13 @@
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.0.2-next.8",
20
+ "version": "1.0.2",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
24
24
  },
25
25
  "hooks": "./hooks/",
26
- "name": "huaweicloud-core",
26
+ "name": "huaweicloud-devkit",
27
27
  "homepage": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
28
28
  "repository": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
29
29
  "license": "Apache-2.0",
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "huaweicloud-devkit",
3
+ "version": "1.0.2",
4
+ "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
+ "author": {
6
+ "name": "HuaweiCloud Mate",
7
+ "url": "https://github.com/huaweicloud"
8
+ },
9
+ "homepage": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
10
+ "repository": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
11
+ "license": "Apache-2.0",
12
+ "keywords": ["huaweicloud", "huawei-cloud", "koocli", "hcloud", "agent", "mcp", "api", "sdk", "skills"],
13
+ "skills": "./skills/",
14
+ "mcpServers": "./.mcp.json",
15
+ "interface": {
16
+ "displayName": "HuaweiCloud Devkit",
17
+ "shortDescription": "HuaweiCloud Devkit - Huawei Cloud guidance, CLI/API/SDK routing, MCP tools, and safety for coding agents.",
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.",
19
+ "developerName": "HuaweiCloud Mate",
20
+ "category": "Cloud",
21
+ "capabilities": ["Read", "Interactive"],
22
+ "websiteURL": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
23
+ "brandColor": "#C7000B",
24
+ "defaultPrompt": [
25
+ "Help me choose the right Huawei Cloud capability.",
26
+ "Check my Huawei Cloud CLI setup safely.",
27
+ "Plan this Huawei Cloud API or SDK task."
28
+ ]
29
+ }
30
+ }
@@ -5,8 +5,7 @@
5
5
  "args": ["./src/mcp-server.mjs"],
6
6
  "cwd": ".",
7
7
  "env": {
8
- "HUAWEICLOUD_AGENT_TOOLKIT_MODE": "local",
9
- "HDKITSERVICE_ENDPOINT": ""
8
+ "HUAWEICLOUD_AGENT_TOOLKIT_MODE": "local"
10
9
  }
11
10
  }
12
11
  }
@@ -1,6 +1,6 @@
1
1
  {
2
- "name": "huaweicloud-core",
3
- "version": "1.0.2-next.8",
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",
@@ -12,7 +12,6 @@
12
12
  "keywords": ["huaweicloud", "huawei-cloud", "koocli", "hcloud", "agent", "mcp", "api", "sdk", "skills"],
13
13
  "skills": "./skills/",
14
14
  "mcpServers": "./.mcp.json",
15
- "hooks": "./hooks/",
16
15
  "interface": {
17
16
  "displayName": "HuaweiCloud Devkit",
18
17
  "shortDescription": "HuaweiCloud Devkit - Huawei Cloud guidance, CLI/API/SDK routing, MCP tools, and safety for coding agents.",
@@ -1,136 +1,159 @@
1
- #!/usr/bin/env python3
2
- """PreToolUse hook for Huawei Cloud agent safety.
3
-
4
- Blocks the highest-risk leakage paths before an agent tool runs:
5
- - reading local Huawei Cloud credential files
6
- - dumping cloud credential environment variables
7
- - directly retrieving cloud secret values
8
- - running unapproved write operations through hcloud
9
-
10
- The MCP server carries the same policy in Node for platforms that do not support
11
- plugin hooks.
12
- """
13
-
14
- import json
15
- import re
16
- import sys
17
- from pathlib import Path
18
-
19
- DENY_PREFIX = "Huawei Cloud safety hook blocked this action: "
20
- RULES_PATH = Path(__file__).resolve().parents[1] / "safety" / "rules" / "cloud-risk-rules.json"
21
-
22
- CONFIG_FILE_RE = re.compile(r"(\.hcloud|\.huaweicloud|hcloud[/\\](config|credentials)|huaweicloud[/\\](config|credentials))", re.I)
23
- ENV_DUMP_RE = re.compile(r"(env|printenv|Get-ChildItem\s+Env:|gci\s+Env:|dir\s+Env:).*(HUAWEICLOUD|HWC_|HCLOUD|OS_)", re.I)
24
- SECRET_READ_RE = re.compile(r"(ShowSecretVersion|GetSecretValue|secret_string|secret_binary)", re.I)
25
- HCLOUD_RE = re.compile(r"(^|\s)hcloud(\.exe)?\s+", re.I)
26
- WRITE_OPERATION_RE = re.compile(
27
- r"\b(BatchCreate|BatchDelete|Create|Delete|Update|Modify|Resize|Reboot|Stop|Start|Restart|Authorize|Revoke|Add|Remove|Associate|Disassociate|Attach|Detach|Enable|Disable)\w*",
28
- re.I,
29
- )
30
- READ_OPERATION_RE = re.compile(r"\b(List|Show|Get|Describe|NovaList|NovaShow)\w*", re.I)
31
-
32
-
33
- def deny(reason):
34
- json.dump(
35
- {
36
- "hookSpecificOutput": {
37
- "hookEventName": "PreToolUse",
38
- "permissionDecision": "deny",
39
- "permissionDecisionReason": DENY_PREFIX + reason,
40
- }
41
- },
42
- sys.stdout,
43
- )
44
- sys.exit(0)
45
-
46
-
47
- def allow():
48
- sys.exit(0)
49
-
50
-
51
- def command_text(tool_input):
52
- if isinstance(tool_input, str):
53
- return tool_input
54
- if isinstance(tool_input, dict):
55
- values = []
56
- for key in ("command", "cmd", "script", "args", "arguments"):
57
- value = tool_input.get(key)
58
- if isinstance(value, list):
59
- values.append(" ".join(str(item) for item in value))
60
- elif value is not None:
61
- values.append(str(value))
62
- if values:
63
- return "\n".join(values)
64
- return json.dumps(tool_input)
65
- return json.dumps(tool_input)
66
-
67
-
68
- def load_cloud_risk_rules():
69
- try:
70
- with RULES_PATH.open("r", encoding="utf-8") as file_obj:
71
- catalog = json.load(file_obj)
72
- return catalog.get("rules", [])
73
- except Exception:
74
- return []
75
-
76
-
77
- def condition_matches(condition, text):
78
- return re.search(condition.get("regex", r"a^"), text, re.I | re.M | re.S) is not None
79
-
80
-
81
- def rule_matches(rule, text):
82
- match = rule.get("match") or {}
83
- all_conditions = match.get("all")
84
- any_conditions = match.get("any")
85
- none_conditions = match.get("none")
86
-
87
- if isinstance(all_conditions, list):
88
- for condition in all_conditions:
89
- if not condition_matches(condition, text):
90
- return False
91
- if isinstance(any_conditions, list):
92
- if not any(condition_matches(condition, text) for condition in any_conditions):
93
- return False
94
- if isinstance(none_conditions, list):
95
- if any(condition_matches(condition, text) for condition in none_conditions):
96
- return False
97
- return isinstance(all_conditions, list) or isinstance(any_conditions, list)
98
-
99
-
100
- def first_denied_command_rule(text):
101
- for rule in load_cloud_risk_rules():
102
- if "command" not in rule.get("stages", []):
103
- continue
104
- if rule.get("severity") != "deny":
105
- continue
106
- if rule_matches(rule, text):
107
- return rule
108
- return None
109
-
110
-
111
- def main():
112
- try:
113
- data = json.load(sys.stdin)
114
- except Exception:
115
- allow()
116
-
117
- tool_name = data.get("tool_name", "")
118
- text = command_text(data.get("tool_input", {}))
119
-
120
- if CONFIG_FILE_RE.search(text):
121
- deny("reading Huawei Cloud credential/profile files can expose AK/SK or tokens. Use redacted toolkit tools.")
122
- if ENV_DUMP_RE.search(text):
123
- deny("dumping cloud credential environment variables is not allowed.")
124
- if SECRET_READ_RE.search(text):
125
- deny("direct secret value retrieval would put plaintext secrets into the agent context.")
126
- denied_rule = first_denied_command_rule(text)
127
- if denied_rule:
128
- deny(f"{denied_rule.get('message')} Remediation: {denied_rule.get('remediation')}")
129
- if tool_name == "Bash" and HCLOUD_RE.search(text) and WRITE_OPERATION_RE.search(text) and not READ_OPERATION_RE.search(text):
130
- deny("unapproved Huawei Cloud write operations must be planned first and explicitly approved by the user.")
131
-
132
- allow()
133
-
134
-
135
- if __name__ == "__main__":
136
- main()
1
+ #!/usr/bin/env python3
2
+ """PreToolUse hook for Huawei Cloud agent safety.
3
+
4
+ Blocks the highest-risk leakage paths before an agent tool runs:
5
+ - reading local Huawei Cloud credential files
6
+ - dumping cloud credential environment variables
7
+ - directly retrieving cloud secret values
8
+ - running unapproved write operations through hcloud
9
+
10
+ The MCP server carries the same policy in Node for platforms that do not support
11
+ plugin hooks.
12
+ """
13
+
14
+ import json
15
+ import re
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ DENY_PREFIX = "Huawei Cloud safety hook blocked this action: "
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"
22
+
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,
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)
53
+ READ_OPERATION_RE = re.compile(r"\b(List|Show|Get|Describe|NovaList|NovaShow)\w*", re.I)
54
+
55
+
56
+ def deny(reason):
57
+ json.dump(
58
+ {
59
+ "hookSpecificOutput": {
60
+ "hookEventName": "PreToolUse",
61
+ "permissionDecision": "deny",
62
+ "permissionDecisionReason": DENY_PREFIX + reason,
63
+ }
64
+ },
65
+ sys.stdout,
66
+ )
67
+ sys.exit(0)
68
+
69
+
70
+ def allow():
71
+ sys.exit(0)
72
+
73
+
74
+ def command_text(tool_input):
75
+ if isinstance(tool_input, str):
76
+ return tool_input
77
+ if isinstance(tool_input, dict):
78
+ values = []
79
+ for key in ("command", "cmd", "script", "args", "arguments"):
80
+ value = tool_input.get(key)
81
+ if isinstance(value, list):
82
+ values.append(" ".join(str(item) for item in value))
83
+ elif value is not None:
84
+ values.append(str(value))
85
+ if values:
86
+ return "\n".join(values)
87
+ return json.dumps(tool_input)
88
+ return json.dumps(tool_input)
89
+
90
+
91
+ def load_cloud_risk_rules():
92
+ try:
93
+ with RULES_PATH.open("r", encoding="utf-8") as file_obj:
94
+ catalog = json.load(file_obj)
95
+ return catalog.get("rules", [])
96
+ except Exception:
97
+ return []
98
+
99
+
100
+ def condition_matches(condition, text):
101
+ return re.search(condition.get("regex", r"a^"), text, re.I | re.M | re.S) is not None
102
+
103
+
104
+ def rule_matches(rule, text):
105
+ match = rule.get("match") or {}
106
+ all_conditions = match.get("all")
107
+ any_conditions = match.get("any")
108
+ none_conditions = match.get("none")
109
+
110
+ if isinstance(all_conditions, list):
111
+ for condition in all_conditions:
112
+ if not condition_matches(condition, text):
113
+ return False
114
+ if isinstance(any_conditions, list):
115
+ if not any(condition_matches(condition, text) for condition in any_conditions):
116
+ return False
117
+ if isinstance(none_conditions, list):
118
+ if any(condition_matches(condition, text) for condition in none_conditions):
119
+ return False
120
+ return isinstance(all_conditions, list) or isinstance(any_conditions, list)
121
+
122
+
123
+ def first_denied_command_rule(text):
124
+ for rule in load_cloud_risk_rules():
125
+ if "command" not in rule.get("stages", []):
126
+ continue
127
+ if rule.get("severity") != "deny":
128
+ continue
129
+ if rule_matches(rule, text):
130
+ return rule
131
+ return None
132
+
133
+
134
+ def main():
135
+ try:
136
+ data = json.load(sys.stdin)
137
+ except Exception:
138
+ allow()
139
+
140
+ tool_name = data.get("tool_name", "")
141
+ text = command_text(data.get("tool_input", {}))
142
+
143
+ if CONFIG_FILE_RE and CONFIG_FILE_RE.search(text):
144
+ deny("reading Huawei Cloud credential/profile files can expose AK/SK or tokens. Use redacted toolkit tools.")
145
+ if ENV_DUMP_RE.search(text):
146
+ deny("dumping cloud credential environment variables is not allowed.")
147
+ if SECRET_READ_RE and SECRET_READ_RE.search(text):
148
+ deny("direct secret value retrieval would put plaintext secrets into the agent context.")
149
+ denied_rule = first_denied_command_rule(text)
150
+ if denied_rule:
151
+ deny(f"{denied_rule.get('message')} Remediation: {denied_rule.get('remediation')}")
152
+ if tool_name == "Bash" and HCLOUD_RE.search(text) and WRITE_OPERATION_RE.search(text) and not READ_OPERATION_RE.search(text):
153
+ deny("unapproved Huawei Cloud write operations must be planned first and explicitly approved by the user.")
154
+
155
+ allow()
156
+
157
+
158
+ if __name__ == "__main__":
159
+ main()