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.
Files changed (25) hide show
  1. package/README.md +25 -2
  2. package/README.zh-CN.md +25 -2
  3. package/package.json +1 -1
  4. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
  5. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
  6. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
  7. package/plugins/huaweicloud-core/.mcp.json +2 -1
  8. package/plugins/huaweicloud-core/safety/policy.json +16 -1
  9. package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +21 -0
  10. package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +67 -0
  11. package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +87 -0
  12. package/plugins/huaweicloud-core/src/auth/credentials.mjs +74 -0
  13. package/plugins/huaweicloud-core/src/auth/service.mjs +63 -0
  14. package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +97 -0
  15. package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +153 -0
  16. package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +105 -0
  17. package/plugins/huaweicloud-core/src/setup-cli.mjs +513 -66
  18. package/plugins/huaweicloud-core/src/tools.mjs +166 -0
  19. package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +427 -0
  20. package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +132 -0
  21. package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +227 -0
  22. package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +202 -0
  23. package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +158 -0
  24. package/plugins/huaweicloud-core/src/ws-exec/index.js +19 -0
  25. 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 `hcloud configure init` to configure AK/SK and region.
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
- > **Prerequisites:** [KooCLI](https://support.huaweicloud.com/qs-hcli/hcli_02_003.html) (`hcloud`) installed and authenticated. Node.js >= 20 required.
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
- > **认证**:执行 `hcloud configure init` 配置 AK/SK 与区域。
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
- > **前置条件:** 需要安装 [KooCLI](https://support.huaweicloud.com/qs-hcli/hcli_02_003.html)(`hcloud`)并完成认证。Node.js >= 20。
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.1",
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.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.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.0",
23
+ "version": "1.0.2-dev.3",
24
24
  "author": {
25
25
  "name": "HuaweiCloud Mate",
26
26
  "url": "https://github.com/huaweicloud"
@@ -7,7 +7,8 @@
7
7
  ],
8
8
  "cwd": ".",
9
9
  "env": {
10
- "HUAWEICLOUD_AGENT_TOOLKIT_MODE": "local"
10
+ "HUAWEICLOUD_AGENT_TOOLKIT_MODE": "local",
11
+ "HDKITSERVICE_ENDPOINT": ""
11
12
  }
12
13
  }
13
14
  }
@@ -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
+ }