huaweicloud-devkit 1.0.2-dev.1 → 1.0.2-dev.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -2
- package/README.zh-CN.md +25 -2
- 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/.mcp.json +2 -1
- package/plugins/huaweicloud-core/safety/policy.json +16 -1
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +21 -0
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +67 -0
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +87 -0
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +74 -0
- package/plugins/huaweicloud-core/src/auth/service.mjs +63 -0
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +97 -0
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +153 -0
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +105 -0
- package/plugins/huaweicloud-core/src/setup-cli.mjs +513 -66
- package/plugins/huaweicloud-core/src/tools.mjs +166 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +427 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +132 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +227 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +202 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +158 -0
- package/plugins/huaweicloud-core/src/ws-exec/index.js +19 -0
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +338 -0
package/README.md
CHANGED
|
@@ -9,6 +9,10 @@ Help AI coding agents use Huawei Cloud safely and accurately — a single integr
|
|
|
9
9
|
|
|
10
10
|
Supports OpenCode, Codex, CodeArts Agent, and WorkBuddy.
|
|
11
11
|
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- Node.js >= 22
|
|
15
|
+
|
|
12
16
|
## Quick Start
|
|
13
17
|
|
|
14
18
|
### OpenCode
|
|
@@ -34,6 +38,15 @@ npx --yes huaweicloud-devkit install --target codex
|
|
|
34
38
|
|
|
35
39
|
> The Codex CLI must be installed first.
|
|
36
40
|
|
|
41
|
+
```bash
|
|
42
|
+
npx --yes huaweicloud-devkit doctor # self-check
|
|
43
|
+
npx --yes huaweicloud-devkit status --target codex
|
|
44
|
+
npx --yes huaweicloud-devkit update --target codex # update
|
|
45
|
+
npx --yes huaweicloud-devkit uninstall --target codex
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
> **Codex Desktop** (Windows): use `--target codex-desktop` with the same commands.
|
|
49
|
+
|
|
37
50
|
### CodeArts Agent
|
|
38
51
|
|
|
39
52
|
```bash
|
|
@@ -46,12 +59,13 @@ npx --yes huaweicloud-devkit install --target codearts
|
|
|
46
59
|
npx --yes huaweicloud-devkit install-hcloud # install KooCLI
|
|
47
60
|
npx --yes huaweicloud-devkit doctor # self-check
|
|
48
61
|
npx --yes huaweicloud-devkit status --target codearts
|
|
62
|
+
npx --yes huaweicloud-devkit update --target codearts
|
|
49
63
|
npx --yes huaweicloud-devkit uninstall --target codearts
|
|
50
64
|
```
|
|
51
65
|
|
|
52
66
|
> **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 → Permissions → Bash mode).
|
|
53
67
|
>
|
|
54
|
-
> **Authentication**: Run `
|
|
68
|
+
> **Authentication**: Run `npx huaweicloud-devkit auth init` to configure unified AK/SK credentials for KooCLI, OBS, and sandbox APIs.
|
|
55
69
|
|
|
56
70
|
### WorkBuddy
|
|
57
71
|
|
|
@@ -61,6 +75,15 @@ npx --yes huaweicloud-devkit install --target workbuddy
|
|
|
61
75
|
|
|
62
76
|
**Restart the session** after installation.
|
|
63
77
|
|
|
78
|
+
```bash
|
|
79
|
+
npx --yes huaweicloud-devkit doctor
|
|
80
|
+
npx --yes huaweicloud-devkit status --target workbuddy
|
|
81
|
+
npx --yes huaweicloud-devkit update --target workbuddy # update
|
|
82
|
+
npx --yes huaweicloud-devkit uninstall --target workbuddy
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
> **Updating**: `update` is incremental per agent — it refreshes only the installed files and leaves your config untouched. Use `update --target all` to update every installed agent at once.
|
|
86
|
+
|
|
64
87
|
### Other Agents
|
|
65
88
|
|
|
66
89
|
For agents that support the Model Context Protocol (MCP):
|
|
@@ -83,7 +106,7 @@ Then install:
|
|
|
83
106
|
npx --yes huaweicloud-devkit install
|
|
84
107
|
```
|
|
85
108
|
|
|
86
|
-
> **
|
|
109
|
+
> **Prerequisite:** Node.js >= 22. Run `npx huaweicloud-devkit auth init` for unified credentials.
|
|
87
110
|
|
|
88
111
|
## What It Does
|
|
89
112
|
|
package/README.zh-CN.md
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
支持 OpenCode、Codex、码道(CodeArts Agent)、WorkBuddy。
|
|
11
11
|
|
|
12
|
+
## 前置条件
|
|
13
|
+
|
|
14
|
+
- Node.js >= 22
|
|
15
|
+
|
|
12
16
|
## 快速开始
|
|
13
17
|
|
|
14
18
|
### OpenCode
|
|
@@ -34,6 +38,15 @@ npx --yes huaweicloud-devkit install --target codex
|
|
|
34
38
|
|
|
35
39
|
> 需要先安装 Codex CLI。
|
|
36
40
|
|
|
41
|
+
```bash
|
|
42
|
+
npx --yes huaweicloud-devkit doctor # 自检
|
|
43
|
+
npx --yes huaweicloud-devkit status --target codex
|
|
44
|
+
npx --yes huaweicloud-devkit update --target codex # 更新
|
|
45
|
+
npx --yes huaweicloud-devkit uninstall --target codex
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
> **Codex Desktop**(Windows):将 `--target codex` 换成 `--target codex-desktop`,命令相同。
|
|
49
|
+
|
|
37
50
|
### CodeArts Agent(码道)
|
|
38
51
|
|
|
39
52
|
```bash
|
|
@@ -46,12 +59,13 @@ npx --yes huaweicloud-devkit install --target codearts
|
|
|
46
59
|
npx --yes huaweicloud-devkit install-hcloud # 安装 KooCLI
|
|
47
60
|
npx --yes huaweicloud-devkit doctor # 自检
|
|
48
61
|
npx --yes huaweicloud-devkit status --target codearts
|
|
62
|
+
npx --yes huaweicloud-devkit update --target codearts
|
|
49
63
|
npx --yes huaweicloud-devkit uninstall --target codearts
|
|
50
64
|
```
|
|
51
65
|
|
|
52
66
|
> **沙箱模式**:码道默认沙箱模式会阻止 KooCLI 运行。`install-hcloud` 自动检测并给出指引——请在码道外终端安装使用 KooCLI,或在码道设置中关闭沙箱模式(设置 → 权限 → Bash 模式)。
|
|
53
67
|
>
|
|
54
|
-
> **认证**:执行 `
|
|
68
|
+
> **认证**:执行 `npx huaweicloud-devkit auth init`,统一配置 KooCLI、OBS 和沙箱接口所需的 AK/SK。
|
|
55
69
|
|
|
56
70
|
### WorkBuddy
|
|
57
71
|
|
|
@@ -61,6 +75,15 @@ npx --yes huaweicloud-devkit install --target workbuddy
|
|
|
61
75
|
|
|
62
76
|
安装后**重启会话**。
|
|
63
77
|
|
|
78
|
+
```bash
|
|
79
|
+
npx --yes huaweicloud-devkit doctor
|
|
80
|
+
npx --yes huaweicloud-devkit status --target workbuddy
|
|
81
|
+
npx --yes huaweicloud-devkit update --target workbuddy # 更新
|
|
82
|
+
npx --yes huaweicloud-devkit uninstall --target workbuddy
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
> **更新机制**:`update` 按 agent 增量更新,只刷新必要文件、不动你的配置文件。`update --target all` 可一次更新所有已安装的 agent。
|
|
86
|
+
|
|
64
87
|
### 其他 Agent
|
|
65
88
|
|
|
66
89
|
支持 MCP 协议的 Agent,手动配置:
|
|
@@ -83,7 +106,7 @@ npx --yes huaweicloud-devkit install --target workbuddy
|
|
|
83
106
|
npx --yes huaweicloud-devkit install
|
|
84
107
|
```
|
|
85
108
|
|
|
86
|
-
> **前置条件:**
|
|
109
|
+
> **前置条件:** Node.js >= 22。执行 `npx huaweicloud-devkit auth init` 完成统一认证。
|
|
87
110
|
|
|
88
111
|
## 功能特性
|
|
89
112
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-devkit",
|
|
3
|
-
"version": "1.0.2-dev.
|
|
3
|
+
"version": "1.0.2-dev.3",
|
|
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": [
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"mcpServers": "./.mcp.json",
|
|
21
21
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
22
22
|
"skills": "./skills/",
|
|
23
|
-
"version": "1.0.
|
|
23
|
+
"version": "1.0.2-dev.3",
|
|
24
24
|
"author": {
|
|
25
25
|
"name": "HuaweiCloud Mate",
|
|
26
26
|
"url": "https://github.com/huaweicloud"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huaweicloud-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2-dev.3",
|
|
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",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"mcpServers": "./.mcp.json",
|
|
21
21
|
"description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
|
|
22
22
|
"skills": "./skills/",
|
|
23
|
-
"version": "1.0.
|
|
23
|
+
"version": "1.0.2-dev.3",
|
|
24
24
|
"author": {
|
|
25
25
|
"name": "HuaweiCloud Mate",
|
|
26
26
|
"url": "https://github.com/huaweicloud"
|
|
@@ -82,5 +82,20 @@
|
|
|
82
82
|
"ls",
|
|
83
83
|
"dir",
|
|
84
84
|
"Get-ChildItem"
|
|
85
|
-
]
|
|
85
|
+
],
|
|
86
|
+
"blockedSandboxCommands": [
|
|
87
|
+
"rm\\s+-rf\\s+/",
|
|
88
|
+
"mkfs",
|
|
89
|
+
"dd\\s+if=",
|
|
90
|
+
":\\(\\)\\{:\\|:&\\};:",
|
|
91
|
+
"shutdown",
|
|
92
|
+
"reboot",
|
|
93
|
+
"init\\s+[06]",
|
|
94
|
+
"fdisk"
|
|
95
|
+
],
|
|
96
|
+
"sandboxWriteTools": [
|
|
97
|
+
"huaweicloud_sandbox_connect",
|
|
98
|
+
"huaweicloud_sandbox_credentials",
|
|
99
|
+
"huaweicloud_sandbox_release"
|
|
100
|
+
]
|
|
86
101
|
}
|
|
@@ -173,6 +173,27 @@
|
|
|
173
173
|
},
|
|
174
174
|
"message": "The generated plan may create high-cost or unbounded capacity.",
|
|
175
175
|
"remediation": "Use small preview defaults, explicit quotas, budget labels, and user approval before creating high-cost resources."
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"id": "hwc-sandbox-destructive-command",
|
|
179
|
+
"title": "Destructive command in sandbox terminal",
|
|
180
|
+
"category": "destructive",
|
|
181
|
+
"severity": "deny",
|
|
182
|
+
"stages": ["command"],
|
|
183
|
+
"match": {
|
|
184
|
+
"any": [
|
|
185
|
+
{ "field": "text", "regex": "rm\\s+-rf\\s+/" },
|
|
186
|
+
{ "field": "text", "regex": "mkfs" },
|
|
187
|
+
{ "field": "text", "regex": "dd\\s+if=" },
|
|
188
|
+
{ "field": "text", "regex": ":\\\\(\\\\)\\\\{[^}]*\\\\|[^}]*&[^}]*\\\\}[^;]*;:" },
|
|
189
|
+
{ "field": "text", "regex": "\\bshutdown\\b" },
|
|
190
|
+
{ "field": "text", "regex": "\\breboot\\b" },
|
|
191
|
+
{ "field": "text", "regex": "\\binit\\s+[06]\\b" },
|
|
192
|
+
{ "field": "text", "regex": "\\bfdisk\\b" }
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
"message": "The sandbox terminal command is destructively dangerous and could destroy the workspace.",
|
|
196
|
+
"remediation": "Use targeted deletions instead of recursive root removal. Avoid disk format, fork bombs, and system shutdown commands in sandbox terminals."
|
|
176
197
|
}
|
|
177
198
|
]
|
|
178
199
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: huawei-sandbox
|
|
3
|
+
description: "Use when creating, connecting, or managing Huawei Cloud Sandbox instances and workspace terminals. Covers sandbox lifecycle (check-user, sign-agreement, connect, release), terminal execution (one-shot and session-based), and credential injection. Triggers on: sandbox, workspace, terminal, hwlink, devstation, hdkitservice, remote exec. NOT for: ECS instances (use huawei-ecs), CCE clusters (use huawei-cce)."
|
|
4
|
+
version: 1
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Huawei Cloud Sandbox
|
|
8
|
+
|
|
9
|
+
**STOP - Do not answer from general knowledge.** Follow the procedure below.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace terminal execution. Covers sandbox lifecycle via hdkitservice API and remote terminal command execution via hwlink protocol.
|
|
14
|
+
|
|
15
|
+
## MCP Tools
|
|
16
|
+
|
|
17
|
+
### User Verification (Prerequisites)
|
|
18
|
+
|
|
19
|
+
| Tool | Purpose |
|
|
20
|
+
|------|---------|
|
|
21
|
+
| `huaweicloud_sandbox_check_user` | Check real-name verification and agreement signing status |
|
|
22
|
+
| `huaweicloud_sandbox_sign_agreement` | Sign unsigned/outdated agreements (required before connect) |
|
|
23
|
+
|
|
24
|
+
### Sandbox Lifecycle
|
|
25
|
+
|
|
26
|
+
| Tool | Purpose |
|
|
27
|
+
|------|---------|
|
|
28
|
+
| `huaweicloud_sandbox_connect` | Connect to sandbox (one user one instance, reuses existing if available) |
|
|
29
|
+
| `huaweicloud_sandbox_credentials` | Inject temporary AK/SK into a running sandbox |
|
|
30
|
+
| `huaweicloud_sandbox_release` | Shut down and delete a sandbox (idempotent) |
|
|
31
|
+
|
|
32
|
+
### Terminal Execution
|
|
33
|
+
|
|
34
|
+
| Tool | Purpose |
|
|
35
|
+
|------|---------|
|
|
36
|
+
| `huaweicloud_sandbox_exec_with_session` | Session-based execution (state persists) |
|
|
37
|
+
| `huaweicloud_sandbox_close_session` | Close a persistent terminal session |
|
|
38
|
+
|
|
39
|
+
## Workflow
|
|
40
|
+
|
|
41
|
+
1. **Check user**: `huaweicloud_sandbox_check_user` — verify `realname_verified` and `agreement_signed`
|
|
42
|
+
2. **Sign agreement** (if needed): `huaweicloud_sandbox_sign_agreement` — when `agreement_signed=false`
|
|
43
|
+
3. **Connect**: `huaweicloud_sandbox_connect` — returns `session_id`, `dev_stage_id`, `connection_id`, `connection_address`
|
|
44
|
+
4. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox
|
|
45
|
+
5. **Execute commands**: `huaweicloud_sandbox_exec_with_session` for interactive work
|
|
46
|
+
6. **Release**: `huaweicloud_sandbox_release` — cleans up sandbox and session
|
|
47
|
+
|
|
48
|
+
## Critical Warnings
|
|
49
|
+
|
|
50
|
+
| Trap | Why |
|
|
51
|
+
|------|-----|
|
|
52
|
+
| Agreement required first | `sandbox_connect` fails if user hasn't signed agreements; run `sandbox_check_user` first |
|
|
53
|
+
| Session state persists | `exec_with_session` preserves `cd`, env vars, aliases between calls |
|
|
54
|
+
| Destructive commands blocked | `rm -rf /`, `mkfs`, `dd if=`, fork bombs are denied by safety policy |
|
|
55
|
+
| Workspace ID = dev_stage_id | Use `dev_stage_id` from `sandbox_connect` as `workspace_id` for terminal exec |
|
|
56
|
+
| Node.js >= 22 required | Sandbox terminal uses built-in WebSocket (globalThis.WebSocket) |
|
|
57
|
+
|
|
58
|
+
## Environment Variables
|
|
59
|
+
|
|
60
|
+
| Variable | Required | Description |
|
|
61
|
+
|----------|----------|-------------|
|
|
62
|
+
| `HW_ACCESS_KEY` | Yes | Huawei Cloud AK |
|
|
63
|
+
| `HW_SECRET_KEY` | Yes | Huawei Cloud SK |
|
|
64
|
+
| `HW_SECURITY_TOKEN` | No | STS security token |
|
|
65
|
+
| `HW_WORKSPACE_ID` | No | Default workspace ID |
|
|
66
|
+
| `HDKITSERVICE_ENDPOINT` | No | hdkitservice API endpoint |
|
|
67
|
+
| `HWLINK_ENDPOINT` | No | DevStation API endpoint |
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
export const SUPPORTED_AGENT_TARGETS = ['opencode', 'codex', 'codex-desktop', 'codearts', 'workbuddy'];
|
|
7
|
+
|
|
8
|
+
function baseHome() {
|
|
9
|
+
return process.env.HUAWEICLOUD_HOME || homedir();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function opencodeConfigFile() {
|
|
13
|
+
const jsonc = join(baseHome(), '.config', 'opencode', 'opencode.jsonc');
|
|
14
|
+
if (existsSync(jsonc)) return jsonc;
|
|
15
|
+
return join(baseHome(), '.config', 'opencode', 'opencode.json');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function readJsonSafe(path) {
|
|
19
|
+
if (!existsSync(path)) return null;
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function opencodeRegistered() {
|
|
28
|
+
const path = opencodeConfigFile();
|
|
29
|
+
const cfg = readJsonSafe(path);
|
|
30
|
+
return Boolean(cfg?.mcp?.['huaweicloud-devkit']);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function codexDesktopRegistered() {
|
|
34
|
+
const path = join(baseHome(), '.codex', 'config.toml');
|
|
35
|
+
if (!existsSync(path)) return false;
|
|
36
|
+
try {
|
|
37
|
+
return readFileSync(path, 'utf8').includes('[mcp_servers.huaweicloud-devkit]');
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function codexCliRegistered() {
|
|
44
|
+
try {
|
|
45
|
+
const r = spawnSync('codex', ['plugin', 'list'], {
|
|
46
|
+
shell: false,
|
|
47
|
+
windowsHide: true,
|
|
48
|
+
stdio: 'pipe',
|
|
49
|
+
timeout: 10000,
|
|
50
|
+
});
|
|
51
|
+
const out = `${r.stdout || ''}${r.stderr || ''}`;
|
|
52
|
+
return out.includes('huaweicloud-core');
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function codeartsRegistered() {
|
|
59
|
+
const paths = [
|
|
60
|
+
join(baseHome(), '.codeartsdoer', 'mcp', 'mcp_settings.json'),
|
|
61
|
+
join(process.cwd(), '.codeartsdoer', 'mcp', 'mcp_settings.json'),
|
|
62
|
+
];
|
|
63
|
+
return paths.some((path) => {
|
|
64
|
+
const cfg = readJsonSafe(path);
|
|
65
|
+
return Boolean(cfg?.mcpServers?.['huaweicloud-devkit']);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function workbuddyRegistered() {
|
|
70
|
+
const cfg = readJsonSafe(join(baseHome(), '.workbuddy', 'mcp.json'));
|
|
71
|
+
return Boolean(cfg?.mcpServers?.['huaweicloud-devkit']);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getAgentRegistrationStatuses(target = 'all') {
|
|
75
|
+
const requested = target === 'all' ? SUPPORTED_AGENT_TARGETS : [target];
|
|
76
|
+
const result = { target, agents: {} };
|
|
77
|
+
for (const agent of requested) {
|
|
78
|
+
let configured = false;
|
|
79
|
+
if (agent === 'opencode') configured = opencodeRegistered();
|
|
80
|
+
if (agent === 'codex-desktop') configured = codexDesktopRegistered();
|
|
81
|
+
if (agent === 'codex') configured = codexCliRegistered();
|
|
82
|
+
if (agent === 'codearts') configured = codeartsRegistered();
|
|
83
|
+
if (agent === 'workbuddy') configured = workbuddyRegistered();
|
|
84
|
+
result.agents[agent] = { configured };
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
|
|
5
|
+
function baseHome() {
|
|
6
|
+
return process.env.HUAWEICLOUD_HOME || homedir();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function globalCredentialsPath() {
|
|
10
|
+
return join(baseHome(), '.config', 'huaweicloud', 'credentials.json');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function obsConfigPath() {
|
|
14
|
+
return join(baseHome(), '.obsutilconfig');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function readGlobalCredentials() {
|
|
18
|
+
const path = globalCredentialsPath();
|
|
19
|
+
if (!existsSync(path)) return null;
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function writeGlobalCredentials(credentials = {}) {
|
|
28
|
+
const path = globalCredentialsPath();
|
|
29
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
30
|
+
const payload = {
|
|
31
|
+
ak: String(credentials.ak || ''),
|
|
32
|
+
sk: String(credentials.sk || ''),
|
|
33
|
+
securityToken: String(credentials.securityToken || ''),
|
|
34
|
+
region: String(credentials.region || ''),
|
|
35
|
+
};
|
|
36
|
+
writeFileSync(path, JSON.stringify(payload, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
37
|
+
return path;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function writeObsConfig(credentials = {}) {
|
|
41
|
+
const region = String(credentials.region || '');
|
|
42
|
+
const ak = String(credentials.ak || '');
|
|
43
|
+
const sk = String(credentials.sk || '');
|
|
44
|
+
if (!region || !ak || !sk) {
|
|
45
|
+
throw new Error('region, ak, and sk are required to write OBS config');
|
|
46
|
+
}
|
|
47
|
+
const path = obsConfigPath();
|
|
48
|
+
const endpoint = credentials.endpoint || `https://obs.${region}.myhuaweicloud.com`;
|
|
49
|
+
const content = `[default]\r\nendpoint=${endpoint}\r\nak=${ak}\r\nsk=${sk}\r\n`;
|
|
50
|
+
writeFileSync(path, content, { encoding: 'utf8', mode: 0o600 });
|
|
51
|
+
return { path, endpoint };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function resolveCredentials(options = {}) {
|
|
55
|
+
let ak = process.env.HW_ACCESS_KEY;
|
|
56
|
+
let sk = process.env.HW_SECRET_KEY;
|
|
57
|
+
let securityToken = process.env.HW_SECURITY_TOKEN;
|
|
58
|
+
let region = process.env.HW_REGION || process.env.HUAWEICLOUD_REGION || '';
|
|
59
|
+
|
|
60
|
+
const stored = readGlobalCredentials();
|
|
61
|
+
if (stored) {
|
|
62
|
+
if (!ak && stored.ak) ak = stored.ak;
|
|
63
|
+
if (!sk && stored.sk) sk = stored.sk;
|
|
64
|
+
if (!securityToken && stored.securityToken) securityToken = stored.securityToken;
|
|
65
|
+
if (!region && stored.region) region = stored.region;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!ak || !sk) {
|
|
69
|
+
if (options.allowMissing) return null;
|
|
70
|
+
throw new Error('Huawei Cloud credentials are not configured. Run "npx huaweicloud-devkit auth init" or set HW_ACCESS_KEY/HW_SECRET_KEY.');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { ak, sk, securityToken, region };
|
|
74
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { getAgentRegistrationStatuses } from './agent-registration.mjs';
|
|
4
|
+
import { globalCredentialsPath, obsConfigPath, readGlobalCredentials, writeObsConfig } from './credentials.mjs';
|
|
5
|
+
|
|
6
|
+
function hcloudInstalled() {
|
|
7
|
+
const bin = process.env.HCLOUD_BIN || 'hcloud';
|
|
8
|
+
try {
|
|
9
|
+
const r = spawnSync(`"${bin}" version`, [], {
|
|
10
|
+
shell: true,
|
|
11
|
+
windowsHide: true,
|
|
12
|
+
stdio: 'pipe',
|
|
13
|
+
timeout: 5000,
|
|
14
|
+
});
|
|
15
|
+
const out = `${r.stdout || ''}${r.stderr || ''}`;
|
|
16
|
+
return r.status === 0 && /KooCLI|Current.*version|当前KooCLI/i.test(out);
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getAuthStatus(target = 'all') {
|
|
23
|
+
const credentials = readGlobalCredentials();
|
|
24
|
+
return {
|
|
25
|
+
target,
|
|
26
|
+
credentialsConfigured: Boolean(credentials?.ak && credentials?.sk),
|
|
27
|
+
credentialsPath: globalCredentialsPath(),
|
|
28
|
+
obsConfigured: existsSync(obsConfigPath()),
|
|
29
|
+
obsConfigPath: obsConfigPath(),
|
|
30
|
+
kooCliInstalled: hcloudInstalled(),
|
|
31
|
+
agents: getAgentRegistrationStatuses(target).agents,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function syncAuth(target = 'all') {
|
|
36
|
+
const credentials = readGlobalCredentials();
|
|
37
|
+
if (!credentials?.ak || !credentials?.sk) {
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
error: 'Global credentials are not configured.',
|
|
41
|
+
nextStep: 'Run "npx huaweicloud-devkit auth init" first.',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let obs = null;
|
|
46
|
+
try {
|
|
47
|
+
obs = writeObsConfig(credentials);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
error: error.message,
|
|
52
|
+
nextStep: 'Run "npx huaweicloud-devkit auth init" to refresh credentials and region.',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
ok: true,
|
|
58
|
+
obs: { configured: true, path: obs.path, endpoint: obs.endpoint },
|
|
59
|
+
credentialsConfigured: true,
|
|
60
|
+
agents: getAgentRegistrationStatuses(target).agents,
|
|
61
|
+
note: 'OBS credentials were synced from the global credential vault. Agent MCP registration is managed by "npx huaweicloud-devkit install --target <agent>".',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { getCredentials } from './hwlink-api.mjs';
|
|
2
|
+
|
|
3
|
+
const HDKIT_BASE_URL =
|
|
4
|
+
process.env.HDKITSERVICE_ENDPOINT ||
|
|
5
|
+
'https://devkit.huaweicloud.com/rest/developer/server/hdkitservice/';
|
|
6
|
+
|
|
7
|
+
async function hdkitRequest(method, path, body, timeoutMs = 300000) {
|
|
8
|
+
const { ak, sk, securitytoken } = getCredentials();
|
|
9
|
+
|
|
10
|
+
const headers = {
|
|
11
|
+
'Content-Type': 'application/json',
|
|
12
|
+
'X-HW-AK': ak,
|
|
13
|
+
'X-HW-SK': sk,
|
|
14
|
+
};
|
|
15
|
+
if (securitytoken) {
|
|
16
|
+
headers['X-HW-Security-Token'] = securitytoken;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const url = `${HDKIT_BASE_URL}${path}`;
|
|
20
|
+
const controller = new AbortController();
|
|
21
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
22
|
+
|
|
23
|
+
let resp;
|
|
24
|
+
try {
|
|
25
|
+
resp = await fetch(url, {
|
|
26
|
+
method,
|
|
27
|
+
headers,
|
|
28
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
29
|
+
signal: controller.signal,
|
|
30
|
+
});
|
|
31
|
+
} finally {
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const text = await resp.text();
|
|
36
|
+
let data;
|
|
37
|
+
try {
|
|
38
|
+
data = JSON.parse(text);
|
|
39
|
+
} catch {
|
|
40
|
+
throw new Error(`hdkitservice returned non-JSON (status ${resp.status}): ${text.slice(0, 200)}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!resp.ok) {
|
|
44
|
+
const err = new Error(
|
|
45
|
+
data.message || `hdkitservice error: ${data.code || resp.status}`
|
|
46
|
+
);
|
|
47
|
+
err.code = data.code;
|
|
48
|
+
err.status = resp.status;
|
|
49
|
+
err.traceId = data.trace_id;
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return data;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function hdkitCheckUser() {
|
|
57
|
+
return await hdkitRequest('GET', 'check-user', undefined, 30000);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function hdkitSignAgreement() {
|
|
61
|
+
return await hdkitRequest('POST', 'sign-agreement', {});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function hdkitConnect(options = {}) {
|
|
65
|
+
const body = {};
|
|
66
|
+
if (options.source) body.source = options.source;
|
|
67
|
+
if (options.env) body.env = options.env;
|
|
68
|
+
if (options.git) body.git = options.git;
|
|
69
|
+
if (options.template_id) body.template_id = options.template_id;
|
|
70
|
+
if (options.flavor_id) body.flavor_id = options.flavor_id;
|
|
71
|
+
|
|
72
|
+
return await hdkitRequest('POST', 'connect', body);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function hdkitCredentials(sessionId, devStageId, enableSts = true) {
|
|
76
|
+
const body = { enable_sts: enableSts };
|
|
77
|
+
if (sessionId) body.session_id = sessionId;
|
|
78
|
+
if (devStageId) body.dev_stage_id = devStageId;
|
|
79
|
+
|
|
80
|
+
if (!sessionId && !devStageId) {
|
|
81
|
+
throw new Error('session_id or dev_stage_id is required');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return await hdkitRequest('POST', 'credentials', body);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function hdkitRelease(sessionId, devStageId) {
|
|
88
|
+
const body = {};
|
|
89
|
+
if (sessionId) body.session_id = sessionId;
|
|
90
|
+
if (devStageId) body.dev_stage_id = devStageId;
|
|
91
|
+
|
|
92
|
+
if (!sessionId && !devStageId) {
|
|
93
|
+
throw new Error('session_id or dev_stage_id is required');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return await hdkitRequest('POST', 'release', body);
|
|
97
|
+
}
|