huaweicloud-devkit 1.1.2-next.6 → 1.1.2-next.8
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/package.json +1 -1
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +15 -15
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-iac/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-iac/references/resource-catalog.md +3 -3
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +8 -8
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +7 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +6 -7
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +13 -13
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +2 -0
- package/plugins/huaweicloud-core/src/setup-cli.mjs +175 -91
- package/plugins/huaweicloud-core/src/telemetry/agent-registry.mjs +2 -1
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.2-next.
|
|
4
|
+
"version": "1.1.2-next.8",
|
|
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",
|
|
@@ -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.2-next.
|
|
20
|
+
"version": "1.1.2-next.8",
|
|
21
21
|
"author": {
|
|
22
22
|
"name": "HuaweiCloud Mate",
|
|
23
23
|
"url": "https://github.com/huaweicloud"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"mcpServers": "./.mcp.json",
|
|
21
21
|
"description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
|
|
22
22
|
"skills": "./skills/",
|
|
23
|
-
"version": "1.1.2-next.
|
|
23
|
+
"version": "1.1.2-next.8",
|
|
24
24
|
"author": {
|
|
25
25
|
"name": "HuaweiCloud Mate",
|
|
26
26
|
"url": "https://github.com/huaweicloud"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"mcpServers": "./.mcp.json",
|
|
18
18
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
19
19
|
"skills": "./skills/",
|
|
20
|
-
"version": "1.1.2-next.
|
|
20
|
+
"version": "1.1.2-next.8",
|
|
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.2-next.
|
|
3
|
+
"version": "1.1.2-next.8",
|
|
4
4
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "HuaweiCloud Mate",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
|
-
"version": "1.1.2-next.
|
|
3
|
+
"version": "1.1.2-next.8",
|
|
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",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
3
|
"id": "huaweicloud-devkit",
|
|
4
4
|
"displayName": "HuaweiCloud DevKit",
|
|
5
|
-
"version": "1.1.2-next.
|
|
5
|
+
"version": "1.1.2-next.8",
|
|
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.",
|
|
@@ -16,24 +16,24 @@ Domain expertise for billing queries (BSS). Covers cost tracking, bill details,
|
|
|
16
16
|
|
|
17
17
|
## Critical Warnings
|
|
18
18
|
|
|
19
|
-
| Trap
|
|
20
|
-
|
|
|
21
|
-
| Bills delayed ~24h
|
|
22
|
-
| BSS Admin role needed
|
|
23
|
-
| Currency conversion varies
|
|
24
|
-
| Region fixed to cn-north-1
|
|
25
|
-
|
|
|
19
|
+
| Trap | Why |
|
|
20
|
+
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
21
|
+
| Bills delayed ~24h | Yesterday's costs may not appear until the next day |
|
|
22
|
+
| BSS Admin role needed | IAM user must have BSS Administrator or Finance role |
|
|
23
|
+
| Currency conversion varies | Cross-region costs use daily exchange rates |
|
|
24
|
+
| Region fixed to cn-north-1 | BSS operations only support `--cli-region=cn-north-1` in KooCLI. This is a KooCLI metadata limitation — the billing data itself covers all regions. |
|
|
25
|
+
| `缺少必填参数 cli-domain-id` / APIGW.0301 | Invalid credentials — KooCLI can't resolve the account-id | Re-run `npx huaweicloud-devkit auth init` |
|
|
26
26
|
|
|
27
27
|
## Common Workflows
|
|
28
28
|
|
|
29
|
-
| Task | Operation
|
|
30
|
-
| -------------------- |
|
|
31
|
-
| List costs | `ListCosts --cli-region=cn-north-1 --project_id=<p>`
|
|
32
|
-
| List customer bills | `ListCustomerBillsFeeRecords --cli-region=cn-north-1 --project_id=<p>`
|
|
33
|
-
| List resource usage | `ListResourceUsage --cli-region=cn-north-1 --project_id=<p>`
|
|
34
|
-
| List sub-customers | `ListConsumeSubCustomers --cli-region=cn-north-1 --project_id=<p>`
|
|
35
|
-
| Show account balance | `ShowCustomerAccountBalances --cli-region=cn-north-1
|
|
36
|
-
| List conversions | `ListConversions --cli-region=cn-north-1 --project_id=<p>`
|
|
29
|
+
| Task | Operation |
|
|
30
|
+
| -------------------- | ---------------------------------------------------------------------- |
|
|
31
|
+
| List costs | `ListCosts --cli-region=cn-north-1 --project_id=<p>` |
|
|
32
|
+
| List customer bills | `ListCustomerBillsFeeRecords --cli-region=cn-north-1 --project_id=<p>` |
|
|
33
|
+
| List resource usage | `ListResourceUsage --cli-region=cn-north-1 --project_id=<p>` |
|
|
34
|
+
| List sub-customers | `ListConsumeSubCustomers --cli-region=cn-north-1 --project_id=<p>` |
|
|
35
|
+
| Show account balance | `ShowCustomerAccountBalances --cli-region=cn-north-1` |
|
|
36
|
+
| List conversions | `ListConversions --cli-region=cn-north-1 --project_id=<p>` |
|
|
37
37
|
|
|
38
38
|
Discover exact parameters with `--help` before executing any command. All BSS operations are read-only.
|
|
39
39
|
|
|
@@ -69,6 +69,6 @@ hcloud IAM AssociateAgencyWithDomainPermission --agency_id=<id> --domain_id=<id>
|
|
|
69
69
|
hcloud FunctionGraph CreateFunction --func_vpc.vpc_id=<vpc> --func_vpc.subnet_id=<subnet> --app_xrole=<name> ...
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
>
|
|
72
|
+
> The `缺少必填参数 cli-domain-id` error means invalid credentials (APIGW.0301) — re-run `auth init`. `CreateAgency` / `AssociateAgencyWithDomainPermission` need the account-id for their `--agency.domain_id` / `--domain_id` params; get it via `hcloud STS GetCallerIdentity --cli-region=<region>`.
|
|
73
73
|
|
|
74
74
|
| QuotaExceeded | Max 10 functions per project per region |
|
|
@@ -86,7 +86,7 @@ Execution: show the to-be-deleted list for final confirmation → delete in **re
|
|
|
86
86
|
- `huaweicloud_run_readonly_command` → discovery (flavors, images, zones, prices)
|
|
87
87
|
- `huaweicloud_list_regions` / `huaweicloud_get_regional_availability` → region intent
|
|
88
88
|
- `huaweicloud_hook_check_deploy_plan` → risk-check the plan before approval
|
|
89
|
-
- Balance and pricing: `huawei-billing` skill. Balance operation (BSS is pinned to cn-north-1 in KooCLI): `hcloud BSS ShowCustomerAccountBalances --cli-region=cn-north-1
|
|
89
|
+
- Balance and pricing: `huawei-billing` skill. Balance operation (BSS is pinned to cn-north-1 in KooCLI): `hcloud BSS ShowCustomerAccountBalances --cli-region=cn-north-1` (domain-id auto-resolves from the AK/SK). If an IAM business param genuinely needs the account-id, run `hcloud STS GetCallerIdentity --cli-region=<region>`
|
|
90
90
|
|
|
91
91
|
## Cross-Skill References
|
|
92
92
|
|
|
@@ -100,8 +100,8 @@ hcloud BSS ListOnDemandResourceRatings --project_id=<pid> \
|
|
|
100
100
|
- **Balance query may be permission-blocked**: `CBC.0151` = IAM user lacks `billing:balance:view` → degrade to "user self-checks balance" and continue (do not block; `Ecs.7000` at order time is recoverable). domain_id 可从报错信息 `iam::<domain_id>:user:<name>` 提取
|
|
101
101
|
|
|
102
102
|
- Account balance: `hcloud BSS ShowCustomerAccountBalances --cli-region=cn-north-1 --cli-domain-id=<domain_id>`
|
|
103
|
-
-
|
|
104
|
-
- If
|
|
103
|
+
- domain-id auto-resolves from the AK/SK; `缺少必填参数 cli-domain-id` signals invalid credentials (APIGW.0301), not a missing domain-id
|
|
104
|
+
- If an IAM business param needs the account-id (`--domain_id` etc.), run `hcloud STS GetCallerIdentity --cli-region=<region>` (`account_id` = domain-id); `tenant_id` from a resource response is a fallback
|
|
105
105
|
- Response shape: `account_balances[].amount` (cash account type=1, voucher account type=5), `debt_amount`, `currency`
|
|
106
106
|
- Cost gate rule: no deployment without a per-resource cost estimate AND a balance check. Verified failure mode when skipped: ECS creation dies with `Ecs.7000 Insufficient account balance` at order submission
|
|
107
107
|
- **Quota pre-check (EIP and friends)**: quota errors surface only at RUN stage (`EIP.7905 Quota exceeded` AFTER plan+approval), wasting an approval cycle. Before provisioning, pre-check counts with `hcloud EIP ListPublicips` (⚠️ beware pagination truncation: `--limit=50` on a 59-EIP account showed "50" - the first "50/50 full" verdict was wrong; count precisely, e.g. loop pages or use the console quota page). If quota is exhausted, surface it at the cost gate instead of after approval. Note: after releasing an EIP, quota counting may lag several minutes - `EIP.7905` can persist briefly even under the limit
|
|
@@ -111,4 +111,4 @@ hcloud BSS ListOnDemandResourceRatings --project_id=<pid> \
|
|
|
111
111
|
- Symptom of a broken profile: every hcloud API returns `APIGW.0301 Incorrect IAM authentication information` while OBS/obsutil keeps working - caused by a stale `securityToken` in the profile
|
|
112
112
|
- Fix: `hcloud configure delete --cli-profile=default`, then re-run `npx huaweicloud-devkit auth init` with permanent AK/SK only (no token). `configure set` refuses empty values, so the token cannot be cleared in place
|
|
113
113
|
- project_id is auto-discovered once signing works - no need to configure it manually
|
|
114
|
-
- VPC/OBS create responses expose `tenant_id` (= domain_id) - a fallback
|
|
114
|
+
- VPC/OBS create responses expose `tenant_id` (= domain_id) - a fallback source for the account-id
|
|
@@ -16,14 +16,14 @@ Domain expertise for Huawei Cloud Identity and Access Management (IAM). Covers u
|
|
|
16
16
|
|
|
17
17
|
## Critical Warnings
|
|
18
18
|
|
|
19
|
-
| Trap
|
|
20
|
-
|
|
|
21
|
-
| NEVER create IAM users for humans
|
|
22
|
-
| NEVER create long-term AK/SK
|
|
23
|
-
| Wildcard policies dangerous
|
|
24
|
-
| Agency trust is powerful
|
|
25
|
-
| Root account must have MFA
|
|
26
|
-
|
|
|
19
|
+
| Trap | Why |
|
|
20
|
+
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
21
|
+
| NEVER create IAM users for humans | Use OneAccess (IAM Identity Center) or federated SSO |
|
|
22
|
+
| NEVER create long-term AK/SK | Use temporary STS tokens via agencies |
|
|
23
|
+
| Wildcard policies dangerous | Effect:Allow + Resource:* = full access. Always scope resources |
|
|
24
|
+
| Agency trust is powerful | Agencies let services assume roles. Always add conditions |
|
|
25
|
+
| Root account must have MFA | Root AK/SK is all-powerful. Enable MFA immediately |
|
|
26
|
+
| `缺少必填参数 cli-domain-id` = invalid creds | KooCLI's account-id lookup failed (APIGW.0301). Re-run `auth init`. Domain-level params (`--domain_id`, `--agency.domain_id`) are separate — get the account-id via `hcloud STS GetCallerIdentity --cli-region=<region>` when needed |
|
|
27
27
|
|
|
28
28
|
## Policy Structure
|
|
29
29
|
|
|
@@ -112,7 +112,13 @@ Setup is a **plugin-side preflight** — the developer should be asked a questio
|
|
|
112
112
|
sudo nginx -s reload 2>/dev/null || true
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
-
7. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox.
|
|
115
|
+
7. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox. Pass `session_id` **or** `dev_stage_id` (at least one; both come from `sandbox_connect`, and `dev_stage_id` may be omitted to reuse the most recent connect). The tool validates the AK/SK against IAM first — an invalid SK is rejected here instead of failing later with `APIGW.0301` during exec. Example:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{ "dev_stage_id": "<dev_stage_id from connect>" }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
After injection, credentials are also written to `/tmp/hw_creds.sh` for shell access (includes `HW_PROJECT_ID` when resolvable):
|
|
116
122
|
|
|
117
123
|
```bash
|
|
118
124
|
# Source credentials in any sandbox shell before using hcloud/devbridge:
|
|
@@ -139,19 +139,18 @@ When switching accounts within the same Agent session, use `huaweicloud_auth_ini
|
|
|
139
139
|
|
|
140
140
|
## Global Services & domain-id
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
KooCLI resolves the account/domain-id automatically from a valid AK/SK, so global services (BSS, IAM, CDN) normally do **not** need an explicit `--cli-domain-id`.
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
If a command fails with `[USE_ERROR]...缺少必填参数 cli-domain-id`, the real cause is almost always **invalid or expired credentials** (`APIGW.0301 Incorrect IAM authentication information`): KooCLI's internal account-id lookup failed and it misreports that as a missing domain-id. Fix the credentials instead (`npx huaweicloud-devkit auth init`).
|
|
145
|
+
|
|
146
|
+
Some IAM operations take a genuine business parameter for the account-id (e.g. `--domain_id`, `--agency.domain_id`, `--agency.trust_domain_id`, `--agency_urn`). Discover those with `--help`; get the account-id value with:
|
|
145
147
|
|
|
146
148
|
```bash
|
|
147
|
-
hcloud STS GetCallerIdentity --cli-region=<region>
|
|
148
|
-
# → account_id IS the domain-id; retry the original call with --cli-domain-id=<account_id>
|
|
149
|
+
hcloud STS GetCallerIdentity --cli-region=<region> # account_id = the account/domain-id
|
|
149
150
|
```
|
|
150
151
|
|
|
151
|
-
- `<region>` is the profile's current region.
|
|
152
|
+
- `<region>` is the profile's current region. STS is not deployed in `cn-north-1` — use another region if needed.
|
|
152
153
|
- Temporary credentials (AK/SK + security token) work too: add `--cli-security-token=<token>`.
|
|
153
|
-
- Prefer `STS GetCallerIdentity` over `IAM KeystoneListAuthDomains` — the latter only works with permanent credentials and outside `cn-north-1`; it fails under temporary credentials.
|
|
154
|
-
- `cli-domain-id` is a KooCLI CLI requirement, not a Huawei Cloud API requirement: the raw `GET /v3/auth/domains` can be signed with just AK/SK.
|
|
155
154
|
|
|
156
155
|
## Preferred Toolkit Tools
|
|
157
156
|
|
|
@@ -12,7 +12,7 @@ Huawei Cloud comprises 200+ services. This skill is the entry point for ANY acti
|
|
|
12
12
|
|
|
13
13
|
## 会话启动
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
首次操作前先调用 `huaweicloud_check_update` 检查插件版本;若未先行检查,插件会在使用中收到服务端升级提示。
|
|
16
16
|
|
|
17
17
|
1. 如果 `updateAvailable == true` 且 `dismissed == false`,先询问用户是否升级
|
|
18
18
|
2. 用户同意 → 调用 `huaweicloud_upgrade`,完成后提示重启会话
|
|
@@ -50,19 +50,19 @@ Use evidence before fixes. Do not guess service behavior when request IDs, regio
|
|
|
50
50
|
|
|
51
51
|
## Common Error Codes
|
|
52
52
|
|
|
53
|
-
| Error | Likely Cause
|
|
54
|
-
| --------------------------------------- |
|
|
55
|
-
| AuthFailure / 401 | AK/SK invalid or expired
|
|
56
|
-
| AccessDenied / 403 | IAM permission missing
|
|
57
|
-
| NoSuchKey / 404 | Resource not found
|
|
58
|
-
| QuotaExceeded | Account limit reached
|
|
59
|
-
| [USE_ERROR] 不正确的参数 | Wrong param name
|
|
60
|
-
| `[USE_ERROR]缺少必填参数 cli-domain-id` |
|
|
61
|
-
| Ecs.0005 | Flavor-image mismatch
|
|
62
|
-
| FSS.0400 | FunctionGraph latest version error
|
|
63
|
-
| FSS.1417 | DEDICATEDGATEWAY missing params
|
|
64
|
-
| APIC.7201 | Missing security_group_id
|
|
65
|
-
| [NETWORK_ERROR] | Transient network failure
|
|
53
|
+
| Error | Likely Cause | Fix |
|
|
54
|
+
| --------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
55
|
+
| AuthFailure / 401 | AK/SK invalid or expired | Regenerate AK/SK, re-run `npx huaweicloud-devkit auth init` |
|
|
56
|
+
| AccessDenied / 403 | IAM permission missing | Check `huawei-iam` skill, add required policy action |
|
|
57
|
+
| NoSuchKey / 404 | Resource not found | Verify resource ID, region, and project_id |
|
|
58
|
+
| QuotaExceeded | Account limit reached | Request quota increase in console |
|
|
59
|
+
| [USE_ERROR] 不正确的参数 | Wrong param name | Run `--help`, check `--param=value` format and nested prefix |
|
|
60
|
+
| `[USE_ERROR]缺少必填参数 cli-domain-id` | Invalid/expired credentials — KooCLI's account-id lookup failed (APIGW.0301) | Re-run `npx huaweicloud-devkit auth init`; KooCLI auto-resolves domain-id once creds are valid |
|
|
61
|
+
| Ecs.0005 | Flavor-image mismatch | Check image `__support_*` against flavor virtualization type |
|
|
62
|
+
| FSS.0400 | FunctionGraph latest version error | Strip `:latest` from function URN |
|
|
63
|
+
| FSS.1417 | DEDICATEDGATEWAY missing params | Add instance_id, group_id, protocol, env_name, env_id |
|
|
64
|
+
| APIC.7201 | Missing security_group_id | Add `--security_group_id` param for APIG CreateInstanceV2 |
|
|
65
|
+
| [NETWORK_ERROR] | Transient network failure | Retry with `maxRetries` param or wait and retry |
|
|
66
66
|
|
|
67
67
|
## KooCLI Error Types
|
|
68
68
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getCredentials } from './hwlink-api.mjs';
|
|
2
2
|
import { getProxyDispatcher } from '../proxy/proxy-agent.mjs';
|
|
3
3
|
import { cacheUserHash } from '../telemetry/telemetry.mjs';
|
|
4
|
+
import { readInstalledVersion } from '../update-check.mjs';
|
|
4
5
|
|
|
5
6
|
function getHdkitBaseUrl() {
|
|
6
7
|
return process.env.HDKITSERVICE_ENDPOINT || 'https://devkit.huaweicloud.com/rest/developer/server/hdkitservice/';
|
|
@@ -19,6 +20,7 @@ async function hdkitRequest(method, path, body, timeoutMs = 300000) {
|
|
|
19
20
|
'Content-Type': 'application/json',
|
|
20
21
|
'X-HW-AK': ak,
|
|
21
22
|
'X-HW-SK': sk,
|
|
23
|
+
'X-HW-Client-Version': readInstalledVersion() || '0.0.0',
|
|
22
24
|
};
|
|
23
25
|
if (securitytoken) {
|
|
24
26
|
headers['X-HW-Security-Token'] = securitytoken;
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from 'node:fs';
|
|
11
11
|
import { join, dirname, resolve } from 'node:path';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
|
-
import { homedir, platform } from 'node:os';
|
|
13
|
+
import { homedir, platform, tmpdir } from 'node:os';
|
|
14
14
|
import { createInterface } from 'node:readline';
|
|
15
15
|
import { spawnSync } from 'node:child_process';
|
|
16
16
|
import { randomUUID } from 'node:crypto';
|
|
@@ -3404,72 +3404,89 @@ async function promptGlobalCleanup() {
|
|
|
3404
3404
|
}
|
|
3405
3405
|
}
|
|
3406
3406
|
|
|
3407
|
+
function openclawStatus() {
|
|
3408
|
+
const cdPluginDir = openclawPluginsDir();
|
|
3409
|
+
const cdSkillsDir = openclawSkillsDir();
|
|
3410
|
+
console.log(
|
|
3411
|
+
` MCP Server: ${existsSync(join(cdPluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
3412
|
+
);
|
|
3413
|
+
console.log(
|
|
3414
|
+
` Safety Policy: ${existsSync(join(cdPluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
3415
|
+
);
|
|
3416
|
+
let cdSkillCount = 0;
|
|
3417
|
+
if (existsSync(cdSkillsDir)) {
|
|
3418
|
+
cdSkillCount = readdirSync(cdSkillsDir, { withFileTypes: true }).filter(
|
|
3419
|
+
(d) => d.isDirectory() && d.name.startsWith('huawei'),
|
|
3420
|
+
).length;
|
|
3421
|
+
}
|
|
3422
|
+
console.log(
|
|
3423
|
+
` Skills: ${cdSkillCount > 0 ? `\x1b[32m${cdSkillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
|
|
3424
|
+
);
|
|
3425
|
+
}
|
|
3426
|
+
|
|
3427
|
+
function codexSectionStatus() {
|
|
3428
|
+
if (!hasCodexCLI()) {
|
|
3429
|
+
console.log(' \x1b[33mCodex CLI not found.\x1b[0m');
|
|
3430
|
+
} else {
|
|
3431
|
+
console.log(` Plugin: ${codexStatus() ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3407
3435
|
async function cmdStatus() {
|
|
3408
|
-
const
|
|
3436
|
+
const scope = parseTarget();
|
|
3409
3437
|
console.log(BANNER);
|
|
3410
3438
|
console.log(`HuaweiCloud DevKit Status\n`);
|
|
3411
3439
|
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
console.log('\n[Hermes Agent]');
|
|
3438
|
-
hermesStatus();
|
|
3439
|
-
}
|
|
3440
|
-
if (target === 'openclaw' || target === 'all') {
|
|
3441
|
-
console.log('\n[OpenClaw]');
|
|
3442
|
-
const cdPluginDir = openclawPluginsDir();
|
|
3443
|
-
const cdSkillsDir = openclawSkillsDir();
|
|
3444
|
-
console.log(
|
|
3445
|
-
` MCP Server: ${existsSync(join(cdPluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
3446
|
-
);
|
|
3447
|
-
console.log(
|
|
3448
|
-
` Safety Policy: ${existsSync(join(cdPluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
3449
|
-
);
|
|
3450
|
-
let cdSkillCount = 0;
|
|
3451
|
-
if (existsSync(cdSkillsDir)) {
|
|
3452
|
-
cdSkillCount = readdirSync(cdSkillsDir, { withFileTypes: true }).filter(
|
|
3453
|
-
(d) => d.isDirectory() && d.name.startsWith('huawei'),
|
|
3454
|
-
).length;
|
|
3455
|
-
}
|
|
3456
|
-
console.log(
|
|
3457
|
-
` Skills: ${cdSkillCount > 0 ? `\x1b[32m${cdSkillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
|
|
3458
|
-
);
|
|
3459
|
-
}
|
|
3460
|
-
if (target === 'atomcode' || target === 'all') {
|
|
3461
|
-
console.log('\n[AtomCode]');
|
|
3462
|
-
atomcodeStatus();
|
|
3463
|
-
}
|
|
3464
|
-
if (target === 'codex' || target === 'all') {
|
|
3465
|
-
console.log('\n[Codex]');
|
|
3466
|
-
if (!hasCodexCLI()) {
|
|
3467
|
-
console.log(' \x1b[33mCodex CLI not found.\x1b[0m');
|
|
3468
|
-
} else {
|
|
3469
|
-
console.log(` Plugin: ${codexStatus() ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
|
|
3440
|
+
const sections = [
|
|
3441
|
+
{ name: 'OpenCode', run: opencodeStatus, on: (target) => target === 'opencode' || target === 'all' },
|
|
3442
|
+
{ name: 'CodeArts', run: codeartsStatus, on: (target) => target === 'codearts' || target === 'all' },
|
|
3443
|
+
{ name: 'CodeArts Work', run: codeartsWorkStatus, on: (target) => target === 'codearts-work' || target === 'all' },
|
|
3444
|
+
{ name: 'WorkBuddy', run: workbuddyStatus, on: (target) => target === 'workbuddy' || target === 'all' },
|
|
3445
|
+
{ name: 'DSH', run: dshStatus, on: (target) => target === 'dsh' || target === 'all' },
|
|
3446
|
+
{ name: 'OfficeAce', run: officeaceStatus, on: (target) => target === 'officeace' || target === 'all' },
|
|
3447
|
+
{ name: 'Hermes Agent', run: hermesStatus, on: (target) => target === 'hermes' || target === 'all' },
|
|
3448
|
+
{ name: 'OpenClaw', run: openclawStatus, on: (target) => target === 'openclaw' || target === 'all' },
|
|
3449
|
+
{ name: 'AtomCode', run: atomcodeStatus, on: (target) => target === 'atomcode' || target === 'all' },
|
|
3450
|
+
{ name: 'Codex', run: codexSectionStatus, on: (target) => target === 'codex' || target === 'all' },
|
|
3451
|
+
];
|
|
3452
|
+
|
|
3453
|
+
// Capture each section's output so results can be grouped by install state
|
|
3454
|
+
// (installed first) instead of the fixed agent order.
|
|
3455
|
+
const originalLog = console.log;
|
|
3456
|
+
const results = [];
|
|
3457
|
+
for (const section of sections) {
|
|
3458
|
+
if (!section.on(scope)) continue;
|
|
3459
|
+
const lines = [];
|
|
3460
|
+
console.log = (...args) => lines.push(args.map((a) => String(a)).join(' '));
|
|
3461
|
+
try {
|
|
3462
|
+
section.run();
|
|
3463
|
+
} finally {
|
|
3464
|
+
console.log = originalLog;
|
|
3470
3465
|
}
|
|
3466
|
+
const plain = lines.join('\n');
|
|
3467
|
+
const hasInstalled = /Installed/.test(plain);
|
|
3468
|
+
const hasMissing = /Not installed|not found/i.test(plain);
|
|
3469
|
+
const state = hasInstalled && hasMissing ? 'partial' : hasInstalled ? 'installed' : hasMissing ? 'not' : 'unknown';
|
|
3470
|
+
results.push({ name: section.name, lines, state });
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
const stateOrder = { installed: 0, partial: 1, unknown: 2, not: 3 };
|
|
3474
|
+
results.sort((a, b) => stateOrder[a.state] - stateOrder[b.state]);
|
|
3475
|
+
|
|
3476
|
+
const namesIn = (state) => results.filter((r) => r.state === state).map((r) => r.name);
|
|
3477
|
+
const summaryParts = [];
|
|
3478
|
+
if (namesIn('installed').length) summaryParts.push(`已安装: ${namesIn('installed').join(', ')}`);
|
|
3479
|
+
if (namesIn('partial').length) summaryParts.push(`部分安装: ${namesIn('partial').join(', ')}`);
|
|
3480
|
+
if (namesIn('not').length) summaryParts.push(`未安装: ${namesIn('not').join(', ')}`);
|
|
3481
|
+
if (summaryParts.length) console.log(` ${summaryParts.join(' | ')}\n`);
|
|
3482
|
+
|
|
3483
|
+
for (const result of results) {
|
|
3484
|
+
console.log(`[${result.name}]`);
|
|
3485
|
+
for (const line of result.lines) console.log(line);
|
|
3486
|
+
console.log('');
|
|
3471
3487
|
}
|
|
3472
|
-
|
|
3488
|
+
|
|
3489
|
+
console.log('Environment:');
|
|
3473
3490
|
console.log(` Node.js: ${process.version}`);
|
|
3474
3491
|
console.log(` Platform: ${platform()}`);
|
|
3475
3492
|
}
|
|
@@ -3812,6 +3829,7 @@ async function cmdDoctor() {
|
|
|
3812
3829
|
{ path: join(codeartsPluginsDir(), '.installed'), name: 'CodeArts' },
|
|
3813
3830
|
{ path: join(codeartsWorkPluginsDir(), '.installed'), name: 'CodeArts Work' },
|
|
3814
3831
|
{ path: join(dshPluginsDir(), '.installed'), name: 'DSH' },
|
|
3832
|
+
{ path: join(hermesPluginsDir(), '.installed'), name: 'Hermes Agent' },
|
|
3815
3833
|
{ path: join(officeacePluginsDir(), '.installed'), name: 'OfficeAce' },
|
|
3816
3834
|
{ path: join(atomcodePluginsDir(), '.installed'), name: 'AtomCode' },
|
|
3817
3835
|
];
|
|
@@ -4033,10 +4051,6 @@ async function cmdUpdate() {
|
|
|
4033
4051
|
console.log(`\x1b[33mMCP 工具在重启各 agent 会话后才生效。\x1b[0m`);
|
|
4034
4052
|
return;
|
|
4035
4053
|
}
|
|
4036
|
-
|
|
4037
|
-
await cmdUninstall();
|
|
4038
|
-
console.log('');
|
|
4039
|
-
await cmdInstall();
|
|
4040
4054
|
}
|
|
4041
4055
|
|
|
4042
4056
|
async function cmdReinstall() {
|
|
@@ -4167,32 +4181,102 @@ async function cmdInstallHcloud() {
|
|
|
4167
4181
|
printSandboxWarning('沙箱模式拦截了 KooCLI 自动安装(无法创建/写入安装目录)。');
|
|
4168
4182
|
}
|
|
4169
4183
|
}
|
|
4170
|
-
} else if (os === 'linux') {
|
|
4171
|
-
|
|
4172
|
-
const pkg =
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
);
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4184
|
+
} else if (os === 'linux' || os === 'darwin') {
|
|
4185
|
+
const label = os === 'linux' ? 'Linux' : 'macOS';
|
|
4186
|
+
const pkg =
|
|
4187
|
+
os === 'linux'
|
|
4188
|
+
? arch === 'arm64'
|
|
4189
|
+
? 'linux-arm64'
|
|
4190
|
+
: 'linux-amd64'
|
|
4191
|
+
: arch === 'arm64'
|
|
4192
|
+
? 'mac-arm64'
|
|
4193
|
+
: 'mac-amd64';
|
|
4194
|
+
const url = `${baseUrl}/huaweicloud-cli-${pkg}.tar.gz`;
|
|
4195
|
+
const binDir = os === 'linux' ? join(homedir(), '.local', 'bin') : '/usr/local/bin';
|
|
4196
|
+
const binPath = join(binDir, 'hcloud');
|
|
4197
|
+
|
|
4198
|
+
const printManual = () => {
|
|
4199
|
+
console.log(`\n[${label}] Manual install (pinned to KooCLI ${kooCliVersion || ''}):`);
|
|
4200
|
+
console.log(` curl -LO "${url}"`);
|
|
4201
|
+
console.log(` tar -zxvf huaweicloud-cli-${pkg}.tar.gz`);
|
|
4202
|
+
console.log(` mv hcloud ${binDir}/`);
|
|
4203
|
+
console.log(` hcloud version`);
|
|
4204
|
+
console.log(`\n Or one-liner (installs LATEST, may differ from pinned ${kooCliVersion || ''}):`);
|
|
4205
|
+
console.log(` curl -sSL ${latestUrl}/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y`);
|
|
4206
|
+
console.log(
|
|
4207
|
+
` \x1b[33m⚠️ The one-liner installs the latest KooCLI. If \`hcloud version\` != ${kooCliVersion}, doctor/check_cli will warn — use the pinned download above instead.\x1b[0m`,
|
|
4208
|
+
);
|
|
4209
|
+
console.log(
|
|
4210
|
+
`\nFull guide: https://support.huaweicloud.com/qs-hcli/hcli_02_003_0${os === 'linux' ? '2' : '3'}.html`,
|
|
4211
|
+
);
|
|
4212
|
+
};
|
|
4213
|
+
|
|
4214
|
+
if (detectCodeartsSandbox() === 'sandbox') {
|
|
4215
|
+
printSandboxWarning('沙箱模式拦截了 KooCLI 自动安装(无法创建/写入安装目录)。');
|
|
4216
|
+
printManual();
|
|
4217
|
+
} else {
|
|
4218
|
+
console.log(`[${label}] Auto-installing to ${binDir}...`);
|
|
4219
|
+
try {
|
|
4220
|
+
const tmpTar = join(tmpdir(), `huaweicloud-cli-${pkg}.tar.gz`);
|
|
4221
|
+
console.log(` Downloading ${url}...`);
|
|
4222
|
+
const dl = spawnSync('curl', ['-fL', url, '-o', tmpTar], { stdio: 'inherit', timeout: 180000 });
|
|
4223
|
+
if (dl.status !== 0) throw new Error(`下载失败 (curl exit ${dl.status})`);
|
|
4224
|
+
|
|
4225
|
+
console.log(' Extracting...');
|
|
4226
|
+
const ex = spawnSync('tar', ['-xzf', tmpTar, '-C', tmpdir()], { stdio: 'inherit', timeout: 60000 });
|
|
4227
|
+
if (ex.status !== 0) throw new Error(`解压失败 (tar exit ${ex.status})`);
|
|
4228
|
+
rmSync(tmpTar, { force: true });
|
|
4229
|
+
const extracted = join(tmpdir(), 'hcloud');
|
|
4230
|
+
if (!existsSync(extracted)) throw new Error('hcloud 二进制未生成,可能已被安全软件拦截');
|
|
4231
|
+
|
|
4232
|
+
try {
|
|
4233
|
+
mkdirSync(binDir, { recursive: true });
|
|
4234
|
+
} catch {}
|
|
4235
|
+
console.log(' Installing...');
|
|
4236
|
+
let mv = spawnSync('mv', [extracted, binPath], { stdio: 'inherit', timeout: 30000 });
|
|
4237
|
+
if (mv.status !== 0) {
|
|
4238
|
+
console.log(' 目标目录需要权限,尝试 sudo mv...');
|
|
4239
|
+
mv = spawnSync('sudo', ['mv', extracted, binPath], { stdio: 'inherit', timeout: 30000 });
|
|
4240
|
+
if (mv.status !== 0) throw new Error(`安装失败 (mv exit ${mv.status})`);
|
|
4241
|
+
}
|
|
4242
|
+
|
|
4243
|
+
console.log(`\n\x1b[32mInstall complete.\x1b[0m`);
|
|
4244
|
+
console.log(` Verify: ${binPath} version`);
|
|
4245
|
+
|
|
4246
|
+
// Ask user before accepting the privacy agreement — never auto-accept.
|
|
4247
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
4248
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
4249
|
+
const agree = await new Promise((resolve) => {
|
|
4250
|
+
rl.question('\n KooCLI requires accepting its privacy agreement. Do you accept? (y/N) ', (answer) => {
|
|
4251
|
+
rl.close();
|
|
4252
|
+
resolve(/^\s*y\s*$/i.test(answer));
|
|
4253
|
+
});
|
|
4254
|
+
});
|
|
4255
|
+
if (agree) {
|
|
4256
|
+
const r = spawnSync(binPath, ['version'], {
|
|
4257
|
+
input: 'y\n',
|
|
4258
|
+
encoding: 'utf8',
|
|
4259
|
+
timeout: 15000,
|
|
4260
|
+
});
|
|
4261
|
+
console.log(
|
|
4262
|
+
r.status === 0
|
|
4263
|
+
? ' \x1b[32mPrivacy agreement accepted. KooCLI ready.\x1b[0m'
|
|
4264
|
+
: ' \x1b[33m无法写入配置目录。请在终端运行: hcloud version 并按提示操作\x1b[0m',
|
|
4265
|
+
);
|
|
4266
|
+
} else {
|
|
4267
|
+
console.log(' \x1b[33m请手动接受隐私协议:在终端运行 hcloud version 并按提示操作\x1b[0m');
|
|
4268
|
+
}
|
|
4269
|
+
} else {
|
|
4270
|
+
console.log(' \x1b[33m非交互会话:请稍后在终端运行 hcloud version 并按提示接受隐私协议\x1b[0m');
|
|
4271
|
+
}
|
|
4272
|
+
} catch (error) {
|
|
4273
|
+
console.log(`\n\x1b[33mAuto-install failed: ${error.message}\x1b[0m`);
|
|
4274
|
+
printManual();
|
|
4275
|
+
if (detectCodeartsSandbox() === 'sandbox') {
|
|
4276
|
+
printSandboxWarning('沙箱模式拦截了 KooCLI 自动安装(无法创建/写入安装目录)。');
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
}
|
|
4196
4280
|
}
|
|
4197
4281
|
|
|
4198
4282
|
console.log('\nAfter install, set HCLOUD_BIN if hcloud is not on PATH.');
|
|
@@ -68,7 +68,7 @@ export const AGENTS = [
|
|
|
68
68
|
id: 'officeace',
|
|
69
69
|
pathPatterns: ['/.office-claw/', '/.officeace/'],
|
|
70
70
|
envVars: ['OFFICEACE_SESSION_ID', 'OFFICE_CLAW_CONFIG_ROOT'],
|
|
71
|
-
clientNames: ['office-claw-mcp-connector-probe'],
|
|
71
|
+
clientNames: ['office-claw-mcp-connector-probe', 'officeace-agent'],
|
|
72
72
|
version: { type: 'officeace' },
|
|
73
73
|
},
|
|
74
74
|
{
|
|
@@ -94,6 +94,7 @@ export const AGENTS = [
|
|
|
94
94
|
id: 'cursor',
|
|
95
95
|
pathPatterns: ['/.cursor/', '/cursor/'],
|
|
96
96
|
envVars: ['CURSOR_SESSION_ID', 'CURSOR_GIT_WORKDIR'],
|
|
97
|
+
clientNames: ['cursor-vscode'],
|
|
97
98
|
version: null,
|
|
98
99
|
},
|
|
99
100
|
{
|