huaweicloud-devkit 1.1.0-next.9 → 1.1.0
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 +27 -1
- package/README.zh-CN.md +26 -1
- package/package.json +3 -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/.hermes-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +29 -12
- package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +136 -24
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +15 -14
- package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +5 -1
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/hc-activity.md +27 -0
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +11 -11
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +6 -5
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +71 -7
- package/plugins/huaweicloud-core/skills/huawei-sandbox/references/nginx-templates.md +4 -0
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +19 -12
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +26 -26
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +12 -6
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +7 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +7 -7
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +14 -0
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +30 -1
- package/plugins/huaweicloud-core/src/auth/service.mjs +33 -0
- package/plugins/huaweicloud-core/src/detect-framework.mjs +25 -3
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +67 -5
- package/plugins/huaweicloud-core/src/mcp-server.mjs +23 -0
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +11 -3
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +118 -23
- package/plugins/huaweicloud-core/src/search-market.mjs +1 -0
- package/plugins/huaweicloud-core/src/setup-cli.mjs +491 -64
- package/plugins/huaweicloud-core/src/tools.mjs +37 -15
|
@@ -105,6 +105,17 @@ function codeartsPluginsDir() {
|
|
|
105
105
|
return join(homedir(), '.codeartsdoer', 'huaweicloud-plugins');
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
// CodeArts Work (CodeArts Space) — user-level only
|
|
109
|
+
function codeartsWorkSkillsDir() {
|
|
110
|
+
return join(homedir(), '.codeartswork', 'skills');
|
|
111
|
+
}
|
|
112
|
+
function codeartsWorkMcpSettingsFile() {
|
|
113
|
+
return join(homedir(), '.codeartswork', 'mcp', 'mcp_settings.json');
|
|
114
|
+
}
|
|
115
|
+
function codeartsWorkPluginsDir() {
|
|
116
|
+
return join(homedir(), '.codeartswork', 'huaweicloud-plugins');
|
|
117
|
+
}
|
|
118
|
+
|
|
108
119
|
function workbuddySkillsDir() {
|
|
109
120
|
return join(homedir(), '.workbuddy', 'skills');
|
|
110
121
|
}
|
|
@@ -463,6 +474,7 @@ function installRuntimeDeps(pluginsDir) {
|
|
|
463
474
|
};
|
|
464
475
|
let r = spawnSync('npm', ['install', '--omit=dev'], spawnOpts);
|
|
465
476
|
const isRetryable = (res) => {
|
|
477
|
+
if (res.status === 0) return false;
|
|
466
478
|
const stderr = (res.stderr || '').toString();
|
|
467
479
|
return res.error?.code === 'EPERM' || res.error?.code === 'EBUSY' || /EPERM|EBUSY/.test(stderr);
|
|
468
480
|
};
|
|
@@ -471,6 +483,11 @@ function installRuntimeDeps(pluginsDir) {
|
|
|
471
483
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2000);
|
|
472
484
|
r = spawnSync('npm', ['install', '--omit=dev'], spawnOpts);
|
|
473
485
|
}
|
|
486
|
+
if (r.status !== 0 && !isRetryable(r)) {
|
|
487
|
+
console.log(` \x1b[33m[RETRY]\x1b[0m npm install failed (exit ${r.status}), retrying in 3s...`);
|
|
488
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 3000);
|
|
489
|
+
r = spawnSync('npm', ['install', '--omit=dev'], spawnOpts);
|
|
490
|
+
}
|
|
474
491
|
const undiciDir = join(pluginsDir, 'node_modules', 'undici');
|
|
475
492
|
if (r.status === 0 && existsSync(undiciDir)) {
|
|
476
493
|
console.log(` Runtime deps installed -> ${join(pluginsDir, 'node_modules')}`);
|
|
@@ -478,12 +495,14 @@ function installRuntimeDeps(pluginsDir) {
|
|
|
478
495
|
const errCode = r.error?.code;
|
|
479
496
|
const err = (r.stderr || '').toString().trim().split(/\r?\n/).slice(-2).join(' ');
|
|
480
497
|
const hint = errCode === 'ENOENT' ? ' (npm not found — ensure Node.js/npm is in PATH)' : '';
|
|
481
|
-
|
|
498
|
+
const msg = `npm install failed in ${pluginsDir}${err ? `: ${err}` : ''}${hint}`;
|
|
499
|
+
console.log(` \x1b[31m[ERROR]\x1b[0m ${msg}`);
|
|
482
500
|
console.log(' Manual fix: cd %s && npm install', pluginsDir);
|
|
483
501
|
if (!existsSync(undiciDir)) {
|
|
484
502
|
console.log(` \x1b[31m[ERROR]\x1b[0m undici is NOT installed. MCP server will fail to start.`);
|
|
485
503
|
console.log(` Run manually: cd "${pluginsDir}" && npm install undici@^8.10.0`);
|
|
486
504
|
}
|
|
505
|
+
throw new Error(msg);
|
|
487
506
|
}
|
|
488
507
|
}
|
|
489
508
|
|
|
@@ -1248,6 +1267,146 @@ function codeartsStatus() {
|
|
|
1248
1267
|
}
|
|
1249
1268
|
}
|
|
1250
1269
|
|
|
1270
|
+
// --- CodeArts Work (CodeArts Space) ---
|
|
1271
|
+
|
|
1272
|
+
function registerCodeartsWorkMcp() {
|
|
1273
|
+
const configPath = codeartsWorkMcpSettingsFile();
|
|
1274
|
+
const mcpPath = join(codeartsWorkPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
1275
|
+
const env = { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' };
|
|
1276
|
+
const hcloudBin = findHcloudBin();
|
|
1277
|
+
if (hcloudBin) env.HCLOUD_BIN = hcloudBin.replace(/\\/g, '/');
|
|
1278
|
+
let config = {};
|
|
1279
|
+
if (existsSync(configPath)) {
|
|
1280
|
+
try {
|
|
1281
|
+
config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
1282
|
+
} catch {
|
|
1283
|
+
console.log(
|
|
1284
|
+
` \x1b[33m[WARN]\x1b[0m Could not parse ${configPath}. Skipping MCP config write; ensure "mcpServers.huaweicloud-devkit" points to ${mcpPath}.`,
|
|
1285
|
+
);
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
const existing = config.mcpServers?.['huaweicloud-devkit'];
|
|
1289
|
+
if (
|
|
1290
|
+
existing &&
|
|
1291
|
+
existing.command === 'node' &&
|
|
1292
|
+
Array.isArray(existing.args) &&
|
|
1293
|
+
existing.args[0] === mcpPath &&
|
|
1294
|
+
existing.timeout === 300000
|
|
1295
|
+
) {
|
|
1296
|
+
console.log(` MCP config unchanged: ${configPath}`);
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
config.mcpServers = config.mcpServers || {};
|
|
1301
|
+
config.mcpServers['huaweicloud-devkit'] = {
|
|
1302
|
+
command: 'node',
|
|
1303
|
+
args: [mcpPath],
|
|
1304
|
+
env,
|
|
1305
|
+
enabled: true,
|
|
1306
|
+
timeout: 300000,
|
|
1307
|
+
};
|
|
1308
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
1309
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
1310
|
+
console.log(` MCP config updated: ${configPath}`);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
async function installCodeArtsWork() {
|
|
1314
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
1315
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
1316
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
1317
|
+
|
|
1318
|
+
copyDir(skillsSrc, codeartsWorkSkillsDir());
|
|
1319
|
+
console.log(` Skills -> ${codeartsWorkSkillsDir()}`);
|
|
1320
|
+
|
|
1321
|
+
const pluginDest = codeartsWorkPluginsDir();
|
|
1322
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
1323
|
+
console.log(` MCP Server -> ${join(pluginDest, 'src')}`);
|
|
1324
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
1325
|
+
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
1326
|
+
|
|
1327
|
+
registerCodeartsWorkMcp();
|
|
1328
|
+
installRuntimeDeps(pluginDest);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
async function updateCodeArtsWork() {
|
|
1332
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
1333
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
1334
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
1335
|
+
const pluginDest = codeartsWorkPluginsDir();
|
|
1336
|
+
|
|
1337
|
+
copyDir(skillsSrc, codeartsWorkSkillsDir());
|
|
1338
|
+
console.log(` Skills updated -> ${codeartsWorkSkillsDir()}`);
|
|
1339
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
1340
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
1341
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
1342
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
1343
|
+
registerCodeartsWorkMcp();
|
|
1344
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
1345
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
1346
|
+
installRuntimeDeps(pluginDest);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
function uninstallCodeArtsWork() {
|
|
1350
|
+
const skillsDir = codeartsWorkSkillsDir();
|
|
1351
|
+
if (existsSync(skillsDir)) {
|
|
1352
|
+
let removed = 0;
|
|
1353
|
+
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
1354
|
+
if (entry.name.startsWith('huawei')) {
|
|
1355
|
+
removeIfExists(join(skillsDir, entry.name));
|
|
1356
|
+
removed++;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
if (removed > 0) console.log(` Removed ${removed} skills`);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
if (removeIfExists(codeartsWorkPluginsDir())) {
|
|
1363
|
+
console.log(' Removed MCP server and safety policy');
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
const configPath = codeartsWorkMcpSettingsFile();
|
|
1367
|
+
if (existsSync(configPath)) {
|
|
1368
|
+
let config = {};
|
|
1369
|
+
try {
|
|
1370
|
+
config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
1371
|
+
} catch {}
|
|
1372
|
+
if (config.mcpServers?.['huaweicloud-devkit']) {
|
|
1373
|
+
delete config.mcpServers['huaweicloud-devkit'];
|
|
1374
|
+
if (Object.keys(config.mcpServers).length === 0) delete config.mcpServers;
|
|
1375
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
1376
|
+
console.log(` Config cleaned: ${configPath}`);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
function codeartsWorkStatus() {
|
|
1382
|
+
const pluginDir = codeartsWorkPluginsDir();
|
|
1383
|
+
console.log(
|
|
1384
|
+
` MCP Server: ${existsSync(join(pluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1385
|
+
);
|
|
1386
|
+
console.log(
|
|
1387
|
+
` Safety Policy: ${existsSync(join(pluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1388
|
+
);
|
|
1389
|
+
let skillCount = 0;
|
|
1390
|
+
if (existsSync(codeartsWorkSkillsDir())) {
|
|
1391
|
+
skillCount = readdirSync(codeartsWorkSkillsDir(), { withFileTypes: true }).filter(
|
|
1392
|
+
(d) => d.isDirectory() && d.name.startsWith('huawei'),
|
|
1393
|
+
).length;
|
|
1394
|
+
}
|
|
1395
|
+
console.log(
|
|
1396
|
+
` Skills: ${skillCount > 0 ? `\x1b[32m${skillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1397
|
+
);
|
|
1398
|
+
if (existsSync(codeartsWorkMcpSettingsFile())) {
|
|
1399
|
+
try {
|
|
1400
|
+
const config = JSON.parse(readFileSync(codeartsWorkMcpSettingsFile(), 'utf8'));
|
|
1401
|
+
console.log(
|
|
1402
|
+
` MCP config: ${config.mcpServers?.['huaweicloud-devkit'] ? '\x1b[32mConfigured\x1b[0m' : '\x1b[31mNot configured\x1b[0m'}`,
|
|
1403
|
+
);
|
|
1404
|
+
} catch {
|
|
1405
|
+
console.log(` MCP config: \x1b[31mInvalid\x1b[0m`);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1251
1410
|
// Returns true when the config file was written, false when it was already correct.
|
|
1252
1411
|
function ensureWorkbuddyMcpConfig() {
|
|
1253
1412
|
const configPath = workbuddyMcpConfigFile();
|
|
@@ -1957,6 +2116,14 @@ function hermesPluginsDir() {
|
|
|
1957
2116
|
return join(hermesHomeDir(), 'huaweicloud-plugins');
|
|
1958
2117
|
}
|
|
1959
2118
|
|
|
2119
|
+
function hermesPythonPluginsDir() {
|
|
2120
|
+
return join(hermesHomeDir(), 'plugins');
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
function hermesSafetyPluginDir() {
|
|
2124
|
+
return join(hermesPythonPluginsDir(), 'huaweicloud-safety');
|
|
2125
|
+
}
|
|
2126
|
+
|
|
1960
2127
|
function hermesConfigFile() {
|
|
1961
2128
|
return join(hermesHomeDir(), 'config.yaml');
|
|
1962
2129
|
}
|
|
@@ -2035,17 +2202,26 @@ function removeHermesMcpConfigBlock() {
|
|
|
2035
2202
|
console.log(' MCP config cleaned');
|
|
2036
2203
|
}
|
|
2037
2204
|
|
|
2205
|
+
function hermesHookScript() {
|
|
2206
|
+
return join(hermesPluginsDir(), 'hooks', 'huaweicloud-safety.py').replace(/\\/g, '/');
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
function hermesHookCommand() {
|
|
2210
|
+
const pythonBin = process.platform === 'win32' ? 'python' : 'python3';
|
|
2211
|
+
return `${pythonBin} ${hermesHookScript()}`;
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2038
2214
|
function ensureHermesHooksConfig() {
|
|
2039
2215
|
const configPath = hermesConfigFile();
|
|
2040
|
-
const
|
|
2041
|
-
const hookScript = join(pluginDest, 'hooks', 'huaweicloud-safety.py').replace(/\\/g, '/');
|
|
2216
|
+
const hookCommand = hermesHookCommand();
|
|
2042
2217
|
|
|
2043
2218
|
const blockLines = [
|
|
2044
2219
|
'hooks:',
|
|
2045
2220
|
' pre_tool_call:',
|
|
2046
2221
|
' - matcher: "terminal"',
|
|
2047
|
-
` command: "
|
|
2222
|
+
` command: "${hookCommand}"`,
|
|
2048
2223
|
' timeout: 5',
|
|
2224
|
+
' fail_closed: true',
|
|
2049
2225
|
];
|
|
2050
2226
|
const block = blockLines.join('\n');
|
|
2051
2227
|
|
|
@@ -2054,10 +2230,21 @@ function ensureHermesHooksConfig() {
|
|
|
2054
2230
|
try {
|
|
2055
2231
|
existing = readFileSync(configPath, 'utf8');
|
|
2056
2232
|
} catch {}
|
|
2057
|
-
if (
|
|
2233
|
+
if (
|
|
2234
|
+
existing.includes('hooks:') &&
|
|
2235
|
+
existing.includes(`command: "${hookCommand}"`) &&
|
|
2236
|
+
existing.includes('fail_closed: true')
|
|
2237
|
+
) {
|
|
2058
2238
|
console.log(` Hooks config unchanged: ${configPath}`);
|
|
2059
2239
|
return false;
|
|
2060
2240
|
}
|
|
2241
|
+
removeHermesHooksConfigBlock();
|
|
2242
|
+
existing = '';
|
|
2243
|
+
if (existsSync(configPath)) {
|
|
2244
|
+
try {
|
|
2245
|
+
existing = readFileSync(configPath, 'utf8');
|
|
2246
|
+
} catch {}
|
|
2247
|
+
}
|
|
2061
2248
|
}
|
|
2062
2249
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
2063
2250
|
const newContent = existing ? `${existing.trimEnd()}\n\n${block}\n` : `${block}\n`;
|
|
@@ -2066,30 +2253,171 @@ function ensureHermesHooksConfig() {
|
|
|
2066
2253
|
return true;
|
|
2067
2254
|
}
|
|
2068
2255
|
|
|
2256
|
+
function hermesHookScriptMtime() {
|
|
2257
|
+
const scriptPath = hermesHookScript();
|
|
2258
|
+
try {
|
|
2259
|
+
return statSync(scriptPath).mtime.toISOString();
|
|
2260
|
+
} catch {
|
|
2261
|
+
return null;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
function hermesAllowlistPath() {
|
|
2266
|
+
return join(hermesHomeDir(), 'shell-hooks-allowlist.json');
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
function ensureHermesHookAllowlist() {
|
|
2270
|
+
const hookCommand = hermesHookCommand();
|
|
2271
|
+
const allowlistPath = hermesAllowlistPath();
|
|
2272
|
+
|
|
2273
|
+
let data = { approvals: [] };
|
|
2274
|
+
if (existsSync(allowlistPath)) {
|
|
2275
|
+
try {
|
|
2276
|
+
const parsed = JSON.parse(readFileSync(allowlistPath, 'utf8'));
|
|
2277
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
2278
|
+
data = parsed;
|
|
2279
|
+
}
|
|
2280
|
+
} catch {}
|
|
2281
|
+
}
|
|
2282
|
+
const approvals = Array.isArray(data.approvals) ? data.approvals : [];
|
|
2283
|
+
const already = approvals.some(
|
|
2284
|
+
(a) => a && typeof a === 'object' && a.event === 'pre_tool_call' && a.command === hookCommand,
|
|
2285
|
+
);
|
|
2286
|
+
if (already) {
|
|
2287
|
+
console.log(` Hook allowlist unchanged: ${allowlistPath}`);
|
|
2288
|
+
return false;
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
const entry = {
|
|
2292
|
+
event: 'pre_tool_call',
|
|
2293
|
+
command: hookCommand,
|
|
2294
|
+
approved_at: new Date().toISOString(),
|
|
2295
|
+
script_mtime_at_approval: hermesHookScriptMtime(),
|
|
2296
|
+
};
|
|
2297
|
+
data.approvals = [...approvals, entry];
|
|
2298
|
+
mkdirSync(dirname(allowlistPath), { recursive: true });
|
|
2299
|
+
writeFileSync(allowlistPath, JSON.stringify(data, null, 2));
|
|
2300
|
+
console.log(` Hook allowlist updated: ${allowlistPath}`);
|
|
2301
|
+
return true;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
function hermesHookAllowlisted() {
|
|
2305
|
+
const allowlistPath = hermesAllowlistPath();
|
|
2306
|
+
if (!existsSync(allowlistPath)) return false;
|
|
2307
|
+
try {
|
|
2308
|
+
const data = JSON.parse(readFileSync(allowlistPath, 'utf8'));
|
|
2309
|
+
const cmd = hermesHookCommand();
|
|
2310
|
+
return (
|
|
2311
|
+
Array.isArray(data?.approvals) &&
|
|
2312
|
+
data.approvals.some((a) => a && typeof a === 'object' && a.event === 'pre_tool_call' && a.command === cmd)
|
|
2313
|
+
);
|
|
2314
|
+
} catch {
|
|
2315
|
+
return false;
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
function hermesHookPluginInit() {
|
|
2320
|
+
const script = hermesHookScript();
|
|
2321
|
+
return [
|
|
2322
|
+
'import importlib.util',
|
|
2323
|
+
'',
|
|
2324
|
+
`SAFETY_SCRIPT = ${JSON.stringify(script)}`,
|
|
2325
|
+
'',
|
|
2326
|
+
'',
|
|
2327
|
+
'def _load_safety():',
|
|
2328
|
+
' spec = importlib.util.spec_from_file_location("huaweicloud_safety_hook", SAFETY_SCRIPT)',
|
|
2329
|
+
' module = importlib.util.module_from_spec(spec)',
|
|
2330
|
+
' spec.loader.exec_module(module)',
|
|
2331
|
+
' return module',
|
|
2332
|
+
'',
|
|
2333
|
+
'',
|
|
2334
|
+
'_safety = _load_safety()',
|
|
2335
|
+
'',
|
|
2336
|
+
'',
|
|
2337
|
+
'def register(ctx):',
|
|
2338
|
+
' ctx.register_hook("pre_tool_call", _check)',
|
|
2339
|
+
'',
|
|
2340
|
+
'',
|
|
2341
|
+
'def _check(tool_name=None, args=None, **kwargs):',
|
|
2342
|
+
' if tool_name != "terminal":',
|
|
2343
|
+
' return None',
|
|
2344
|
+
' if not (isinstance(args, dict) and "command" in args):',
|
|
2345
|
+
' return None',
|
|
2346
|
+
' reason = _safety.evaluate(tool_name, args)',
|
|
2347
|
+
' if reason:',
|
|
2348
|
+
' return {"action": "block", "message": _safety.DENY_PREFIX + reason}',
|
|
2349
|
+
' return None',
|
|
2350
|
+
'',
|
|
2351
|
+
].join('\n');
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
function ensureHermesHookPlugin() {
|
|
2355
|
+
const pluginDir = hermesSafetyPluginDir();
|
|
2356
|
+
const manifestPath = join(pluginDir, 'plugin.yaml');
|
|
2357
|
+
const initPath = join(pluginDir, '__init__.py');
|
|
2358
|
+
|
|
2359
|
+
const manifest = [
|
|
2360
|
+
'name: huaweicloud-safety',
|
|
2361
|
+
`version: "${pkgVersion}"`,
|
|
2362
|
+
'description: Huawei Cloud safety pre_tool_call hook',
|
|
2363
|
+
'author: HuaweiCloud Mate',
|
|
2364
|
+
'provides_hooks:',
|
|
2365
|
+
' - pre_tool_call',
|
|
2366
|
+
'',
|
|
2367
|
+
].join('\n');
|
|
2368
|
+
const init = hermesHookPluginInit();
|
|
2369
|
+
|
|
2370
|
+
const manifestCurrent = existsSync(manifestPath) ? readFileSync(manifestPath, 'utf8') : '';
|
|
2371
|
+
const initCurrent = existsSync(initPath) ? readFileSync(initPath, 'utf8') : '';
|
|
2372
|
+
if (manifestCurrent === manifest && initCurrent === init) {
|
|
2373
|
+
console.log(` Hook plugin unchanged: ${pluginDir}`);
|
|
2374
|
+
return false;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
mkdirSync(pluginDir, { recursive: true });
|
|
2378
|
+
writeFileSync(manifestPath, manifest);
|
|
2379
|
+
writeFileSync(initPath, init);
|
|
2380
|
+
console.log(` Hook plugin installed: ${pluginDir}`);
|
|
2381
|
+
return true;
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
function removeHermesHookPlugin() {
|
|
2385
|
+
const pluginDir = hermesSafetyPluginDir();
|
|
2386
|
+
if (!existsSync(pluginDir)) return false;
|
|
2387
|
+
rmSync(pluginDir, { recursive: true, force: true });
|
|
2388
|
+
console.log(` Removed hook plugin: ${pluginDir}`);
|
|
2389
|
+
const parent = hermesPythonPluginsDir();
|
|
2390
|
+
try {
|
|
2391
|
+
if (existsSync(parent) && readdirSync(parent).length === 0) {
|
|
2392
|
+
rmSync(parent, { recursive: true, force: true });
|
|
2393
|
+
}
|
|
2394
|
+
} catch {}
|
|
2395
|
+
return true;
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
function hermesHookPluginInstalled() {
|
|
2399
|
+
return (
|
|
2400
|
+
existsSync(join(hermesSafetyPluginDir(), 'plugin.yaml')) && existsSync(join(hermesSafetyPluginDir(), '__init__.py'))
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2069
2404
|
function removeHermesHooksConfigBlock() {
|
|
2070
2405
|
const configPath = hermesConfigFile();
|
|
2071
2406
|
if (!existsSync(configPath)) return;
|
|
2072
2407
|
const lines = readFileSync(configPath, 'utf8').split(/\r?\n/);
|
|
2408
|
+
let inHooksBlock = false;
|
|
2073
2409
|
const out = [];
|
|
2074
|
-
let skip = false;
|
|
2075
2410
|
for (const line of lines) {
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
}
|
|
2080
|
-
if (skip) {
|
|
2081
|
-
if (line.trim() === '') continue;
|
|
2082
|
-
if (/^\s{2,}/.test(line) && line.trim() !== '') continue;
|
|
2083
|
-
skip = false;
|
|
2084
|
-
}
|
|
2085
|
-
if (line.trim() === 'hooks:') {
|
|
2086
|
-
continue;
|
|
2087
|
-
}
|
|
2088
|
-
if (line.trim() === 'pre_tool_call:') {
|
|
2411
|
+
const trimmed = line.trim();
|
|
2412
|
+
if (trimmed === 'hooks:' && !inHooksBlock) {
|
|
2413
|
+
inHooksBlock = true;
|
|
2089
2414
|
continue;
|
|
2090
2415
|
}
|
|
2091
|
-
if (
|
|
2092
|
-
|
|
2416
|
+
if (inHooksBlock) {
|
|
2417
|
+
if (trimmed === '' || line.startsWith(' ') || line.startsWith('\t')) {
|
|
2418
|
+
continue;
|
|
2419
|
+
}
|
|
2420
|
+
inHooksBlock = false;
|
|
2093
2421
|
}
|
|
2094
2422
|
out.push(line);
|
|
2095
2423
|
}
|
|
@@ -2155,6 +2483,8 @@ async function installHermes() {
|
|
|
2155
2483
|
|
|
2156
2484
|
if (!skipMcp) ensureHermesMcpConfig();
|
|
2157
2485
|
ensureHermesHooksConfig();
|
|
2486
|
+
ensureHermesHookAllowlist();
|
|
2487
|
+
ensureHermesHookPlugin();
|
|
2158
2488
|
if (!skipMcp) installRuntimeDeps(pluginDest);
|
|
2159
2489
|
if (!skipMcp) ensureHermesMcpSdk();
|
|
2160
2490
|
}
|
|
@@ -2177,6 +2507,8 @@ async function updateHermes() {
|
|
|
2177
2507
|
console.log(` Safety Hooks updated -> ${join(pluginDest, 'hooks')}`);
|
|
2178
2508
|
ensureHermesMcpConfig();
|
|
2179
2509
|
ensureHermesHooksConfig();
|
|
2510
|
+
ensureHermesHookAllowlist();
|
|
2511
|
+
ensureHermesHookPlugin();
|
|
2180
2512
|
ensureHermesMcpSdk();
|
|
2181
2513
|
mkdirSync(pluginDest, { recursive: true });
|
|
2182
2514
|
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
@@ -2186,6 +2518,40 @@ async function updateHermes() {
|
|
|
2186
2518
|
function uninstallHermes() {
|
|
2187
2519
|
const skillsDir = hermesSkillsDir();
|
|
2188
2520
|
let removed = 0;
|
|
2521
|
+
|
|
2522
|
+
// 1. Remove hook config from config.yaml (before deleting script files)
|
|
2523
|
+
removeHermesHooksConfigBlock();
|
|
2524
|
+
console.log(' Hooks config removed');
|
|
2525
|
+
|
|
2526
|
+
// 1b. Remove the Python safety hook plugin (v0.9.0+ enforcement path)
|
|
2527
|
+
removeHermesHookPlugin();
|
|
2528
|
+
|
|
2529
|
+
// 2. Clean shell-hooks-allowlist.json (remove approved hook references)
|
|
2530
|
+
const hermesHome = hermesHomeDir();
|
|
2531
|
+
const allowlistPath = join(hermesHome, 'shell-hooks-allowlist.json');
|
|
2532
|
+
if (existsSync(allowlistPath)) {
|
|
2533
|
+
try {
|
|
2534
|
+
const allowlist = JSON.parse(readFileSync(allowlistPath, 'utf8'));
|
|
2535
|
+
const before = (allowlist.approvals || []).length;
|
|
2536
|
+
allowlist.approvals = (allowlist.approvals || []).filter((a) => {
|
|
2537
|
+
const cmd = typeof a === 'string' ? a : a?.command;
|
|
2538
|
+
return !(typeof cmd === 'string' && cmd.includes('huaweicloud-safety.py'));
|
|
2539
|
+
});
|
|
2540
|
+
if (allowlist.approvals.length < before) {
|
|
2541
|
+
writeFileSync(allowlistPath, JSON.stringify(allowlist, null, 2));
|
|
2542
|
+
console.log(` Removed ${before - allowlist.approvals.length} hook approvals from allowlist`);
|
|
2543
|
+
}
|
|
2544
|
+
} catch {
|
|
2545
|
+
removeIfExists(allowlistPath);
|
|
2546
|
+
console.log(' Removed hook allowlist file');
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
// 3. Remove MCP config from config.yaml
|
|
2551
|
+
removeHermesMcpConfigBlock();
|
|
2552
|
+
console.log(' MCP config removed');
|
|
2553
|
+
|
|
2554
|
+
// 4. Remove skills (file deletion comes after config cleanup)
|
|
2189
2555
|
if (existsSync(skillsDir)) {
|
|
2190
2556
|
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
2191
2557
|
if (entry.name.startsWith('huawei')) {
|
|
@@ -2202,11 +2568,10 @@ function uninstallHermes() {
|
|
|
2202
2568
|
} catch {}
|
|
2203
2569
|
}
|
|
2204
2570
|
|
|
2571
|
+
// 5. Remove plugin directory (hook script files deleted last)
|
|
2205
2572
|
if (removeIfExists(hermesPluginsDir())) {
|
|
2206
2573
|
console.log(' Removed MCP server, safety policy and hooks');
|
|
2207
2574
|
}
|
|
2208
|
-
removeHermesMcpConfigBlock();
|
|
2209
|
-
removeHermesHooksConfigBlock();
|
|
2210
2575
|
}
|
|
2211
2576
|
|
|
2212
2577
|
function hermesStatus() {
|
|
@@ -2221,6 +2586,12 @@ function hermesStatus() {
|
|
|
2221
2586
|
console.log(
|
|
2222
2587
|
` Safety Hooks: ${existsSync(join(pluginDir, 'hooks', 'huaweicloud-safety.py')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
2223
2588
|
);
|
|
2589
|
+
console.log(
|
|
2590
|
+
` Hook allowlist: ${hermesHookAllowlisted() ? '\x1b[32mApproved\x1b[0m' : '\x1b[31mNot allowlisted\x1b[0m'}`,
|
|
2591
|
+
);
|
|
2592
|
+
console.log(
|
|
2593
|
+
` Hook plugin: ${hermesHookPluginInstalled() ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
2594
|
+
);
|
|
2224
2595
|
console.log(` MCP Python SDK: ${hermesMcpSdkOk() ? '\x1b[32mReady\x1b[0m' : '\x1b[31mMissing\x1b[0m'}`);
|
|
2225
2596
|
let skillCount = 0;
|
|
2226
2597
|
if (existsSync(skillsDir)) {
|
|
@@ -2285,6 +2656,7 @@ function autoDetectTarget() {
|
|
|
2285
2656
|
['opencode', () => existsSync(join(homedir(), '.config', 'opencode'))],
|
|
2286
2657
|
['codex-desktop', () => existsSync(join(homedir(), '.codex'))],
|
|
2287
2658
|
['codearts', () => existsSync(join(homedir(), '.codeartsdoer'))],
|
|
2659
|
+
['codearts-work', () => existsSync(join(homedir(), '.codeartswork'))],
|
|
2288
2660
|
['workbuddy', () => existsSync(join(homedir(), '.workbuddy'))],
|
|
2289
2661
|
[
|
|
2290
2662
|
'dsh',
|
|
@@ -2337,6 +2709,10 @@ async function cmdInstall() {
|
|
|
2337
2709
|
console.log('\n[CodeArts]');
|
|
2338
2710
|
await installCodeArts();
|
|
2339
2711
|
}
|
|
2712
|
+
if (target === 'codearts-work' || target === 'all') {
|
|
2713
|
+
console.log('\n[CodeArts Work]');
|
|
2714
|
+
await installCodeArtsWork();
|
|
2715
|
+
}
|
|
2340
2716
|
if (target === 'workbuddy' || target === 'all') {
|
|
2341
2717
|
console.log('\n[WorkBuddy]');
|
|
2342
2718
|
await installWorkBuddy();
|
|
@@ -2388,23 +2764,25 @@ async function cmdInstall() {
|
|
|
2388
2764
|
const appName =
|
|
2389
2765
|
target === 'codearts'
|
|
2390
2766
|
? 'CodeArts'
|
|
2391
|
-
: target === '
|
|
2392
|
-
? '
|
|
2393
|
-
: target === 'codex'
|
|
2394
|
-
? 'Codex'
|
|
2395
|
-
: target === '
|
|
2396
|
-
? '
|
|
2397
|
-
: target === '
|
|
2398
|
-
? '
|
|
2399
|
-
: target === '
|
|
2400
|
-
? '
|
|
2401
|
-
: target === '
|
|
2402
|
-
? '
|
|
2403
|
-
: target === '
|
|
2404
|
-
? '
|
|
2405
|
-
: target === '
|
|
2406
|
-
? '
|
|
2407
|
-
:
|
|
2767
|
+
: target === 'codearts-work'
|
|
2768
|
+
? 'CodeArts Work'
|
|
2769
|
+
: target === 'codex-desktop'
|
|
2770
|
+
? 'Codex Desktop'
|
|
2771
|
+
: target === 'codex'
|
|
2772
|
+
? 'Codex'
|
|
2773
|
+
: target === 'workbuddy'
|
|
2774
|
+
? 'WorkBuddy'
|
|
2775
|
+
: target === 'dsh'
|
|
2776
|
+
? 'DSH'
|
|
2777
|
+
: target === 'officeace'
|
|
2778
|
+
? 'OfficeAce'
|
|
2779
|
+
: target === 'hermes'
|
|
2780
|
+
? 'Hermes Agent'
|
|
2781
|
+
: target === 'openclaw'
|
|
2782
|
+
? 'OpenClaw'
|
|
2783
|
+
: target === 'atomcode'
|
|
2784
|
+
? 'AtomCode'
|
|
2785
|
+
: '当前 agent';
|
|
2408
2786
|
const pad = ' '.repeat(24 - appName.length);
|
|
2409
2787
|
if (target === 'officeace') {
|
|
2410
2788
|
console.log(`\n\x1b[1m\x1b[33m╔══════════════════════════════════════════════════════════╗`);
|
|
@@ -2458,17 +2836,19 @@ async function cmdInstall() {
|
|
|
2458
2836
|
? dshPluginsDir()
|
|
2459
2837
|
: target === 'codearts'
|
|
2460
2838
|
? codeartsPluginsDir()
|
|
2461
|
-
: target === '
|
|
2462
|
-
?
|
|
2463
|
-
: target === '
|
|
2464
|
-
?
|
|
2465
|
-
: target === '
|
|
2466
|
-
?
|
|
2467
|
-
: target === '
|
|
2468
|
-
?
|
|
2469
|
-
: target === '
|
|
2470
|
-
?
|
|
2471
|
-
:
|
|
2839
|
+
: target === 'codearts-work'
|
|
2840
|
+
? codeartsWorkPluginsDir()
|
|
2841
|
+
: target === 'workbuddy'
|
|
2842
|
+
? workbuddyPluginsDir()
|
|
2843
|
+
: target === 'officeace'
|
|
2844
|
+
? officeacePluginsDir()
|
|
2845
|
+
: target === 'openclaw'
|
|
2846
|
+
? codexDesktopPluginsDir()
|
|
2847
|
+
: target === 'atomcode'
|
|
2848
|
+
? atomcodePluginsDir()
|
|
2849
|
+
: target === 'codex-desktop'
|
|
2850
|
+
? codexDesktopPluginsDir()
|
|
2851
|
+
: opencodePluginsDir();
|
|
2472
2852
|
mkdirSync(markerDir, { recursive: true });
|
|
2473
2853
|
writeFileSync(join(markerDir, '.installed'), new Date().toISOString());
|
|
2474
2854
|
if (target === 'opencode' || target === 'all') {
|
|
@@ -2477,6 +2857,9 @@ async function cmdInstall() {
|
|
|
2477
2857
|
if (target === 'codearts' || target === 'all') {
|
|
2478
2858
|
console.log('Or describe your Huawei Cloud task in CodeArts');
|
|
2479
2859
|
}
|
|
2860
|
+
if (target === 'codearts-work' || target === 'all') {
|
|
2861
|
+
console.log('Or describe your Huawei Cloud task in CodeArts Work');
|
|
2862
|
+
}
|
|
2480
2863
|
if (target === 'codex' || target === 'all') {
|
|
2481
2864
|
console.log('Or mention @huaweicloud-core in Codex');
|
|
2482
2865
|
}
|
|
@@ -2510,6 +2893,10 @@ async function cmdUninstall() {
|
|
|
2510
2893
|
console.log('\n[CodeArts]');
|
|
2511
2894
|
uninstallCodeArts();
|
|
2512
2895
|
}
|
|
2896
|
+
if (target === 'codearts-work' || target === 'all') {
|
|
2897
|
+
console.log('\n[CodeArts Work]');
|
|
2898
|
+
uninstallCodeArtsWork();
|
|
2899
|
+
}
|
|
2513
2900
|
if (target === 'workbuddy' || target === 'all') {
|
|
2514
2901
|
console.log('\n[WorkBuddy]');
|
|
2515
2902
|
uninstallWorkBuddy();
|
|
@@ -2574,6 +2961,10 @@ async function cmdStatus() {
|
|
|
2574
2961
|
console.log('\n[CodeArts]');
|
|
2575
2962
|
codeartsStatus();
|
|
2576
2963
|
}
|
|
2964
|
+
if (target === 'codearts-work' || target === 'all') {
|
|
2965
|
+
console.log('\n[CodeArts Work]');
|
|
2966
|
+
codeartsWorkStatus();
|
|
2967
|
+
}
|
|
2577
2968
|
if (target === 'workbuddy' || target === 'all') {
|
|
2578
2969
|
console.log('\n[WorkBuddy]');
|
|
2579
2970
|
workbuddyStatus();
|
|
@@ -2652,6 +3043,7 @@ async function cmdDoctor() {
|
|
|
2652
3043
|
const opencodePluginDir = opencodePluginsDir();
|
|
2653
3044
|
const codexPluginDir = codexDesktopPluginsDir();
|
|
2654
3045
|
const codeartsPluginDir = codeartsPluginsDir();
|
|
3046
|
+
const codeartsWorkPluginDir = codeartsWorkPluginsDir();
|
|
2655
3047
|
const workbuddyPluginDir = workbuddyPluginsDir();
|
|
2656
3048
|
const dshPluginDir = dshPluginsDir();
|
|
2657
3049
|
const officeacePluginDir = officeacePluginsDir();
|
|
@@ -2661,6 +3053,7 @@ async function cmdDoctor() {
|
|
|
2661
3053
|
existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs')) ||
|
|
2662
3054
|
existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
2663
3055
|
existsSync(join(codeartsPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
3056
|
+
existsSync(join(codeartsWorkPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
2664
3057
|
existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
2665
3058
|
existsSync(join(dshPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
2666
3059
|
existsSync(join(officeacePluginDir, 'src', 'mcp-server.mjs')) ||
|
|
@@ -2672,17 +3065,19 @@ async function cmdDoctor() {
|
|
|
2672
3065
|
? 'Codex Desktop'
|
|
2673
3066
|
: existsSync(join(codeartsPluginDir, 'src', 'mcp-server.mjs'))
|
|
2674
3067
|
? 'CodeArts'
|
|
2675
|
-
: existsSync(join(
|
|
2676
|
-
? '
|
|
2677
|
-
: existsSync(join(
|
|
2678
|
-
? '
|
|
2679
|
-
: existsSync(join(
|
|
2680
|
-
? '
|
|
2681
|
-
: existsSync(join(
|
|
2682
|
-
? '
|
|
2683
|
-
: existsSync(join(
|
|
2684
|
-
? '
|
|
2685
|
-
: ''
|
|
3068
|
+
: existsSync(join(codeartsWorkPluginDir, 'src', 'mcp-server.mjs'))
|
|
3069
|
+
? 'CodeArts Work'
|
|
3070
|
+
: existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs'))
|
|
3071
|
+
? 'WorkBuddy'
|
|
3072
|
+
: existsSync(join(dshPluginDir, 'src', 'mcp-server.mjs'))
|
|
3073
|
+
? 'DSH'
|
|
3074
|
+
: existsSync(join(officeacePluginDir, 'src', 'mcp-server.mjs'))
|
|
3075
|
+
? 'OfficeAce'
|
|
3076
|
+
: existsSync(join(hermesPluginDir, 'src', 'mcp-server.mjs'))
|
|
3077
|
+
? 'Hermes Agent'
|
|
3078
|
+
: existsSync(join(atomcodePluginDir, 'src', 'mcp-server.mjs'))
|
|
3079
|
+
? 'AtomCode'
|
|
3080
|
+
: '';
|
|
2686
3081
|
check('MCP server installed', mcpOk, 'Run: npx huaweicloud-devkit install');
|
|
2687
3082
|
|
|
2688
3083
|
if (mcpOk) {
|
|
@@ -2693,6 +3088,7 @@ async function cmdDoctor() {
|
|
|
2693
3088
|
opencodePluginDir,
|
|
2694
3089
|
codexPluginDir,
|
|
2695
3090
|
codeartsPluginDir,
|
|
3091
|
+
codeartsWorkPluginDir,
|
|
2696
3092
|
workbuddyPluginDir,
|
|
2697
3093
|
dshPluginDir,
|
|
2698
3094
|
officeacePluginDir,
|
|
@@ -2705,6 +3101,7 @@ async function cmdDoctor() {
|
|
|
2705
3101
|
existsSync(join(opencodePluginDir, 'safety', 'policy.json')) ||
|
|
2706
3102
|
existsSync(join(codexPluginDir, 'safety', 'policy.json')) ||
|
|
2707
3103
|
existsSync(join(codeartsPluginDir, 'safety', 'policy.json')) ||
|
|
3104
|
+
existsSync(join(codeartsWorkPluginDir, 'safety', 'policy.json')) ||
|
|
2708
3105
|
existsSync(join(workbuddyPluginDir, 'safety', 'policy.json')) ||
|
|
2709
3106
|
existsSync(join(dshPluginDir, 'safety', 'policy.json')) ||
|
|
2710
3107
|
existsSync(join(officeacePluginDir, 'safety', 'policy.json')) ||
|
|
@@ -2745,6 +3142,16 @@ async function cmdDoctor() {
|
|
|
2745
3142
|
}
|
|
2746
3143
|
} catch {}
|
|
2747
3144
|
}
|
|
3145
|
+
const codeartsWorkCfg = codeartsWorkMcpSettingsFile();
|
|
3146
|
+
if (!mcpConfigured && existsSync(codeartsWorkCfg)) {
|
|
3147
|
+
try {
|
|
3148
|
+
const cfg = JSON.parse(readFileSync(codeartsWorkCfg, 'utf8'));
|
|
3149
|
+
if (cfg.mcpServers && cfg.mcpServers['huaweicloud-devkit']) {
|
|
3150
|
+
mcpConfigured = true;
|
|
3151
|
+
mcpCfgTarget = 'CodeArts Work';
|
|
3152
|
+
}
|
|
3153
|
+
} catch {}
|
|
3154
|
+
}
|
|
2748
3155
|
const workbuddyCfg = workbuddyMcpConfigFile();
|
|
2749
3156
|
if (!mcpConfigured && existsSync(workbuddyCfg)) {
|
|
2750
3157
|
try {
|
|
@@ -2853,6 +3260,7 @@ async function cmdDoctor() {
|
|
|
2853
3260
|
opencodeSkillsDir(),
|
|
2854
3261
|
codexDesktopSkillsDir(),
|
|
2855
3262
|
codeartsSkillsDir(),
|
|
3263
|
+
codeartsWorkSkillsDir(),
|
|
2856
3264
|
workbuddySkillsDir(),
|
|
2857
3265
|
dshSkillsDir(),
|
|
2858
3266
|
officeaceSkillsDir(),
|
|
@@ -2912,6 +3320,7 @@ async function cmdDoctor() {
|
|
|
2912
3320
|
{ path: join(codexDesktopPluginsDir(), '.installed'), name: 'Codex Desktop' },
|
|
2913
3321
|
{ path: join(workbuddyPluginsDir(), '.installed'), name: 'WorkBuddy' },
|
|
2914
3322
|
{ path: join(codeartsPluginsDir(), '.installed'), name: 'CodeArts' },
|
|
3323
|
+
{ path: join(codeartsWorkPluginsDir(), '.installed'), name: 'CodeArts Work' },
|
|
2915
3324
|
{ path: join(dshPluginsDir(), '.installed'), name: 'DSH' },
|
|
2916
3325
|
{ path: join(officeacePluginsDir(), '.installed'), name: 'OfficeAce' },
|
|
2917
3326
|
{ path: join(atomcodePluginsDir(), '.installed'), name: 'AtomCode' },
|
|
@@ -2984,6 +3393,18 @@ async function cmdUpdate() {
|
|
|
2984
3393
|
return;
|
|
2985
3394
|
}
|
|
2986
3395
|
|
|
3396
|
+
if (target === 'codearts-work') {
|
|
3397
|
+
if (!existsSync(join(codeartsWorkPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
3398
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3401
|
+
console.log('[CodeArts Work]');
|
|
3402
|
+
await updateCodeArtsWork();
|
|
3403
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
3404
|
+
console.log(`\x1b[33mMCP 工具在重启 CodeArts Work 会话后才生效。\x1b[0m`);
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
|
|
2987
3408
|
if (target === 'workbuddy') {
|
|
2988
3409
|
if (!existsSync(join(workbuddyPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2989
3410
|
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
@@ -3073,6 +3494,11 @@ async function cmdUpdate() {
|
|
|
3073
3494
|
await updateCodeArts();
|
|
3074
3495
|
updatedAny = true;
|
|
3075
3496
|
}
|
|
3497
|
+
if (existsSync(join(codeartsWorkPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
3498
|
+
console.log('\n[CodeArts Work]');
|
|
3499
|
+
await updateCodeArtsWork();
|
|
3500
|
+
updatedAny = true;
|
|
3501
|
+
}
|
|
3076
3502
|
if (existsSync(join(workbuddyPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
3077
3503
|
console.log('\n[WorkBuddy]');
|
|
3078
3504
|
await updateWorkBuddy();
|
|
@@ -3598,7 +4024,7 @@ async function main() {
|
|
|
3598
4024
|
default:
|
|
3599
4025
|
console.log(BANNER);
|
|
3600
4026
|
console.log(
|
|
3601
|
-
'Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|workbuddy|dsh|officeace|hermes|openclaw|atomcode|all>]\n',
|
|
4027
|
+
'Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|codearts-work|workbuddy|dsh|officeace|hermes|openclaw|atomcode|all>]\n',
|
|
3602
4028
|
);
|
|
3603
4029
|
console.log('Commands:');
|
|
3604
4030
|
console.log(' install Install skills, MCP server, safety policy');
|
|
@@ -3613,12 +4039,13 @@ async function main() {
|
|
|
3613
4039
|
console.log(' help Show this help');
|
|
3614
4040
|
console.log('\nOptions:');
|
|
3615
4041
|
console.log(
|
|
3616
|
-
' --target Target agent: opencode (default), codex, codearts, workbuddy, dsh, officeace, hermes, openclaw, atomcode, all',
|
|
4042
|
+
' --target Target agent: opencode (default), codex, codearts, codearts-work, workbuddy, dsh, officeace, hermes, openclaw, atomcode, all',
|
|
3617
4043
|
);
|
|
3618
4044
|
console.log('\nExamples:');
|
|
3619
4045
|
console.log(' npx huaweicloud-devkit install');
|
|
3620
4046
|
console.log(' npx huaweicloud-devkit install --target codex');
|
|
3621
4047
|
console.log(' npx huaweicloud-devkit install --target codearts');
|
|
4048
|
+
console.log(' npx huaweicloud-devkit install --target codearts-work');
|
|
3622
4049
|
console.log(' npx huaweicloud-devkit install --target workbuddy');
|
|
3623
4050
|
console.log(' npx huaweicloud-devkit install --target dsh');
|
|
3624
4051
|
console.log(' npx huaweicloud-devkit install --target officeace');
|