huaweicloud-devkit 1.1.2 → 1.1.3-next.1

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Discussions](https://img.shields.io/badge/Discussions-Join%20the%20discussion-blue)](https://github.com/huaweicloud/huaweicloud-devkit/discussions)
4
4
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
5
5
  [![CI](https://github.com/huaweicloud/huaweicloud-devkit/actions/workflows/ci.yml/badge.svg)](https://github.com/huaweicloud/huaweicloud-devkit/actions/workflows/ci.yml)
6
- [![Beta](https://img.shields.io/badge/beta-v1.1.0-orange)](https://github.com/huaweicloud/huaweicloud-devkit)
6
+ [![npm version](https://img.shields.io/npm/v/huaweicloud-devkit)](https://www.npmjs.com/package/huaweicloud-devkit)
7
7
 
8
8
  **[中文](README.zh-CN.md) | English**
9
9
 
@@ -246,7 +246,7 @@ Any agent that supports MCP can use the standard config:
246
246
 
247
247
  No installation required — `npx` handles everything.
248
248
 
249
- > Set `HW_ACCESS_KEY`/`HW_SECRET_KEY` in the MCP config `env` field for project-level credentials.
249
+ > For manual MCP registrations like this, do not put credentials in the config. `HW_ACCESS_KEY`/`HW_SECRET_KEY` are reserved for **platform/CI-injected** accounts (e.g. a DevSpace-managed default account) — configure your own account via `npx huaweicloud-devkit auth init` (the single entry point), and switch accounts at runtime with the `huaweicloud_auth_init` / `huaweicloud_auth_switch` MCP tools. See `plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md` for the full credential-resolution priority.
250
250
 
251
251
  #### Connecting over Remote (HTTP)
252
252
 
@@ -284,7 +284,24 @@ npx --yes huaweicloud-devkit install-hcloud
284
284
  npx --yes huaweicloud-devkit auth init
285
285
  ```
286
286
 
287
- Synchronizes AK/SK to KooCLI, OBS, and sandbox APIs in one step.
287
+ Synchronizes AK/SK to KooCLI, OBS, and sandbox APIs in one step — this is the **single entry point**. Never hard-code AK/SK into agent or shell config.
288
+
289
+ **Account switching at runtime** (within an agent session): use the MCP tools `huaweicloud_auth_init` (in-memory, highest priority) or `huaweicloud_auth_switch` (actions: `temporary` / `persist` / `clear`). In sandbox/DevSpace environments where a default account is injected via `HW_ACCESS_KEY`/`HW_SECRET_KEY`, a plain `auth init` will not override it — use `huaweicloud_auth_switch action=persist` to make the session account win.
290
+
291
+ **Credential resolution priority** (highest first):
292
+
293
+ | # | Source | Set by |
294
+ | --- | ------------------------------------------------------- | ----------------------------------------------------------------------------- |
295
+ | 1 | Runtime (session) credentials | `huaweicloud_auth_init` / `huaweicloud_auth_switch action=temporary` |
296
+ | 2 | S1 global file with `configuredBySession: true` | `huaweicloud_auth_switch action=persist` |
297
+ | 3 | Environment variables (`HW_ACCESS_KEY`/`HW_SECRET_KEY`) | platform/DevSpace-injected default account |
298
+ | 4 | CodeArts / CodeArts Work | `.codeartsdoer/mcp/mcp_settings.json` / `.codeartswork/mcp/mcp_settings.json` |
299
+ | 5 | S1 global file (no session flag) | `auth init` |
300
+ | 6 | KooCLI profile | `~/.hcloud/config.json` (KooCLI commands only) |
301
+
302
+ > **Security**: never put your own AK/SK into the MCP config `env` field — they'd be stored in plaintext and could leak if the config file is committed to git. `env` is for platform/CI injection only.
303
+
304
+ Full details: `plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md`.
288
305
 
289
306
  ### Install All Agents
290
307
 
package/README.zh-CN.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![参与讨论](https://img.shields.io/badge/参与讨论-Join%20the%20discussion-blue)](https://github.com/huaweicloud/huaweicloud-devkit/discussions)
4
4
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
5
5
  [![CI](https://github.com/huaweicloud/huaweicloud-devkit/actions/workflows/ci.yml/badge.svg)](https://github.com/huaweicloud/huaweicloud-devkit/actions/workflows/ci.yml)
6
- [![Beta](https://img.shields.io/badge/beta-v1.1.0-orange)](https://github.com/huaweicloud/huaweicloud-devkit)
6
+ [![npm version](https://img.shields.io/npm/v/huaweicloud-devkit)](https://www.npmjs.com/package/huaweicloud-devkit)
7
7
 
8
8
  **中文 | [English](README.md)**
9
9
 
@@ -246,7 +246,7 @@ npx --yes huaweicloud-devkit uninstall --target atomcode
246
246
 
247
247
  无需预安装 — `npx` 自动处理一切。
248
248
 
249
- > 项目级 AK/SK 可通过 MCP 配置的 `env` 字段设置 `HW_ACCESS_KEY`/`HW_SECRET_KEY`。
249
+ > 像上面这种手动 MCP 注册方式,请勿在配置里写凭据。`HW_ACCESS_KEY`/`HW_SECRET_KEY` 是保留给**平台/CI 注入**的账号用的(例如 DevSpace 托管的默认账号)——自己的账号统一通过 `npx huaweicloud-devkit auth init` 配置(唯一入口),会话内切换账号用 `huaweicloud_auth_init` / `huaweicloud_auth_switch` MCP 工具。完整凭据解析优先级见 `plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md`。
250
250
 
251
251
  #### 通过 Remote(HTTP)连接
252
252
 
@@ -284,7 +284,24 @@ npx --yes huaweicloud-devkit install-hcloud
284
284
  npx --yes huaweicloud-devkit auth init
285
285
  ```
286
286
 
287
- 一步同步 AK/SK 到 KooCLI、OBS 和沙箱接口。
287
+ 一步同步 AK/SK 到 KooCLI、OBS 和沙箱接口——这是**唯一入口**,切勿把 AK/SK 手写进 Agent 或 shell 配置。
288
+
289
+ **会话内切换账号**:使用 MCP 工具 `huaweicloud_auth_init`(内存态,优先级最高)或 `huaweicloud_auth_switch`(`temporary` / `persist` / `clear`)。在沙箱/DevSpace 环境中若默认账号经 `HW_ACCESS_KEY`/`HW_SECRET_KEY` 注入,单纯 `auth init` 无法覆盖——需 `huaweicloud_auth_switch action=persist` 让会话账号生效。
290
+
291
+ **凭据解析优先级**(从高到低):
292
+
293
+ | # | 来源 | 由谁设置 |
294
+ | --- | --------------------------------------------- | ----------------------------------------------------------------------------- |
295
+ | 1 | 运行时(会话)凭据 | `huaweicloud_auth_init` / `huaweicloud_auth_switch action=temporary` |
296
+ | 2 | 带 `configuredBySession: true` 的 S1 全局文件 | `huaweicloud_auth_switch action=persist` |
297
+ | 3 | 环境变量(`HW_ACCESS_KEY`/`HW_SECRET_KEY`) | 平台/DevSpace 注入的默认账号 |
298
+ | 4 | CodeArts / CodeArts Work | `.codeartsdoer/mcp/mcp_settings.json` / `.codeartswork/mcp/mcp_settings.json` |
299
+ | 5 | S1 全局文件(无会话标记) | `auth init` |
300
+ | 6 | KooCLI profile | `~/.hcloud/config.json`(仅 KooCLI 命令) |
301
+
302
+ > **安全**:切勿把自己账号的 AK/SK 写入 MCP 配置的 `env` 字段——会以明文存储,配置文件提交 git 时即泄密;`env` 仅用于平台/CI 注入。
303
+
304
+ 完整说明:`plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md`。
288
305
 
289
306
  ### 安装所有 Agent
290
307
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
3
  "mcpName": "io.github.huaweicloud/huaweicloud-devkit",
4
- "version": "1.1.2",
4
+ "version": "1.1.3-next.1",
5
5
  "kooCliVersion": "7.2.12",
6
6
  "description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
7
7
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "mcpServers": "./.mcp.json",
18
18
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
19
19
  "skills": "./skills/",
20
- "version": "1.1.2",
20
+ "version": "1.1.3-next.1",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
@@ -20,7 +20,7 @@
20
20
  "mcpServers": "./.mcp.json",
21
21
  "description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
22
22
  "skills": "./skills/",
23
- "version": "1.1.2",
23
+ "version": "1.1.3-next.1",
24
24
  "author": {
25
25
  "name": "HuaweiCloud Mate",
26
26
  "url": "https://github.com/huaweicloud"
@@ -17,7 +17,7 @@
17
17
  "mcpServers": "./.mcp.json",
18
18
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
19
19
  "skills": "./skills/",
20
- "version": "1.1.2",
20
+ "version": "1.1.3-next.1",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.2",
3
+ "version": "1.1.3-next.1",
4
4
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
5
  "author": {
6
6
  "name": "HuaweiCloud Mate",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.2",
3
+ "version": "1.1.3-next.1",
4
4
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
5
  "author": {
6
6
  "name": "HuaweiCloud Mate",
@@ -2,7 +2,7 @@
2
2
  "name": "huaweicloud-devkit",
3
3
  "id": "huaweicloud-devkit",
4
4
  "displayName": "HuaweiCloud DevKit",
5
- "version": "1.1.2",
5
+ "version": "1.1.3-next.1",
6
6
  "family": "bundle-plugin",
7
7
  "bundleFormat": "codex",
8
8
  "description": "Guide coding agents to use Huawei Cloud safely — KooCLI, APIs, SDKs, 28 MCP tools, skills, and safety guardrails.",
@@ -47,3 +47,4 @@ hcloud OBS stat obs://<bucket>
47
47
  - Region is immutable after creation
48
48
  - ACL does NOT cascade to objects — set both bucket-level and object-level access
49
49
  - OBS uses separate credentials (`~/.obsutilconfig`, configure via `hcloud OBS config -i`)
50
+ - **Return code is unreliable for `mb`**: it can exit non-zero (e.g. 6) while the bucket is actually created (output still carries a request id). Confirm with `stat obs://<bucket>` / `OBS ls` instead of the exit code; re-running `mb` on the same name is idempotent.
@@ -54,3 +54,5 @@ curl http://my-static-site.obs-website.cn-north-4.myhuaweicloud.com
54
54
  - **Always use `-f`**: Without `-f`, obsutil prompts "Please input (y/n)" on large uploads — Agent hangs (TIMEOUT).
55
55
  - **Use `-flat` for root files**: Without `-flat`, `dist/` becomes `bucket/dist/` prefix. Static sites need files at bucket root.
56
56
  - **Objects don't inherit bucket ACL**: Setting bucket to public-read does NOT make individual objects public. You must also set object-level ACL with `chattri obs://<bucket>/ -r -f -acl=public-read` or use `-acl=public-read` during upload.
57
+ - **`mb` may return a non-zero exit code even on success**: `hcloud OBS mb` occasionally exits with code 6 (or other non-zero) while the bucket is actually created — the output still contains a request id and subsequent `cp`/`chattri` succeed. Judge success by the response text (request id) and confirm with `hcloud OBS ls` / `stat obs://<bucket>`, NOT the exit code alone. `mb` is idempotent, so re-issuing the same bucket name is harmless.
58
+ - **Website endpoint is eventually consistent (~1 min)**: after `PUT /?website` returns 200, the website endpoint (`http://<bucket>.obs-website.<region>.myhuaweicloud.com`) may return 404 `NoSuchWebsiteConfiguration` for up to ~1 minute. This is propagation delay, NOT a configuration failure. Verify immediately via the object URL (`https://<bucket>.obs.<region>.myhuaweicloud.com/index.html`, works before the website endpoint), then poll the website endpoint with a short backoff until it returns 200 before reporting success.
@@ -12,7 +12,7 @@ Huawei Cloud comprises 200+ services. This skill is the entry point for ANY acti
12
12
 
13
13
  ## 会话启动
14
14
 
15
- 首次操作前必须先调用 `huaweicloud_check_update` 检查插件版本:
15
+ 首次操作前先调用 `huaweicloud_check_update` 检查插件版本;若未先行检查,插件会在使用中收到服务端升级提示。
16
16
 
17
17
  1. 如果 `updateAvailable == true` 且 `dismissed == false`,先询问用户是否升级
18
18
  2. 用户同意 → 调用 `huaweicloud_upgrade`,完成后提示重启会话
@@ -45,15 +45,27 @@ export function readKooCliProfiles() {
45
45
  const current = String(raw.current || 'default');
46
46
  const profiles = Array.isArray(raw.profiles) ? raw.profiles : [];
47
47
  const mtimeMs = statSync(configPath).mtimeMs;
48
+ // KooCLI 7.x stores AK/SK encrypted (authEncrypt=true): the on-disk values
49
+ // are ciphertext, so a fingerprint computed from them can never match the
50
+ // plaintext S1/S3 stores. Blind the comparable fields instead of treating
51
+ // ciphertext as a real credential and misreporting a S2 drift (#533).
52
+ // KooCLI serializes the flag as the string "true", not a boolean.
53
+ const encrypted = raw.authEncrypt === true || raw.authEncrypt === 'true';
48
54
  return {
49
55
  current,
50
56
  mtimeMs,
51
57
  configPath,
52
- profiles: profiles.map((p) => ({
53
- name: String(p.name || ''),
54
- fingerprint: fingerprint(p.accessKeyId, p.secretAccessKey),
55
- accessKeyId: p.accessKeyId || '',
56
- })),
58
+ authEncrypt: encrypted,
59
+ profiles: profiles.map((p) =>
60
+ encrypted
61
+ ? { name: String(p.name || ''), fingerprint: '', accessKeyId: '', encrypted: true }
62
+ : {
63
+ name: String(p.name || ''),
64
+ fingerprint: fingerprint(p.accessKeyId, p.secretAccessKey),
65
+ accessKeyId: p.accessKeyId || '',
66
+ encrypted: false,
67
+ },
68
+ ),
57
69
  };
58
70
  } catch {
59
71
  return { error: 'KooCLI config parse failed' };
@@ -138,6 +150,9 @@ export function scanState() {
138
150
  runtimeFingerprint,
139
151
  },
140
152
  kooCliCurrent: kooCli.error ? null : kooCli.current,
153
+ // S2 is encrypted storage (authEncrypt) → its fingerprint is unavailable,
154
+ // so the S2-current drift check is intentionally skipped (#533).
155
+ s2Encrypted: Boolean(!kooCli.error && kooCli.authEncrypt),
141
156
  inconsistencies,
142
157
  hasRuntime,
143
158
  runtimeFingerprint,
@@ -1,6 +1,7 @@
1
1
  import { getCredentials } from './hwlink-api.mjs';
2
2
  import { getProxyDispatcher } from '../proxy/proxy-agent.mjs';
3
3
  import { cacheUserHash } from '../telemetry/telemetry.mjs';
4
+ import { readInstalledVersion } from '../update-check.mjs';
4
5
 
5
6
  function getHdkitBaseUrl() {
6
7
  return process.env.HDKITSERVICE_ENDPOINT || 'https://devkit.huaweicloud.com/rest/developer/server/hdkitservice/';
@@ -19,6 +20,7 @@ async function hdkitRequest(method, path, body, timeoutMs = 300000) {
19
20
  'Content-Type': 'application/json',
20
21
  'X-HW-AK': ak,
21
22
  'X-HW-SK': sk,
23
+ 'X-HW-Client-Version': readInstalledVersion() || '0.0.0',
22
24
  };
23
25
  if (securitytoken) {
24
26
  headers['X-HW-Security-Token'] = securitytoken;
@@ -3120,10 +3120,6 @@ async function cmdInstall() {
3120
3120
  if (target === 'codearts-work' || target === 'all') {
3121
3121
  await runInstallStep('codearts-work', '\n[CodeArts Work]', installCodeArtsWork);
3122
3122
  }
3123
- if (target === 'codearts-work' || target === 'all') {
3124
- console.log('\n[CodeArts Work]');
3125
- await installCodeArtsWork();
3126
- }
3127
3123
  if (target === 'workbuddy' || target === 'all') {
3128
3124
  await runInstallStep('workbuddy', '\n[WorkBuddy]', installWorkBuddy);
3129
3125
  }
@@ -3142,26 +3138,6 @@ async function cmdInstall() {
3142
3138
  if (target === 'atomcode' || target === 'all') {
3143
3139
  await runInstallStep('atomcode', '\n[AtomCode]', installAtomCode);
3144
3140
  }
3145
- if (target === 'dsh' || target === 'all') {
3146
- console.log('\n[DSH]');
3147
- await installDsh();
3148
- }
3149
- if (target === 'officeace' || target === 'all') {
3150
- console.log('\n[OfficeAce]');
3151
- await installOfficeAce();
3152
- }
3153
- if (target === 'hermes' || target === 'all') {
3154
- console.log('\n[Hermes Agent]');
3155
- await installHermes();
3156
- }
3157
- if (target === 'openclaw' || target === 'all') {
3158
- console.log('\n[OpenClaw]');
3159
- await installOpenClaw();
3160
- }
3161
- if (target === 'atomcode' || target === 'all') {
3162
- console.log('\n[AtomCode]');
3163
- await installAtomCode();
3164
- }
3165
3141
  if (target === 'codex' || target === 'all') {
3166
3142
  await runInstallStep('codex', '\n[Codex]', () => {
3167
3143
  if (!hasCodexCLI()) {
@@ -4075,10 +4051,6 @@ async function cmdUpdate() {
4075
4051
  console.log(`\x1b[33mMCP 工具在重启各 agent 会话后才生效。\x1b[0m`);
4076
4052
  return;
4077
4053
  }
4078
-
4079
- await cmdUninstall();
4080
- console.log('');
4081
- await cmdInstall();
4082
4054
  }
4083
4055
 
4084
4056
  async function cmdReinstall() {
@@ -68,7 +68,7 @@ export const AGENTS = [
68
68
  id: 'officeace',
69
69
  pathPatterns: ['/.office-claw/', '/.officeace/'],
70
70
  envVars: ['OFFICEACE_SESSION_ID', 'OFFICE_CLAW_CONFIG_ROOT'],
71
- clientNames: ['office-claw-mcp-connector-probe'],
71
+ clientNames: ['office-claw-mcp-connector-probe', 'officeace-agent'],
72
72
  version: { type: 'officeace' },
73
73
  },
74
74
  {
@@ -94,6 +94,7 @@ export const AGENTS = [
94
94
  id: 'cursor',
95
95
  pathPatterns: ['/.cursor/', '/cursor/'],
96
96
  envVars: ['CURSOR_SESSION_ID', 'CURSOR_GIT_WORKDIR'],
97
+ clientNames: ['cursor-vscode'],
97
98
  version: null,
98
99
  },
99
100
  {
@@ -1012,7 +1012,31 @@ function refreshUserHashAfterAuthChange({ regenerate = true } = {}) {
1012
1012
  }
1013
1013
  }
1014
1014
 
1015
- export async function callTool(name, args = {}) {
1015
+ // Reject invalid numeric args up front instead of silently coercing them to
1016
+ // NaN (which downstream defaults would absorb as "no timeout set") — see #530.
1017
+ const NUMERIC_ARG_KEYS = ['timeoutMs', 'maxRetries', 'timeout_ms'];
1018
+
1019
+ function normalizeNumericArgs(args) {
1020
+ const out = { ...args };
1021
+ for (const key of NUMERIC_ARG_KEYS) {
1022
+ const value = out[key];
1023
+ if (value === undefined || value === null) continue;
1024
+ const num = Number(value);
1025
+ // timeouts must be positive; maxRetries may be 0 (means "no retries").
1026
+ const isRetries = key === 'maxRetries';
1027
+ const valid = Number.isFinite(num) && (isRetries ? Number.isSafeInteger(num) && num >= 0 : num > 0);
1028
+ if (!valid) {
1029
+ throw new Error(
1030
+ `Invalid parameter "${key}": expected a ${isRetries ? 'non-negative integer' : 'positive number'}, received ${JSON.stringify(value)}.`,
1031
+ );
1032
+ }
1033
+ out[key] = num;
1034
+ }
1035
+ return out;
1036
+ }
1037
+
1038
+ export async function callTool(name, rawArgs = {}) {
1039
+ const args = normalizeNumericArgs(rawArgs);
1016
1040
  const toolValue = toolInvokeValue(name, args);
1017
1041
  trackToolInvoke(name, toolValue);
1018
1042