huaweicloud-devkit 1.0.2-dev.5 → 1.0.2-dev.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.0.2-dev.5",
3
+ "version": "1.0.2-dev.7",
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.2-dev.5",
23
+ "version": "1.0.2-dev.7",
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.2-dev.5",
3
+ "version": "1.0.2-dev.7",
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.2-dev.5",
23
+ "version": "1.0.2-dev.7",
24
24
  "author": {
25
25
  "name": "HuaweiCloud Mate",
26
26
  "url": "https://github.com/huaweicloud"
@@ -1,6 +1,6 @@
1
1
  ---
2
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)."
3
+ description: "Use when creating, connecting, or managing Huawei Cloud Sandbox instances and workspace terminals, or when a task needs a temporary runtime to deploy, run, or preview a web application. Covers sandbox lifecycle (check-user, sign-agreement, connect, release), session-based terminal execution, and credential injection. Triggers on: sandbox, workspace, terminal, web app deployment, deploy web app, preview app, hwlink, devstation, hdkitservice, remote exec. NOT for: ECS instances (use huawei-ecs), CCE clusters (use huawei-cce)."
4
4
  version: 1
5
5
  ---
6
6
 
@@ -12,6 +12,12 @@ version: 1
12
12
 
13
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
14
 
15
+ ## Activation
16
+
17
+ - **Proactive offering**: when the developer's task needs a temporary runtime (e.g. "deploy this web app", "run this app and preview it"), offer the sandbox proactively — the developer never has to say "use sandbox". Prompt: "This task can be satisfied by a sandbox — use it?"
18
+ - **Do not intercept a specified target**: if the task already names a deployment target (ECS, CCE, an existing server), follow that target instead of offering the sandbox. Offer the sandbox only when the task needs a temporary runtime or no target is specified.
19
+ - The developer never needs to name or understand the sandbox as a separate service. Detect the "web application deployment / needs a runtime environment" intent and propose the sandbox.
20
+
15
21
  ## MCP Tools
16
22
 
17
23
  ### User Verification (Prerequisites)
@@ -38,22 +44,39 @@ Domain expertise for Huawei Cloud Sandbox (DevStation) instances and workspace t
38
44
 
39
45
  ## Workflow
40
46
 
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
+ Setup is a **plugin-side preflight**the developer should be asked a question only once, when the agreement actually needs signing:
48
+
49
+ 1. **Check user** (transparent): `huaweicloud_sandbox_check_user` — verify `realname_verified` and `agreement_signed`
50
+ 2. **Real-name verification** (only if `realname_verified=false`): tell the developer once, "Huawei Cloud requires real-name verification before using the sandbox." and stop do not retry `connect` in a loop
51
+ 3. **Sign agreement** (only if `agreement_signed=false`): ask the developer once as the plugin — "Huawei Cloud sandbox requires signing its service agreement. May I sign it on your behalf?" — then call `huaweicloud_sandbox_sign_agreement`. Do not expose the underlying sandbox/DevBridge service as a separate entity the developer must understand or sign up for
52
+ 4. **Connect**: `huaweicloud_sandbox_connect` — returns `session_id`, `dev_stage_id`, `connection_id`, `connection_address`
53
+ 5. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox
54
+ 6. **Execute commands**: `huaweicloud_sandbox_exec_with_session` for interactive work
55
+ 7. **Release**: `huaweicloud_sandbox_release` — cleans up sandbox and session
47
56
 
48
57
  ## Critical Warnings
49
58
 
50
59
  | Trap | Why |
51
60
  |------|-----|
52
- | Agreement required first | `sandbox_connect` fails if user hasn't signed agreements; run `sandbox_check_user` first |
61
+ | Agreement required first | `sandbox_connect` fails if the agreement isn't signed; the `sandbox_check_user` preflight detects this, so surface it to the developer only when signing is needed |
62
+ | Real-name required | `sandbox_connect` fails if `realname_verified=false`; tell the developer once and stop, don't loop on connect |
53
63
  | Session state persists | `exec_with_session` preserves `cd`, env vars, aliases between calls |
54
64
  | Destructive commands blocked | `rm -rf /`, `mkfs`, `dd if=`, fork bombs are denied by safety policy |
55
65
  | 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) |
66
+ | Node.js >= 22 required | Sandbox terminal uses built-in WebSocket (globalThis.WebSocket); if Node.js is missing, install it from the Huawei Cloud mirror (see "Node.js in the sandbox") |
67
+
68
+ ## Node.js in the sandbox
69
+
70
+ If the sandbox has no Node.js, download it from the Huawei Cloud mirror. Pick the tarball matching the sandbox arch (`uname -m`: `aarch64` -> arm64, `x86_64` -> x64):
71
+
72
+ ```bash
73
+ # aarch64 sandbox:
74
+ curl -fsSL https://mirrors.huaweicloud.com/nodejs/v24.19.0/node-v24.19.0-linux-arm64.tar.gz -o node.tar.gz
75
+ # x86_64 sandbox:
76
+ curl -fsSL https://mirrors.huaweicloud.com/nodejs/v24.19.0/node-v24.19.0-linux-x64.tar.gz -o node.tar.gz
77
+ sudo tar -xzf node.tar.gz -C /usr/local --strip-components=1
78
+ node --version
79
+ ```
57
80
 
58
81
  ## Environment Variables
59
82
 
@@ -1,7 +1,24 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
 
5
+ // Verify a credential file ended up with 0600. On Windows-mounted drives inside WSL
6
+ // (drvfs/9p) chmod is silently ignored, so the file can be world-readable (0777).
7
+ // Native Windows has no POSIX modes (statSync always reports 0666), so skip the check there.
8
+ function ensurePrivateMode(path) {
9
+ if (process.platform === 'win32') return;
10
+ try { chmodSync(path, 0o600); } catch {}
11
+ try {
12
+ const mode = statSync(path).mode & 0o777;
13
+ if (mode !== 0o600) {
14
+ console.warn(`\x1b[33m[WARN]\x1b[0m Could not set 0600 on ${path} (current mode ${mode.toString(8)}). Credentials may be readable by other users.`);
15
+ console.warn(`\x1b[33m If running under WSL, move the credential home to the Linux filesystem:\x1b[0m`);
16
+ console.warn(`\x1b[33m export HUAWEICLOUD_HOME=$HOME (then re-run auth init)\x1b[0m`);
17
+ console.warn(`\x1b[33m Or skip file storage entirely with HW_ACCESS_KEY/HW_SECRET_KEY environment variables.\x1b[0m`);
18
+ }
19
+ } catch {}
20
+ }
21
+
5
22
  function baseHome() {
6
23
  return process.env.HUAWEICLOUD_HOME || homedir();
7
24
  }
@@ -34,6 +51,7 @@ export function writeGlobalCredentials(credentials = {}) {
34
51
  region: String(credentials.region || ''),
35
52
  };
36
53
  writeFileSync(path, JSON.stringify(payload, null, 2), { encoding: 'utf8', mode: 0o600 });
54
+ ensurePrivateMode(path);
37
55
  return path;
38
56
  }
39
57
 
@@ -41,13 +59,16 @@ export function writeObsConfig(credentials = {}) {
41
59
  const region = String(credentials.region || '');
42
60
  const ak = String(credentials.ak || '');
43
61
  const sk = String(credentials.sk || '');
62
+ const securityToken = String(credentials.securityToken || '');
44
63
  if (!region || !ak || !sk) {
45
64
  throw new Error('region, ak, and sk are required to write OBS config');
46
65
  }
47
66
  const path = obsConfigPath();
48
67
  const endpoint = credentials.endpoint || `https://obs.${region}.myhuaweicloud.com`;
49
- const content = `[default]\r\nendpoint=${endpoint}\r\nak=${ak}\r\nsk=${sk}\r\n`;
68
+ // Flat key=value format (no [default] section) as written by KooCLI 7.x `hcloud OBS config`.
69
+ const content = `endpoint=${endpoint}\nak=${ak}\nsk=${sk}${securityToken ? `\ntoken=${securityToken}` : ''}\n`;
50
70
  writeFileSync(path, content, { encoding: 'utf8', mode: 0o600 });
71
+ ensurePrivateMode(path);
51
72
  return { path, endpoint };
52
73
  }
53
74
 
@@ -1,7 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import { stdin, stdout } from 'node:process';
3
+ import { rmSync, existsSync } from 'node:fs';
4
+ import { resolve, dirname } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
3
6
  import { TOOL_DEFINITIONS, callTool } from './tools.mjs';
4
7
 
8
+ // The MCP server is now loaded by a live agent session. Clear the install marker
9
+ // in this plugin dir so `doctor` no longer reports "restart needed".
10
+ try {
11
+ const pluginDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
12
+ const marker = resolve(pluginDir, '.installed');
13
+ if (existsSync(marker)) rmSync(marker, { force: true });
14
+ } catch {}
15
+
5
16
  let buffer = Buffer.alloc(0);
6
17
  let useContentLengthFraming = true;
7
18
 
@@ -1417,6 +1417,13 @@ function printAuthStatus(status) {
1417
1417
  async function cmdAuthInit() {
1418
1418
  console.log(BANNER);
1419
1419
  console.log('HuaweiCloud DevKit Unified Authentication Setup\n');
1420
+ console.log('\x1b[1m获取 AK/SK(如果还没有):\x1b[0m');
1421
+ console.log(' 1. 打开华为云控制台:');
1422
+ console.log(' https://console.huaweicloud.com/console/?region=cn-north-4#/home');
1423
+ console.log(' 2. 点击右上角账号名 -> 我的凭证');
1424
+ console.log(' 3. 进入"访问密钥"页签 -> 点击"新增访问密钥",完成身份验证');
1425
+ console.log(' 4. 下载凭证文件(内含 AK 和 SK)。');
1426
+ console.log(' 注意:SK 只在创建密钥时显示一次,请妥善保存该文件。\n');
1420
1427
 
1421
1428
  let ak = process.env.HW_ACCESS_KEY || '';
1422
1429
  let sk = process.env.HW_SECRET_KEY || '';
@@ -1445,22 +1452,21 @@ async function cmdAuthInit() {
1445
1452
 
1446
1453
 
1447
1454
  const vaultPath = writeGlobalCredentials({ ak, sk, securityToken, region });
1448
- console.log(`\nCredentials stored: ${vaultPath}`);
1449
1455
 
1450
1456
  try {
1451
- const obs = writeObsConfig({ ak, sk, securityToken, region });
1452
- console.log(`OBS config synced: ${obs.path} (${obs.endpoint})`);
1457
+ writeObsConfig({ ak, sk, securityToken, region });
1453
1458
  } catch (error) {
1454
1459
  console.log(`OBS config sync failed: ${error.message}`);
1455
1460
  }
1456
1461
 
1457
1462
  if (findHcloudBin()) {
1458
1463
  const result = configureHcloud({ ak, sk, region });
1459
- console.log(result.ok ? 'KooCLI profile updated.' : `KooCLI update failed: ${result.error || result.code}`);
1464
+ if (!result.ok) console.log(`KooCLI update failed: ${result.error || result.code}`);
1460
1465
  } else {
1461
1466
  console.log('KooCLI not found. Run "npx huaweicloud-devkit install-hcloud" and then "auth sync".');
1462
1467
  }
1463
1468
 
1469
+ console.log('\nCredentials synchronized.');
1464
1470
  console.log('\nNext steps:');
1465
1471
  console.log(' npx huaweicloud-devkit install --target all');
1466
1472
  console.log(' Restart your agent sessions.');
@@ -1480,12 +1486,10 @@ async function cmdAuthSync() {
1480
1486
 
1481
1487
  const result = syncAuth(target);
1482
1488
  if (result.ok) {
1483
- console.log(`OBS config synced: ${result.obs.path} (${result.obs.endpoint})`);
1489
+ console.log('Credentials synchronized.');
1484
1490
  } else {
1485
1491
  console.error(result.error);
1486
1492
  }
1487
- console.log('Agent MCP registration:');
1488
- printAuthAgents(result.agents);
1489
1493
  }
1490
1494
 
1491
1495
  async function cmdAuthStatus() {
@@ -614,7 +614,8 @@ async function setupObsConfigFromHcloud(profile) {
614
614
  }
615
615
 
616
616
  const endpoint = `https://obs.${region}.myhuaweicloud.com`;
617
- const configContent = `[default]\r\nendpoint=${endpoint}\r\nak=${accessKeyId}\r\nsk=${secretAccessKey}\r\n`;
617
+ // Flat key=value format (no [default] section) as written by KooCLI 7.x `hcloud OBS config`.
618
+ const configContent = `endpoint=${endpoint}\nak=${accessKeyId}\nsk=${secretAccessKey}\n`;
618
619
 
619
620
  try {
620
621
  writeFileSync(obsConfigPath, configContent, { encoding: 'utf8', mode: 0o600 });