huaweicloud-devkit 1.1.2-next.7 → 1.1.2-next.8

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,7 +1,7 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
3
  "mcpName": "io.github.huaweicloud/huaweicloud-devkit",
4
- "version": "1.1.2-next.7",
4
+ "version": "1.1.2-next.8",
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-next.7",
20
+ "version": "1.1.2-next.8",
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-next.7",
23
+ "version": "1.1.2-next.8",
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-next.7",
20
+ "version": "1.1.2-next.8",
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-next.7",
3
+ "version": "1.1.2-next.8",
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-next.7",
3
+ "version": "1.1.2-next.8",
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-next.7",
5
+ "version": "1.1.2-next.8",
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.",
@@ -112,7 +112,13 @@ Setup is a **plugin-side preflight** — the developer should be asked a questio
112
112
  sudo nginx -s reload 2>/dev/null || true
113
113
  ```
114
114
 
115
- 7. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox. After injection, credentials are also written to `/tmp/hw_creds.sh` for shell access:
115
+ 7. **Inject credentials** (optional): `huaweicloud_sandbox_credentials` — enables cloud API access from sandbox. Pass `session_id` **or** `dev_stage_id` (at least one; both come from `sandbox_connect`, and `dev_stage_id` may be omitted to reuse the most recent connect). The tool validates the AK/SK against IAM first — an invalid SK is rejected here instead of failing later with `APIGW.0301` during exec. Example:
116
+
117
+ ```json
118
+ { "dev_stage_id": "<dev_stage_id from connect>" }
119
+ ```
120
+
121
+ After injection, credentials are also written to `/tmp/hw_creds.sh` for shell access (includes `HW_PROJECT_ID` when resolvable):
116
122
 
117
123
  ```bash
118
124
  # Source credentials in any sandbox shell before using hcloud/devbridge:
@@ -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`,完成后提示重启会话
@@ -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;
@@ -10,7 +10,7 @@ import {
10
10
  } from 'node:fs';
11
11
  import { join, dirname, resolve } from 'node:path';
12
12
  import { fileURLToPath } from 'node:url';
13
- import { homedir, platform } from 'node:os';
13
+ import { homedir, platform, tmpdir } from 'node:os';
14
14
  import { createInterface } from 'node:readline';
15
15
  import { spawnSync } from 'node:child_process';
16
16
  import { randomUUID } from 'node:crypto';
@@ -3404,72 +3404,89 @@ async function promptGlobalCleanup() {
3404
3404
  }
3405
3405
  }
3406
3406
 
3407
+ function openclawStatus() {
3408
+ const cdPluginDir = openclawPluginsDir();
3409
+ const cdSkillsDir = openclawSkillsDir();
3410
+ console.log(
3411
+ ` MCP Server: ${existsSync(join(cdPluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
3412
+ );
3413
+ console.log(
3414
+ ` Safety Policy: ${existsSync(join(cdPluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
3415
+ );
3416
+ let cdSkillCount = 0;
3417
+ if (existsSync(cdSkillsDir)) {
3418
+ cdSkillCount = readdirSync(cdSkillsDir, { withFileTypes: true }).filter(
3419
+ (d) => d.isDirectory() && d.name.startsWith('huawei'),
3420
+ ).length;
3421
+ }
3422
+ console.log(
3423
+ ` Skills: ${cdSkillCount > 0 ? `\x1b[32m${cdSkillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
3424
+ );
3425
+ }
3426
+
3427
+ function codexSectionStatus() {
3428
+ if (!hasCodexCLI()) {
3429
+ console.log(' \x1b[33mCodex CLI not found.\x1b[0m');
3430
+ } else {
3431
+ console.log(` Plugin: ${codexStatus() ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
3432
+ }
3433
+ }
3434
+
3407
3435
  async function cmdStatus() {
3408
- const target = parseTarget();
3436
+ const scope = parseTarget();
3409
3437
  console.log(BANNER);
3410
3438
  console.log(`HuaweiCloud DevKit Status\n`);
3411
3439
 
3412
- if (target === 'opencode' || target === 'all') {
3413
- console.log('[OpenCode]');
3414
- opencodeStatus();
3415
- }
3416
- if (target === 'codearts' || target === 'all') {
3417
- console.log('\n[CodeArts]');
3418
- codeartsStatus();
3419
- }
3420
- if (target === 'codearts-work' || target === 'all') {
3421
- console.log('\n[CodeArts Work]');
3422
- codeartsWorkStatus();
3423
- }
3424
- if (target === 'workbuddy' || target === 'all') {
3425
- console.log('\n[WorkBuddy]');
3426
- workbuddyStatus();
3427
- }
3428
- if (target === 'dsh' || target === 'all') {
3429
- console.log('\n[DSH]');
3430
- dshStatus();
3431
- }
3432
- if (target === 'officeace' || target === 'all') {
3433
- console.log('\n[OfficeAce]');
3434
- officeaceStatus();
3435
- }
3436
- if (target === 'hermes' || target === 'all') {
3437
- console.log('\n[Hermes Agent]');
3438
- hermesStatus();
3439
- }
3440
- if (target === 'openclaw' || target === 'all') {
3441
- console.log('\n[OpenClaw]');
3442
- const cdPluginDir = openclawPluginsDir();
3443
- const cdSkillsDir = openclawSkillsDir();
3444
- console.log(
3445
- ` MCP Server: ${existsSync(join(cdPluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
3446
- );
3447
- console.log(
3448
- ` Safety Policy: ${existsSync(join(cdPluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
3449
- );
3450
- let cdSkillCount = 0;
3451
- if (existsSync(cdSkillsDir)) {
3452
- cdSkillCount = readdirSync(cdSkillsDir, { withFileTypes: true }).filter(
3453
- (d) => d.isDirectory() && d.name.startsWith('huawei'),
3454
- ).length;
3455
- }
3456
- console.log(
3457
- ` Skills: ${cdSkillCount > 0 ? `\x1b[32m${cdSkillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
3458
- );
3459
- }
3460
- if (target === 'atomcode' || target === 'all') {
3461
- console.log('\n[AtomCode]');
3462
- atomcodeStatus();
3463
- }
3464
- if (target === 'codex' || target === 'all') {
3465
- console.log('\n[Codex]');
3466
- if (!hasCodexCLI()) {
3467
- console.log(' \x1b[33mCodex CLI not found.\x1b[0m');
3468
- } else {
3469
- console.log(` Plugin: ${codexStatus() ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
3440
+ const sections = [
3441
+ { name: 'OpenCode', run: opencodeStatus, on: (target) => target === 'opencode' || target === 'all' },
3442
+ { name: 'CodeArts', run: codeartsStatus, on: (target) => target === 'codearts' || target === 'all' },
3443
+ { name: 'CodeArts Work', run: codeartsWorkStatus, on: (target) => target === 'codearts-work' || target === 'all' },
3444
+ { name: 'WorkBuddy', run: workbuddyStatus, on: (target) => target === 'workbuddy' || target === 'all' },
3445
+ { name: 'DSH', run: dshStatus, on: (target) => target === 'dsh' || target === 'all' },
3446
+ { name: 'OfficeAce', run: officeaceStatus, on: (target) => target === 'officeace' || target === 'all' },
3447
+ { name: 'Hermes Agent', run: hermesStatus, on: (target) => target === 'hermes' || target === 'all' },
3448
+ { name: 'OpenClaw', run: openclawStatus, on: (target) => target === 'openclaw' || target === 'all' },
3449
+ { name: 'AtomCode', run: atomcodeStatus, on: (target) => target === 'atomcode' || target === 'all' },
3450
+ { name: 'Codex', run: codexSectionStatus, on: (target) => target === 'codex' || target === 'all' },
3451
+ ];
3452
+
3453
+ // Capture each section's output so results can be grouped by install state
3454
+ // (installed first) instead of the fixed agent order.
3455
+ const originalLog = console.log;
3456
+ const results = [];
3457
+ for (const section of sections) {
3458
+ if (!section.on(scope)) continue;
3459
+ const lines = [];
3460
+ console.log = (...args) => lines.push(args.map((a) => String(a)).join(' '));
3461
+ try {
3462
+ section.run();
3463
+ } finally {
3464
+ console.log = originalLog;
3470
3465
  }
3466
+ const plain = lines.join('\n');
3467
+ const hasInstalled = /Installed/.test(plain);
3468
+ const hasMissing = /Not installed|not found/i.test(plain);
3469
+ const state = hasInstalled && hasMissing ? 'partial' : hasInstalled ? 'installed' : hasMissing ? 'not' : 'unknown';
3470
+ results.push({ name: section.name, lines, state });
3471
+ }
3472
+
3473
+ const stateOrder = { installed: 0, partial: 1, unknown: 2, not: 3 };
3474
+ results.sort((a, b) => stateOrder[a.state] - stateOrder[b.state]);
3475
+
3476
+ const namesIn = (state) => results.filter((r) => r.state === state).map((r) => r.name);
3477
+ const summaryParts = [];
3478
+ if (namesIn('installed').length) summaryParts.push(`已安装: ${namesIn('installed').join(', ')}`);
3479
+ if (namesIn('partial').length) summaryParts.push(`部分安装: ${namesIn('partial').join(', ')}`);
3480
+ if (namesIn('not').length) summaryParts.push(`未安装: ${namesIn('not').join(', ')}`);
3481
+ if (summaryParts.length) console.log(` ${summaryParts.join(' | ')}\n`);
3482
+
3483
+ for (const result of results) {
3484
+ console.log(`[${result.name}]`);
3485
+ for (const line of result.lines) console.log(line);
3486
+ console.log('');
3471
3487
  }
3472
- console.log('\nEnvironment:');
3488
+
3489
+ console.log('Environment:');
3473
3490
  console.log(` Node.js: ${process.version}`);
3474
3491
  console.log(` Platform: ${platform()}`);
3475
3492
  }
@@ -3812,6 +3829,7 @@ async function cmdDoctor() {
3812
3829
  { path: join(codeartsPluginsDir(), '.installed'), name: 'CodeArts' },
3813
3830
  { path: join(codeartsWorkPluginsDir(), '.installed'), name: 'CodeArts Work' },
3814
3831
  { path: join(dshPluginsDir(), '.installed'), name: 'DSH' },
3832
+ { path: join(hermesPluginsDir(), '.installed'), name: 'Hermes Agent' },
3815
3833
  { path: join(officeacePluginsDir(), '.installed'), name: 'OfficeAce' },
3816
3834
  { path: join(atomcodePluginsDir(), '.installed'), name: 'AtomCode' },
3817
3835
  ];
@@ -4033,10 +4051,6 @@ async function cmdUpdate() {
4033
4051
  console.log(`\x1b[33mMCP 工具在重启各 agent 会话后才生效。\x1b[0m`);
4034
4052
  return;
4035
4053
  }
4036
-
4037
- await cmdUninstall();
4038
- console.log('');
4039
- await cmdInstall();
4040
4054
  }
4041
4055
 
4042
4056
  async function cmdReinstall() {
@@ -4167,32 +4181,102 @@ async function cmdInstallHcloud() {
4167
4181
  printSandboxWarning('沙箱模式拦截了 KooCLI 自动安装(无法创建/写入安装目录)。');
4168
4182
  }
4169
4183
  }
4170
- } else if (os === 'linux') {
4171
- console.log('[Linux] Auto-install (pinned to KooCLI <version>):'.replace('<version>', kooCliVersion || ''));
4172
- const pkg = arch === 'arm64' ? 'linux-arm64' : 'linux-amd64';
4173
- console.log(` curl -LO "${baseUrl}/huaweicloud-cli-${pkg}.tar.gz"`);
4174
- console.log(` tar -zxvf huaweicloud-cli-${pkg}.tar.gz`);
4175
- console.log(` mv hcloud ~/.local/bin/`);
4176
- console.log(` hcloud version`);
4177
- console.log(`\n Or one-liner (installs LATEST, may differ from pinned ${kooCliVersion || ''}):`);
4178
- console.log(` curl -sSL ${latestUrl}/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y`);
4179
- console.log(
4180
- ` \x1b[33m⚠️ The one-liner installs the latest KooCLI. If \`hcloud version\` != ${kooCliVersion}, doctor/check_cli will warn — use the pinned download above instead.\x1b[0m`,
4181
- );
4182
- console.log(`\nFull guide: https://support.huaweicloud.com/qs-hcli/hcli_02_003_02.html`);
4183
- } else if (os === 'darwin') {
4184
- console.log('[macOS] Auto-install (pinned to KooCLI <version>):'.replace('<version>', kooCliVersion || ''));
4185
- const pkg = arch === 'arm64' ? 'mac-arm64' : 'mac-amd64';
4186
- console.log(` curl -LO "${baseUrl}/huaweicloud-cli-${pkg}.tar.gz"`);
4187
- console.log(` tar -zxvf huaweicloud-cli-${pkg}.tar.gz`);
4188
- console.log(` mv hcloud /usr/local/bin/`);
4189
- console.log(` hcloud version`);
4190
- console.log(`\n Or one-liner (installs LATEST, may differ from pinned ${kooCliVersion || ''}):`);
4191
- console.log(` curl -sSL ${latestUrl}/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y`);
4192
- console.log(
4193
- ` \x1b[33m⚠️ The one-liner installs the latest KooCLI. If \`hcloud version\` != ${kooCliVersion}, doctor/check_cli will warn — use the pinned download above instead.\x1b[0m`,
4194
- );
4195
- console.log(`\nFull guide: https://support.huaweicloud.com/qs-hcli/hcli_02_003_03.html`);
4184
+ } else if (os === 'linux' || os === 'darwin') {
4185
+ const label = os === 'linux' ? 'Linux' : 'macOS';
4186
+ const pkg =
4187
+ os === 'linux'
4188
+ ? arch === 'arm64'
4189
+ ? 'linux-arm64'
4190
+ : 'linux-amd64'
4191
+ : arch === 'arm64'
4192
+ ? 'mac-arm64'
4193
+ : 'mac-amd64';
4194
+ const url = `${baseUrl}/huaweicloud-cli-${pkg}.tar.gz`;
4195
+ const binDir = os === 'linux' ? join(homedir(), '.local', 'bin') : '/usr/local/bin';
4196
+ const binPath = join(binDir, 'hcloud');
4197
+
4198
+ const printManual = () => {
4199
+ console.log(`\n[${label}] Manual install (pinned to KooCLI ${kooCliVersion || ''}):`);
4200
+ console.log(` curl -LO "${url}"`);
4201
+ console.log(` tar -zxvf huaweicloud-cli-${pkg}.tar.gz`);
4202
+ console.log(` mv hcloud ${binDir}/`);
4203
+ console.log(` hcloud version`);
4204
+ console.log(`\n Or one-liner (installs LATEST, may differ from pinned ${kooCliVersion || ''}):`);
4205
+ console.log(` curl -sSL ${latestUrl}/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y`);
4206
+ console.log(
4207
+ ` \x1b[33m⚠️ The one-liner installs the latest KooCLI. If \`hcloud version\` != ${kooCliVersion}, doctor/check_cli will warn — use the pinned download above instead.\x1b[0m`,
4208
+ );
4209
+ console.log(
4210
+ `\nFull guide: https://support.huaweicloud.com/qs-hcli/hcli_02_003_0${os === 'linux' ? '2' : '3'}.html`,
4211
+ );
4212
+ };
4213
+
4214
+ if (detectCodeartsSandbox() === 'sandbox') {
4215
+ printSandboxWarning('沙箱模式拦截了 KooCLI 自动安装(无法创建/写入安装目录)。');
4216
+ printManual();
4217
+ } else {
4218
+ console.log(`[${label}] Auto-installing to ${binDir}...`);
4219
+ try {
4220
+ const tmpTar = join(tmpdir(), `huaweicloud-cli-${pkg}.tar.gz`);
4221
+ console.log(` Downloading ${url}...`);
4222
+ const dl = spawnSync('curl', ['-fL', url, '-o', tmpTar], { stdio: 'inherit', timeout: 180000 });
4223
+ if (dl.status !== 0) throw new Error(`下载失败 (curl exit ${dl.status})`);
4224
+
4225
+ console.log(' Extracting...');
4226
+ const ex = spawnSync('tar', ['-xzf', tmpTar, '-C', tmpdir()], { stdio: 'inherit', timeout: 60000 });
4227
+ if (ex.status !== 0) throw new Error(`解压失败 (tar exit ${ex.status})`);
4228
+ rmSync(tmpTar, { force: true });
4229
+ const extracted = join(tmpdir(), 'hcloud');
4230
+ if (!existsSync(extracted)) throw new Error('hcloud 二进制未生成,可能已被安全软件拦截');
4231
+
4232
+ try {
4233
+ mkdirSync(binDir, { recursive: true });
4234
+ } catch {}
4235
+ console.log(' Installing...');
4236
+ let mv = spawnSync('mv', [extracted, binPath], { stdio: 'inherit', timeout: 30000 });
4237
+ if (mv.status !== 0) {
4238
+ console.log(' 目标目录需要权限,尝试 sudo mv...');
4239
+ mv = spawnSync('sudo', ['mv', extracted, binPath], { stdio: 'inherit', timeout: 30000 });
4240
+ if (mv.status !== 0) throw new Error(`安装失败 (mv exit ${mv.status})`);
4241
+ }
4242
+
4243
+ console.log(`\n\x1b[32mInstall complete.\x1b[0m`);
4244
+ console.log(` Verify: ${binPath} version`);
4245
+
4246
+ // Ask user before accepting the privacy agreement — never auto-accept.
4247
+ if (process.stdin.isTTY && process.stdout.isTTY) {
4248
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
4249
+ const agree = await new Promise((resolve) => {
4250
+ rl.question('\n KooCLI requires accepting its privacy agreement. Do you accept? (y/N) ', (answer) => {
4251
+ rl.close();
4252
+ resolve(/^\s*y\s*$/i.test(answer));
4253
+ });
4254
+ });
4255
+ if (agree) {
4256
+ const r = spawnSync(binPath, ['version'], {
4257
+ input: 'y\n',
4258
+ encoding: 'utf8',
4259
+ timeout: 15000,
4260
+ });
4261
+ console.log(
4262
+ r.status === 0
4263
+ ? ' \x1b[32mPrivacy agreement accepted. KooCLI ready.\x1b[0m'
4264
+ : ' \x1b[33m无法写入配置目录。请在终端运行: hcloud version 并按提示操作\x1b[0m',
4265
+ );
4266
+ } else {
4267
+ console.log(' \x1b[33m请手动接受隐私协议:在终端运行 hcloud version 并按提示操作\x1b[0m');
4268
+ }
4269
+ } else {
4270
+ console.log(' \x1b[33m非交互会话:请稍后在终端运行 hcloud version 并按提示接受隐私协议\x1b[0m');
4271
+ }
4272
+ } catch (error) {
4273
+ console.log(`\n\x1b[33mAuto-install failed: ${error.message}\x1b[0m`);
4274
+ printManual();
4275
+ if (detectCodeartsSandbox() === 'sandbox') {
4276
+ printSandboxWarning('沙箱模式拦截了 KooCLI 自动安装(无法创建/写入安装目录)。');
4277
+ }
4278
+ }
4279
+ }
4196
4280
  }
4197
4281
 
4198
4282
  console.log('\nAfter install, set HCLOUD_BIN if hcloud is not on PATH.');
@@ -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
  {