huaweicloud-devkit 1.0.2-dev.2 → 1.0.2-dev.4
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/skills/huawei-sandbox/SKILL.md +1 -3
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +1 -1
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +4 -4
- package/plugins/huaweicloud-core/src/setup-cli.mjs +335 -66
- package/plugins/huaweicloud-core/src/tools.mjs +1 -22
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.4",
|
|
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.4",
|
|
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.4",
|
|
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.4",
|
|
24
24
|
"author": {
|
|
25
25
|
"name": "HuaweiCloud Mate",
|
|
26
26
|
"url": "https://github.com/huaweicloud"
|
|
@@ -33,7 +33,6 @@ Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace t
|
|
|
33
33
|
|
|
34
34
|
| Tool | Purpose |
|
|
35
35
|
|------|---------|
|
|
36
|
-
| `huaweicloud_sandbox_exec` | One-shot command execution (no session reuse) |
|
|
37
36
|
| `huaweicloud_sandbox_exec_with_session` | Session-based execution (state persists) |
|
|
38
37
|
| `huaweicloud_sandbox_close_session` | Close a persistent terminal session |
|
|
39
38
|
|
|
@@ -43,7 +42,7 @@ Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace t
|
|
|
43
42
|
2. **Sign agreement** (if needed): `huaweicloud_sandbox_sign_agreement` — when `agreement_signed=false`
|
|
44
43
|
3. **Connect**: `huaweicloud_sandbox_connect` — returns `session_id`, `dev_stage_id`, `connection_id`, `connection_address`
|
|
45
44
|
4. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox
|
|
46
|
-
5. **Execute commands**: `huaweicloud_sandbox_exec_with_session` for interactive work
|
|
45
|
+
5. **Execute commands**: `huaweicloud_sandbox_exec_with_session` for interactive work
|
|
47
46
|
6. **Release**: `huaweicloud_sandbox_release` — cleans up sandbox and session
|
|
48
47
|
|
|
49
48
|
## Critical Warnings
|
|
@@ -52,7 +51,6 @@ Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace t
|
|
|
52
51
|
|------|-----|
|
|
53
52
|
| Agreement required first | `sandbox_connect` fails if user hasn't signed agreements; run `sandbox_check_user` first |
|
|
54
53
|
| Session state persists | `exec_with_session` preserves `cd`, env vars, aliases between calls |
|
|
55
|
-
| No session reuse in `exec` | Each `exec` call creates a new connection; previous state is lost |
|
|
56
54
|
| Destructive commands blocked | `rm -rf /`, `mkfs`, `dd if=`, fork bombs are denied by safety policy |
|
|
57
55
|
| Workspace ID = dev_stage_id | Use `dev_stage_id` from `sandbox_connect` as `workspace_id` for terminal exec |
|
|
58
56
|
| Node.js >= 22 required | Sandbox terminal uses built-in WebSocket (globalThis.WebSocket) |
|
|
@@ -2,7 +2,7 @@ import { getCredentials } from './hwlink-api.mjs';
|
|
|
2
2
|
|
|
3
3
|
const HDKIT_BASE_URL =
|
|
4
4
|
process.env.HDKITSERVICE_ENDPOINT ||
|
|
5
|
-
'
|
|
5
|
+
'https://devkit.huaweicloud.com/rest/developer/server/hdkitservice/';
|
|
6
6
|
|
|
7
7
|
async function hdkitRequest(method, path, body, timeoutMs = 300000) {
|
|
8
8
|
const { ak, sk, securitytoken } = getCredentials();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
4
|
import { createConnection, getCredentials } from './hwlink-api.mjs';
|
|
5
5
|
|
|
6
6
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const
|
|
7
|
+
export const WS_EXEC_INDEX_URL = pathToFileURL(join(__dirname, '..', 'ws-exec', 'index.js')).href;
|
|
8
8
|
|
|
9
9
|
const DEFAULT_WORKSPACE_ID = process.env.HW_WORKSPACE_ID || '0107bd9997aa4287bd2b4890b49af07d';
|
|
10
10
|
|
|
@@ -55,7 +55,7 @@ async function getSession(workspaceId, username, timeoutMs) {
|
|
|
55
55
|
const { ak, sk, securitytoken } = getCredentials();
|
|
56
56
|
const { wsUrl, source } = await createConnection(workspaceId, ak, sk, securitytoken);
|
|
57
57
|
|
|
58
|
-
const { connectHwlinkTerminalSession } = await import(
|
|
58
|
+
const { connectHwlinkTerminalSession } = await import(WS_EXEC_INDEX_URL);
|
|
59
59
|
const session = await connectHwlinkTerminalSession({
|
|
60
60
|
url: wsUrl,
|
|
61
61
|
source,
|
|
@@ -71,7 +71,7 @@ export async function execOneShot(workspaceId, command, username, timeoutMs) {
|
|
|
71
71
|
const { ak, sk, securitytoken } = getCredentials();
|
|
72
72
|
const { wsUrl, source } = await createConnection(workspaceId, ak, sk, securitytoken);
|
|
73
73
|
|
|
74
|
-
const { executeHwlinkCommand } = await import(
|
|
74
|
+
const { executeHwlinkCommand } = await import(WS_EXEC_INDEX_URL);
|
|
75
75
|
return await executeHwlinkCommand({
|
|
76
76
|
url: wsUrl,
|
|
77
77
|
source,
|
|
@@ -129,11 +129,21 @@ function removeIfExists(p) {
|
|
|
129
129
|
|
|
130
130
|
function updateOpenCodeConfig(pluginDir) {
|
|
131
131
|
const configPath = opencodeConfigFile();
|
|
132
|
+
const mcpPath = join(pluginDir, 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
132
133
|
let config = {};
|
|
133
134
|
if (existsSync(configPath)) {
|
|
134
|
-
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {
|
|
135
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {
|
|
136
|
+
console.log(` \x1b[33m[WARN]\x1b[0m Could not parse ${configPath} (jsonc comments?). Skipping MCP config write; ensure "mcp.huaweicloud-devkit" points to ${mcpPath}.`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const existing = config.mcp?.['huaweicloud-devkit'];
|
|
140
|
+
if (existing && existing.type === 'local'
|
|
141
|
+
&& Array.isArray(existing.command) && existing.command[0] === 'node'
|
|
142
|
+
&& existing.command[1] === mcpPath) {
|
|
143
|
+
console.log(` OpenCode MCP config unchanged: ${configPath}`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
135
146
|
}
|
|
136
|
-
const mcpPath = join(pluginDir, 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
137
147
|
config.mcp = config.mcp || {};
|
|
138
148
|
config.mcp['huaweicloud-devkit'] = {
|
|
139
149
|
type: 'local',
|
|
@@ -298,6 +308,101 @@ function uninstallOpenCode() {
|
|
|
298
308
|
removeOpenCodeConfig();
|
|
299
309
|
}
|
|
300
310
|
|
|
311
|
+
// Remove huawei* entries in targetDir that no longer exist in sourceDir (stale files from an older version).
|
|
312
|
+
function pruneStale(targetDir, sourceDir) {
|
|
313
|
+
if (!existsSync(targetDir) || !existsSync(sourceDir)) return 0;
|
|
314
|
+
const sourceNames = new Set(readdirSync(sourceDir));
|
|
315
|
+
let removed = 0;
|
|
316
|
+
for (const entry of readdirSync(targetDir, { withFileTypes: true })) {
|
|
317
|
+
if (!entry.name.startsWith('huawei')) continue;
|
|
318
|
+
if (!sourceNames.has(entry.name)) {
|
|
319
|
+
removeIfExists(join(targetDir, entry.name));
|
|
320
|
+
removed++;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return removed;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
327
|
+
async function updateOpenCode() {
|
|
328
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
329
|
+
const commandsSrc = join(PACKAGE_ROOT, 'integrations', 'opencode', 'commands');
|
|
330
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
331
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
332
|
+
const pluginDest = opencodePluginsDir();
|
|
333
|
+
|
|
334
|
+
copyDir(skillsSrc, opencodeSkillsDir());
|
|
335
|
+
const staleSkills = pruneStale(opencodeSkillsDir(), skillsSrc);
|
|
336
|
+
console.log(` Skills updated -> ${opencodeSkillsDir()}${staleSkills > 0 ? ` (removed ${staleSkills} stale)` : ''}`);
|
|
337
|
+
copyDir(commandsSrc, opencodeCommandsDir());
|
|
338
|
+
const staleCommands = pruneStale(opencodeCommandsDir(), commandsSrc);
|
|
339
|
+
console.log(` Commands updated -> ${opencodeCommandsDir()}${staleCommands > 0 ? ` (removed ${staleCommands} stale)` : ''}`);
|
|
340
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
341
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
342
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
343
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
344
|
+
updateOpenCodeConfig(pluginDest);
|
|
345
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
346
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function codexMcpServerPath() {
|
|
350
|
+
return join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function codexConfigSectionText(mcpPath) {
|
|
354
|
+
return [
|
|
355
|
+
'[mcp_servers.huaweicloud-devkit]',
|
|
356
|
+
'command = "node"',
|
|
357
|
+
`args = ["${mcpPath}"]`,
|
|
358
|
+
'',
|
|
359
|
+
'[mcp_servers.huaweicloud-devkit.env]',
|
|
360
|
+
'HUAWEICLOUD_AGENT_TOOLKIT_MODE = "local"',
|
|
361
|
+
'',
|
|
362
|
+
].join('\n');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Returns true when the config file was written, false when it was already correct.
|
|
366
|
+
function ensureCodexConfigSection(mcpPath) {
|
|
367
|
+
const configPath = codexConfigToml();
|
|
368
|
+
let existing = '';
|
|
369
|
+
if (existsSync(configPath)) {
|
|
370
|
+
try { existing = readFileSync(configPath, 'utf8'); } catch {}
|
|
371
|
+
if (existing.includes('[mcp_servers.huaweicloud-devkit]')) {
|
|
372
|
+
if (existing.includes(`args = ["${mcpPath}"]`)) {
|
|
373
|
+
console.log(` Config unchanged: ${configPath}`);
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
removeCodexConfigSection();
|
|
377
|
+
existing = '';
|
|
378
|
+
if (existsSync(configPath)) {
|
|
379
|
+
try { existing = readFileSync(configPath, 'utf8'); } catch {}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
384
|
+
if (existing && !existing.endsWith('\n')) existing += '\n';
|
|
385
|
+
writeFileSync(configPath, existing + codexConfigSectionText(mcpPath));
|
|
386
|
+
console.log(` Config updated: ${configPath}`);
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function removeCodexConfigSection() {
|
|
391
|
+
const configPath = codexConfigToml();
|
|
392
|
+
if (!existsSync(configPath)) return;
|
|
393
|
+
const lines = readFileSync(configPath, 'utf8').split(/\r?\n/);
|
|
394
|
+
const out = [];
|
|
395
|
+
let skip = false;
|
|
396
|
+
for (const line of lines) {
|
|
397
|
+
if (/^\[mcp_servers\.huaweicloud-devkit(\]|\.)/.test(line)) { skip = true; continue; }
|
|
398
|
+
if (skip && line.startsWith('[')) skip = false;
|
|
399
|
+
if (!skip) out.push(line);
|
|
400
|
+
}
|
|
401
|
+
while (out.length > 0 && out[out.length - 1].trim() === '') out.pop();
|
|
402
|
+
writeFileSync(configPath, out.join('\n') + (out.length > 0 ? '\n' : ''));
|
|
403
|
+
console.log(' Config cleaned');
|
|
404
|
+
}
|
|
405
|
+
|
|
301
406
|
async function installCodexDesktop() {
|
|
302
407
|
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
303
408
|
const commandsSrc = join(PACKAGE_ROOT, 'integrations', 'opencode', 'commands');
|
|
@@ -315,7 +420,7 @@ async function installCodexDesktop() {
|
|
|
315
420
|
console.log(` Safety Policy -> ${join(codexDesktopPluginsDir(), 'safety')}`);
|
|
316
421
|
|
|
317
422
|
// Generate .mcp.json with absolute paths for Codex Desktop MCP server discovery
|
|
318
|
-
const mcpServerAbsPath =
|
|
423
|
+
const mcpServerAbsPath = codexMcpServerPath();
|
|
319
424
|
const mcpConfig = {
|
|
320
425
|
mcpServers: {
|
|
321
426
|
'huaweicloud-devkit': {
|
|
@@ -335,29 +440,50 @@ async function installCodexDesktop() {
|
|
|
335
440
|
console.log(` Plugin Manifest -> ${join(codexDesktopPluginsDir(), '.codex-plugin')}`);
|
|
336
441
|
}
|
|
337
442
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
443
|
+
ensureCodexConfigSection(mcpServerAbsPath);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
447
|
+
async function updateCodexDesktop() {
|
|
448
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
449
|
+
const commandsSrc = join(PACKAGE_ROOT, 'integrations', 'opencode', 'commands');
|
|
450
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
451
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
452
|
+
const pluginDest = codexDesktopPluginsDir();
|
|
453
|
+
|
|
454
|
+
copyDir(skillsSrc, codexDesktopSkillsDir());
|
|
455
|
+
const staleSkills = pruneStale(codexDesktopSkillsDir(), skillsSrc);
|
|
456
|
+
console.log(` Skills updated -> ${codexDesktopSkillsDir()}${staleSkills > 0 ? ` (removed ${staleSkills} stale)` : ''}`);
|
|
457
|
+
copyDir(commandsSrc, codexDesktopCommandsDir());
|
|
458
|
+
const staleCommands = pruneStale(codexDesktopCommandsDir(), commandsSrc);
|
|
459
|
+
console.log(` Commands updated -> ${codexDesktopCommandsDir()}${staleCommands > 0 ? ` (removed ${staleCommands} stale)` : ''}`);
|
|
460
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
461
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
462
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
463
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
464
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
465
|
+
|
|
466
|
+
const mcpServerAbsPath = codexMcpServerPath();
|
|
467
|
+
const mcpConfig = {
|
|
468
|
+
mcpServers: {
|
|
469
|
+
'huaweicloud-devkit': {
|
|
470
|
+
command: 'node',
|
|
471
|
+
args: [mcpServerAbsPath],
|
|
472
|
+
env: { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' },
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
writeFileSync(join(pluginDest, '.mcp.json'), JSON.stringify(mcpConfig, null, 2));
|
|
477
|
+
console.log(` MCP Config updated -> ${join(pluginDest, '.mcp.json')}`);
|
|
478
|
+
|
|
479
|
+
const codexPluginSrc = join(PLUGIN_ROOT, '.codex-plugin');
|
|
480
|
+
if (existsSync(codexPluginSrc)) {
|
|
481
|
+
copyDir(codexPluginSrc, join(pluginDest, '.codex-plugin'));
|
|
482
|
+
console.log(` Plugin Manifest updated -> ${join(pluginDest, '.codex-plugin')}`);
|
|
356
483
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
writeFileSync(
|
|
360
|
-
console.log(` Config updated: ${configPath}`);
|
|
484
|
+
|
|
485
|
+
ensureCodexConfigSection(mcpServerAbsPath);
|
|
486
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
361
487
|
}
|
|
362
488
|
|
|
363
489
|
function uninstallCodexDesktop() {
|
|
@@ -388,32 +514,28 @@ function uninstallCodexDesktop() {
|
|
|
388
514
|
if (removeIfExists(codexDesktopPluginsDir())) {
|
|
389
515
|
console.log(' Removed MCP server and safety policy');
|
|
390
516
|
}
|
|
391
|
-
|
|
392
|
-
if (existsSync(configPath)) {
|
|
393
|
-
const lines = readFileSync(configPath, 'utf8').split(/\r?\n/);
|
|
394
|
-
const out = [];
|
|
395
|
-
let skip = false;
|
|
396
|
-
for (const line of lines) {
|
|
397
|
-
if (/^\[mcp_servers\.huaweicloud-devkit(\]|\.)/.test(line)) { skip = true; continue; }
|
|
398
|
-
if (skip && line.startsWith('[')) skip = false;
|
|
399
|
-
if (!skip) out.push(line);
|
|
400
|
-
}
|
|
401
|
-
while (out.length > 0 && out[out.length - 1].trim() === '') out.pop();
|
|
402
|
-
writeFileSync(configPath, out.join('\n') + (out.length > 0 ? '\n' : ''));
|
|
403
|
-
console.log(' Config cleaned');
|
|
404
|
-
}
|
|
517
|
+
removeCodexConfigSection();
|
|
405
518
|
}
|
|
406
519
|
|
|
407
520
|
function registerCodeartsMcp(configPath) {
|
|
408
521
|
const mcpPath = join(codeartsPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
522
|
+
const env = { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' };
|
|
523
|
+
const hcloudBin = findHcloudBin();
|
|
524
|
+
if (hcloudBin) env.HCLOUD_BIN = hcloudBin.replace(/\\/g, '/');
|
|
409
525
|
let config = {};
|
|
410
526
|
if (existsSync(configPath)) {
|
|
411
|
-
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {
|
|
527
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {
|
|
528
|
+
console.log(` \x1b[33m[WARN]\x1b[0m Could not parse ${configPath}. Skipping MCP config write; ensure "mcpServers.huaweicloud-devkit" points to ${mcpPath}.`);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const existing = config.mcpServers?.['huaweicloud-devkit'];
|
|
532
|
+
if (existing && existing.command === 'node'
|
|
533
|
+
&& Array.isArray(existing.args) && existing.args[0] === mcpPath) {
|
|
534
|
+
console.log(` MCP config unchanged: ${configPath}`);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
412
537
|
}
|
|
413
538
|
config.mcpServers = config.mcpServers || {};
|
|
414
|
-
const env = { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' };
|
|
415
|
-
const hcloudBin = findHcloudBin();
|
|
416
|
-
if (hcloudBin) env.HCLOUD_BIN = hcloudBin.replace(/\\/g, '/');
|
|
417
539
|
config.mcpServers['huaweicloud-devkit'] = {
|
|
418
540
|
command: 'node',
|
|
419
541
|
args: [mcpPath],
|
|
@@ -445,6 +567,28 @@ async function installCodeArts() {
|
|
|
445
567
|
registerCodeartsMcp(codeartsProjectMcpSettingsFile());
|
|
446
568
|
}
|
|
447
569
|
|
|
570
|
+
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
571
|
+
async function updateCodeArts() {
|
|
572
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
573
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
574
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
575
|
+
const pluginDest = codeartsPluginsDir();
|
|
576
|
+
|
|
577
|
+
for (const dir of [codeartsSkillsDir(), codeartsProjectSkillsDir()]) {
|
|
578
|
+
copyDir(skillsSrc, dir);
|
|
579
|
+
const stale = pruneStale(dir, skillsSrc);
|
|
580
|
+
console.log(` Skills updated -> ${dir}${stale > 0 ? ` (removed ${stale} stale)` : ''}`);
|
|
581
|
+
}
|
|
582
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
583
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
584
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
585
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
586
|
+
registerCodeartsMcp(codeartsMcpSettingsFile());
|
|
587
|
+
registerCodeartsMcp(codeartsProjectMcpSettingsFile());
|
|
588
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
589
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
590
|
+
}
|
|
591
|
+
|
|
448
592
|
function uninstallCodeArts() {
|
|
449
593
|
let removed = 0;
|
|
450
594
|
for (const skillsDir of [codeartsSkillsDir(), codeartsProjectSkillsDir()]) {
|
|
@@ -494,6 +638,38 @@ function codeartsStatus() {
|
|
|
494
638
|
}
|
|
495
639
|
}
|
|
496
640
|
|
|
641
|
+
// Returns true when the config file was written, false when it was already correct.
|
|
642
|
+
function ensureWorkbuddyMcpConfig() {
|
|
643
|
+
const configPath = workbuddyMcpConfigFile();
|
|
644
|
+
const mcpPath = join(workbuddyPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
645
|
+
const env = { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' };
|
|
646
|
+
const hcloudBin = findHcloudBin();
|
|
647
|
+
if (hcloudBin) env.HCLOUD_BIN = hcloudBin.replace(/\\/g, '/');
|
|
648
|
+
let config = {};
|
|
649
|
+
if (existsSync(configPath)) {
|
|
650
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {
|
|
651
|
+
console.log(` \x1b[33m[WARN]\x1b[0m Could not parse ${configPath}. Skipping MCP config write; ensure "mcpServers.huaweicloud-devkit" points to ${mcpPath}.`);
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
const existing = config.mcpServers?.['huaweicloud-devkit'];
|
|
655
|
+
if (existing && existing.command === 'node'
|
|
656
|
+
&& Array.isArray(existing.args) && existing.args[0] === mcpPath) {
|
|
657
|
+
console.log(` MCP config unchanged: ${configPath}`);
|
|
658
|
+
return false;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
config.mcpServers = config.mcpServers || {};
|
|
662
|
+
config.mcpServers['huaweicloud-devkit'] = {
|
|
663
|
+
command: 'node',
|
|
664
|
+
args: [mcpPath],
|
|
665
|
+
env,
|
|
666
|
+
};
|
|
667
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
668
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
669
|
+
console.log(` MCP config updated: ${configPath}`);
|
|
670
|
+
return true;
|
|
671
|
+
}
|
|
672
|
+
|
|
497
673
|
async function installWorkBuddy() {
|
|
498
674
|
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
499
675
|
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
@@ -508,25 +684,26 @@ async function installWorkBuddy() {
|
|
|
508
684
|
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
509
685
|
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
510
686
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
const
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
687
|
+
ensureWorkbuddyMcpConfig();
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
691
|
+
async function updateWorkBuddy() {
|
|
692
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
693
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
694
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
695
|
+
const pluginDest = workbuddyPluginsDir();
|
|
696
|
+
|
|
697
|
+
copyDir(skillsSrc, workbuddySkillsDir());
|
|
698
|
+
const stale = pruneStale(workbuddySkillsDir(), skillsSrc);
|
|
699
|
+
console.log(` Skills updated -> ${workbuddySkillsDir()}${stale > 0 ? ` (removed ${stale} stale)` : ''}`);
|
|
700
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
701
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
702
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
703
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
704
|
+
ensureWorkbuddyMcpConfig();
|
|
705
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
706
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
530
707
|
}
|
|
531
708
|
|
|
532
709
|
function uninstallWorkBuddy() {
|
|
@@ -905,13 +1082,105 @@ async function cmdUpdate() {
|
|
|
905
1082
|
console.log(BANNER);
|
|
906
1083
|
const target = parseTarget();
|
|
907
1084
|
|
|
908
|
-
if (target === 'opencode'
|
|
909
|
-
if (!existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs'))
|
|
910
|
-
|
|
911
|
-
|
|
1085
|
+
if (target === 'opencode') {
|
|
1086
|
+
if (!existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1087
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
console.log('[OpenCode]');
|
|
1091
|
+
await updateOpenCode();
|
|
1092
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1093
|
+
console.log(`\x1b[33mMCP 工具在重启 OpenCode 会话后才生效。\x1b[0m`);
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
if (target === 'codex-desktop') {
|
|
1098
|
+
if (!existsSync(join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1099
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
console.log('[Codex Desktop]');
|
|
1103
|
+
await updateCodexDesktop();
|
|
1104
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1105
|
+
console.log(`\x1b[33mMCP 工具在重启 Codex Desktop 会话后才生效。\x1b[0m`);
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if (target === 'codex') {
|
|
1110
|
+
if (!hasCodexCLI()) {
|
|
1111
|
+
console.log(` \x1b[31mCodex CLI not found.\x1b[0m`);
|
|
1112
|
+
if (process.platform === 'win32') {
|
|
1113
|
+
console.log(` \x1b[33mTip: use --target codex-desktop for Codex Desktop on Windows\x1b[0m`);
|
|
1114
|
+
}
|
|
1115
|
+
console.log(` \x1b[31mInstall Codex CLI: https://github.com/openai/codex-cli\x1b[0m`);
|
|
1116
|
+
process.exitCode = 1;
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
console.log('[Codex]');
|
|
1120
|
+
installCodex();
|
|
1121
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1122
|
+
console.log(`\x1b[33mRestart the Codex session for changes to take effect.\x1b[0m`);
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
if (target === 'codearts') {
|
|
1127
|
+
if (!existsSync(join(codeartsPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1128
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
console.log('[CodeArts]');
|
|
1132
|
+
await updateCodeArts();
|
|
1133
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1134
|
+
console.log(`\x1b[33mMCP 工具在重启 CodeArts 会话后才生效。\x1b[0m`);
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
if (target === 'workbuddy') {
|
|
1139
|
+
if (!existsSync(join(workbuddyPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
912
1140
|
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
913
1141
|
return;
|
|
914
1142
|
}
|
|
1143
|
+
console.log('[WorkBuddy]');
|
|
1144
|
+
await updateWorkBuddy();
|
|
1145
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1146
|
+
console.log(`\x1b[33mMCP 工具在重启 WorkBuddy 会话后才生效。\x1b[0m`);
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
if (target === 'all') {
|
|
1151
|
+
let updatedAny = false;
|
|
1152
|
+
if (existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1153
|
+
console.log('[OpenCode]');
|
|
1154
|
+
await updateOpenCode();
|
|
1155
|
+
updatedAny = true;
|
|
1156
|
+
}
|
|
1157
|
+
if (existsSync(join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1158
|
+
console.log('\n[Codex Desktop]');
|
|
1159
|
+
await updateCodexDesktop();
|
|
1160
|
+
updatedAny = true;
|
|
1161
|
+
}
|
|
1162
|
+
if (existsSync(join(codeartsPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1163
|
+
console.log('\n[CodeArts]');
|
|
1164
|
+
await updateCodeArts();
|
|
1165
|
+
updatedAny = true;
|
|
1166
|
+
}
|
|
1167
|
+
if (existsSync(join(workbuddyPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
1168
|
+
console.log('\n[WorkBuddy]');
|
|
1169
|
+
await updateWorkBuddy();
|
|
1170
|
+
updatedAny = true;
|
|
1171
|
+
}
|
|
1172
|
+
if (codexStatus()) {
|
|
1173
|
+
console.log('\n[Codex]');
|
|
1174
|
+
installCodex();
|
|
1175
|
+
updatedAny = true;
|
|
1176
|
+
}
|
|
1177
|
+
if (!updatedAny) {
|
|
1178
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
1182
|
+
console.log(`\x1b[33mMCP 工具在重启各 agent 会话后才生效。\x1b[0m`);
|
|
1183
|
+
return;
|
|
915
1184
|
}
|
|
916
1185
|
|
|
917
1186
|
await cmdUninstall();
|
|
@@ -6,7 +6,7 @@ import { join, dirname } from 'node:path';
|
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
8
|
import { searchMarketplace } from './search-market.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import { execWithSession, closeSession, DEFAULT_WORKSPACE_ID } from './sandbox/session-manager.mjs';
|
|
10
10
|
import { hdkitCheckUser, hdkitSignAgreement, hdkitConnect, hdkitCredentials, hdkitRelease } from './sandbox/hdkitservice-api.mjs';
|
|
11
11
|
import { getAuthStatus, syncAuth } from './auth/service.mjs';
|
|
12
12
|
import { readGlobalCredentials, writeObsConfig as writeObsConfigFile } from './auth/credentials.mjs';
|
|
@@ -309,20 +309,6 @@ export const TOOL_DEFINITIONS = [
|
|
|
309
309
|
},
|
|
310
310
|
},
|
|
311
311
|
},
|
|
312
|
-
{
|
|
313
|
-
name: 'huaweicloud_sandbox_exec',
|
|
314
|
-
description: 'Execute a command on a Huawei Cloud workspace terminal via hwlink (one-shot, no session reuse). Each call creates a new connection. Shell state does NOT persist across calls.',
|
|
315
|
-
inputSchema: {
|
|
316
|
-
type: 'object',
|
|
317
|
-
required: ['command'],
|
|
318
|
-
properties: {
|
|
319
|
-
command: { type: 'string', description: 'The shell command to execute on the remote workspace' },
|
|
320
|
-
workspace_id: { type: 'string', description: 'The workspace ID' },
|
|
321
|
-
username: { type: 'string', description: 'Login username for the remote terminal (default: root)' },
|
|
322
|
-
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 30000)' },
|
|
323
|
-
},
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
312
|
{
|
|
327
313
|
name: 'huaweicloud_sandbox_exec_with_session',
|
|
328
314
|
description: 'Execute a command on a workspace terminal with session reuse (state persists across calls). Shell state (cd, env vars, aliases) carries over between calls.',
|
|
@@ -458,13 +444,6 @@ export async function callTool(name, args = {}) {
|
|
|
458
444
|
return getAuthStatus(args.target || 'all');
|
|
459
445
|
case 'huaweicloud_auth_sync':
|
|
460
446
|
return syncAuth(args.target || 'all');
|
|
461
|
-
case 'huaweicloud_sandbox_exec': {
|
|
462
|
-
const sandboxWsId1 = args.workspace_id || DEFAULT_WORKSPACE_ID;
|
|
463
|
-
const sandboxUser1 = args.username || 'root';
|
|
464
|
-
const sandboxTimeout1 = args.timeout_ms || 30000;
|
|
465
|
-
const sandboxResult1 = await execOneShot(sandboxWsId1, args.command, sandboxUser1, sandboxTimeout1);
|
|
466
|
-
return { stdout: sandboxResult1.stdout, exitCode: sandboxResult1.exitCode };
|
|
467
|
-
}
|
|
468
447
|
case 'huaweicloud_sandbox_exec_with_session': {
|
|
469
448
|
const sandboxWsId2 = args.workspace_id || DEFAULT_WORKSPACE_ID;
|
|
470
449
|
const sandboxUser2 = args.username || 'root';
|