huaweicloud-devkit 1.1.3-next.0 → 1.1.3-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +1 -1
- package/README.md +42 -9
- package/README.zh-CN.md +37 -10
- package/integrations/opencode/commands/huaweicloud-doctor.md +1 -1
- package/integrations/opencode/skills/huaweicloud-core/SKILL.md +1 -1
- package/package.json +4 -3
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -4
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +4 -4
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -4
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +4 -4
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -4
- package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +1 -0
- package/plugins/huaweicloud-core/skills/huawei-obs/references/static-website.md +2 -0
- package/plugins/huaweicloud-core/src/auth/reconcile.mjs +20 -5
- package/plugins/huaweicloud-core/src/setup-cli.mjs +250 -16
- package/plugins/huaweicloud-core/src/tools.mjs +25 -1
- package/plugins/huaweicloud-core/src/update-check.mjs +3 -1
package/README.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
[](https://github.com/huaweicloud/huaweicloud-devkit/discussions)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://github.com/huaweicloud/huaweicloud-devkit/actions/workflows/ci.yml)
|
|
6
|
-
[](https://www.npmjs.com/package/huaweicloud-devkit)
|
|
7
|
+
[](https://github.com/huaweicloud/huaweicloud-devkit)
|
|
7
8
|
|
|
8
9
|
**[中文](README.zh-CN.md) | English**
|
|
9
10
|
|
|
@@ -31,7 +32,18 @@ Supports OpenCode, Codex, CodeArts Agent, WorkBuddy, DeepSeek Harness (DSH), Off
|
|
|
31
32
|
|
|
32
33
|
## Quick Start
|
|
33
34
|
|
|
34
|
-
> If `--target` is omitted, the installer auto-detects agents on your machine
|
|
35
|
+
> If `--target` is omitted, the installer auto-detects agents on your machine:
|
|
36
|
+
>
|
|
37
|
+
> - **None detected**: interactive terminals ask what you want (install to one
|
|
38
|
+
> explicit target / install to all / wire up a generic MCP agent);
|
|
39
|
+
> non-interactive shells error out with the supported target list.
|
|
40
|
+
> - **One detected**: installs directly to it.
|
|
41
|
+
> - **Multiple detected**: interactive terminals show a multi-select chooser;
|
|
42
|
+
> non-interactive shells error and point at `--target <agent>` / `--target all`.
|
|
43
|
+
> For a one-shot full setup, run `npx --yes huaweicloud-devkit install --target all`
|
|
44
|
+
> (Codex is skipped when its CLI is missing).
|
|
45
|
+
|
|
46
|
+
The commands below are global (they act on every agent):
|
|
35
47
|
|
|
36
48
|
```bash
|
|
37
49
|
npx --yes huaweicloud-devkit version # print CLI version and installed plugin versions per agent
|
|
@@ -51,7 +63,7 @@ npx --yes huaweicloud-devkit doctor --target opencode
|
|
|
51
63
|
npx --yes huaweicloud-devkit status --target opencode
|
|
52
64
|
npx --yes huaweicloud-devkit update --target opencode
|
|
53
65
|
npx --yes huaweicloud-devkit uninstall --target opencode
|
|
54
|
-
rm -rf ~/.npm/_npx/ # Linux/macOS
|
|
66
|
+
rm -rf ~/.npm/_npx/ # Linux/macOS; Windows: rmdir /s /q %LOCALAPPDATA%\npm-cache\_npx
|
|
55
67
|
```
|
|
56
68
|
|
|
57
69
|
### Codex
|
|
@@ -211,7 +223,7 @@ npx --yes huaweicloud-devkit install --target openclaw
|
|
|
211
223
|
npx --yes huaweicloud-devkit status --target openclaw
|
|
212
224
|
npx --yes huaweicloud-devkit update --target openclaw
|
|
213
225
|
npx --yes huaweicloud-devkit uninstall --target openclaw
|
|
214
|
-
rm -rf ~/.npm/_npx/ # Linux/macOS
|
|
226
|
+
rm -rf ~/.npm/_npx/ # Linux/macOS; Windows: rmdir /s /q %LOCALAPPDATA%\npm-cache\_npx
|
|
215
227
|
```
|
|
216
228
|
|
|
217
229
|
### AtomCode
|
|
@@ -246,7 +258,7 @@ Any agent that supports MCP can use the standard config:
|
|
|
246
258
|
|
|
247
259
|
No installation required — `npx` handles everything.
|
|
248
260
|
|
|
249
|
-
>
|
|
261
|
+
> For manual MCP registrations like this, do not put credentials in the config. `HW_ACCESS_KEY`/`HW_SECRET_KEY` are reserved for **platform/CI-injected** accounts (e.g. a DevSpace-managed default account) — configure your own account via `npx huaweicloud-devkit auth init` (the single entry point), and switch accounts at runtime with the `huaweicloud_auth_init` / `huaweicloud_auth_switch` MCP tools. See `plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md` for the full credential-resolution priority.
|
|
250
262
|
|
|
251
263
|
#### Connecting over Remote (HTTP)
|
|
252
264
|
|
|
@@ -256,7 +268,7 @@ If your agent supports `type: "remote"` (Streamable HTTP) instead of stdio, star
|
|
|
256
268
|
npx --yes huaweicloud-devkit-mcp --transport remote
|
|
257
269
|
```
|
|
258
270
|
|
|
259
|
-
It listens on `127.0.0.1:9528` by default
|
|
271
|
+
It listens on `127.0.0.1:9528` by default. Then connect with a remote config (opencode example):
|
|
260
272
|
|
|
261
273
|
```jsonc
|
|
262
274
|
{
|
|
@@ -284,7 +296,24 @@ npx --yes huaweicloud-devkit install-hcloud
|
|
|
284
296
|
npx --yes huaweicloud-devkit auth init
|
|
285
297
|
```
|
|
286
298
|
|
|
287
|
-
Synchronizes AK/SK to KooCLI, OBS, and sandbox APIs in one step.
|
|
299
|
+
Synchronizes AK/SK to KooCLI, OBS, and sandbox APIs in one step — this is the **single entry point**. Never hard-code AK/SK into agent or shell config.
|
|
300
|
+
|
|
301
|
+
**Account switching at runtime** (within an agent session): use the MCP tools `huaweicloud_auth_init` (in-memory, highest priority) or `huaweicloud_auth_switch` (actions: `temporary` / `persist` / `clear`). In sandbox/DevSpace environments where a default account is injected via `HW_ACCESS_KEY`/`HW_SECRET_KEY`, a plain `auth init` will not override it — use `huaweicloud_auth_switch action=persist` to make the session account win.
|
|
302
|
+
|
|
303
|
+
**Credential resolution priority** (highest first):
|
|
304
|
+
|
|
305
|
+
| # | Source | Set by |
|
|
306
|
+
| --- | ------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
307
|
+
| 1 | Runtime (session) credentials | `huaweicloud_auth_init` / `huaweicloud_auth_switch action=temporary` |
|
|
308
|
+
| 2 | S1 global file with `configuredBySession: true` | `huaweicloud_auth_switch action=persist` |
|
|
309
|
+
| 3 | Environment variables (`HW_ACCESS_KEY`/`HW_SECRET_KEY`) | platform/DevSpace-injected default account |
|
|
310
|
+
| 4 | CodeArts / CodeArts Work | `.codeartsdoer/mcp/mcp_settings.json` / `.codeartswork/mcp/mcp_settings.json` |
|
|
311
|
+
| 5 | S1 global file (no session flag) | `auth init` |
|
|
312
|
+
| 6 | KooCLI profile | `~/.hcloud/config.json` (KooCLI commands only) |
|
|
313
|
+
|
|
314
|
+
> **Security**: never put your own AK/SK into the MCP config `env` field — they'd be stored in plaintext and could leak if the config file is committed to git. `env` is for platform/CI injection only.
|
|
315
|
+
|
|
316
|
+
Full details: `plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md`.
|
|
288
317
|
|
|
289
318
|
### Install All Agents
|
|
290
319
|
|
|
@@ -295,7 +324,7 @@ npx --yes huaweicloud-devkit install --target all
|
|
|
295
324
|
### Update All Agents
|
|
296
325
|
|
|
297
326
|
```bash
|
|
298
|
-
npx huaweicloud-devkit version
|
|
327
|
+
npx --yes huaweicloud-devkit@latest version
|
|
299
328
|
npx --yes huaweicloud-devkit@latest update --target all
|
|
300
329
|
```
|
|
301
330
|
|
|
@@ -305,7 +334,7 @@ locally cached older one.
|
|
|
305
334
|
|
|
306
335
|
## What It Does
|
|
307
336
|
|
|
308
|
-
- **Guided cloud operations** — agents get step-by-step guidance for 20+ Huawei Cloud services (ECS, OBS, VPC, RDS, GaussDB, FunctionGraph, APIG, CCE, and more)
|
|
337
|
+
- **Guided cloud operations** — agents get step-by-step guidance for 20+ commonly used Huawei Cloud services (ECS, OBS, VPC, RDS, GaussDB, FunctionGraph, APIG, CCE, and more)
|
|
309
338
|
- **Safety-first execution** — all write operations require explicit user approval; credentials and secrets are automatically redacted from output
|
|
310
339
|
- **Pre-execution risk checks** — public exposure, credential leaks, and destructive operations are caught before they run
|
|
311
340
|
- **Regional awareness** — auto-discovers available regions and checks service availability before creating resources
|
|
@@ -315,6 +344,10 @@ locally cached older one.
|
|
|
315
344
|
|
|
316
345
|
ECS, OBS, VPC, IAM, RDS, GaussDB, FunctionGraph, APIG, CCE, SMN/DMS, ModelArts, Cloud Eye, CTS, DEW, Billing, CBR, WAF/AAD, DDS/DCS, Deployment, and Getting Started guides.
|
|
317
346
|
|
|
347
|
+
> Above is the pre-wired guidance list; the remaining 200+ Huawei Cloud services
|
|
348
|
+
> are still reachable via KooCLI / API / SDK routing (see capability-discovery
|
|
349
|
+
> and cli-and-auth meta-skills).
|
|
350
|
+
|
|
318
351
|
## Documentation
|
|
319
352
|
|
|
320
353
|
- [Architecture](docs/architecture.md)
|
package/README.zh-CN.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# HuaweiCloud DevKit
|
|
2
2
|
|
|
3
|
-
[](https://github.com/huaweicloud/huaweicloud-devkit/discussions)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://github.com/huaweicloud/huaweicloud-devkit/actions/workflows/ci.yml)
|
|
6
|
-
[](https://www.npmjs.com/package/huaweicloud-devkit)
|
|
7
|
+
[](https://github.com/huaweicloud/huaweicloud-devkit)
|
|
7
8
|
|
|
8
9
|
**中文 | [English](README.md)**
|
|
9
10
|
|
|
@@ -31,7 +32,14 @@
|
|
|
31
32
|
|
|
32
33
|
## 快速开始
|
|
33
34
|
|
|
34
|
-
> 省略 `--target` 时,安装器会自动检测机器上的 agent
|
|
35
|
+
> 省略 `--target` 时,安装器会自动检测机器上的 agent:
|
|
36
|
+
>
|
|
37
|
+
> - **未检测到**:交互终端会询问你如何继续(指定 target 安装 / 全部安装 / 接入通用 MCP agent);非交互终端报错并列出支持列表。
|
|
38
|
+
> - **检测到单个**:直接安装到该 agent。
|
|
39
|
+
> - **检测到多个**:交互终端弹出多选;非交互终端报错并提示 `--target <agent>` 或 `--target all`。
|
|
40
|
+
> 需要一步全量安装时执行 `npx --yes huaweicloud-devkit install --target all`(Codex 缺少 CLI 时跳过)。
|
|
41
|
+
|
|
42
|
+
以下为全局命令(一次性作用于所有 agent):
|
|
35
43
|
|
|
36
44
|
```bash
|
|
37
45
|
npx --yes huaweicloud-devkit version # 查看 CLI 版本和各 agent 已安装的插件版本
|
|
@@ -51,7 +59,7 @@ npx --yes huaweicloud-devkit doctor --target opencode
|
|
|
51
59
|
npx --yes huaweicloud-devkit status --target opencode
|
|
52
60
|
npx --yes huaweicloud-devkit update --target opencode
|
|
53
61
|
npx --yes huaweicloud-devkit uninstall --target opencode
|
|
54
|
-
rm -rf ~/.npm/_npx/ # 仅 Linux/macOS;Windows
|
|
62
|
+
rm -rf ~/.npm/_npx/ # 仅 Linux/macOS;Windows:rmdir /s /q %LOCALAPPDATA%\npm-cache\_npx
|
|
55
63
|
```
|
|
56
64
|
|
|
57
65
|
### Codex
|
|
@@ -211,7 +219,7 @@ npx --yes huaweicloud-devkit install --target openclaw
|
|
|
211
219
|
npx --yes huaweicloud-devkit status --target openclaw
|
|
212
220
|
npx --yes huaweicloud-devkit update --target openclaw
|
|
213
221
|
npx --yes huaweicloud-devkit uninstall --target openclaw
|
|
214
|
-
rm -rf ~/.npm/_npx/ # 仅 Linux/macOS;Windows
|
|
222
|
+
rm -rf ~/.npm/_npx/ # 仅 Linux/macOS;Windows:rmdir /s /q %LOCALAPPDATA%\npm-cache\_npx
|
|
215
223
|
```
|
|
216
224
|
|
|
217
225
|
### AtomCode
|
|
@@ -246,7 +254,7 @@ npx --yes huaweicloud-devkit uninstall --target atomcode
|
|
|
246
254
|
|
|
247
255
|
无需预安装 — `npx` 自动处理一切。
|
|
248
256
|
|
|
249
|
-
>
|
|
257
|
+
> 像上面这种手动 MCP 注册方式,请勿在配置里写凭据。`HW_ACCESS_KEY`/`HW_SECRET_KEY` 是保留给**平台/CI 注入**的账号用的(例如 DevSpace 托管的默认账号)——自己的账号统一通过 `npx huaweicloud-devkit auth init` 配置(唯一入口),会话内切换账号用 `huaweicloud_auth_init` / `huaweicloud_auth_switch` MCP 工具。完整凭据解析优先级见 `plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md`。
|
|
250
258
|
|
|
251
259
|
#### 通过 Remote(HTTP)连接
|
|
252
260
|
|
|
@@ -256,7 +264,7 @@ npx --yes huaweicloud-devkit uninstall --target atomcode
|
|
|
256
264
|
npx --yes huaweicloud-devkit-mcp --transport remote
|
|
257
265
|
```
|
|
258
266
|
|
|
259
|
-
默认监听 `127.0.0.1:9528
|
|
267
|
+
默认监听 `127.0.0.1:9528`。随后以远程方式连接(以 opencode 为例):
|
|
260
268
|
|
|
261
269
|
```jsonc
|
|
262
270
|
{
|
|
@@ -284,7 +292,24 @@ npx --yes huaweicloud-devkit install-hcloud
|
|
|
284
292
|
npx --yes huaweicloud-devkit auth init
|
|
285
293
|
```
|
|
286
294
|
|
|
287
|
-
一步同步 AK/SK 到 KooCLI、OBS
|
|
295
|
+
一步同步 AK/SK 到 KooCLI、OBS 和沙箱接口——这是**唯一入口**,切勿把 AK/SK 手写进 Agent 或 shell 配置。
|
|
296
|
+
|
|
297
|
+
**会话内切换账号**:使用 MCP 工具 `huaweicloud_auth_init`(内存态,优先级最高)或 `huaweicloud_auth_switch`(`temporary` / `persist` / `clear`)。在沙箱/DevSpace 环境中若默认账号经 `HW_ACCESS_KEY`/`HW_SECRET_KEY` 注入,单纯 `auth init` 无法覆盖——需 `huaweicloud_auth_switch action=persist` 让会话账号生效。
|
|
298
|
+
|
|
299
|
+
**凭据解析优先级**(从高到低):
|
|
300
|
+
|
|
301
|
+
| # | 来源 | 由谁设置 |
|
|
302
|
+
| --- | --------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
303
|
+
| 1 | 运行时(会话)凭据 | `huaweicloud_auth_init` / `huaweicloud_auth_switch action=temporary` |
|
|
304
|
+
| 2 | 带 `configuredBySession: true` 的 S1 全局文件 | `huaweicloud_auth_switch action=persist` |
|
|
305
|
+
| 3 | 环境变量(`HW_ACCESS_KEY`/`HW_SECRET_KEY`) | 平台/DevSpace 注入的默认账号 |
|
|
306
|
+
| 4 | CodeArts / CodeArts Work | `.codeartsdoer/mcp/mcp_settings.json` / `.codeartswork/mcp/mcp_settings.json` |
|
|
307
|
+
| 5 | S1 全局文件(无会话标记) | `auth init` |
|
|
308
|
+
| 6 | KooCLI profile | `~/.hcloud/config.json`(仅 KooCLI 命令) |
|
|
309
|
+
|
|
310
|
+
> **安全**:切勿把自己账号的 AK/SK 写入 MCP 配置的 `env` 字段——会以明文存储,配置文件提交 git 时即泄密;`env` 仅用于平台/CI 注入。
|
|
311
|
+
|
|
312
|
+
完整说明:`plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md`。
|
|
288
313
|
|
|
289
314
|
### 安装所有 Agent
|
|
290
315
|
|
|
@@ -295,7 +320,7 @@ npx --yes huaweicloud-devkit install --target all
|
|
|
295
320
|
### 更新所有 Agent
|
|
296
321
|
|
|
297
322
|
```bash
|
|
298
|
-
npx huaweicloud-devkit version
|
|
323
|
+
npx --yes huaweicloud-devkit@latest version
|
|
299
324
|
npx --yes huaweicloud-devkit@latest update --target all
|
|
300
325
|
```
|
|
301
326
|
|
|
@@ -303,7 +328,7 @@ npx --yes huaweicloud-devkit@latest update --target all
|
|
|
303
328
|
|
|
304
329
|
## 功能特性
|
|
305
330
|
|
|
306
|
-
- **引导式云操作** — Agent 获得 20+
|
|
331
|
+
- **引导式云操作** — Agent 获得 20+ 常用华为云服务的分步操作指引(ECS、OBS、VPC、RDS、GaussDB、FunctionGraph、APIG、CCE 等)
|
|
307
332
|
- **安全优先执行** — 所有写操作需用户明确批准;凭证和密钥自动脱敏
|
|
308
333
|
- **执行前风险检查** — 公网暴露、凭证泄露、破坏性操作在执行前即被拦截
|
|
309
334
|
- **区域感知** — 自动发现可用区域,创建资源前检查服务可用性
|
|
@@ -313,6 +338,8 @@ npx --yes huaweicloud-devkit@latest update --target all
|
|
|
313
338
|
|
|
314
339
|
ECS、OBS、VPC、IAM、RDS、GaussDB、FunctionGraph、APIG、CCE、SMN/DMS、ModelArts、Cloud Eye、CTS、DEW、Billing、CBR、WAF/AAD、DDS/DCS、Deployment,以及入门指南。
|
|
315
340
|
|
|
341
|
+
> 以上为预置指引的服务列表;其余 200+ 华为云服务仍可通过 KooCLI / API / SDK 路由调用(见 capability-discovery 与 cli-and-auth 元技能)。
|
|
342
|
+
|
|
316
343
|
## 文档
|
|
317
344
|
|
|
318
345
|
- [架构](docs/architecture.md)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: Self-check Huawei Cloud DevKit readiness — hcloud, MCP, skills, auth.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
Use HuaweiCloud
|
|
5
|
+
Use HuaweiCloud DevKit tools and skills to perform a comprehensive readiness check:
|
|
6
6
|
|
|
7
7
|
1. Check if KooCLI `hcloud` is installed: `huaweicloud_check_cli`. If not, guide user to install.
|
|
8
8
|
2. Check if hcloud has active credentials: `hcloud configure list` (redacted).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: huaweicloud-core
|
|
3
|
-
description: OpenCode entry skill for HuaweiCloud
|
|
3
|
+
description: OpenCode entry skill for HuaweiCloud DevKit. Use when a developer asks OpenCode to use Huawei Cloud Skills, KooCLI, APIs, SDKs, future MCP tools, or low-priority Terraform guidance.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Huawei Cloud Core For OpenCode
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
3
|
"mcpName": "io.github.huaweicloud/huaweicloud-devkit",
|
|
4
|
-
"version": "1.1.3-next.
|
|
4
|
+
"version": "1.1.3-next.2",
|
|
5
5
|
"kooCliVersion": "7.2.12",
|
|
6
6
|
"description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
|
|
7
7
|
"type": "module",
|
|
@@ -33,10 +33,11 @@
|
|
|
33
33
|
"test": "node --test \"test/*.test.mjs\"",
|
|
34
34
|
"format": "prettier --write .",
|
|
35
35
|
"format:check": "prettier --check .",
|
|
36
|
-
"lint:md": "markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#.git\" \"#test/**\" \"#docs/**\"",
|
|
36
|
+
"lint:md": "markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#.git\" \"#test/**\" \"#docs/**\" \"#.superpowers/**\"",
|
|
37
37
|
"lint:js": "eslint .",
|
|
38
38
|
"lint": "npm run lint:md && npm run lint:js",
|
|
39
|
-
"validate": "node ./scripts/validate-package.mjs",
|
|
39
|
+
"validate": "node ./scripts/validate-package.mjs && node ./scripts/sync-readme-badge.mjs",
|
|
40
|
+
"badge:sync": "node ./scripts/sync-readme-badge.mjs --write",
|
|
40
41
|
"pack:verify": "node ./scripts/pack-verify.mjs",
|
|
41
42
|
"postinstall": "node ./bin/dsh-postinstall.cjs"
|
|
42
43
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"interface": {
|
|
3
|
-
"displayName": "HuaweiCloud
|
|
4
|
-
"shortDescription": "HuaweiCloud
|
|
5
|
-
"longDescription": "HuaweiCloud
|
|
3
|
+
"displayName": "HuaweiCloud DevKit",
|
|
4
|
+
"shortDescription": "HuaweiCloud DevKit - Huawei Cloud guidance, CLI/API/SDK routing, MCP tools, and safety for coding agents.",
|
|
5
|
+
"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.",
|
|
6
6
|
"developerName": "HuaweiCloud Mate",
|
|
7
7
|
"category": "Cloud",
|
|
8
8
|
"capabilities": ["Read", "Interactive"],
|
|
@@ -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.3-next.
|
|
20
|
+
"version": "1.1.3-next.2",
|
|
21
21
|
"author": {
|
|
22
22
|
"name": "HuaweiCloud Mate",
|
|
23
23
|
"url": "https://github.com/huaweicloud"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"interface": {
|
|
3
|
-
"shortDescription": "HuaweiCloud
|
|
3
|
+
"shortDescription": "HuaweiCloud DevKit - Huawei Cloud guidance, CLI/API/SDK routing, MCP tools, and safety for coding agents.",
|
|
4
4
|
"developerName": "HuaweiCloud Mate",
|
|
5
5
|
"capabilities": ["Read", "Interactive"],
|
|
6
6
|
"brandColor": "#C7000B",
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
"Check my Huawei Cloud CLI setup safely.",
|
|
14
14
|
"Plan this Huawei Cloud API or SDK task."
|
|
15
15
|
],
|
|
16
|
-
"longDescription": "HuaweiCloud
|
|
16
|
+
"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.",
|
|
17
17
|
"websiteURL": "https://github.com/huaweicloud/HuaweiCloud-Devkit",
|
|
18
|
-
"displayName": "HuaweiCloud
|
|
18
|
+
"displayName": "HuaweiCloud DevKit"
|
|
19
19
|
},
|
|
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.3-next.
|
|
23
|
+
"version": "1.1.3-next.2",
|
|
24
24
|
"author": {
|
|
25
25
|
"name": "HuaweiCloud Mate",
|
|
26
26
|
"url": "https://github.com/huaweicloud"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"interface": {
|
|
3
|
-
"displayName": "HuaweiCloud
|
|
4
|
-
"shortDescription": "HuaweiCloud
|
|
5
|
-
"longDescription": "HuaweiCloud
|
|
3
|
+
"displayName": "HuaweiCloud DevKit",
|
|
4
|
+
"shortDescription": "HuaweiCloud DevKit - Huawei Cloud guidance, CLI/API/SDK routing, MCP tools, and safety for coding agents.",
|
|
5
|
+
"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.",
|
|
6
6
|
"developerName": "HuaweiCloud Mate",
|
|
7
7
|
"category": "Cloud",
|
|
8
8
|
"capabilities": ["Read", "Interactive"],
|
|
@@ -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.3-next.
|
|
20
|
+
"version": "1.1.3-next.2",
|
|
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.3-next.
|
|
3
|
+
"version": "1.1.3-next.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",
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"skills": "./skills/",
|
|
14
14
|
"mcpServers": "./.mcp.json",
|
|
15
15
|
"interface": {
|
|
16
|
-
"displayName": "HuaweiCloud
|
|
17
|
-
"shortDescription": "HuaweiCloud
|
|
18
|
-
"longDescription": "HuaweiCloud
|
|
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
19
|
"developerName": "HuaweiCloud Mate",
|
|
20
20
|
"category": "Cloud",
|
|
21
21
|
"capabilities": ["Read", "Interactive"],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
|
-
"version": "1.1.3-next.
|
|
3
|
+
"version": "1.1.3-next.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",
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"skills": "./skills/",
|
|
14
14
|
"mcpServers": "./.mcp.json",
|
|
15
15
|
"interface": {
|
|
16
|
-
"displayName": "HuaweiCloud
|
|
17
|
-
"shortDescription": "HuaweiCloud
|
|
18
|
-
"longDescription": "HuaweiCloud
|
|
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
19
|
"developerName": "HuaweiCloud Mate",
|
|
20
20
|
"category": "Cloud",
|
|
21
21
|
"capabilities": ["Read", "Interactive"],
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
3
|
"id": "huaweicloud-devkit",
|
|
4
4
|
"displayName": "HuaweiCloud DevKit",
|
|
5
|
-
"version": "1.1.3-next.
|
|
5
|
+
"version": "1.1.3-next.2",
|
|
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.",
|
|
@@ -47,3 +47,4 @@ hcloud OBS stat obs://<bucket>
|
|
|
47
47
|
- Region is immutable after creation
|
|
48
48
|
- ACL does NOT cascade to objects — set both bucket-level and object-level access
|
|
49
49
|
- OBS uses separate credentials (`~/.obsutilconfig`, configure via `hcloud OBS config -i`)
|
|
50
|
+
- **Return code is unreliable for `mb`**: it can exit non-zero (e.g. 6) while the bucket is actually created (output still carries a request id). Confirm with `stat obs://<bucket>` / `OBS ls` instead of the exit code; re-running `mb` on the same name is idempotent.
|
|
@@ -54,3 +54,5 @@ curl http://my-static-site.obs-website.cn-north-4.myhuaweicloud.com
|
|
|
54
54
|
- **Always use `-f`**: Without `-f`, obsutil prompts "Please input (y/n)" on large uploads — Agent hangs (TIMEOUT).
|
|
55
55
|
- **Use `-flat` for root files**: Without `-flat`, `dist/` becomes `bucket/dist/` prefix. Static sites need files at bucket root.
|
|
56
56
|
- **Objects don't inherit bucket ACL**: Setting bucket to public-read does NOT make individual objects public. You must also set object-level ACL with `chattri obs://<bucket>/ -r -f -acl=public-read` or use `-acl=public-read` during upload.
|
|
57
|
+
- **`mb` may return a non-zero exit code even on success**: `hcloud OBS mb` occasionally exits with code 6 (or other non-zero) while the bucket is actually created — the output still contains a request id and subsequent `cp`/`chattri` succeed. Judge success by the response text (request id) and confirm with `hcloud OBS ls` / `stat obs://<bucket>`, NOT the exit code alone. `mb` is idempotent, so re-issuing the same bucket name is harmless.
|
|
58
|
+
- **Website endpoint is eventually consistent (~1 min)**: after `PUT /?website` returns 200, the website endpoint (`http://<bucket>.obs-website.<region>.myhuaweicloud.com`) may return 404 `NoSuchWebsiteConfiguration` for up to ~1 minute. This is propagation delay, NOT a configuration failure. Verify immediately via the object URL (`https://<bucket>.obs.<region>.myhuaweicloud.com/index.html`, works before the website endpoint), then poll the website endpoint with a short backoff until it returns 200 before reporting success.
|
|
@@ -45,15 +45,27 @@ export function readKooCliProfiles() {
|
|
|
45
45
|
const current = String(raw.current || 'default');
|
|
46
46
|
const profiles = Array.isArray(raw.profiles) ? raw.profiles : [];
|
|
47
47
|
const mtimeMs = statSync(configPath).mtimeMs;
|
|
48
|
+
// KooCLI 7.x stores AK/SK encrypted (authEncrypt=true): the on-disk values
|
|
49
|
+
// are ciphertext, so a fingerprint computed from them can never match the
|
|
50
|
+
// plaintext S1/S3 stores. Blind the comparable fields instead of treating
|
|
51
|
+
// ciphertext as a real credential and misreporting a S2 drift (#533).
|
|
52
|
+
// KooCLI serializes the flag as the string "true", not a boolean.
|
|
53
|
+
const encrypted = raw.authEncrypt === true || raw.authEncrypt === 'true';
|
|
48
54
|
return {
|
|
49
55
|
current,
|
|
50
56
|
mtimeMs,
|
|
51
57
|
configPath,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
authEncrypt: encrypted,
|
|
59
|
+
profiles: profiles.map((p) =>
|
|
60
|
+
encrypted
|
|
61
|
+
? { name: String(p.name || ''), fingerprint: '', accessKeyId: '', encrypted: true }
|
|
62
|
+
: {
|
|
63
|
+
name: String(p.name || ''),
|
|
64
|
+
fingerprint: fingerprint(p.accessKeyId, p.secretAccessKey),
|
|
65
|
+
accessKeyId: p.accessKeyId || '',
|
|
66
|
+
encrypted: false,
|
|
67
|
+
},
|
|
68
|
+
),
|
|
57
69
|
};
|
|
58
70
|
} catch {
|
|
59
71
|
return { error: 'KooCLI config parse failed' };
|
|
@@ -138,6 +150,9 @@ export function scanState() {
|
|
|
138
150
|
runtimeFingerprint,
|
|
139
151
|
},
|
|
140
152
|
kooCliCurrent: kooCli.error ? null : kooCli.current,
|
|
153
|
+
// S2 is encrypted storage (authEncrypt) → its fingerprint is unavailable,
|
|
154
|
+
// so the S2-current drift check is intentionally skipped (#533).
|
|
155
|
+
s2Encrypted: Boolean(!kooCli.error && kooCli.authEncrypt),
|
|
141
156
|
inconsistencies,
|
|
142
157
|
hasRuntime,
|
|
143
158
|
runtimeFingerprint,
|
|
@@ -1047,7 +1047,7 @@ async function installCodexDesktop() {
|
|
|
1047
1047
|
|
|
1048
1048
|
// Register in personal marketplace (Codex discovers plugins from ~/.agents/plugins/marketplace.json)
|
|
1049
1049
|
ensureCodexMarketplaceEntry();
|
|
1050
|
-
console.log(' \x1b[33m请到插件 → 个人 → HuaweiCloud
|
|
1050
|
+
console.log(' \x1b[33m请到插件 → 个人 → HuaweiCloud DevKit → 安装\x1b[0m');
|
|
1051
1051
|
|
|
1052
1052
|
// Clean up old install locations from pre-marketplace era
|
|
1053
1053
|
removeIfExists(join(homedir(), '.agents', 'skills'));
|
|
@@ -3009,7 +3009,7 @@ function opencodeStatus() {
|
|
|
3009
3009
|
}
|
|
3010
3010
|
}
|
|
3011
3011
|
|
|
3012
|
-
function
|
|
3012
|
+
function detectAgents() {
|
|
3013
3013
|
const checks = [
|
|
3014
3014
|
['opencode', () => existsSync(join(homedir(), '.config', 'opencode'))],
|
|
3015
3015
|
['codex-desktop', () => existsSync(join(homedir(), '.codex'))],
|
|
@@ -3028,7 +3028,11 @@ function autoDetectTarget() {
|
|
|
3028
3028
|
['openclaw', () => existsSync(join(homedir(), '.openclaw'))],
|
|
3029
3029
|
['atomcode', () => existsSync(atomcodeHome())],
|
|
3030
3030
|
];
|
|
3031
|
-
|
|
3031
|
+
return checks.filter(([, check]) => check()).map(([name]) => name);
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
function autoDetectTarget() {
|
|
3035
|
+
const detected = detectAgents();
|
|
3032
3036
|
if (detected.length === 0) {
|
|
3033
3037
|
console.error('No supported agent detected.');
|
|
3034
3038
|
console.error(`Supported: ${SUPPORTED_AGENT_TARGETS.join(', ')} (or "all")`);
|
|
@@ -3039,6 +3043,208 @@ function autoDetectTarget() {
|
|
|
3039
3043
|
return 'all';
|
|
3040
3044
|
}
|
|
3041
3045
|
|
|
3046
|
+
const AGENT_LABELS = {
|
|
3047
|
+
opencode: 'OpenCode',
|
|
3048
|
+
'codex-desktop': 'Codex Desktop',
|
|
3049
|
+
codearts: 'CodeArts',
|
|
3050
|
+
'codearts-work': 'CodeArts Work',
|
|
3051
|
+
workbuddy: 'WorkBuddy',
|
|
3052
|
+
dsh: 'DSH',
|
|
3053
|
+
officeace: 'OfficeAce',
|
|
3054
|
+
hermes: 'Hermes',
|
|
3055
|
+
openclaw: 'OpenClaw',
|
|
3056
|
+
atomcode: 'AtomCode',
|
|
3057
|
+
};
|
|
3058
|
+
|
|
3059
|
+
function promptSelectAgents(detected) {
|
|
3060
|
+
if (!process.stdin.isTTY) {
|
|
3061
|
+
console.error(
|
|
3062
|
+
`Multiple agents detected (${detected.join(', ')}). Cannot prompt in a non-interactive shell — ` +
|
|
3063
|
+
'specify `--target <agent>` or `--target all`.',
|
|
3064
|
+
);
|
|
3065
|
+
return Promise.resolve(null);
|
|
3066
|
+
}
|
|
3067
|
+
const question = [
|
|
3068
|
+
'Multiple agents detected. Select which to install (multi-select):',
|
|
3069
|
+
...detected.map((name, i) => ` ${i + 1}) ${AGENT_LABELS[name] || name}`),
|
|
3070
|
+
'Enter numbers separated by commas/spaces, "all" for every detected, Enter to install all detected, or "0" to cancel: ',
|
|
3071
|
+
].join('\n');
|
|
3072
|
+
return new Promise((resolve) => {
|
|
3073
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
3074
|
+
rl.question(question, (a) => {
|
|
3075
|
+
rl.close();
|
|
3076
|
+
const answer = (a || '').trim().toLowerCase();
|
|
3077
|
+
if (answer === '0' || answer === 'n' || answer === 'no' || answer === 'q') {
|
|
3078
|
+
resolve(null);
|
|
3079
|
+
return;
|
|
3080
|
+
}
|
|
3081
|
+
if (answer === '' || answer === 'all') {
|
|
3082
|
+
resolve(detected.slice());
|
|
3083
|
+
return;
|
|
3084
|
+
}
|
|
3085
|
+
const parts = answer.split(/[\s,,]+/).filter(Boolean);
|
|
3086
|
+
const indices = new Set();
|
|
3087
|
+
for (const part of parts) {
|
|
3088
|
+
const n = Number(part);
|
|
3089
|
+
if (Number.isSafeInteger(n) && n >= 1 && n <= detected.length) {
|
|
3090
|
+
indices.add(n - 1);
|
|
3091
|
+
} else {
|
|
3092
|
+
console.error(`Invalid selection: "${part}". Installing all detected.`);
|
|
3093
|
+
resolve(detected.slice());
|
|
3094
|
+
return;
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
resolve([...indices].map((i) => detected[i]));
|
|
3098
|
+
});
|
|
3099
|
+
});
|
|
3100
|
+
}
|
|
3101
|
+
|
|
3102
|
+
const MCP_ENTRY = {
|
|
3103
|
+
command: 'npx',
|
|
3104
|
+
args: ['-y', '-p', 'huaweicloud-devkit', 'huaweicloud-devkit-mcp'],
|
|
3105
|
+
};
|
|
3106
|
+
|
|
3107
|
+
function promptZeroDetect() {
|
|
3108
|
+
if (!process.stdin.isTTY) {
|
|
3109
|
+
console.error('No supported agent detected.');
|
|
3110
|
+
console.error(`Supported: ${SUPPORTED_AGENT_TARGETS.join(', ')} (or "all")`);
|
|
3111
|
+
console.error('Use --target <agent> to specify.');
|
|
3112
|
+
return Promise.resolve({ abort: true });
|
|
3113
|
+
}
|
|
3114
|
+
const question = [
|
|
3115
|
+
'No supported agent detected.',
|
|
3116
|
+
'What would you like to do?',
|
|
3117
|
+
' 1) Install to a specific agent (e.g. codex)',
|
|
3118
|
+
' 2) Install to all supported agents (--target all)',
|
|
3119
|
+
' 3) Wire up a generic MCP agent (Claude Code / Cursor / custom MCP client)',
|
|
3120
|
+
' 0) Exit',
|
|
3121
|
+
'Enter choice: ',
|
|
3122
|
+
].join('\n');
|
|
3123
|
+
return new Promise((resolve) => {
|
|
3124
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
3125
|
+
const ask = () => {
|
|
3126
|
+
rl.question(question, (a) => {
|
|
3127
|
+
const answer = (a || '').trim().toLowerCase();
|
|
3128
|
+
if (answer === '1') {
|
|
3129
|
+
rl.question('Enter agent target (e.g. codex): ', (t) => {
|
|
3130
|
+
const val = (t || '').trim().toLowerCase();
|
|
3131
|
+
rl.close();
|
|
3132
|
+
if (val === 'all' || SUPPORTED_AGENT_TARGETS.includes(val)) {
|
|
3133
|
+
resolve({ explicit: true, target: val });
|
|
3134
|
+
} else {
|
|
3135
|
+
console.error(`Unknown target: ${val}`);
|
|
3136
|
+
console.error(`Supported: ${SUPPORTED_AGENT_TARGETS.join(', ')} (or "all")`);
|
|
3137
|
+
resolve({ abort: true });
|
|
3138
|
+
}
|
|
3139
|
+
});
|
|
3140
|
+
return;
|
|
3141
|
+
}
|
|
3142
|
+
if (answer === '2') {
|
|
3143
|
+
rl.close();
|
|
3144
|
+
resolve({ explicit: true, target: 'all' });
|
|
3145
|
+
return;
|
|
3146
|
+
}
|
|
3147
|
+
if (answer === '3') {
|
|
3148
|
+
rl.close();
|
|
3149
|
+
resolve({ mcp: true });
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
3152
|
+
if (answer === '' || answer === '0' || answer === 'n' || answer === 'no' || answer === 'q') {
|
|
3153
|
+
rl.close();
|
|
3154
|
+
resolve({ abort: true });
|
|
3155
|
+
return;
|
|
3156
|
+
}
|
|
3157
|
+
console.error(`Invalid choice: "${answer}"`);
|
|
3158
|
+
ask();
|
|
3159
|
+
});
|
|
3160
|
+
};
|
|
3161
|
+
ask();
|
|
3162
|
+
});
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
function printMCPConfigSnippet(reason) {
|
|
3166
|
+
console.log(` ${reason}`);
|
|
3167
|
+
console.log(' Add this to your agent MCP config (stdio):');
|
|
3168
|
+
const snippet = JSON.stringify({ mcpServers: { 'huaweicloud-devkit': MCP_ENTRY } }, null, 2)
|
|
3169
|
+
.split('\n')
|
|
3170
|
+
.map((line) => ` ${line}`)
|
|
3171
|
+
.join('\n');
|
|
3172
|
+
console.log(snippet);
|
|
3173
|
+
console.log(' Or run the remote server and use a remote (Streamable HTTP) config:');
|
|
3174
|
+
console.log(' npx --yes huaweicloud-devkit-mcp --transport remote # listens on 127.0.0.1:9528');
|
|
3175
|
+
console.log(' See README → "Other Agents" for details.');
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
function configureMCPAgent(targetFile, agentLabel) {
|
|
3179
|
+
let config = {};
|
|
3180
|
+
let existed = false;
|
|
3181
|
+
if (existsSync(targetFile)) {
|
|
3182
|
+
existed = true;
|
|
3183
|
+
try {
|
|
3184
|
+
config = JSON.parse(readFileSync(targetFile, 'utf8'));
|
|
3185
|
+
} catch {
|
|
3186
|
+
console.error(` [${agentLabel}] ${targetFile} is not valid JSON; leaving it untouched.`);
|
|
3187
|
+
return false;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
config.mcpServers = config.mcpServers || {};
|
|
3191
|
+
if (config.mcpServers['huaweicloud-devkit']) {
|
|
3192
|
+
console.log(` [${agentLabel}] mcpServers.huaweicloud-devkit already configured; skipping.`);
|
|
3193
|
+
return true;
|
|
3194
|
+
}
|
|
3195
|
+
if (existed) copyFileSync(targetFile, `${targetFile}.bak`);
|
|
3196
|
+
config.mcpServers['huaweicloud-devkit'] = { ...MCP_ENTRY };
|
|
3197
|
+
mkdirSync(dirname(targetFile), { recursive: true });
|
|
3198
|
+
writeFileSync(targetFile, JSON.stringify(config, null, 2), 'utf8');
|
|
3199
|
+
console.log(` [${agentLabel}] MCP server configured in ${targetFile}. Restart the session to apply.`);
|
|
3200
|
+
return true;
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
function configureGenericMCP() {
|
|
3204
|
+
console.log('Configuring a generic MCP agent (MCP tools only; skills/hooks are not installed)...');
|
|
3205
|
+
const targets = [];
|
|
3206
|
+
const claudeFile = process.env.CLAUDE_CONFIG_DIR
|
|
3207
|
+
? join(process.env.CLAUDE_CONFIG_DIR, '.claude.json')
|
|
3208
|
+
: join(homedir(), '.claude.json');
|
|
3209
|
+
if (existsSync(claudeFile)) targets.push(['Claude Code', claudeFile]);
|
|
3210
|
+
const cursorFile = join(homedir(), '.cursor', 'mcp.json');
|
|
3211
|
+
if (existsSync(cursorFile) || existsSync(join(homedir(), '.cursor'))) {
|
|
3212
|
+
targets.push(['Cursor', cursorFile]);
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
if (targets.length === 0) {
|
|
3216
|
+
printMCPConfigSnippet('No known MCP agent detected; here is a config snippet you can paste:');
|
|
3217
|
+
return false;
|
|
3218
|
+
}
|
|
3219
|
+
let anyConfigured = false;
|
|
3220
|
+
for (const [label, file] of targets) {
|
|
3221
|
+
if (configureMCPAgent(file, label)) anyConfigured = true;
|
|
3222
|
+
}
|
|
3223
|
+
if (!anyConfigured) {
|
|
3224
|
+
console.log(' Nothing to configure for the detected MCP agents.');
|
|
3225
|
+
}
|
|
3226
|
+
return anyConfigured;
|
|
3227
|
+
}
|
|
3228
|
+
|
|
3229
|
+
async function resolveInstallTarget() {
|
|
3230
|
+
const idx = process.argv.indexOf('--target');
|
|
3231
|
+
if (idx >= 0) {
|
|
3232
|
+
const val = (process.argv[idx + 1] || '').toLowerCase();
|
|
3233
|
+
if (val === 'all' || SUPPORTED_AGENT_TARGETS.includes(val)) {
|
|
3234
|
+
return { explicit: true, target: val };
|
|
3235
|
+
}
|
|
3236
|
+
console.error(`Unknown target: ${val}`);
|
|
3237
|
+
console.error(`Supported: ${SUPPORTED_AGENT_TARGETS.join(', ')} (or "all")`);
|
|
3238
|
+
process.exit(1);
|
|
3239
|
+
}
|
|
3240
|
+
const detected = detectAgents();
|
|
3241
|
+
if (detected.length === 1) return { explicit: true, target: detected[0] };
|
|
3242
|
+
if (detected.length === 0) return promptZeroDetect();
|
|
3243
|
+
const chosen = await promptSelectAgents(detected);
|
|
3244
|
+
if (!chosen) return { abort: true };
|
|
3245
|
+
return { explicit: false, subset: chosen };
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3042
3248
|
function parseTarget() {
|
|
3043
3249
|
const idx = process.argv.indexOf('--target');
|
|
3044
3250
|
if (idx < 0) return autoDetectTarget();
|
|
@@ -3084,13 +3290,37 @@ function writeInstallMarker(target) {
|
|
|
3084
3290
|
}
|
|
3085
3291
|
|
|
3086
3292
|
async function cmdInstall() {
|
|
3087
|
-
const target = parseTarget();
|
|
3088
3293
|
console.log(BANNER);
|
|
3089
|
-
|
|
3294
|
+
const plan = await resolveInstallTarget();
|
|
3295
|
+
if (plan.abort) {
|
|
3296
|
+
process.exitCode = 1;
|
|
3297
|
+
return;
|
|
3298
|
+
}
|
|
3299
|
+
if (plan.mcp) {
|
|
3300
|
+
const ok = configureGenericMCP();
|
|
3301
|
+
process.exitCode = ok ? 0 : 1;
|
|
3302
|
+
return;
|
|
3303
|
+
}
|
|
3304
|
+
const hasExplicitTarget = plan.explicit;
|
|
3305
|
+
const target = plan.explicit ? plan.target : null;
|
|
3306
|
+
const selected = new Set(plan.explicit ? [] : plan.subset);
|
|
3307
|
+
|
|
3308
|
+
if (hasExplicitTarget) {
|
|
3309
|
+
console.log(`Installing HuaweiCloud DevKit for ${target}...\n`);
|
|
3310
|
+
} else {
|
|
3311
|
+
console.log(`Installing HuaweiCloud DevKit to ${[...selected].join(', ')}...\n`);
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3090
3314
|
checkNode();
|
|
3091
3315
|
checkForUpdate();
|
|
3092
3316
|
const installFailures = [];
|
|
3093
3317
|
|
|
3318
|
+
function shouldInstall(name) {
|
|
3319
|
+
if (name === 'codex') return hasExplicitTarget && (target === 'codex' || target === 'all');
|
|
3320
|
+
if (hasExplicitTarget) return target === name || target === 'all';
|
|
3321
|
+
return selected.has(name);
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3094
3324
|
async function runInstallStep(stepTarget, title, fn) {
|
|
3095
3325
|
console.log(title);
|
|
3096
3326
|
try {
|
|
@@ -3108,37 +3338,37 @@ async function cmdInstall() {
|
|
|
3108
3338
|
}
|
|
3109
3339
|
}
|
|
3110
3340
|
|
|
3111
|
-
if (
|
|
3341
|
+
if (shouldInstall('opencode')) {
|
|
3112
3342
|
await runInstallStep('opencode', '[OpenCode]', installOpenCode);
|
|
3113
3343
|
}
|
|
3114
|
-
if (
|
|
3344
|
+
if (shouldInstall('codex-desktop')) {
|
|
3115
3345
|
await runInstallStep('codex-desktop', '\n[Codex Desktop]', installCodexDesktop);
|
|
3116
3346
|
}
|
|
3117
|
-
if (
|
|
3347
|
+
if (shouldInstall('codearts')) {
|
|
3118
3348
|
await runInstallStep('codearts', '\n[CodeArts]', installCodeArts);
|
|
3119
3349
|
}
|
|
3120
|
-
if (
|
|
3350
|
+
if (shouldInstall('codearts-work')) {
|
|
3121
3351
|
await runInstallStep('codearts-work', '\n[CodeArts Work]', installCodeArtsWork);
|
|
3122
3352
|
}
|
|
3123
|
-
if (
|
|
3353
|
+
if (shouldInstall('workbuddy')) {
|
|
3124
3354
|
await runInstallStep('workbuddy', '\n[WorkBuddy]', installWorkBuddy);
|
|
3125
3355
|
}
|
|
3126
|
-
if (
|
|
3356
|
+
if (shouldInstall('dsh')) {
|
|
3127
3357
|
await runInstallStep('dsh', '\n[DSH]', installDsh);
|
|
3128
3358
|
}
|
|
3129
|
-
if (
|
|
3359
|
+
if (shouldInstall('officeace')) {
|
|
3130
3360
|
await runInstallStep('officeace', '\n[OfficeAce]', installOfficeAce);
|
|
3131
3361
|
}
|
|
3132
|
-
if (
|
|
3362
|
+
if (shouldInstall('hermes')) {
|
|
3133
3363
|
await runInstallStep('hermes', '\n[Hermes Agent]', installHermes);
|
|
3134
3364
|
}
|
|
3135
|
-
if (
|
|
3365
|
+
if (shouldInstall('openclaw')) {
|
|
3136
3366
|
await runInstallStep('openclaw', '\n[OpenClaw]', installOpenClaw);
|
|
3137
3367
|
}
|
|
3138
|
-
if (
|
|
3368
|
+
if (shouldInstall('atomcode')) {
|
|
3139
3369
|
await runInstallStep('atomcode', '\n[AtomCode]', installAtomCode);
|
|
3140
3370
|
}
|
|
3141
|
-
if (
|
|
3371
|
+
if (shouldInstall('codex')) {
|
|
3142
3372
|
await runInstallStep('codex', '\n[Codex]', () => {
|
|
3143
3373
|
if (!hasCodexCLI()) {
|
|
3144
3374
|
if (target === 'codex') {
|
|
@@ -4068,6 +4298,10 @@ async function cmdReinstall() {
|
|
|
4068
4298
|
let confirmed = false;
|
|
4069
4299
|
async function confirm(msg) {
|
|
4070
4300
|
if (confirmed) return true;
|
|
4301
|
+
if (!process.stdin.isTTY) {
|
|
4302
|
+
console.log('Non-interactive shell: skipping confirmation (declined).');
|
|
4303
|
+
return false;
|
|
4304
|
+
}
|
|
4071
4305
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
4072
4306
|
return new Promise((ok) => {
|
|
4073
4307
|
rl.question(`${msg} [y/N] `, (a) => {
|
|
@@ -1012,7 +1012,31 @@ function refreshUserHashAfterAuthChange({ regenerate = true } = {}) {
|
|
|
1012
1012
|
}
|
|
1013
1013
|
}
|
|
1014
1014
|
|
|
1015
|
-
|
|
1015
|
+
// Reject invalid numeric args up front instead of silently coercing them to
|
|
1016
|
+
// NaN (which downstream defaults would absorb as "no timeout set") — see #530.
|
|
1017
|
+
const NUMERIC_ARG_KEYS = ['timeoutMs', 'maxRetries', 'timeout_ms'];
|
|
1018
|
+
|
|
1019
|
+
function normalizeNumericArgs(args) {
|
|
1020
|
+
const out = { ...args };
|
|
1021
|
+
for (const key of NUMERIC_ARG_KEYS) {
|
|
1022
|
+
const value = out[key];
|
|
1023
|
+
if (value === undefined || value === null) continue;
|
|
1024
|
+
const num = Number(value);
|
|
1025
|
+
// timeouts must be positive; maxRetries may be 0 (means "no retries").
|
|
1026
|
+
const isRetries = key === 'maxRetries';
|
|
1027
|
+
const valid = Number.isFinite(num) && (isRetries ? Number.isSafeInteger(num) && num >= 0 : num > 0);
|
|
1028
|
+
if (!valid) {
|
|
1029
|
+
throw new Error(
|
|
1030
|
+
`Invalid parameter "${key}": expected a ${isRetries ? 'non-negative integer' : 'positive number'}, received ${JSON.stringify(value)}.`,
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
out[key] = num;
|
|
1034
|
+
}
|
|
1035
|
+
return out;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
export async function callTool(name, rawArgs = {}) {
|
|
1039
|
+
const args = normalizeNumericArgs(rawArgs);
|
|
1016
1040
|
const toolValue = toolInvokeValue(name, args);
|
|
1017
1041
|
trackToolInvoke(name, toolValue);
|
|
1018
1042
|
|
|
@@ -323,7 +323,9 @@ export async function upgradePackage({ target = 'all', version = 'latest' } = {}
|
|
|
323
323
|
return { success: false, error: 'version 参数仅支持 latest。目标版本由插件自动判定。' };
|
|
324
324
|
}
|
|
325
325
|
const { doQuery = queryDistTags, spawnFn = defaultSpawn } = options;
|
|
326
|
-
|
|
326
|
+
// Tests inject currentVersion explicitly - the repo package.json version changes
|
|
327
|
+
// between prerelease and stable lines, which must not flip the upgrade-tag logic.
|
|
328
|
+
const previousVersion = options.currentVersion || readInstalledVersion();
|
|
327
329
|
let distTags;
|
|
328
330
|
try {
|
|
329
331
|
distTags = await doQuery();
|