huaweicloud-devkit 1.0.2-next.12 → 1.0.2-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 +8 -28
- package/README.zh-CN.md +8 -28
- package/package.json +3 -2
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +32 -9
- package/plugins/huaweicloud-core/openclaw.plugin.json +19 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +16 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +1 -0
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +24 -4
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +65 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +11 -4
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +2 -2
- package/plugins/huaweicloud-core/src/setup-cli.mjs +126 -57
- package/plugins/huaweicloud-core/src/tools.mjs +32 -3
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
Help AI coding agents use Huawei Cloud safely and accurately — a single integration that gives agents cloud knowledge, CLI tooling, and safety guardrails.
|
|
10
10
|
|
|
11
|
-
Supports OpenCode,
|
|
11
|
+
Supports OpenCode, CodeArts Agent, WorkBuddy, DeepSeek Harness (DSH), and OfficeAce.
|
|
12
12
|
|
|
13
13
|
## Prerequisites
|
|
14
14
|
|
|
@@ -33,23 +33,6 @@ npx --yes huaweicloud-devkit update --target opencode
|
|
|
33
33
|
npx --yes huaweicloud-devkit uninstall --target opencode
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
### Codex
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npx --yes huaweicloud-devkit install --target codex
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
> The Codex CLI must be installed first.
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
npx --yes huaweicloud-devkit doctor --target codex
|
|
46
|
-
npx --yes huaweicloud-devkit status --target codex
|
|
47
|
-
npx --yes huaweicloud-devkit update --target codex
|
|
48
|
-
npx --yes huaweicloud-devkit uninstall --target codex
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
> **Codex Desktop** (Windows): use `--target codex-desktop` with the same commands.
|
|
52
|
-
|
|
53
36
|
### CodeArts Agent
|
|
54
37
|
|
|
55
38
|
```bash
|
|
@@ -65,7 +48,7 @@ npx --yes huaweicloud-devkit update --target codearts
|
|
|
65
48
|
npx --yes huaweicloud-devkit uninstall --target codearts
|
|
66
49
|
```
|
|
67
50
|
|
|
68
|
-
> **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 →
|
|
51
|
+
> **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).
|
|
69
52
|
|
|
70
53
|
### WorkBuddy
|
|
71
54
|
|
|
@@ -116,26 +99,22 @@ npx --yes huaweicloud-devkit uninstall --target officeace
|
|
|
116
99
|
|
|
117
100
|
### Other Agents
|
|
118
101
|
|
|
119
|
-
|
|
102
|
+
Any agent that supports MCP can use the standard config:
|
|
120
103
|
|
|
121
104
|
```json
|
|
122
105
|
{
|
|
123
106
|
"mcpServers": {
|
|
124
107
|
"huaweicloud-devkit": {
|
|
125
|
-
"command": "
|
|
126
|
-
"args": ["
|
|
108
|
+
"command": "npx",
|
|
109
|
+
"args": ["-y", "-p", "huaweicloud-devkit@next", "huaweicloud-devkit-mcp"]
|
|
127
110
|
}
|
|
128
111
|
}
|
|
129
112
|
}
|
|
130
113
|
```
|
|
131
114
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
npx --yes huaweicloud-devkit install
|
|
136
|
-
```
|
|
115
|
+
No installation required — `npx` handles everything.
|
|
137
116
|
|
|
138
|
-
>
|
|
117
|
+
> Set `HW_ACCESS_KEY`/`HW_SECRET_KEY` in the MCP config `env` field for project-level credentials.
|
|
139
118
|
|
|
140
119
|
### Install KooCLI
|
|
141
120
|
|
|
@@ -171,6 +150,7 @@ npx --yes huaweicloud-devkit update --target all
|
|
|
171
150
|
- **Safety-first execution** — all write operations require explicit user approval; credentials and secrets are automatically redacted from output
|
|
172
151
|
- **Pre-execution risk checks** — public exposure, credential leaks, and destructive operations are caught before they run
|
|
173
152
|
- **Regional awareness** — auto-discovers available regions and checks service availability before creating resources
|
|
153
|
+
- **Sandbox (DevStation)** — temporary cloud runtime for web app deployment with instant public URL preview
|
|
174
154
|
|
|
175
155
|
## Supported Services
|
|
176
156
|
|
package/README.zh-CN.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
帮助 AI 编码助手安全、准确地使用华为云——一站式集成云知识、CLI 工具和安全护栏。
|
|
10
10
|
|
|
11
|
-
支持 OpenCode
|
|
11
|
+
支持 OpenCode、码道(CodeArts Agent)、WorkBuddy、DeepSeek Harness(DSH)、OfficeAce。
|
|
12
12
|
|
|
13
13
|
## 前置条件
|
|
14
14
|
|
|
@@ -33,23 +33,6 @@ npx --yes huaweicloud-devkit update --target opencode
|
|
|
33
33
|
npx --yes huaweicloud-devkit uninstall --target opencode
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
### Codex
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npx --yes huaweicloud-devkit install --target codex
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
> 需要先安装 Codex CLI。
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
npx --yes huaweicloud-devkit doctor --target codex
|
|
46
|
-
npx --yes huaweicloud-devkit status --target codex
|
|
47
|
-
npx --yes huaweicloud-devkit update --target codex
|
|
48
|
-
npx --yes huaweicloud-devkit uninstall --target codex
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
> **Codex Desktop**(Windows):将 `--target codex` 换成 `--target codex-desktop`,命令相同。
|
|
52
|
-
|
|
53
36
|
### CodeArts Agent(码道)
|
|
54
37
|
|
|
55
38
|
```bash
|
|
@@ -65,7 +48,7 @@ npx --yes huaweicloud-devkit update --target codearts
|
|
|
65
48
|
npx --yes huaweicloud-devkit uninstall --target codearts
|
|
66
49
|
```
|
|
67
50
|
|
|
68
|
-
> **沙箱模式**:码道默认沙箱模式会阻止 KooCLI 运行。`install-hcloud` 自动检测并给出指引——请在码道外终端安装使用 KooCLI,或在码道设置中关闭沙箱模式(设置 →
|
|
51
|
+
> **沙箱模式**:码道默认沙箱模式会阻止 KooCLI 运行。`install-hcloud` 自动检测并给出指引——请在码道外终端安装使用 KooCLI,或在码道设置中关闭沙箱模式(设置 → 对话流 → 智能体 终端命令运行模式 → 自动运行)。
|
|
69
52
|
|
|
70
53
|
### WorkBuddy
|
|
71
54
|
|
|
@@ -116,26 +99,22 @@ npx --yes huaweicloud-devkit uninstall --target officeace
|
|
|
116
99
|
|
|
117
100
|
### 其他 Agent
|
|
118
101
|
|
|
119
|
-
|
|
102
|
+
任何支持 MCP 协议的 Agent,直接使用标准 MCP 配置:
|
|
120
103
|
|
|
121
104
|
```json
|
|
122
105
|
{
|
|
123
106
|
"mcpServers": {
|
|
124
107
|
"huaweicloud-devkit": {
|
|
125
|
-
"command": "
|
|
126
|
-
"args": ["
|
|
108
|
+
"command": "npx",
|
|
109
|
+
"args": ["-y", "-p", "huaweicloud-devkit@next", "huaweicloud-devkit-mcp"]
|
|
127
110
|
}
|
|
128
111
|
}
|
|
129
112
|
}
|
|
130
113
|
```
|
|
131
114
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
npx --yes huaweicloud-devkit install
|
|
136
|
-
```
|
|
115
|
+
无需预安装 — `npx` 自动处理一切。
|
|
137
116
|
|
|
138
|
-
>
|
|
117
|
+
> 项目级 AK/SK 可通过 MCP 配置的 `env` 字段设置 `HW_ACCESS_KEY`/`HW_SECRET_KEY`。
|
|
139
118
|
|
|
140
119
|
### 安装 KooCLI
|
|
141
120
|
|
|
@@ -171,6 +150,7 @@ npx --yes huaweicloud-devkit update --target all
|
|
|
171
150
|
- **安全优先执行** — 所有写操作需用户明确批准;凭证和密钥自动脱敏
|
|
172
151
|
- **执行前风险检查** — 公网暴露、凭证泄露、破坏性操作在执行前即被拦截
|
|
173
152
|
- **区域感知** — 自动发现可用区域,创建资源前检查服务可用性
|
|
153
|
+
- **沙箱(DevStation)** — 临时云端运行环境,部署 Web 应用并即刻获得公网预览地址
|
|
174
154
|
|
|
175
155
|
## 支持的服务
|
|
176
156
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
|
-
"version": "1.0.2-next.
|
|
3
|
+
"version": "1.0.2-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": [
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
|
-
"url": "https://github.com/huaweicloud/
|
|
18
|
+
"url": "https://github.com/huaweicloud/huaweicloud-devkit.git"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"test": "node --test",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"postinstall": "node -e \"console.log('\\nHuaweiCloud DevKit installed. Run: npx huaweicloud-devkit install\\n')\""
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
+
"better-sqlite3": "^13.0.3",
|
|
32
33
|
"undici": "^8.10.0"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
@@ -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.0.2-next.
|
|
20
|
+
"version": "1.0.2-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-core",
|
|
3
|
-
"version": "1.0.2-next.
|
|
3
|
+
"version": "1.0.2-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",
|
|
@@ -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.0.2-next.
|
|
20
|
+
"version": "1.0.2-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-core",
|
|
3
|
-
"version": "1.0.2-next.
|
|
3
|
+
"version": "1.0.2-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",
|
|
@@ -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,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "huaweicloud-devkit",
|
|
3
|
+
"displayName": "HuaweiCloud DevKit",
|
|
4
|
+
"version": "1.0.2-next.16",
|
|
5
|
+
"family": "bundle-plugin",
|
|
6
|
+
"bundleFormat": "codex",
|
|
7
|
+
"description": "Guide coding agents to use Huawei Cloud safely — KooCLI, APIs, SDKs, 28 MCP tools, skills, and safety guardrails.",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "HuaweiCloud Mate",
|
|
10
|
+
"url": "https://github.com/huaweicloud"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/huaweicloud/huaweicloud-devkit",
|
|
13
|
+
"repository": "https://github.com/huaweicloud/huaweicloud-devkit",
|
|
14
|
+
"license": "Apache-2.0",
|
|
15
|
+
"keywords": ["huaweicloud", "huawei-cloud", "koocli", "hcloud", "agent", "mcp", "api", "sdk", "skills", "cloud"],
|
|
16
|
+
"category": "Cloud",
|
|
17
|
+
"capabilities": ["Read", "Interactive"],
|
|
18
|
+
"brandColor": "#C7000B"
|
|
19
|
+
}
|
|
@@ -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`
|
|
@@ -100,6 +100,7 @@ When the intent is to **deploy, host, or preview a web app or static website** (
|
|
|
100
100
|
|
|
101
101
|
- Use huaweicloud_search_docs to discover skills covering a topic
|
|
102
102
|
- Use huaweicloud_retrieve_skill to load a full SKILL.md after routing
|
|
103
|
+
- Use huaweicloud_auth_init to switch credentials at runtime when different accounts co-exist in the same session
|
|
103
104
|
- If no built-in skill matches, consult huaweicloud-capability-discovery for external marketplace options
|
|
104
105
|
- Use huaweicloud_list_regions before creating regional resources
|
|
105
106
|
- Use huaweicloud_get_regional_availability when unsure about service-region pairs
|
|
@@ -1,7 +1,8 @@
|
|
|
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
7
|
export const SUPPORTED_AGENT_TARGETS = [
|
|
7
8
|
'opencode',
|
|
@@ -113,12 +114,31 @@ function officeaceCapabilitiesDir() {
|
|
|
113
114
|
return dotDir;
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
function officeaceSqlitePath() {
|
|
118
|
+
const capDir = officeaceCapabilitiesDir();
|
|
119
|
+
return join(resolve(capDir, '..'), 'data', 'mcp-connectors.sqlite');
|
|
120
|
+
}
|
|
121
|
+
|
|
116
122
|
function officeaceRegistered() {
|
|
123
|
+
let hasMcp = false;
|
|
124
|
+
const dbPath = officeaceSqlitePath();
|
|
125
|
+
if (existsSync(dbPath)) {
|
|
126
|
+
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
|
127
|
+
if (nodeMajor >= 22) {
|
|
128
|
+
try {
|
|
129
|
+
const { DatabaseSync } = createRequire(import.meta.url)('node:sqlite');
|
|
130
|
+
const db = new DatabaseSync(dbPath, { readonly: true });
|
|
131
|
+
const row = db.prepare("SELECT enabled FROM mcp_connectors WHERE name = 'huaweicloud-devkit'").get();
|
|
132
|
+
db.close();
|
|
133
|
+
hasMcp = Boolean(row?.enabled);
|
|
134
|
+
} catch {}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
117
138
|
const capFile = join(officeaceCapabilitiesDir(), 'capabilities.json');
|
|
118
139
|
const cfg = readJsonSafe(capFile);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const hasSkills = cfg.capabilities.some((c) => c.id === 'huaweicloud-core' && c.type === 'skill');
|
|
140
|
+
const hasSkills = cfg?.capabilities?.some((c) => c.id === 'huaweicloud-core' && c.type === 'skill') ?? false;
|
|
141
|
+
|
|
122
142
|
return hasMcp || hasSkills;
|
|
123
143
|
}
|
|
124
144
|
|
|
@@ -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
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
3
2
|
import { stdin, stdout } from 'node:process';
|
|
4
|
-
import { rmSync, existsSync } from 'node:fs';
|
|
5
|
-
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import { rmSync, existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { resolve, dirname, join } from 'node:path';
|
|
6
5
|
import { fileURLToPath } from 'node:url';
|
|
7
6
|
import { TOOL_DEFINITIONS, callTool } from './tools.mjs';
|
|
8
7
|
|
|
@@ -27,6 +26,14 @@ try {
|
|
|
27
26
|
if (existsSync(marker)) rmSync(marker, { force: true });
|
|
28
27
|
} catch {}
|
|
29
28
|
|
|
29
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
30
|
+
const pluginRoot = resolve(__dirname, '..');
|
|
31
|
+
const packageRoot = resolve(pluginRoot, '..', '..');
|
|
32
|
+
let pkgVersion = '0.0.0';
|
|
33
|
+
try {
|
|
34
|
+
pkgVersion = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8')).version;
|
|
35
|
+
} catch {}
|
|
36
|
+
|
|
30
37
|
let buffer = Buffer.alloc(0);
|
|
31
38
|
let useContentLengthFraming = true;
|
|
32
39
|
|
|
@@ -104,7 +111,7 @@ async function dispatch(method, params) {
|
|
|
104
111
|
},
|
|
105
112
|
serverInfo: {
|
|
106
113
|
name: 'huaweicloud-devkit',
|
|
107
|
-
version:
|
|
114
|
+
version: pkgVersion,
|
|
108
115
|
},
|
|
109
116
|
};
|
|
110
117
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
-
import {
|
|
2
|
+
import { resolveCredentialsWithRuntime } from '../auth/credentials.mjs';
|
|
3
3
|
import { getProxyDispatcher } from '../proxy/proxy-agent.mjs';
|
|
4
4
|
|
|
5
5
|
const BASE_URL = process.env.HWLINK_ENDPOINT || 'https://devstation.myhuaweicloud.com';
|
|
@@ -77,7 +77,7 @@ function signRequest(method, path, query, body, ak, sk, securitytoken) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export function getCredentials() {
|
|
80
|
-
const credentials =
|
|
80
|
+
const credentials = resolveCredentialsWithRuntime();
|
|
81
81
|
return { ak: credentials.ak, sk: credentials.sk, securitytoken: credentials.securityToken };
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -13,6 +13,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
13
13
|
import { homedir, platform } from 'node:os';
|
|
14
14
|
import { createInterface } from 'node:readline';
|
|
15
15
|
import { spawnSync } from 'node:child_process';
|
|
16
|
+
import { randomUUID } from 'node:crypto';
|
|
17
|
+
import Database from 'better-sqlite3';
|
|
16
18
|
import { getAuthStatus, syncAuth } from './auth/service.mjs';
|
|
17
19
|
import { SUPPORTED_AGENT_TARGETS } from './auth/agent-registration.mjs';
|
|
18
20
|
import {
|
|
@@ -150,69 +152,128 @@ function officeacePluginsDir() {
|
|
|
150
152
|
return join(officeaceCapabilitiesDir(), 'huaweicloud-plugins');
|
|
151
153
|
}
|
|
152
154
|
|
|
153
|
-
function
|
|
154
|
-
const
|
|
155
|
-
|
|
155
|
+
function officeaceSqlitePath() {
|
|
156
|
+
const capDir = officeaceCapabilitiesDir();
|
|
157
|
+
return join(resolve(capDir, '..'), 'data', 'mcp-connectors.sqlite');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function openOfficeaceDb() {
|
|
161
|
+
return new Database(officeaceSqlitePath());
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function officeaceGetOwnerUserId() {
|
|
165
|
+
if (!existsSync(officeaceSqlitePath())) return null;
|
|
156
166
|
try {
|
|
157
|
-
|
|
167
|
+
const db = openOfficeaceDb();
|
|
168
|
+
const row = db.prepare('SELECT owner_user_id FROM mcp_connectors WHERE owner_user_id IS NOT NULL LIMIT 1').get();
|
|
169
|
+
db.close();
|
|
170
|
+
return row?.owner_user_id || null;
|
|
158
171
|
} catch {
|
|
159
|
-
return
|
|
172
|
+
return null;
|
|
160
173
|
}
|
|
161
174
|
}
|
|
162
175
|
|
|
163
|
-
function
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
176
|
+
function ensureOfficeaceMcpInSqlite() {
|
|
177
|
+
const dbPath = officeaceSqlitePath();
|
|
178
|
+
if (!existsSync(dbPath)) {
|
|
179
|
+
console.log(` \x1b[31mOfficeAce database not found: ${dbPath}\x1b[0m`);
|
|
180
|
+
console.log(` \x1b[33mPlease ensure OfficeAce is installed and has been launched at least once.\x1b[0m`);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
167
183
|
|
|
168
|
-
function ensureOfficeaceCapabilities() {
|
|
169
|
-
const capFile = officeaceCapabilitiesFile();
|
|
170
184
|
const mcpPath = join(officeacePluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
171
|
-
const env = { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' };
|
|
185
|
+
const env = [{ key: 'HUAWEICLOUD_AGENT_TOOLKIT_MODE', value: 'local', sensitive: false }];
|
|
172
186
|
const hcloudBin = findHcloudBin();
|
|
173
|
-
if (hcloudBin) env.HCLOUD_BIN
|
|
187
|
+
if (hcloudBin) env.push({ key: 'HCLOUD_BIN', value: hcloudBin.replace(/\\/g, '/'), sensitive: false });
|
|
174
188
|
|
|
175
|
-
const
|
|
176
|
-
let
|
|
189
|
+
const now = Date.now();
|
|
190
|
+
let db;
|
|
191
|
+
try {
|
|
192
|
+
db = openOfficeaceDb();
|
|
177
193
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
Array.isArray(existing.mcpServer?.args) &&
|
|
196
|
-
existing.mcpServer.args[0] === mcpPath
|
|
197
|
-
) {
|
|
198
|
-
console.log(` MCP config unchanged: ${capFile}`);
|
|
194
|
+
const existing = db
|
|
195
|
+
.prepare("SELECT id, command, args_json FROM mcp_connectors WHERE name = 'huaweicloud-devkit'")
|
|
196
|
+
.get();
|
|
197
|
+
|
|
198
|
+
const argsJson = JSON.stringify([mcpPath]);
|
|
199
|
+
const envJson = JSON.stringify(env);
|
|
200
|
+
|
|
201
|
+
if (existing) {
|
|
202
|
+
if (existing.command === 'node' && existing.args_json === argsJson) {
|
|
203
|
+
console.log(` MCP config unchanged: ${dbPath}`);
|
|
204
|
+
db.close();
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
db.prepare(
|
|
208
|
+
'UPDATE mcp_connectors SET command = ?, args_json = ?, env_json = ?, updated_at = ?, status = ?, enabled = 1 WHERE id = ?',
|
|
209
|
+
).run('node', argsJson, envJson, now, 'disconnected', existing.id);
|
|
210
|
+
console.log(` MCP config updated: ${dbPath}`);
|
|
199
211
|
} else {
|
|
200
|
-
|
|
201
|
-
|
|
212
|
+
const ownerUserId = officeaceGetOwnerUserId();
|
|
213
|
+
if (!ownerUserId) {
|
|
214
|
+
console.log(` \x1b[31mCannot determine owner_user_id from database\x1b[0m`);
|
|
215
|
+
db.close();
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
db.prepare(
|
|
219
|
+
`INSERT INTO mcp_connectors (id, owner_user_id, type, name, normalized_name, transport, timeout_ms, command, args_json, env_json, enabled, status, created_at, updated_at, version, seeded)
|
|
220
|
+
VALUES (?, ?, 'custom', 'huaweicloud-devkit', 'huaweicloud-devkit', 'stdio', 60000, 'node', ?, ?, 1, 'disconnected', ?, ?, 1, 0)`,
|
|
221
|
+
).run(randomUUID(), ownerUserId, argsJson, envJson, now, now);
|
|
222
|
+
console.log(` MCP config created: ${dbPath}`);
|
|
202
223
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
224
|
+
db.close();
|
|
225
|
+
return true;
|
|
226
|
+
} catch (err) {
|
|
227
|
+
if (db) {
|
|
228
|
+
try {
|
|
229
|
+
db.close();
|
|
230
|
+
} catch {}
|
|
231
|
+
}
|
|
232
|
+
console.log(` \x1b[31mFailed to write MCP config: ${err.message}\x1b[0m`);
|
|
233
|
+
return false;
|
|
206
234
|
}
|
|
235
|
+
}
|
|
207
236
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
237
|
+
function removeOfficeaceMcpFromSqlite() {
|
|
238
|
+
const dbPath = officeaceSqlitePath();
|
|
239
|
+
if (!existsSync(dbPath)) return;
|
|
240
|
+
let db;
|
|
241
|
+
try {
|
|
242
|
+
db = openOfficeaceDb();
|
|
243
|
+
db.prepare(
|
|
244
|
+
"DELETE FROM mcp_connector_tools WHERE connector_id IN (SELECT id FROM mcp_connectors WHERE name = 'huaweicloud-devkit')",
|
|
245
|
+
).run();
|
|
246
|
+
const result2 = db.prepare("DELETE FROM mcp_connectors WHERE name = 'huaweicloud-devkit'").run();
|
|
247
|
+
if (result2.changes > 0) {
|
|
248
|
+
console.log(` MCP config removed: ${dbPath}`);
|
|
249
|
+
}
|
|
250
|
+
db.close();
|
|
251
|
+
} catch (err) {
|
|
252
|
+
if (db) {
|
|
253
|
+
try {
|
|
254
|
+
db.close();
|
|
255
|
+
} catch {}
|
|
256
|
+
}
|
|
257
|
+
console.log(` \x1b[31mFailed to remove MCP config: ${err.message}\x1b[0m`);
|
|
211
258
|
}
|
|
212
|
-
return changed;
|
|
213
259
|
}
|
|
214
260
|
|
|
215
|
-
function
|
|
261
|
+
function readCapabilitiesJson() {
|
|
262
|
+
const capFile = officeaceCapabilitiesFile();
|
|
263
|
+
if (!existsSync(capFile)) return { capabilities: [] };
|
|
264
|
+
try {
|
|
265
|
+
return JSON.parse(readFileSync(capFile, 'utf8'));
|
|
266
|
+
} catch {
|
|
267
|
+
return { capabilities: [] };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function writeCapabilitiesJson(config) {
|
|
272
|
+
mkdirSync(officeaceCapabilitiesDir(), { recursive: true });
|
|
273
|
+
writeFileSync(officeaceCapabilitiesFile(), JSON.stringify(config, null, 2));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function removeOfficeaceSkillCapabilities() {
|
|
216
277
|
const capFile = officeaceCapabilitiesFile();
|
|
217
278
|
if (!existsSync(capFile)) return;
|
|
218
279
|
let config;
|
|
@@ -226,14 +287,13 @@ function removeOfficeaceCapabilities() {
|
|
|
226
287
|
config.capabilities = config.capabilities.filter(
|
|
227
288
|
(c) =>
|
|
228
289
|
!(
|
|
229
|
-
(c.id === 'huaweicloud-devkit' && c.type === 'mcp') ||
|
|
230
290
|
(c.id === 'huaweicloud-core' && c.type === 'skill') ||
|
|
231
291
|
(c.source === 'custom' && c.id && c.id.startsWith('huawei'))
|
|
232
292
|
),
|
|
233
293
|
);
|
|
234
294
|
if (config.capabilities.length !== origLen) {
|
|
235
295
|
writeCapabilitiesJson(config);
|
|
236
|
-
console.log(`
|
|
296
|
+
console.log(` Skill capabilities cleaned: ${capFile}`);
|
|
237
297
|
}
|
|
238
298
|
}
|
|
239
299
|
|
|
@@ -317,7 +377,7 @@ function printSandboxWarning(reason) {
|
|
|
317
377
|
console.log(`\x1b[31m 请任选其一继续:\x1b[0m`);
|
|
318
378
|
console.log(`\x1b[31m A. 在码道外的终端安装并使用 KooCLI (推荐):`);
|
|
319
379
|
console.log(`\x1b[31m https://support.huaweicloud.com/qs-hcli/hcli_02_003.html`);
|
|
320
|
-
console.log(`\x1b[31m B. 在码道设置中关闭沙箱模式后重试 (设置 →
|
|
380
|
+
console.log(`\x1b[31m B. 在码道设置中关闭沙箱模式后重试 (设置 → 对话流 → 智能体 终端命令运行模式 → 自动运行)`);
|
|
321
381
|
console.log(`\x1b[31m 关闭沙箱后重新运行: npx huaweicloud-devkit install-hcloud\x1b[0m`);
|
|
322
382
|
}
|
|
323
383
|
|
|
@@ -1367,7 +1427,7 @@ async function installOfficeAce() {
|
|
|
1367
1427
|
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
1368
1428
|
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
1369
1429
|
|
|
1370
|
-
|
|
1430
|
+
ensureOfficeaceMcpInSqlite();
|
|
1371
1431
|
registerOfficeaceSkillEntries();
|
|
1372
1432
|
}
|
|
1373
1433
|
|
|
@@ -1384,7 +1444,7 @@ async function updateOfficeAce() {
|
|
|
1384
1444
|
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
1385
1445
|
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
1386
1446
|
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
1387
|
-
|
|
1447
|
+
ensureOfficeaceMcpInSqlite();
|
|
1388
1448
|
registerOfficeaceSkillEntries();
|
|
1389
1449
|
mkdirSync(pluginDest, { recursive: true });
|
|
1390
1450
|
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
@@ -1412,7 +1472,8 @@ function uninstallOfficeAce() {
|
|
|
1412
1472
|
if (removeIfExists(officeacePluginsDir())) {
|
|
1413
1473
|
console.log(' Removed MCP server and safety policy');
|
|
1414
1474
|
}
|
|
1415
|
-
|
|
1475
|
+
removeOfficeaceSkillCapabilities();
|
|
1476
|
+
removeOfficeaceMcpFromSqlite();
|
|
1416
1477
|
}
|
|
1417
1478
|
|
|
1418
1479
|
function officeaceStatus() {
|
|
@@ -1433,15 +1494,23 @@ function officeaceStatus() {
|
|
|
1433
1494
|
console.log(
|
|
1434
1495
|
` Skills: ${skillCount > 0 ? `\x1b[32m${skillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1435
1496
|
);
|
|
1436
|
-
const
|
|
1437
|
-
if (existsSync(
|
|
1497
|
+
const dbPath = officeaceSqlitePath();
|
|
1498
|
+
if (existsSync(dbPath)) {
|
|
1438
1499
|
try {
|
|
1439
|
-
const
|
|
1440
|
-
const
|
|
1441
|
-
|
|
1500
|
+
const db = openOfficeaceDb();
|
|
1501
|
+
const row = db.prepare("SELECT enabled, status FROM mcp_connectors WHERE name = 'huaweicloud-devkit'").get();
|
|
1502
|
+
db.close();
|
|
1503
|
+
if (row) {
|
|
1504
|
+
const statusIcon = row.status === 'connected' ? '\x1b[32m' : '\x1b[33m';
|
|
1505
|
+
console.log(` MCP config: ${statusIcon}${row.status}\x1b[0m (enabled: ${row.enabled ? 'yes' : 'no'})`);
|
|
1506
|
+
} else {
|
|
1507
|
+
console.log(` MCP config: \x1b[31mNot configured\x1b[0m`);
|
|
1508
|
+
}
|
|
1442
1509
|
} catch {
|
|
1443
1510
|
console.log(` MCP config: \x1b[31mInvalid\x1b[0m`);
|
|
1444
1511
|
}
|
|
1512
|
+
} else {
|
|
1513
|
+
console.log(` MCP config: \x1b[31mDatabase not found\x1b[0m`);
|
|
1445
1514
|
}
|
|
1446
1515
|
}
|
|
1447
1516
|
|
|
@@ -16,7 +16,12 @@ import {
|
|
|
16
16
|
} from './sandbox/session-manager.mjs';
|
|
17
17
|
import { hdkitCheckUser, hdkitSignAgreement, hdkitConnect, hdkitCredentials } from './sandbox/hdkitservice-api.mjs';
|
|
18
18
|
import { getAuthStatus, syncAuth } from './auth/service.mjs';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
readGlobalCredentials,
|
|
21
|
+
writeObsConfig as writeObsConfigFile,
|
|
22
|
+
setRuntimeCredentials,
|
|
23
|
+
clearRuntimeCredentials,
|
|
24
|
+
} from './auth/credentials.mjs';
|
|
20
25
|
|
|
21
26
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
27
|
const SKILLS_ROOT_DEV = join(__dirname, '..', 'skills');
|
|
@@ -404,6 +409,20 @@ export const TOOL_DEFINITIONS = [
|
|
|
404
409
|
},
|
|
405
410
|
},
|
|
406
411
|
},
|
|
412
|
+
{
|
|
413
|
+
name: 'huaweicloud_auth_init',
|
|
414
|
+
description:
|
|
415
|
+
'Set or clear runtime Huawei Cloud credentials (AK/SK) for this MCP session. Runtime credentials take highest priority over environment variables and config files for all subsequent API calls. Use when switching accounts within the same Agent session — call with AK/SK to switch, or with clear=true to fall back to env/file credentials.',
|
|
416
|
+
inputSchema: {
|
|
417
|
+
type: 'object',
|
|
418
|
+
properties: {
|
|
419
|
+
ak: { type: 'string', description: 'Huawei Cloud Access Key (required unless clear=true)' },
|
|
420
|
+
sk: { type: 'string', description: 'Huawei Cloud Secret Key (required unless clear=true)' },
|
|
421
|
+
region: { type: 'string', description: 'Default region (optional)' },
|
|
422
|
+
clear: { type: 'boolean', description: 'Set to true to clear runtime credentials and revert to env/file' },
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
},
|
|
407
426
|
{
|
|
408
427
|
name: 'huaweicloud_sandbox_exec_with_session',
|
|
409
428
|
description:
|
|
@@ -570,6 +589,16 @@ export async function callTool(name, args = {}) {
|
|
|
570
589
|
return getAuthStatus(args.target || 'all');
|
|
571
590
|
case 'huaweicloud_auth_sync':
|
|
572
591
|
return syncAuth(args.target || 'all');
|
|
592
|
+
case 'huaweicloud_auth_init':
|
|
593
|
+
if (args.clear) {
|
|
594
|
+
clearRuntimeCredentials();
|
|
595
|
+
return { status: 'cleared', message: 'Runtime credentials cleared. Fallback to env/file.' };
|
|
596
|
+
}
|
|
597
|
+
if (!args.ak || !args.sk) {
|
|
598
|
+
throw new Error('ak and sk are required. Set clear=true to clear runtime credentials.');
|
|
599
|
+
}
|
|
600
|
+
setRuntimeCredentials(args.ak, args.sk, undefined, args.region);
|
|
601
|
+
return { status: 'ok', message: 'Runtime credentials set for this MCP session.' };
|
|
573
602
|
case 'huaweicloud_sandbox_exec_with_session': {
|
|
574
603
|
const sandboxWsId2 = args.workspace_id || DEFAULT_WORKSPACE_ID;
|
|
575
604
|
const sandboxUser2 = args.username || 'root';
|
|
@@ -796,7 +825,7 @@ async function setupObsConfigFromHcloud(profile) {
|
|
|
796
825
|
const SERVICE_EXAMPLES = {
|
|
797
826
|
ECS: { list: 'ECS ListServersDetails', create: 'ECS CreateServers', show: 'IMS GlanceShowImage' },
|
|
798
827
|
VPC: { list: 'VPC ListVpcs', create: 'VPC CreateVpc', show: 'VPC ShowVpc' },
|
|
799
|
-
|
|
828
|
+
FUNCTIONGRAPH: {
|
|
800
829
|
list: 'FunctionGraph ListFunctions',
|
|
801
830
|
create: 'FunctionGraph CreateFunction',
|
|
802
831
|
show: 'FunctionGraph ShowFunctionConfig',
|
|
@@ -805,7 +834,7 @@ const SERVICE_EXAMPLES = {
|
|
|
805
834
|
OBS: { list: 'OBS ls', create: 'OBS mb obs://<bucket>', show: 'OBS stat obs://<bucket>/<key>' },
|
|
806
835
|
RDS: { list: 'RDS ListInstances', create: 'RDS CreateInstance', show: 'RDS ShowInstance' },
|
|
807
836
|
CES: { list: 'CES ListAlarms', create: 'CES CreateAlarm', show: 'CES ListMetrics' },
|
|
808
|
-
|
|
837
|
+
GAUSSDB: { list: 'GaussDB ListInstances', create: 'GaussDB CreateInstance', show: 'GaussDB ShowInstance' },
|
|
809
838
|
DDS: { list: 'DDS ListInstances', create: 'DDS CreateInstance', show: 'DDS ShowInstance' },
|
|
810
839
|
DCS: { list: 'DCS ListInstances', create: 'DCS CreateInstance', show: 'DCS ShowInstance' },
|
|
811
840
|
};
|