huaweicloud-devkit 1.0.2-next.9 → 1.0.2
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/.agents/plugins/marketplace.json +1 -1
- package/README.md +85 -51
- package/README.zh-CN.md +85 -49
- package/package.json +11 -5
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
- package/plugins/huaweicloud-core/.mcp.json +1 -2
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -18
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +32 -9
- package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +67 -20
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +11 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +16 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +27 -24
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +12 -0
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +103 -2
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +65 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +31 -4
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +6 -1
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +7 -2
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +14 -4
- package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +432 -10
- package/plugins/huaweicloud-core/src/setup-cli.mjs +823 -22
- package/plugins/huaweicloud-core/src/tools.mjs +239 -27
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +18 -4
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +9 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +267 -0
- package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +9 -5
|
@@ -13,6 +13,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
13
13
|
import { homedir, platform } from 'node:os';
|
|
14
14
|
import { createInterface } from 'node:readline';
|
|
15
15
|
import { spawnSync } from 'node:child_process';
|
|
16
|
+
import { randomUUID } from 'node:crypto';
|
|
17
|
+
import Database from 'better-sqlite3';
|
|
16
18
|
import { getAuthStatus, syncAuth } from './auth/service.mjs';
|
|
17
19
|
import { SUPPORTED_AGENT_TARGETS } from './auth/agent-registration.mjs';
|
|
18
20
|
import {
|
|
@@ -126,6 +128,242 @@ function dshPluginsDir() {
|
|
|
126
128
|
return join(dshRoot(), 'huaweicloud-plugins');
|
|
127
129
|
}
|
|
128
130
|
|
|
131
|
+
function readOfficeaceRegistryInstallDir() {
|
|
132
|
+
if (platform() !== 'win32') return null;
|
|
133
|
+
try {
|
|
134
|
+
const r = spawnSync('reg', ['query', 'HKCU\\SOFTWARE\\OfficeAce\\OfficeAce', '/v', 'InstallDir'], {
|
|
135
|
+
encoding: 'utf8',
|
|
136
|
+
windowsHide: true,
|
|
137
|
+
timeout: 5000,
|
|
138
|
+
});
|
|
139
|
+
if (r.status === 0) {
|
|
140
|
+
const m = r.stdout.match(/InstallDir\s+REG_SZ\s+(.+)/);
|
|
141
|
+
if (m) return m[1].trim();
|
|
142
|
+
}
|
|
143
|
+
} catch {}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function officeaceCapabilitiesDir() {
|
|
148
|
+
const configRoot = process.env.OFFICE_CLAW_CONFIG_ROOT;
|
|
149
|
+
if (configRoot && existsSync(join(configRoot, 'capabilities.json'))) return configRoot;
|
|
150
|
+
const regDir = readOfficeaceRegistryInstallDir();
|
|
151
|
+
if (regDir) {
|
|
152
|
+
const dir = join(regDir, '.office-claw');
|
|
153
|
+
if (existsSync(join(dir, 'capabilities.json'))) return dir;
|
|
154
|
+
}
|
|
155
|
+
if (platform() === 'win32') {
|
|
156
|
+
const bases = [process.env.ProgramFiles, 'C:\\Program Files', 'D:\\Program Files'];
|
|
157
|
+
if (process.env.LOCALAPPDATA) bases.push(join(process.env.LOCALAPPDATA, 'Programs'));
|
|
158
|
+
for (const base of bases) {
|
|
159
|
+
if (!base) continue;
|
|
160
|
+
const dir = join(base, 'OfficeAce', '.office-claw');
|
|
161
|
+
if (existsSync(join(dir, 'capabilities.json'))) return dir;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function officeaceCapabilitiesDirSafe() {
|
|
168
|
+
return officeaceCapabilitiesDir() || join(homedir(), '.office-claw');
|
|
169
|
+
}
|
|
170
|
+
function officeaceCapabilitiesFile() {
|
|
171
|
+
return join(officeaceCapabilitiesDirSafe(), 'capabilities.json');
|
|
172
|
+
}
|
|
173
|
+
function officeaceSkillsDir() {
|
|
174
|
+
return join(officeaceCapabilitiesDirSafe(), 'skills');
|
|
175
|
+
}
|
|
176
|
+
function officeacePluginsDir() {
|
|
177
|
+
return join(officeaceCapabilitiesDirSafe(), 'huaweicloud-plugins');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function officeaceSqlitePath() {
|
|
181
|
+
const capDir = officeaceCapabilitiesDirSafe();
|
|
182
|
+
return join(resolve(capDir, '..'), 'data', 'mcp-connectors.sqlite');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function openOfficeaceDb() {
|
|
186
|
+
return new Database(officeaceSqlitePath());
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function officeaceGetOwnerUserId() {
|
|
190
|
+
if (!existsSync(officeaceSqlitePath())) return null;
|
|
191
|
+
try {
|
|
192
|
+
const db = openOfficeaceDb();
|
|
193
|
+
const row = db.prepare('SELECT owner_user_id FROM mcp_connectors WHERE owner_user_id IS NOT NULL LIMIT 1').get();
|
|
194
|
+
db.close();
|
|
195
|
+
return row?.owner_user_id || null;
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function ensureOfficeaceMcpInSqlite() {
|
|
202
|
+
const dbPath = officeaceSqlitePath();
|
|
203
|
+
if (!existsSync(dbPath)) {
|
|
204
|
+
console.log(` \x1b[31mOfficeAce database not found: ${dbPath}\x1b[0m`);
|
|
205
|
+
console.log(` \x1b[33mPlease ensure OfficeAce is installed and has been launched at least once.\x1b[0m`);
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const mcpPath = join(officeacePluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
210
|
+
const env = [{ key: 'HUAWEICLOUD_AGENT_TOOLKIT_MODE', value: 'local', sensitive: false }];
|
|
211
|
+
const hcloudBin = findHcloudBin();
|
|
212
|
+
if (hcloudBin) env.push({ key: 'HCLOUD_BIN', value: hcloudBin.replace(/\\/g, '/'), sensitive: false });
|
|
213
|
+
|
|
214
|
+
const now = Date.now();
|
|
215
|
+
let db;
|
|
216
|
+
try {
|
|
217
|
+
db = openOfficeaceDb();
|
|
218
|
+
|
|
219
|
+
const existing = db
|
|
220
|
+
.prepare("SELECT id, command, args_json FROM mcp_connectors WHERE name = 'huaweicloud-devkit'")
|
|
221
|
+
.get();
|
|
222
|
+
|
|
223
|
+
const argsJson = JSON.stringify([mcpPath]);
|
|
224
|
+
const envJson = JSON.stringify(env);
|
|
225
|
+
|
|
226
|
+
if (existing) {
|
|
227
|
+
if (existing.command === 'node' && existing.args_json === argsJson) {
|
|
228
|
+
console.log(` MCP config unchanged: ${dbPath}`);
|
|
229
|
+
db.close();
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
db.prepare(
|
|
233
|
+
'UPDATE mcp_connectors SET command = ?, args_json = ?, env_json = ?, updated_at = ?, status = ?, enabled = 1 WHERE id = ?',
|
|
234
|
+
).run('node', argsJson, envJson, now, 'disconnected', existing.id);
|
|
235
|
+
console.log(` MCP config updated: ${dbPath}`);
|
|
236
|
+
} else {
|
|
237
|
+
const ownerUserId = officeaceGetOwnerUserId();
|
|
238
|
+
if (!ownerUserId) {
|
|
239
|
+
console.log(` \x1b[31mCannot determine owner_user_id from database\x1b[0m`);
|
|
240
|
+
db.close();
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
db.prepare(
|
|
244
|
+
`INSERT INTO mcp_connectors (id, owner_user_id, type, name, normalized_name, transport, timeout_ms, command, args_json, env_json, enabled, status, created_at, updated_at, version, seeded)
|
|
245
|
+
VALUES (?, ?, 'custom', 'huaweicloud-devkit', 'huaweicloud-devkit', 'stdio', 60000, 'node', ?, ?, 1, 'disconnected', ?, ?, 1, 0)`,
|
|
246
|
+
).run(randomUUID(), ownerUserId, argsJson, envJson, now, now);
|
|
247
|
+
console.log(` MCP config created: ${dbPath}`);
|
|
248
|
+
}
|
|
249
|
+
db.close();
|
|
250
|
+
return true;
|
|
251
|
+
} catch (err) {
|
|
252
|
+
if (db) {
|
|
253
|
+
try {
|
|
254
|
+
db.close();
|
|
255
|
+
} catch {}
|
|
256
|
+
}
|
|
257
|
+
console.log(` \x1b[31mFailed to write MCP config: ${err.message}\x1b[0m`);
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function removeOfficeaceMcpFromSqlite() {
|
|
263
|
+
const dbPath = officeaceSqlitePath();
|
|
264
|
+
if (!existsSync(dbPath)) return;
|
|
265
|
+
let db;
|
|
266
|
+
try {
|
|
267
|
+
db = openOfficeaceDb();
|
|
268
|
+
db.prepare(
|
|
269
|
+
"DELETE FROM mcp_connector_tools WHERE connector_id IN (SELECT id FROM mcp_connectors WHERE name = 'huaweicloud-devkit')",
|
|
270
|
+
).run();
|
|
271
|
+
const result2 = db.prepare("DELETE FROM mcp_connectors WHERE name = 'huaweicloud-devkit'").run();
|
|
272
|
+
if (result2.changes > 0) {
|
|
273
|
+
console.log(` MCP config removed: ${dbPath}`);
|
|
274
|
+
}
|
|
275
|
+
db.close();
|
|
276
|
+
} catch (err) {
|
|
277
|
+
if (db) {
|
|
278
|
+
try {
|
|
279
|
+
db.close();
|
|
280
|
+
} catch {}
|
|
281
|
+
}
|
|
282
|
+
console.log(` \x1b[31mFailed to remove MCP config: ${err.message}\x1b[0m`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function readCapabilitiesJson() {
|
|
287
|
+
const capFile = officeaceCapabilitiesFile();
|
|
288
|
+
if (!existsSync(capFile)) return { capabilities: [] };
|
|
289
|
+
try {
|
|
290
|
+
return JSON.parse(readFileSync(capFile, 'utf8'));
|
|
291
|
+
} catch {
|
|
292
|
+
return { capabilities: [] };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function writeCapabilitiesJson(config) {
|
|
297
|
+
mkdirSync(officeaceCapabilitiesDirSafe(), { recursive: true });
|
|
298
|
+
writeFileSync(officeaceCapabilitiesFile(), JSON.stringify(config, null, 2));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function removeOfficeaceSkillCapabilities() {
|
|
302
|
+
const capFile = officeaceCapabilitiesFile();
|
|
303
|
+
if (!existsSync(capFile)) return;
|
|
304
|
+
let config;
|
|
305
|
+
try {
|
|
306
|
+
config = JSON.parse(readFileSync(capFile, 'utf8'));
|
|
307
|
+
} catch {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (!Array.isArray(config.capabilities)) return;
|
|
311
|
+
const origLen = config.capabilities.length;
|
|
312
|
+
config.capabilities = config.capabilities.filter(
|
|
313
|
+
(c) =>
|
|
314
|
+
!(
|
|
315
|
+
(c.id === 'huaweicloud-core' && c.type === 'skill') ||
|
|
316
|
+
(c.source === 'custom' && c.id && c.id.startsWith('huawei'))
|
|
317
|
+
),
|
|
318
|
+
);
|
|
319
|
+
if (config.capabilities.length !== origLen) {
|
|
320
|
+
writeCapabilitiesJson(config);
|
|
321
|
+
console.log(` Skill capabilities cleaned: ${capFile}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function registerOfficeaceSkillEntries() {
|
|
326
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
327
|
+
if (!existsSync(skillsSrc)) return;
|
|
328
|
+
const skillNames = readdirSync(skillsSrc, { withFileTypes: true })
|
|
329
|
+
.filter((d) => d.isDirectory() && d.name.startsWith('huawei'))
|
|
330
|
+
.map((d) => d.name);
|
|
331
|
+
|
|
332
|
+
const config = readCapabilitiesJson();
|
|
333
|
+
let changed = false;
|
|
334
|
+
|
|
335
|
+
for (const name of skillNames) {
|
|
336
|
+
const existingIdx = config.capabilities.findIndex((c) => c.id === name && c.type === 'skill');
|
|
337
|
+
if (existingIdx >= 0) continue;
|
|
338
|
+
config.capabilities.push({
|
|
339
|
+
id: name,
|
|
340
|
+
type: 'skill',
|
|
341
|
+
enabled: true,
|
|
342
|
+
source: 'custom',
|
|
343
|
+
selfEvolution: 'suggest',
|
|
344
|
+
followGlobalSelfEvolution: true,
|
|
345
|
+
});
|
|
346
|
+
changed = true;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (!config.capabilities.some((c) => c.id === 'huaweicloud-core' && c.type === 'skill')) {
|
|
350
|
+
config.capabilities.push({
|
|
351
|
+
id: 'huaweicloud-core',
|
|
352
|
+
type: 'skill',
|
|
353
|
+
enabled: true,
|
|
354
|
+
source: 'custom',
|
|
355
|
+
selfEvolution: 'suggest',
|
|
356
|
+
followGlobalSelfEvolution: true,
|
|
357
|
+
});
|
|
358
|
+
changed = true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (changed) {
|
|
362
|
+
writeCapabilitiesJson(config);
|
|
363
|
+
console.log(` Skill entries registered: ${officeaceCapabilitiesFile()}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
129
367
|
const DSH_MCP_PATCH_START = '# HuaweiCloud DevKit DSH integration start';
|
|
130
368
|
const DSH_MCP_PATCH_END = '# HuaweiCloud DevKit DSH integration end';
|
|
131
369
|
|
|
@@ -164,14 +402,14 @@ function printSandboxWarning(reason) {
|
|
|
164
402
|
console.log(`\x1b[31m 请任选其一继续:\x1b[0m`);
|
|
165
403
|
console.log(`\x1b[31m A. 在码道外的终端安装并使用 KooCLI (推荐):`);
|
|
166
404
|
console.log(`\x1b[31m https://support.huaweicloud.com/qs-hcli/hcli_02_003.html`);
|
|
167
|
-
console.log(`\x1b[31m B. 在码道设置中关闭沙箱模式后重试 (设置 →
|
|
405
|
+
console.log(`\x1b[31m B. 在码道设置中关闭沙箱模式后重试 (设置 → 对话流 → 智能体 终端命令运行模式 → 自动运行)`);
|
|
168
406
|
console.log(`\x1b[31m 关闭沙箱后重新运行: npx huaweicloud-devkit install-hcloud\x1b[0m`);
|
|
169
407
|
}
|
|
170
408
|
|
|
171
409
|
function checkNode() {
|
|
172
410
|
const v = process.versions.node.split('.').map(Number);
|
|
173
|
-
if (v[0] <
|
|
174
|
-
console.error(`\x1b[31mNode.js >=
|
|
411
|
+
if (v[0] < 22) {
|
|
412
|
+
console.error(`\x1b[31mNode.js >= 22 required (current: ${process.version})\x1b[0m`);
|
|
175
413
|
process.exit(1);
|
|
176
414
|
}
|
|
177
415
|
console.log(` Node.js ${process.version} \x1b[32mOK\x1b[0m`);
|
|
@@ -191,6 +429,35 @@ function copyDir(src, dest) {
|
|
|
191
429
|
}
|
|
192
430
|
}
|
|
193
431
|
|
|
432
|
+
function installRuntimeDeps(pluginsDir) {
|
|
433
|
+
const pkgJson = {
|
|
434
|
+
name: 'huaweicloud-devkit',
|
|
435
|
+
version: pkgVersion,
|
|
436
|
+
type: 'module',
|
|
437
|
+
dependencies: { undici: '^8.10.0' },
|
|
438
|
+
};
|
|
439
|
+
mkdirSync(pluginsDir, { recursive: true });
|
|
440
|
+
writeFileSync(join(pluginsDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
441
|
+
const r = spawnSync('npm', ['install', '--omit=dev'], {
|
|
442
|
+
cwd: pluginsDir,
|
|
443
|
+
windowsHide: true,
|
|
444
|
+
stdio: 'pipe',
|
|
445
|
+
timeout: 120000,
|
|
446
|
+
});
|
|
447
|
+
const undiciDir = join(pluginsDir, 'node_modules', 'undici');
|
|
448
|
+
if (r.status === 0 && existsSync(undiciDir)) {
|
|
449
|
+
console.log(` Runtime deps installed -> ${join(pluginsDir, 'node_modules')}`);
|
|
450
|
+
} else {
|
|
451
|
+
const err = (r.stderr || '').toString().trim().split(/\r?\n/).slice(-2).join(' ');
|
|
452
|
+
console.log(` \x1b[33m[WARN]\x1b[0m npm install failed in ${pluginsDir}${err ? `: ${err}` : ''}`);
|
|
453
|
+
console.log(' Manual fix: cd %s && npm install', pluginsDir);
|
|
454
|
+
if (!existsSync(undiciDir)) {
|
|
455
|
+
console.log(` \x1b[31m[ERROR]\x1b[0m undici is NOT installed. MCP server will fail to start.`);
|
|
456
|
+
console.log(` Run manually: cd "${pluginsDir}" && npm install undici@^8.10.0`);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
194
461
|
function removeIfExists(p) {
|
|
195
462
|
if (existsSync(p)) {
|
|
196
463
|
try {
|
|
@@ -223,7 +490,8 @@ function updateOpenCodeConfig(pluginDir) {
|
|
|
223
490
|
existing.type === 'local' &&
|
|
224
491
|
Array.isArray(existing.command) &&
|
|
225
492
|
existing.command[0] === 'node' &&
|
|
226
|
-
existing.command[1] === mcpPath
|
|
493
|
+
existing.command[1] === mcpPath &&
|
|
494
|
+
existing.timeout === 300000
|
|
227
495
|
) {
|
|
228
496
|
console.log(` OpenCode MCP config unchanged: ${configPath}`);
|
|
229
497
|
return;
|
|
@@ -234,6 +502,7 @@ function updateOpenCodeConfig(pluginDir) {
|
|
|
234
502
|
type: 'local',
|
|
235
503
|
command: ['node', mcpPath],
|
|
236
504
|
enabled: true,
|
|
505
|
+
timeout: 300000,
|
|
237
506
|
};
|
|
238
507
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
239
508
|
console.log(` OpenCode MCP config updated: ${configPath}`);
|
|
@@ -373,6 +642,7 @@ async function installOpenCode() {
|
|
|
373
642
|
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
374
643
|
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
375
644
|
updateOpenCodeConfig(pluginDest);
|
|
645
|
+
installRuntimeDeps(pluginDest);
|
|
376
646
|
}
|
|
377
647
|
|
|
378
648
|
function uninstallOpenCode() {
|
|
@@ -445,6 +715,7 @@ async function updateOpenCode() {
|
|
|
445
715
|
updateOpenCodeConfig(pluginDest);
|
|
446
716
|
mkdirSync(pluginDest, { recursive: true });
|
|
447
717
|
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
718
|
+
installRuntimeDeps(pluginDest);
|
|
448
719
|
}
|
|
449
720
|
|
|
450
721
|
function codexMcpServerPath() {
|
|
@@ -549,6 +820,7 @@ async function installCodexDesktop() {
|
|
|
549
820
|
}
|
|
550
821
|
|
|
551
822
|
ensureCodexConfigSection(mcpServerAbsPath);
|
|
823
|
+
installRuntimeDeps(codexDesktopPluginsDir());
|
|
552
824
|
}
|
|
553
825
|
|
|
554
826
|
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
@@ -596,6 +868,7 @@ async function updateCodexDesktop() {
|
|
|
596
868
|
|
|
597
869
|
ensureCodexConfigSection(mcpServerAbsPath);
|
|
598
870
|
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
871
|
+
installRuntimeDeps(pluginDest);
|
|
599
872
|
}
|
|
600
873
|
|
|
601
874
|
function uninstallCodexDesktop() {
|
|
@@ -645,7 +918,13 @@ function registerCodeartsMcp(configPath) {
|
|
|
645
918
|
return;
|
|
646
919
|
}
|
|
647
920
|
const existing = config.mcpServers?.['huaweicloud-devkit'];
|
|
648
|
-
if (
|
|
921
|
+
if (
|
|
922
|
+
existing &&
|
|
923
|
+
existing.command === 'node' &&
|
|
924
|
+
Array.isArray(existing.args) &&
|
|
925
|
+
existing.args[0] === mcpPath &&
|
|
926
|
+
existing.timeout === 300000
|
|
927
|
+
) {
|
|
649
928
|
console.log(` MCP config unchanged: ${configPath}`);
|
|
650
929
|
return;
|
|
651
930
|
}
|
|
@@ -656,6 +935,7 @@ function registerCodeartsMcp(configPath) {
|
|
|
656
935
|
args: [mcpPath],
|
|
657
936
|
env,
|
|
658
937
|
enabled: true,
|
|
938
|
+
timeout: 300000,
|
|
659
939
|
};
|
|
660
940
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
661
941
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
@@ -680,6 +960,7 @@ async function installCodeArts() {
|
|
|
680
960
|
|
|
681
961
|
registerCodeartsMcp(codeartsMcpSettingsFile());
|
|
682
962
|
registerCodeartsMcp(codeartsProjectMcpSettingsFile());
|
|
963
|
+
installRuntimeDeps(pluginDest);
|
|
683
964
|
}
|
|
684
965
|
|
|
685
966
|
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
@@ -702,6 +983,7 @@ async function updateCodeArts() {
|
|
|
702
983
|
registerCodeartsMcp(codeartsProjectMcpSettingsFile());
|
|
703
984
|
mkdirSync(pluginDest, { recursive: true });
|
|
704
985
|
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
986
|
+
installRuntimeDeps(pluginDest);
|
|
705
987
|
}
|
|
706
988
|
|
|
707
989
|
function uninstallCodeArts() {
|
|
@@ -782,7 +1064,13 @@ function ensureWorkbuddyMcpConfig() {
|
|
|
782
1064
|
return false;
|
|
783
1065
|
}
|
|
784
1066
|
const existing = config.mcpServers?.['huaweicloud-devkit'];
|
|
785
|
-
if (
|
|
1067
|
+
if (
|
|
1068
|
+
existing &&
|
|
1069
|
+
existing.command === 'node' &&
|
|
1070
|
+
Array.isArray(existing.args) &&
|
|
1071
|
+
existing.args[0] === mcpPath &&
|
|
1072
|
+
existing.timeout === 300000
|
|
1073
|
+
) {
|
|
786
1074
|
console.log(` MCP config unchanged: ${configPath}`);
|
|
787
1075
|
return false;
|
|
788
1076
|
}
|
|
@@ -792,6 +1080,7 @@ function ensureWorkbuddyMcpConfig() {
|
|
|
792
1080
|
command: 'node',
|
|
793
1081
|
args: [mcpPath],
|
|
794
1082
|
env,
|
|
1083
|
+
timeout: 300000,
|
|
795
1084
|
};
|
|
796
1085
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
797
1086
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
@@ -814,6 +1103,7 @@ async function installWorkBuddy() {
|
|
|
814
1103
|
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
815
1104
|
|
|
816
1105
|
ensureWorkbuddyMcpConfig();
|
|
1106
|
+
installRuntimeDeps(pluginDest);
|
|
817
1107
|
}
|
|
818
1108
|
|
|
819
1109
|
// Incremental update: overwrite copied files, prune stale ones, and only touch the config when necessary.
|
|
@@ -833,6 +1123,7 @@ async function updateWorkBuddy() {
|
|
|
833
1123
|
ensureWorkbuddyMcpConfig();
|
|
834
1124
|
mkdirSync(pluginDest, { recursive: true });
|
|
835
1125
|
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
1126
|
+
installRuntimeDeps(pluginDest);
|
|
836
1127
|
}
|
|
837
1128
|
|
|
838
1129
|
function uninstallWorkBuddy() {
|
|
@@ -1097,6 +1388,7 @@ async function installDsh() {
|
|
|
1097
1388
|
tryInstallDshMcpClient();
|
|
1098
1389
|
mkdirSync(pluginDest, { recursive: true });
|
|
1099
1390
|
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
1391
|
+
installRuntimeDeps(pluginDest);
|
|
1100
1392
|
}
|
|
1101
1393
|
|
|
1102
1394
|
async function updateDsh() {
|
|
@@ -1116,6 +1408,7 @@ async function updateDsh() {
|
|
|
1116
1408
|
tryInstallDshMcpClient();
|
|
1117
1409
|
mkdirSync(pluginDest, { recursive: true });
|
|
1118
1410
|
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
1411
|
+
installRuntimeDeps(pluginDest);
|
|
1119
1412
|
}
|
|
1120
1413
|
|
|
1121
1414
|
function uninstallDsh() {
|
|
@@ -1166,6 +1459,335 @@ function dshStatus() {
|
|
|
1166
1459
|
);
|
|
1167
1460
|
}
|
|
1168
1461
|
|
|
1462
|
+
async function promptOfficeaceInstallDir() {
|
|
1463
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1464
|
+
const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
1465
|
+
while (true) {
|
|
1466
|
+
const path = (await ask(' Install directory: ')).trim();
|
|
1467
|
+
if (!path) {
|
|
1468
|
+
console.log(' \x1b[33mPath cannot be empty.\x1b[0m');
|
|
1469
|
+
continue;
|
|
1470
|
+
}
|
|
1471
|
+
const capFile = join(path, '.office-claw', 'capabilities.json');
|
|
1472
|
+
if (existsSync(capFile)) {
|
|
1473
|
+
rl.close();
|
|
1474
|
+
return path;
|
|
1475
|
+
}
|
|
1476
|
+
console.log(` \x1b[33mcapabilities.json not found at: ${capFile}\x1b[0m`);
|
|
1477
|
+
console.log(' Please verify the path and try again.');
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
async function installOfficeAce() {
|
|
1482
|
+
if (!officeaceCapabilitiesDir()) {
|
|
1483
|
+
if (process.stdin.isTTY) {
|
|
1484
|
+
console.log(' \x1b[33mOfficeAce install directory not found automatically.\x1b[0m');
|
|
1485
|
+
console.log(' Please enter the OfficeAce install directory.');
|
|
1486
|
+
const entered = await promptOfficeaceInstallDir();
|
|
1487
|
+
process.env.OFFICE_CLAW_CONFIG_ROOT = join(entered, '.office-claw');
|
|
1488
|
+
} else {
|
|
1489
|
+
console.log(
|
|
1490
|
+
' \x1b[31mOfficeAce install directory not found. Please set OFFICE_CLAW_CONFIG_ROOT env var and retry.\x1b[0m',
|
|
1491
|
+
);
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
1496
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
1497
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
1498
|
+
const pluginDest = officeacePluginsDir();
|
|
1499
|
+
|
|
1500
|
+
copyDir(skillsSrc, officeaceSkillsDir());
|
|
1501
|
+
console.log(` Skills -> ${officeaceSkillsDir()}`);
|
|
1502
|
+
|
|
1503
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
1504
|
+
console.log(` MCP Server -> ${join(pluginDest, 'src')}`);
|
|
1505
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
1506
|
+
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
1507
|
+
|
|
1508
|
+
installRuntimeDeps(pluginDest);
|
|
1509
|
+
ensureOfficeaceMcpInSqlite();
|
|
1510
|
+
registerOfficeaceSkillEntries();
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
async function updateOfficeAce() {
|
|
1514
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
1515
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
1516
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
1517
|
+
const pluginDest = officeacePluginsDir();
|
|
1518
|
+
|
|
1519
|
+
copyDir(skillsSrc, officeaceSkillsDir());
|
|
1520
|
+
const stale = pruneStale(officeaceSkillsDir(), skillsSrc);
|
|
1521
|
+
console.log(` Skills updated -> ${officeaceSkillsDir()}${stale > 0 ? ` (removed ${stale} stale)` : ''}`);
|
|
1522
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
1523
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
1524
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
1525
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
1526
|
+
installRuntimeDeps(pluginDest);
|
|
1527
|
+
ensureOfficeaceMcpInSqlite();
|
|
1528
|
+
registerOfficeaceSkillEntries();
|
|
1529
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
1530
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
function uninstallOfficeAce() {
|
|
1534
|
+
const skillsDir = officeaceSkillsDir();
|
|
1535
|
+
let removed = 0;
|
|
1536
|
+
if (existsSync(skillsDir)) {
|
|
1537
|
+
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
1538
|
+
if (entry.name.startsWith('huawei')) {
|
|
1539
|
+
removeIfExists(join(skillsDir, entry.name));
|
|
1540
|
+
removed++;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
if (removed > 0) console.log(` Removed ${removed} skills`);
|
|
1544
|
+
try {
|
|
1545
|
+
if (readdirSync(skillsDir).length === 0) {
|
|
1546
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
1547
|
+
console.log(` Removed empty skills directory: ${skillsDir}`);
|
|
1548
|
+
}
|
|
1549
|
+
} catch {}
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
if (removeIfExists(officeacePluginsDir())) {
|
|
1553
|
+
console.log(' Removed MCP server and safety policy');
|
|
1554
|
+
}
|
|
1555
|
+
removeOfficeaceSkillCapabilities();
|
|
1556
|
+
removeOfficeaceMcpFromSqlite();
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
function officeaceStatus() {
|
|
1560
|
+
const pluginDir = officeacePluginsDir();
|
|
1561
|
+
const skillsDir = officeaceSkillsDir();
|
|
1562
|
+
console.log(
|
|
1563
|
+
` MCP Server: ${existsSync(join(pluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1564
|
+
);
|
|
1565
|
+
console.log(
|
|
1566
|
+
` Safety Policy: ${existsSync(join(pluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1567
|
+
);
|
|
1568
|
+
let skillCount = 0;
|
|
1569
|
+
if (existsSync(skillsDir)) {
|
|
1570
|
+
skillCount = readdirSync(skillsDir, { withFileTypes: true }).filter(
|
|
1571
|
+
(d) => d.isDirectory() && d.name.startsWith('huawei'),
|
|
1572
|
+
).length;
|
|
1573
|
+
}
|
|
1574
|
+
console.log(
|
|
1575
|
+
` Skills: ${skillCount > 0 ? `\x1b[32m${skillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1576
|
+
);
|
|
1577
|
+
const dbPath = officeaceSqlitePath();
|
|
1578
|
+
if (existsSync(dbPath)) {
|
|
1579
|
+
try {
|
|
1580
|
+
const db = openOfficeaceDb();
|
|
1581
|
+
const row = db.prepare("SELECT enabled, status FROM mcp_connectors WHERE name = 'huaweicloud-devkit'").get();
|
|
1582
|
+
db.close();
|
|
1583
|
+
if (row) {
|
|
1584
|
+
const statusIcon = row.status === 'connected' ? '\x1b[32m' : '\x1b[33m';
|
|
1585
|
+
console.log(` MCP config: ${statusIcon}${row.status}\x1b[0m (enabled: ${row.enabled ? 'yes' : 'no'})`);
|
|
1586
|
+
} else {
|
|
1587
|
+
console.log(` MCP config: \x1b[31mNot configured\x1b[0m`);
|
|
1588
|
+
}
|
|
1589
|
+
} catch {
|
|
1590
|
+
console.log(` MCP config: \x1b[31mInvalid\x1b[0m`);
|
|
1591
|
+
}
|
|
1592
|
+
} else {
|
|
1593
|
+
console.log(` MCP config: \x1b[31mDatabase not found\x1b[0m`);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// ── Hermes Agent ──
|
|
1598
|
+
|
|
1599
|
+
function hermesHomeDir() {
|
|
1600
|
+
if (process.env.HERMES_HOME) return process.env.HERMES_HOME;
|
|
1601
|
+
// Hermes on Windows stores under LOCALAPPDATA, not ~/.hermes
|
|
1602
|
+
if (platform() === 'win32' && process.env.LOCALAPPDATA) {
|
|
1603
|
+
return join(process.env.LOCALAPPDATA, 'hermes');
|
|
1604
|
+
}
|
|
1605
|
+
return join(homedir(), '.hermes');
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
function hermesSkillsDir() {
|
|
1609
|
+
return join(hermesHomeDir(), 'skills');
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
function hermesPluginsDir() {
|
|
1613
|
+
return join(hermesHomeDir(), 'huaweicloud-plugins');
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
function hermesConfigFile() {
|
|
1617
|
+
return join(hermesHomeDir(), 'config.yaml');
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
// Returns true when the config file was written, false when it was already correct.
|
|
1621
|
+
function ensureHermesMcpConfig() {
|
|
1622
|
+
const configPath = hermesConfigFile();
|
|
1623
|
+
const mcpPath = join(hermesPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
1624
|
+
const hcloudBin = findHcloudBin();
|
|
1625
|
+
|
|
1626
|
+
const blockLines = [
|
|
1627
|
+
'mcp_servers:',
|
|
1628
|
+
' huaweicloud-devkit:',
|
|
1629
|
+
' command: "node"',
|
|
1630
|
+
` args: ["${mcpPath}"]`,
|
|
1631
|
+
' env:',
|
|
1632
|
+
' HUAWEICLOUD_AGENT_TOOLKIT_MODE: "local"',
|
|
1633
|
+
];
|
|
1634
|
+
if (hcloudBin) {
|
|
1635
|
+
blockLines.push(` HCLOUD_BIN: "${hcloudBin.replace(/\\/g, '/')}"`);
|
|
1636
|
+
}
|
|
1637
|
+
const block = blockLines.join('\n');
|
|
1638
|
+
|
|
1639
|
+
let existing = '';
|
|
1640
|
+
if (existsSync(configPath)) {
|
|
1641
|
+
try {
|
|
1642
|
+
existing = readFileSync(configPath, 'utf8');
|
|
1643
|
+
} catch {}
|
|
1644
|
+
if (existing.includes('mcp_servers:') && existing.includes('huaweicloud-devkit')) {
|
|
1645
|
+
if (existing.includes(`args: ["${mcpPath}"]`)) {
|
|
1646
|
+
console.log(` MCP config unchanged: ${configPath}`);
|
|
1647
|
+
return false;
|
|
1648
|
+
}
|
|
1649
|
+
removeHermesMcpConfigBlock();
|
|
1650
|
+
existing = '';
|
|
1651
|
+
if (existsSync(configPath)) {
|
|
1652
|
+
try {
|
|
1653
|
+
existing = readFileSync(configPath, 'utf8');
|
|
1654
|
+
} catch {}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
1659
|
+
const newContent = existing ? `${existing.trimEnd()}\n\n${block}\n` : `${block}\n`;
|
|
1660
|
+
writeFileSync(configPath, newContent);
|
|
1661
|
+
console.log(` MCP config updated: ${configPath}`);
|
|
1662
|
+
return true;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
function removeHermesMcpConfigBlock() {
|
|
1666
|
+
const configPath = hermesConfigFile();
|
|
1667
|
+
if (!existsSync(configPath)) return;
|
|
1668
|
+
const lines = readFileSync(configPath, 'utf8').split(/\r?\n/);
|
|
1669
|
+
const out = [];
|
|
1670
|
+
let skip = false;
|
|
1671
|
+
for (const line of lines) {
|
|
1672
|
+
if (/^\s*huaweicloud-devkit\s*:/.test(line)) {
|
|
1673
|
+
skip = true;
|
|
1674
|
+
continue;
|
|
1675
|
+
}
|
|
1676
|
+
if (skip) {
|
|
1677
|
+
// Continue skipping indented lines (the server config block)
|
|
1678
|
+
if (/^\s{2,}/.test(line) && line.trim() !== '') continue;
|
|
1679
|
+
// Stop skipping at a top-level key or empty line
|
|
1680
|
+
skip = false;
|
|
1681
|
+
}
|
|
1682
|
+
// If we just skipped the only server under mcp_servers, remove the key too
|
|
1683
|
+
if (line.trim() === 'mcp_servers:') {
|
|
1684
|
+
// Peek ahead to check if this mcp_servers block only has huaweicloud-devkit
|
|
1685
|
+
const remaining = out.join('\n');
|
|
1686
|
+
// Remove trailing mcp_servers: if it's the last meaningful line
|
|
1687
|
+
continue; // skip the mcp_servers key for now, re-add if other servers exist
|
|
1688
|
+
}
|
|
1689
|
+
out.push(line);
|
|
1690
|
+
}
|
|
1691
|
+
// Reconstruct: add mcp_servers back if there are other servers
|
|
1692
|
+
const cleaned = out.join('\n').trimEnd();
|
|
1693
|
+
writeFileSync(configPath, cleaned ? `${cleaned}\n` : '');
|
|
1694
|
+
console.log(' MCP config cleaned');
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
async function installHermes() {
|
|
1698
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
1699
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
1700
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
1701
|
+
const pluginDest = hermesPluginsDir();
|
|
1702
|
+
|
|
1703
|
+
copyDir(skillsSrc, hermesSkillsDir());
|
|
1704
|
+
console.log(` Skills -> ${hermesSkillsDir()}`);
|
|
1705
|
+
|
|
1706
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
1707
|
+
console.log(` MCP Server -> ${join(pluginDest, 'src')}`);
|
|
1708
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
1709
|
+
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
1710
|
+
|
|
1711
|
+
ensureHermesMcpConfig();
|
|
1712
|
+
installRuntimeDeps(pluginDest);
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
async function updateHermes() {
|
|
1716
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
1717
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
1718
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
1719
|
+
const pluginDest = hermesPluginsDir();
|
|
1720
|
+
|
|
1721
|
+
copyDir(skillsSrc, hermesSkillsDir());
|
|
1722
|
+
const stale = pruneStale(hermesSkillsDir(), skillsSrc);
|
|
1723
|
+
console.log(` Skills updated -> ${hermesSkillsDir()}${stale > 0 ? ` (removed ${stale} stale)` : ''}`);
|
|
1724
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
1725
|
+
console.log(` MCP Server updated -> ${join(pluginDest, 'src')}`);
|
|
1726
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
1727
|
+
console.log(` Safety Policy updated -> ${join(pluginDest, 'safety')}`);
|
|
1728
|
+
ensureHermesMcpConfig();
|
|
1729
|
+
mkdirSync(pluginDest, { recursive: true });
|
|
1730
|
+
writeFileSync(join(pluginDest, '.installed'), new Date().toISOString());
|
|
1731
|
+
installRuntimeDeps(pluginDest);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
function uninstallHermes() {
|
|
1735
|
+
const skillsDir = hermesSkillsDir();
|
|
1736
|
+
let removed = 0;
|
|
1737
|
+
if (existsSync(skillsDir)) {
|
|
1738
|
+
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
1739
|
+
if (entry.name.startsWith('huawei')) {
|
|
1740
|
+
removeIfExists(join(skillsDir, entry.name));
|
|
1741
|
+
removed++;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
if (removed > 0) console.log(` Removed ${removed} skills`);
|
|
1745
|
+
try {
|
|
1746
|
+
if (readdirSync(skillsDir).length === 0) {
|
|
1747
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
1748
|
+
console.log(` Removed empty skills directory: ${skillsDir}`);
|
|
1749
|
+
}
|
|
1750
|
+
} catch {}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
if (removeIfExists(hermesPluginsDir())) {
|
|
1754
|
+
console.log(' Removed MCP server and safety policy');
|
|
1755
|
+
}
|
|
1756
|
+
removeHermesMcpConfigBlock();
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
function hermesStatus() {
|
|
1760
|
+
const pluginDir = hermesPluginsDir();
|
|
1761
|
+
const skillsDir = hermesSkillsDir();
|
|
1762
|
+
console.log(
|
|
1763
|
+
` MCP Server: ${existsSync(join(pluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1764
|
+
);
|
|
1765
|
+
console.log(
|
|
1766
|
+
` Safety Policy: ${existsSync(join(pluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1767
|
+
);
|
|
1768
|
+
let skillCount = 0;
|
|
1769
|
+
if (existsSync(skillsDir)) {
|
|
1770
|
+
skillCount = readdirSync(skillsDir, { withFileTypes: true }).filter(
|
|
1771
|
+
(d) => d.isDirectory() && d.name.startsWith('huawei'),
|
|
1772
|
+
).length;
|
|
1773
|
+
}
|
|
1774
|
+
console.log(
|
|
1775
|
+
` Skills: ${skillCount > 0 ? `\x1b[32m${skillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
|
|
1776
|
+
);
|
|
1777
|
+
const configPath = hermesConfigFile();
|
|
1778
|
+
if (existsSync(configPath)) {
|
|
1779
|
+
try {
|
|
1780
|
+
const config = readFileSync(configPath, 'utf8');
|
|
1781
|
+
const configured = config.includes('mcp_servers:') && config.includes('huaweicloud-devkit');
|
|
1782
|
+
console.log(` MCP config: ${configured ? '\x1b[32mConfigured\x1b[0m' : '\x1b[31mNot configured\x1b[0m'}`);
|
|
1783
|
+
} catch {
|
|
1784
|
+
console.log(` MCP config: \x1b[31mInvalid\x1b[0m`);
|
|
1785
|
+
}
|
|
1786
|
+
} else {
|
|
1787
|
+
console.log(` MCP config: \x1b[31mNot found\x1b[0m`);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1169
1791
|
function opencodeStatus() {
|
|
1170
1792
|
const pluginDir = opencodePluginsDir();
|
|
1171
1793
|
const skillsDir = opencodeSkillsDir();
|
|
@@ -1210,6 +1832,9 @@ function autoDetectTarget() {
|
|
|
1210
1832
|
return existsSync(dsh);
|
|
1211
1833
|
},
|
|
1212
1834
|
],
|
|
1835
|
+
['officeace', () => existsSync(officeaceCapabilitiesDir())],
|
|
1836
|
+
['hermes', () => existsSync(hermesHomeDir())],
|
|
1837
|
+
['openclaw', () => existsSync(join(homedir(), '.openclaw'))],
|
|
1213
1838
|
];
|
|
1214
1839
|
const detected = checks.filter(([, check]) => check()).map(([name]) => name);
|
|
1215
1840
|
if (detected.length === 0) {
|
|
@@ -1258,6 +1883,18 @@ async function cmdInstall() {
|
|
|
1258
1883
|
console.log('\n[DSH]');
|
|
1259
1884
|
await installDsh();
|
|
1260
1885
|
}
|
|
1886
|
+
if (target === 'officeace' || target === 'all') {
|
|
1887
|
+
console.log('\n[OfficeAce]');
|
|
1888
|
+
await installOfficeAce();
|
|
1889
|
+
}
|
|
1890
|
+
if (target === 'hermes' || target === 'all') {
|
|
1891
|
+
console.log('\n[Hermes Agent]');
|
|
1892
|
+
await installHermes();
|
|
1893
|
+
}
|
|
1894
|
+
if (target === 'openclaw' || target === 'all') {
|
|
1895
|
+
console.log('\n[OpenClaw]');
|
|
1896
|
+
await installCodexDesktop();
|
|
1897
|
+
}
|
|
1261
1898
|
if (target === 'codex' || target === 'all') {
|
|
1262
1899
|
console.log('\n[Codex]');
|
|
1263
1900
|
if (!hasCodexCLI()) {
|
|
@@ -1293,13 +1930,26 @@ async function cmdInstall() {
|
|
|
1293
1930
|
? 'WorkBuddy'
|
|
1294
1931
|
: target === 'dsh'
|
|
1295
1932
|
? 'DSH'
|
|
1296
|
-
: '
|
|
1933
|
+
: target === 'officeace'
|
|
1934
|
+
? 'OfficeAce'
|
|
1935
|
+
: target === 'hermes'
|
|
1936
|
+
? 'Hermes Agent'
|
|
1937
|
+
: target === 'openclaw'
|
|
1938
|
+
? 'OpenClaw'
|
|
1939
|
+
: '当前 agent';
|
|
1297
1940
|
const pad = ' '.repeat(24 - appName.length);
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1941
|
+
if (target === 'officeace') {
|
|
1942
|
+
console.log(`\n\x1b[1m\x1b[33m╔══════════════════════════════════════════════════════╗`);
|
|
1943
|
+
console.log(`\x1b[1m\x1b[33m║ 打开连接器 → 我的连接器 → huaweicloud-devkit ║`);
|
|
1944
|
+
console.log(`\x1b[1m\x1b[33m║ → 连接 → 回到对话 → 输入框开启连接器 ║`);
|
|
1945
|
+
console.log(`\x1b[1m\x1b[33m╚══════════════════════════════════════════════════════╝\x1b[0m`);
|
|
1946
|
+
} else {
|
|
1947
|
+
console.log(`\n\x1b[1m\x1b[33m╔══════════════════════════════════════════════════════╗`);
|
|
1948
|
+
console.log(`\x1b[1m\x1b[33m║ MCP 工具在重启 ${appName} 会话后才生效${pad}║`);
|
|
1949
|
+
console.log(`\x1b[1m\x1b[33m║ 关闭当前会话 → 重新打开,直接描述华为云任务即可 ║`);
|
|
1950
|
+
console.log(`\x1b[1m\x1b[33m║ 重启前请勿执行 hcloud 命令,避免 AK/SK 泄露 ║`);
|
|
1951
|
+
console.log(`\x1b[1m\x1b[33m╚══════════════════════════════════════════════════════╝\x1b[0m`);
|
|
1952
|
+
}
|
|
1303
1953
|
|
|
1304
1954
|
const hcloudOk = checkHcloud();
|
|
1305
1955
|
if (!hcloudOk) {
|
|
@@ -1312,7 +1962,11 @@ async function cmdInstall() {
|
|
|
1312
1962
|
console.log(`\n\x1b[1m下一步:\x1b[0m`);
|
|
1313
1963
|
console.log(` 1. 配置统一凭据:npx huaweicloud-devkit auth init`);
|
|
1314
1964
|
console.log(` 2. 配置代理(企业内网):npx huaweicloud-devkit proxy init`);
|
|
1315
|
-
|
|
1965
|
+
if (target === 'officeace') {
|
|
1966
|
+
console.log(` 3. 打开连接器 → 我的连接器 → huaweicloud-devkit → 连接 → 回到对话 → 输入框开启连接器`);
|
|
1967
|
+
} else {
|
|
1968
|
+
console.log(` 3. 重启 ${appName} 会话(MCP 工具重启后生效)`);
|
|
1969
|
+
}
|
|
1316
1970
|
console.log(` 4. 运行自检:npx huaweicloud-devkit doctor`);
|
|
1317
1971
|
|
|
1318
1972
|
// Write install marker for doctor to detect
|
|
@@ -1323,9 +1977,13 @@ async function cmdInstall() {
|
|
|
1323
1977
|
? codeartsPluginsDir()
|
|
1324
1978
|
: target === 'workbuddy'
|
|
1325
1979
|
? workbuddyPluginsDir()
|
|
1326
|
-
: target === '
|
|
1327
|
-
?
|
|
1328
|
-
:
|
|
1980
|
+
: target === 'officeace'
|
|
1981
|
+
? officeacePluginsDir()
|
|
1982
|
+
: target === 'openclaw'
|
|
1983
|
+
? codexDesktopPluginsDir()
|
|
1984
|
+
: target === 'codex-desktop'
|
|
1985
|
+
? codexDesktopPluginsDir()
|
|
1986
|
+
: opencodePluginsDir();
|
|
1329
1987
|
mkdirSync(markerDir, { recursive: true });
|
|
1330
1988
|
writeFileSync(join(markerDir, '.installed'), new Date().toISOString());
|
|
1331
1989
|
if (target === 'opencode' || target === 'all') {
|
|
@@ -1343,6 +2001,12 @@ async function cmdInstall() {
|
|
|
1343
2001
|
if (target === 'dsh' || target === 'all') {
|
|
1344
2002
|
console.log('Or describe your Huawei Cloud task in DSH');
|
|
1345
2003
|
}
|
|
2004
|
+
if (target === 'officeace' || target === 'all') {
|
|
2005
|
+
console.log('Or describe your Huawei Cloud task in OfficeAce');
|
|
2006
|
+
}
|
|
2007
|
+
if (target === 'openclaw' || target === 'all') {
|
|
2008
|
+
console.log('Or describe your Huawei Cloud task in OpenClaw');
|
|
2009
|
+
}
|
|
1346
2010
|
}
|
|
1347
2011
|
|
|
1348
2012
|
async function cmdUninstall() {
|
|
@@ -1366,6 +2030,18 @@ async function cmdUninstall() {
|
|
|
1366
2030
|
console.log('\n[DSH]');
|
|
1367
2031
|
uninstallDsh();
|
|
1368
2032
|
}
|
|
2033
|
+
if (target === 'officeace' || target === 'all') {
|
|
2034
|
+
console.log('\n[OfficeAce]');
|
|
2035
|
+
uninstallOfficeAce();
|
|
2036
|
+
}
|
|
2037
|
+
if (target === 'hermes' || target === 'all') {
|
|
2038
|
+
console.log('\n[Hermes Agent]');
|
|
2039
|
+
uninstallHermes();
|
|
2040
|
+
}
|
|
2041
|
+
if (target === 'openclaw' || target === 'all') {
|
|
2042
|
+
console.log('\n[OpenClaw]');
|
|
2043
|
+
uninstallCodexDesktop();
|
|
2044
|
+
}
|
|
1369
2045
|
if (target === 'codex-desktop' || target === 'codex' || target === 'all') {
|
|
1370
2046
|
console.log('\n[Codex]');
|
|
1371
2047
|
uninstallCodexDesktop();
|
|
@@ -1414,6 +2090,34 @@ async function cmdStatus() {
|
|
|
1414
2090
|
console.log('\n[DSH]');
|
|
1415
2091
|
dshStatus();
|
|
1416
2092
|
}
|
|
2093
|
+
if (target === 'officeace' || target === 'all') {
|
|
2094
|
+
console.log('\n[OfficeAce]');
|
|
2095
|
+
officeaceStatus();
|
|
2096
|
+
}
|
|
2097
|
+
if (target === 'hermes' || target === 'all') {
|
|
2098
|
+
console.log('\n[Hermes Agent]');
|
|
2099
|
+
hermesStatus();
|
|
2100
|
+
}
|
|
2101
|
+
if (target === 'openclaw' || target === 'all') {
|
|
2102
|
+
console.log('\n[OpenClaw]');
|
|
2103
|
+
const cdPluginDir = codexDesktopPluginsDir();
|
|
2104
|
+
const cdSkillsDir = codexDesktopSkillsDir();
|
|
2105
|
+
console.log(
|
|
2106
|
+
` MCP Server: ${existsSync(join(cdPluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
2107
|
+
);
|
|
2108
|
+
console.log(
|
|
2109
|
+
` Safety Policy: ${existsSync(join(cdPluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`,
|
|
2110
|
+
);
|
|
2111
|
+
let cdSkillCount = 0;
|
|
2112
|
+
if (existsSync(cdSkillsDir)) {
|
|
2113
|
+
cdSkillCount = readdirSync(cdSkillsDir, { withFileTypes: true }).filter(
|
|
2114
|
+
(d) => d.isDirectory() && d.name.startsWith('huawei'),
|
|
2115
|
+
).length;
|
|
2116
|
+
}
|
|
2117
|
+
console.log(
|
|
2118
|
+
` Skills: ${cdSkillCount > 0 ? `\x1b[32m${cdSkillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`,
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
1417
2121
|
if (target === 'codex' || target === 'all') {
|
|
1418
2122
|
console.log('\n[Codex]');
|
|
1419
2123
|
if (!hasCodexCLI()) {
|
|
@@ -1446,7 +2150,7 @@ async function cmdDoctor() {
|
|
|
1446
2150
|
}
|
|
1447
2151
|
|
|
1448
2152
|
// Node.js
|
|
1449
|
-
check('Node.js >=
|
|
2153
|
+
check('Node.js >= 22', process.versions.node.split('.')[0] >= 22, 'Run: nvm install 22 && nvm use 22');
|
|
1450
2154
|
|
|
1451
2155
|
// MCP server — check OpenCode, Codex Desktop, CodeArts, WorkBuddy, and DSH paths
|
|
1452
2156
|
const opencodePluginDir = opencodePluginsDir();
|
|
@@ -1454,12 +2158,16 @@ async function cmdDoctor() {
|
|
|
1454
2158
|
const codeartsPluginDir = codeartsPluginsDir();
|
|
1455
2159
|
const workbuddyPluginDir = workbuddyPluginsDir();
|
|
1456
2160
|
const dshPluginDir = dshPluginsDir();
|
|
2161
|
+
const officeacePluginDir = officeacePluginsDir();
|
|
2162
|
+
const hermesPluginDir = hermesPluginsDir();
|
|
1457
2163
|
const mcpOk =
|
|
1458
2164
|
existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs')) ||
|
|
1459
2165
|
existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
1460
2166
|
existsSync(join(codeartsPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
1461
2167
|
existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
1462
|
-
existsSync(join(dshPluginDir, 'src', 'mcp-server.mjs'))
|
|
2168
|
+
existsSync(join(dshPluginDir, 'src', 'mcp-server.mjs')) ||
|
|
2169
|
+
existsSync(join(officeacePluginDir, 'src', 'mcp-server.mjs')) ||
|
|
2170
|
+
existsSync(join(hermesPluginDir, 'src', 'mcp-server.mjs'));
|
|
1463
2171
|
const mcpTarget = existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs'))
|
|
1464
2172
|
? 'OpenCode'
|
|
1465
2173
|
: existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs'))
|
|
@@ -1470,7 +2178,11 @@ async function cmdDoctor() {
|
|
|
1470
2178
|
? 'WorkBuddy'
|
|
1471
2179
|
: existsSync(join(dshPluginDir, 'src', 'mcp-server.mjs'))
|
|
1472
2180
|
? 'DSH'
|
|
1473
|
-
: ''
|
|
2181
|
+
: existsSync(join(officeacePluginDir, 'src', 'mcp-server.mjs'))
|
|
2182
|
+
? 'OfficeAce'
|
|
2183
|
+
: existsSync(join(hermesPluginDir, 'src', 'mcp-server.mjs'))
|
|
2184
|
+
? 'Hermes Agent'
|
|
2185
|
+
: '';
|
|
1474
2186
|
check('MCP server installed', mcpOk, 'Run: npx huaweicloud-devkit install');
|
|
1475
2187
|
|
|
1476
2188
|
if (mcpOk) {
|
|
@@ -1482,7 +2194,9 @@ async function cmdDoctor() {
|
|
|
1482
2194
|
existsSync(join(codexPluginDir, 'safety', 'policy.json')) ||
|
|
1483
2195
|
existsSync(join(codeartsPluginDir, 'safety', 'policy.json')) ||
|
|
1484
2196
|
existsSync(join(workbuddyPluginDir, 'safety', 'policy.json')) ||
|
|
1485
|
-
existsSync(join(dshPluginDir, 'safety', 'policy.json'))
|
|
2197
|
+
existsSync(join(dshPluginDir, 'safety', 'policy.json')) ||
|
|
2198
|
+
existsSync(join(officeacePluginDir, 'safety', 'policy.json')) ||
|
|
2199
|
+
existsSync(join(hermesPluginDir, 'safety', 'policy.json'));
|
|
1486
2200
|
check('Safety policy installed', safetyOk, 'Run: npx huaweicloud-devkit install');
|
|
1487
2201
|
|
|
1488
2202
|
// MCP config — check OpenCode, Codex Desktop, CodeArts, WorkBuddy, and DSH
|
|
@@ -1532,6 +2246,33 @@ async function cmdDoctor() {
|
|
|
1532
2246
|
mcpConfigured = true;
|
|
1533
2247
|
mcpCfgTarget = 'DSH';
|
|
1534
2248
|
}
|
|
2249
|
+
if (!mcpConfigured) {
|
|
2250
|
+
const officeaceCfg = officeaceCapabilitiesFile();
|
|
2251
|
+
if (existsSync(officeaceCfg)) {
|
|
2252
|
+
try {
|
|
2253
|
+
const cfg = JSON.parse(readFileSync(officeaceCfg, 'utf8'));
|
|
2254
|
+
if (
|
|
2255
|
+
Array.isArray(cfg.capabilities) &&
|
|
2256
|
+
cfg.capabilities.some((c) => c.id === 'huaweicloud-devkit' && c.type === 'mcp')
|
|
2257
|
+
) {
|
|
2258
|
+
mcpConfigured = true;
|
|
2259
|
+
mcpCfgTarget = 'OfficeAce';
|
|
2260
|
+
}
|
|
2261
|
+
} catch {}
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
if (!mcpConfigured) {
|
|
2265
|
+
const hermesCfg = hermesConfigFile();
|
|
2266
|
+
if (existsSync(hermesCfg)) {
|
|
2267
|
+
try {
|
|
2268
|
+
const cfg = readFileSync(hermesCfg, 'utf8');
|
|
2269
|
+
if (cfg.includes('mcp_servers:') && cfg.includes('huaweicloud-devkit')) {
|
|
2270
|
+
mcpConfigured = true;
|
|
2271
|
+
mcpCfgTarget = 'Hermes Agent';
|
|
2272
|
+
}
|
|
2273
|
+
} catch {}
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
1535
2276
|
check(
|
|
1536
2277
|
'MCP configured',
|
|
1537
2278
|
mcpConfigured,
|
|
@@ -1581,6 +2322,8 @@ async function cmdDoctor() {
|
|
|
1581
2322
|
codeartsSkillsDir(),
|
|
1582
2323
|
workbuddySkillsDir(),
|
|
1583
2324
|
dshSkillsDir(),
|
|
2325
|
+
officeaceSkillsDir(),
|
|
2326
|
+
hermesSkillsDir(),
|
|
1584
2327
|
];
|
|
1585
2328
|
let skillCount = 0;
|
|
1586
2329
|
const missingSkills = [];
|
|
@@ -1636,6 +2379,7 @@ async function cmdDoctor() {
|
|
|
1636
2379
|
{ path: join(workbuddyPluginsDir(), '.installed'), name: 'WorkBuddy' },
|
|
1637
2380
|
{ path: join(codeartsPluginsDir(), '.installed'), name: 'CodeArts' },
|
|
1638
2381
|
{ path: join(dshPluginsDir(), '.installed'), name: 'DSH' },
|
|
2382
|
+
{ path: join(officeacePluginsDir(), '.installed'), name: 'OfficeAce' },
|
|
1639
2383
|
];
|
|
1640
2384
|
for (const marker of installedMarkers) {
|
|
1641
2385
|
if (existsSync(marker.path)) {
|
|
@@ -1729,6 +2473,42 @@ async function cmdUpdate() {
|
|
|
1729
2473
|
return;
|
|
1730
2474
|
}
|
|
1731
2475
|
|
|
2476
|
+
if (target === 'officeace') {
|
|
2477
|
+
if (!existsSync(join(officeacePluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2478
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2481
|
+
console.log('[OfficeAce]');
|
|
2482
|
+
await updateOfficeAce();
|
|
2483
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
2484
|
+
console.log(`\x1b[33m打开连接器 → 我的连接器 → huaweicloud-devkit → 连接 → 回到对话 → 输入框开启连接器\x1b[0m`);
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
if (target === 'hermes') {
|
|
2489
|
+
if (!existsSync(join(hermesPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2490
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
2491
|
+
return;
|
|
2492
|
+
}
|
|
2493
|
+
console.log('[Hermes Agent]');
|
|
2494
|
+
await updateHermes();
|
|
2495
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
2496
|
+
console.log(`\x1b[33mRestart Hermes Agent for changes to take effect.\x1b[0m`);
|
|
2497
|
+
return;
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
if (target === 'openclaw') {
|
|
2501
|
+
if (!existsSync(join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2502
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
console.log('[OpenClaw]');
|
|
2506
|
+
await updateCodexDesktop();
|
|
2507
|
+
console.log(`\n\x1b[32mUpdate complete.\x1b[0m`);
|
|
2508
|
+
console.log(`\x1b[33mRestart OpenClaw for changes to take effect.\x1b[0m`);
|
|
2509
|
+
return;
|
|
2510
|
+
}
|
|
2511
|
+
|
|
1732
2512
|
if (target === 'all') {
|
|
1733
2513
|
let updatedAny = false;
|
|
1734
2514
|
if (existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
@@ -1756,6 +2536,21 @@ async function cmdUpdate() {
|
|
|
1756
2536
|
await updateDsh();
|
|
1757
2537
|
updatedAny = true;
|
|
1758
2538
|
}
|
|
2539
|
+
if (existsSync(join(officeacePluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2540
|
+
console.log('\n[OfficeAce]');
|
|
2541
|
+
await updateOfficeAce();
|
|
2542
|
+
updatedAny = true;
|
|
2543
|
+
}
|
|
2544
|
+
if (existsSync(join(hermesPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2545
|
+
console.log('\n[Hermes Agent]');
|
|
2546
|
+
await updateHermes();
|
|
2547
|
+
updatedAny = true;
|
|
2548
|
+
}
|
|
2549
|
+
if (existsSync(join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs'))) {
|
|
2550
|
+
console.log('\n[OpenClaw]');
|
|
2551
|
+
await updateCodexDesktop();
|
|
2552
|
+
updatedAny = true;
|
|
2553
|
+
}
|
|
1759
2554
|
if (codexStatus()) {
|
|
1760
2555
|
console.log('\n[Codex]');
|
|
1761
2556
|
installCodex();
|
|
@@ -2250,7 +3045,9 @@ async function main() {
|
|
|
2250
3045
|
case '-h':
|
|
2251
3046
|
default:
|
|
2252
3047
|
console.log(BANNER);
|
|
2253
|
-
console.log(
|
|
3048
|
+
console.log(
|
|
3049
|
+
'Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|workbuddy|dsh|officeace|hermes|openclaw|all>]\n',
|
|
3050
|
+
);
|
|
2254
3051
|
console.log('Commands:');
|
|
2255
3052
|
console.log(' install Install skills, MCP server, safety policy');
|
|
2256
3053
|
console.log(' uninstall Remove installed files');
|
|
@@ -2263,13 +3060,17 @@ async function main() {
|
|
|
2263
3060
|
console.log(' proxy Manage proxy config: init | show | clear');
|
|
2264
3061
|
console.log(' help Show this help');
|
|
2265
3062
|
console.log('\nOptions:');
|
|
2266
|
-
console.log(
|
|
3063
|
+
console.log(
|
|
3064
|
+
' --target Target agent: opencode (default), codex, codearts, workbuddy, dsh, officeace, hermes, openclaw, all',
|
|
3065
|
+
);
|
|
2267
3066
|
console.log('\nExamples:');
|
|
2268
3067
|
console.log(' npx huaweicloud-devkit install');
|
|
2269
3068
|
console.log(' npx huaweicloud-devkit install --target codex');
|
|
2270
3069
|
console.log(' npx huaweicloud-devkit install --target codearts');
|
|
2271
3070
|
console.log(' npx huaweicloud-devkit install --target workbuddy');
|
|
2272
3071
|
console.log(' npx huaweicloud-devkit install --target dsh');
|
|
3072
|
+
console.log(' npx huaweicloud-devkit install --target officeace');
|
|
3073
|
+
console.log(' npx huaweicloud-devkit install --target hermes');
|
|
2273
3074
|
console.log(' npx huaweicloud-devkit install --target all');
|
|
2274
3075
|
console.log(' npx huaweicloud-devkit auth init');
|
|
2275
3076
|
console.log(' npx huaweicloud-devkit auth sync --target all');
|