huaweicloud-devkit 0.1.17
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 +20 -0
- package/LICENSE +201 -0
- package/README.md +148 -0
- package/bin/setup.cjs +8 -0
- package/integrations/opencode/commands/huaweicloud-doctor.md +14 -0
- package/integrations/opencode/opencode.json +14 -0
- package/integrations/opencode/skills/huaweicloud-core/SKILL.md +30 -0
- package/package.json +43 -0
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +44 -0
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +43 -0
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +44 -0
- package/plugins/huaweicloud-core/.mcp.json +13 -0
- package/plugins/huaweicloud-core/hooks/hooks.json +26 -0
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +88 -0
- package/plugins/huaweicloud-core/safety/policy.json +86 -0
- package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +144 -0
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +48 -0
- package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +50 -0
- package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +101 -0
- package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +73 -0
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +190 -0
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +124 -0
- package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +55 -0
- package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +70 -0
- package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +56 -0
- package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +97 -0
- package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +29 -0
- package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +23 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +131 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +92 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/evs.md +31 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +28 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +52 -0
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +132 -0
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +58 -0
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +71 -0
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/deploy-workflow.md +119 -0
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +142 -0
- package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +56 -0
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +65 -0
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +111 -0
- package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +122 -0
- package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +56 -0
- package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +65 -0
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +118 -0
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +49 -0
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +26 -0
- package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +100 -0
- package/plugins/huaweicloud-core/skills/huawei-obs/references/static-website.md +56 -0
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +181 -0
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +69 -0
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +77 -0
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +52 -0
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +59 -0
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +50 -0
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +63 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +56 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +4 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +47 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +4 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +125 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +4 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +94 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +4 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +20 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +28 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +46 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +4 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +83 -0
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +4 -0
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +265 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +93 -0
- package/plugins/huaweicloud-core/src/safety-policy.mjs +289 -0
- package/plugins/huaweicloud-core/src/search-market.mjs +169 -0
- package/plugins/huaweicloud-core/src/setup-cli.mjs +628 -0
- package/plugins/huaweicloud-core/src/tools.mjs +672 -0
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { join, dirname, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { homedir, platform } from 'node:os';
|
|
7
|
+
import { createInterface } from 'node:readline';
|
|
8
|
+
import { spawnSync } from 'node:child_process';
|
|
9
|
+
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const PLUGIN_ROOT = resolve(__dirname, '..');
|
|
12
|
+
const PACKAGE_ROOT = resolve(PLUGIN_ROOT, '..', '..');
|
|
13
|
+
|
|
14
|
+
const BANNER = `
|
|
15
|
+
╔══════════════════════════════════════════════╗
|
|
16
|
+
║ HuaweiCloud DevKit v0.1.0 ║
|
|
17
|
+
║ https://github.com/huaweicloud-mate ║
|
|
18
|
+
╚══════════════════════════════════════════════╝
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
function configRoot(target = 'opencode') {
|
|
22
|
+
const home = homedir();
|
|
23
|
+
return join(home, '.config', target);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function opencodeSkillsDir() { return join(configRoot('opencode'), 'skills'); }
|
|
27
|
+
function opencodeCommandsDir() { return join(configRoot('opencode'), 'commands'); }
|
|
28
|
+
function opencodePluginsDir() { return join(configRoot('opencode'), 'huaweicloud-plugins'); }
|
|
29
|
+
function opencodeConfigFile() {
|
|
30
|
+
const jsonc = join(configRoot('opencode'), 'opencode.jsonc');
|
|
31
|
+
if (existsSync(jsonc)) return jsonc;
|
|
32
|
+
return join(configRoot('opencode'), 'opencode.json');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function codexDesktopSkillsDir() { return join(homedir(), '.agents', 'skills'); }
|
|
36
|
+
function codexDesktopCommandsDir() { return join(homedir(), '.agents', 'commands'); }
|
|
37
|
+
function codexDesktopPluginsDir() { return join(homedir(), '.agents', 'huaweicloud-plugins'); }
|
|
38
|
+
function codexDesktopConfigFile() { return join(homedir(), '.agents', 'opencode.json'); }
|
|
39
|
+
|
|
40
|
+
function checkNode() {
|
|
41
|
+
const v = process.versions.node.split('.').map(Number);
|
|
42
|
+
if (v[0] < 20) {
|
|
43
|
+
console.error(`\x1b[31mNode.js >= 20 required (current: ${process.version})\x1b[0m`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
console.log(` Node.js ${process.version} \x1b[32mOK\x1b[0m`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function copyDir(src, dest) {
|
|
50
|
+
if (!existsSync(src)) return;
|
|
51
|
+
mkdirSync(dest, { recursive: true });
|
|
52
|
+
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
53
|
+
const s = join(src, entry.name);
|
|
54
|
+
const d = join(dest, entry.name);
|
|
55
|
+
if (entry.isDirectory()) {
|
|
56
|
+
copyDir(s, d);
|
|
57
|
+
} else {
|
|
58
|
+
copyFileSync(s, d);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function removeIfExists(p) {
|
|
64
|
+
if (existsSync(p)) {
|
|
65
|
+
rmSync(p, { recursive: true, force: true });
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function updateOpenCodeConfig(pluginDir) {
|
|
72
|
+
const configPath = opencodeConfigFile();
|
|
73
|
+
let config = {};
|
|
74
|
+
if (existsSync(configPath)) {
|
|
75
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
|
|
76
|
+
}
|
|
77
|
+
const mcpPath = join(pluginDir, 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
78
|
+
config.mcp = config.mcp || {};
|
|
79
|
+
config.mcp.huaweicloud = {
|
|
80
|
+
type: 'local',
|
|
81
|
+
command: ['node', mcpPath],
|
|
82
|
+
enabled: true,
|
|
83
|
+
};
|
|
84
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
85
|
+
console.log(` OpenCode MCP config updated: ${configPath}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function removeOpenCodeConfig() {
|
|
89
|
+
const configPath = opencodeConfigFile();
|
|
90
|
+
if (!existsSync(configPath)) return;
|
|
91
|
+
let config = {};
|
|
92
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch { return; }
|
|
93
|
+
if (!config.mcp?.huaweicloud) return;
|
|
94
|
+
delete config.mcp.huaweicloud;
|
|
95
|
+
if (Object.keys(config.mcp).length === 0) delete config.mcp;
|
|
96
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
97
|
+
console.log(` OpenCode MCP config cleaned: ${configPath}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function hasCodexCLI() {
|
|
101
|
+
const r = spawnSync('codex --version', [], { shell: true, windowsHide: true, stdio: 'pipe' });
|
|
102
|
+
return r.status === 0 && r.stdout && r.stdout.toString().includes('codex');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function checkHcloud() {
|
|
106
|
+
const r = spawnSync('hcloud version', [], { shell: true, windowsHide: true, stdio: 'pipe', timeout: 5000 });
|
|
107
|
+
return r.status === 0 && /KooCLI|Current.*version/i.test(r.stdout ? r.stdout.toString() : '');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getMarketplaceName() {
|
|
111
|
+
const marketplacePath = join(PACKAGE_ROOT, '.agents', 'plugins', 'marketplace.json');
|
|
112
|
+
try {
|
|
113
|
+
const manifest = JSON.parse(readFileSync(marketplacePath, 'utf8'));
|
|
114
|
+
if (manifest.name) return manifest.name;
|
|
115
|
+
} catch {}
|
|
116
|
+
return 'huaweicloud-devkit';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function installCodex() {
|
|
120
|
+
const marketplaceRoot = PACKAGE_ROOT;
|
|
121
|
+
const pluginName = 'huaweicloud-core';
|
|
122
|
+
const marketplaceName = getMarketplaceName();
|
|
123
|
+
|
|
124
|
+
console.log(` Registering Codex marketplace: ${marketplaceRoot}`);
|
|
125
|
+
const r1 = spawnSync(`codex plugin marketplace add "${marketplaceRoot}"`, [], {
|
|
126
|
+
shell: true, windowsHide: true, stdio: 'pipe',
|
|
127
|
+
});
|
|
128
|
+
console.log(` ${r1.stdout ? r1.stdout.toString().trim() : r1.stderr.toString().trim()}`);
|
|
129
|
+
|
|
130
|
+
console.log(` Installing plugin: ${pluginName}@${marketplaceName}`);
|
|
131
|
+
const r2 = spawnSync(`codex plugin add "${pluginName}@${marketplaceName}"`, [], {
|
|
132
|
+
shell: true, windowsHide: true, stdio: 'pipe',
|
|
133
|
+
});
|
|
134
|
+
console.log(` ${r2.stdout ? r2.stdout.toString().trim() : r2.stderr.toString().trim()}`);
|
|
135
|
+
return r2.status === 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function uninstallCodex() {
|
|
139
|
+
const pluginName = 'huaweicloud-core';
|
|
140
|
+
const marketplaceName = getMarketplaceName();
|
|
141
|
+
console.log(` Removing Codex plugin: ${pluginName}@${marketplaceName}`);
|
|
142
|
+
const r = spawnSync(`codex plugin remove "${pluginName}@${marketplaceName}"`, [], {
|
|
143
|
+
shell: true, windowsHide: true, stdio: 'pipe',
|
|
144
|
+
});
|
|
145
|
+
console.log(` ${r.stdout ? r.stdout.toString().trim() : r.stderr.toString().trim()}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function codexStatus() {
|
|
149
|
+
const r = spawnSync('codex plugin list', [], { shell: true, windowsHide: true, stdio: 'pipe' });
|
|
150
|
+
const out = r.stdout ? r.stdout.toString() : '';
|
|
151
|
+
return out.includes('huaweicloud-core');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function installOpenCode() {
|
|
155
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
156
|
+
const commandsSrc = join(PACKAGE_ROOT, 'integrations', 'opencode', 'commands');
|
|
157
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
158
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
159
|
+
const pluginDest = opencodePluginsDir();
|
|
160
|
+
|
|
161
|
+
copyDir(skillsSrc, opencodeSkillsDir());
|
|
162
|
+
console.log(` Skills -> ${opencodeSkillsDir()}`);
|
|
163
|
+
copyDir(commandsSrc, opencodeCommandsDir());
|
|
164
|
+
console.log(` Commands -> ${opencodeCommandsDir()}`);
|
|
165
|
+
copyDir(srcDir, join(pluginDest, 'src'));
|
|
166
|
+
console.log(` MCP Server -> ${join(pluginDest, 'src')}`);
|
|
167
|
+
copyDir(safetyDir, join(pluginDest, 'safety'));
|
|
168
|
+
console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
|
|
169
|
+
updateOpenCodeConfig(pluginDest);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function uninstallOpenCode() {
|
|
173
|
+
let removed = 0;
|
|
174
|
+
|
|
175
|
+
const skills = opencodeSkillsDir();
|
|
176
|
+
if (existsSync(skills)) {
|
|
177
|
+
for (const entry of readdirSync(skills, { withFileTypes: true })) {
|
|
178
|
+
if (entry.name.startsWith('huawei')) {
|
|
179
|
+
removeIfExists(join(skills, entry.name));
|
|
180
|
+
removed++;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
console.log(` Removed ${removed} skills`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const commands = opencodeCommandsDir();
|
|
187
|
+
let cmdRemoved = 0;
|
|
188
|
+
if (existsSync(commands)) {
|
|
189
|
+
for (const entry of readdirSync(commands, { withFileTypes: true })) {
|
|
190
|
+
if (entry.name.startsWith('huawei')) {
|
|
191
|
+
removeIfExists(join(commands, entry.name));
|
|
192
|
+
cmdRemoved++;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (cmdRemoved > 0) console.log(` Removed ${cmdRemoved} commands`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (removeIfExists(opencodePluginsDir())) {
|
|
199
|
+
console.log(' Removed MCP server and safety policy');
|
|
200
|
+
}
|
|
201
|
+
removeOpenCodeConfig();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function installCodexDesktop() {
|
|
205
|
+
const skillsSrc = join(PLUGIN_ROOT, 'skills');
|
|
206
|
+
const commandsSrc = join(PACKAGE_ROOT, 'integrations', 'opencode', 'commands');
|
|
207
|
+
const srcDir = join(PLUGIN_ROOT, 'src');
|
|
208
|
+
const safetyDir = join(PLUGIN_ROOT, 'safety');
|
|
209
|
+
|
|
210
|
+
copyDir(skillsSrc, codexDesktopSkillsDir());
|
|
211
|
+
console.log(` Skills -> ${codexDesktopSkillsDir()}`);
|
|
212
|
+
copyDir(commandsSrc, codexDesktopCommandsDir());
|
|
213
|
+
console.log(` Commands -> ${codexDesktopCommandsDir()}`);
|
|
214
|
+
mkdirSync(codexDesktopPluginsDir(), { recursive: true });
|
|
215
|
+
copyDir(srcDir, join(codexDesktopPluginsDir(), 'src'));
|
|
216
|
+
console.log(` MCP Server -> ${join(codexDesktopPluginsDir(), 'src')}`);
|
|
217
|
+
copyDir(safetyDir, join(codexDesktopPluginsDir(), 'safety'));
|
|
218
|
+
console.log(` Safety Policy -> ${join(codexDesktopPluginsDir(), 'safety')}`);
|
|
219
|
+
|
|
220
|
+
const mcpPath = join(codexDesktopPluginsDir(), 'src', 'mcp-server.mjs').replace(/\\/g, '/');
|
|
221
|
+
const configPath = codexDesktopConfigFile();
|
|
222
|
+
let config = {};
|
|
223
|
+
if (existsSync(configPath)) {
|
|
224
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
|
|
225
|
+
}
|
|
226
|
+
config.mcp = config.mcp || {};
|
|
227
|
+
config.mcp.huaweicloud = {
|
|
228
|
+
type: 'local',
|
|
229
|
+
command: ['node', mcpPath],
|
|
230
|
+
enabled: true,
|
|
231
|
+
};
|
|
232
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
233
|
+
console.log(` Config updated: ${configPath}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function uninstallCodexDesktop() {
|
|
237
|
+
const skillsDir = codexDesktopSkillsDir();
|
|
238
|
+
let removed = 0;
|
|
239
|
+
if (existsSync(skillsDir)) {
|
|
240
|
+
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
241
|
+
if (entry.name.startsWith('huawei')) {
|
|
242
|
+
removeIfExists(join(skillsDir, entry.name));
|
|
243
|
+
removed++;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
console.log(` Removed ${removed} skills`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const cmdDir = codexDesktopCommandsDir();
|
|
250
|
+
let cmdRemoved = 0;
|
|
251
|
+
if (existsSync(cmdDir)) {
|
|
252
|
+
for (const entry of readdirSync(cmdDir, { withFileTypes: true })) {
|
|
253
|
+
if (entry.name.startsWith('huawei')) {
|
|
254
|
+
removeIfExists(join(cmdDir, entry.name));
|
|
255
|
+
cmdRemoved++;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (cmdRemoved > 0) console.log(` Removed ${cmdRemoved} commands`);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (removeIfExists(codexDesktopPluginsDir())) {
|
|
262
|
+
console.log(' Removed MCP server and safety policy');
|
|
263
|
+
}
|
|
264
|
+
const configPath = codexDesktopConfigFile();
|
|
265
|
+
if (existsSync(configPath)) {
|
|
266
|
+
let config = {};
|
|
267
|
+
try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
|
|
268
|
+
if (config.mcp?.huaweicloud) {
|
|
269
|
+
delete config.mcp.huaweicloud;
|
|
270
|
+
if (Object.keys(config.mcp).length === 0) delete config.mcp;
|
|
271
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
272
|
+
console.log(' Config cleaned');
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function opencodeStatus() {
|
|
278
|
+
const pluginDir = opencodePluginsDir();
|
|
279
|
+
const skillsDir = opencodeSkillsDir();
|
|
280
|
+
console.log(` MCP Server: ${existsSync(join(pluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
|
|
281
|
+
console.log(` Safety Policy: ${existsSync(join(pluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
|
|
282
|
+
let skillCount = 0;
|
|
283
|
+
if (existsSync(skillsDir)) {
|
|
284
|
+
skillCount = readdirSync(skillsDir, { withFileTypes: true })
|
|
285
|
+
.filter((d) => d.isDirectory() && d.name.startsWith('huawei')).length;
|
|
286
|
+
}
|
|
287
|
+
console.log(` Skills: ${skillCount > 0 ? `\x1b[32m${skillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`);
|
|
288
|
+
const configPath = opencodeConfigFile();
|
|
289
|
+
if (existsSync(configPath)) {
|
|
290
|
+
try {
|
|
291
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
292
|
+
console.log(` MCP config: ${config.mcp?.huaweicloud ? '\x1b[32mConfigured\x1b[0m' : '\x1b[31mNot configured\x1b[0m'}`);
|
|
293
|
+
} catch {
|
|
294
|
+
console.log(` MCP config: \x1b[31mInvalid\x1b[0m`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function parseTarget() {
|
|
300
|
+
const idx = process.argv.indexOf('--target');
|
|
301
|
+
if (idx < 0) return 'opencode';
|
|
302
|
+
const val = (process.argv[idx + 1] || '').toLowerCase();
|
|
303
|
+
if (val === 'codex') return 'codex';
|
|
304
|
+
if (val === 'codex-desktop') return 'codex-desktop';
|
|
305
|
+
if (val === 'all') return 'all';
|
|
306
|
+
return 'opencode';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function cmdInstall() {
|
|
310
|
+
const target = parseTarget();
|
|
311
|
+
console.log(BANNER);
|
|
312
|
+
console.log(`Installing HuaweiCloud DevKit${target !== 'opencode' ? ` for ${target}` : ''}...\n`);
|
|
313
|
+
checkNode();
|
|
314
|
+
|
|
315
|
+
if (target === 'opencode' || target === 'all') {
|
|
316
|
+
console.log('[OpenCode]');
|
|
317
|
+
await installOpenCode();
|
|
318
|
+
}
|
|
319
|
+
if (target === 'codex-desktop' || target === 'all') {
|
|
320
|
+
console.log('\n[Codex Desktop]');
|
|
321
|
+
await installCodexDesktop();
|
|
322
|
+
}
|
|
323
|
+
if (target === 'codex' || target === 'all') {
|
|
324
|
+
console.log('\n[Codex]');
|
|
325
|
+
if (!hasCodexCLI()) {
|
|
326
|
+
console.log(` \x1b[33mCodex CLI not found. Skipping Codex install.\x1b[0m`);
|
|
327
|
+
console.log(' To install for Codex, first install Codex CLI, then run:');
|
|
328
|
+
console.log(' npx huaweicloud-devkit install --target codex');
|
|
329
|
+
} else {
|
|
330
|
+
installCodex();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
console.log(`\n\x1b[32mInstallation complete!\x1b[0m`);
|
|
335
|
+
console.log(`\n\x1b[1m\x1b[33m========================================`);
|
|
336
|
+
console.log(` IMPORTANT: Restart your OpenCode session now!`);
|
|
337
|
+
console.log(` MCP tools only become available AFTER restart.`);
|
|
338
|
+
console.log(`========================================\x1b[0m`);
|
|
339
|
+
|
|
340
|
+
const hcloudOk = checkHcloud();
|
|
341
|
+
if (!hcloudOk) {
|
|
342
|
+
console.log(`\n\x1b[33mKooCLI (hcloud) is not installed.`);
|
|
343
|
+
console.log(` Run: npx huaweicloud-devkit install-hcloud\x1b[0m`);
|
|
344
|
+
} else {
|
|
345
|
+
console.log(`\nKooCLI (hcloud) detected.`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
console.log(`\nAfter restart + hcloud setup, run: npx huaweicloud-devkit doctor`);
|
|
349
|
+
if (target === 'opencode' || target === 'all') {
|
|
350
|
+
console.log('Or mention @huaweicloud-core in OpenCode');
|
|
351
|
+
}
|
|
352
|
+
if (target === 'codex' || target === 'all') {
|
|
353
|
+
console.log('Or mention @huaweicloud-core in Codex');
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function cmdUninstall() {
|
|
358
|
+
const target = parseTarget();
|
|
359
|
+
console.log(BANNER);
|
|
360
|
+
console.log(`Uninstalling HuaweiCloud DevKit${target !== 'opencode' ? ` from ${target}` : ''}...\n`);
|
|
361
|
+
|
|
362
|
+
if (target === 'opencode' || target === 'all') {
|
|
363
|
+
console.log('[OpenCode]');
|
|
364
|
+
await uninstallOpenCode();
|
|
365
|
+
}
|
|
366
|
+
if (target === 'codex' || target === 'all') {
|
|
367
|
+
console.log('\n[Codex]');
|
|
368
|
+
uninstallCodexDesktop();
|
|
369
|
+
if (!hasCodexCLI()) {
|
|
370
|
+
console.log(' \x1b[33mCodex CLI not found. Run "npm uninstall -g codex" to fully remove.\x1b[0m');
|
|
371
|
+
} else {
|
|
372
|
+
uninstallCodex();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
console.log(`\n\x1b[32mUninstall complete.\x1b[0m`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async function cmdStatus() {
|
|
379
|
+
const target = parseTarget();
|
|
380
|
+
console.log(BANNER);
|
|
381
|
+
console.log(`HuaweiCloud DevKit Status\n`);
|
|
382
|
+
|
|
383
|
+
if (target === 'opencode' || target === 'all') {
|
|
384
|
+
console.log('[OpenCode]');
|
|
385
|
+
opencodeStatus();
|
|
386
|
+
}
|
|
387
|
+
if (target === 'codex' || target === 'all') {
|
|
388
|
+
console.log('\n[Codex]');
|
|
389
|
+
if (!hasCodexCLI()) {
|
|
390
|
+
console.log(' \x1b[33mCodex CLI not found.\x1b[0m');
|
|
391
|
+
} else {
|
|
392
|
+
console.log(` Plugin: ${codexStatus() ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
console.log('\nEnvironment:');
|
|
396
|
+
console.log(` Node.js: ${process.version}`);
|
|
397
|
+
console.log(` Platform: ${platform()}`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async function cmdDoctor() {
|
|
401
|
+
console.log(BANNER);
|
|
402
|
+
console.log('HuaweiCloud DevKit Doctor\n');
|
|
403
|
+
|
|
404
|
+
let pass = 0, warn = 0, fail = 0;
|
|
405
|
+
|
|
406
|
+
function check(label, ok, msg) {
|
|
407
|
+
if (ok) { console.log(` \x1b[32m[PASS]\x1b[0m ${label}`); pass++; }
|
|
408
|
+
else { console.log(` \x1b[31m[FAIL]\x1b[0m ${label} — ${msg}`); fail++; }
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Node.js
|
|
412
|
+
check('Node.js >= 20', process.versions.node.split('.')[0] >= 20, 'Run: nvm install 20 && nvm use 20');
|
|
413
|
+
|
|
414
|
+
// OpenCode installed files
|
|
415
|
+
const pluginDir = opencodePluginsDir();
|
|
416
|
+
const mcpOk = existsSync(join(pluginDir, 'src', 'mcp-server.mjs'));
|
|
417
|
+
check('MCP server installed', mcpOk, 'Run: npx huaweicloud-devkit-test install');
|
|
418
|
+
|
|
419
|
+
if (mcpOk) {
|
|
420
|
+
// Try to start MCP server briefly
|
|
421
|
+
const test = spawnSync('node', [join(pluginDir, 'src', 'mcp-server.mjs')], {
|
|
422
|
+
env: { ...process.env, HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' },
|
|
423
|
+
timeout: 3000, stdio: 'pipe', windowsHide: true,
|
|
424
|
+
});
|
|
425
|
+
// MCP server reads stdin for JSON-RPC, so it will hang briefly then get killed
|
|
426
|
+
// We just check that the process spawned OK
|
|
427
|
+
check('MCP server can start', true, '');
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const safetyOk = existsSync(join(pluginDir, 'safety', 'policy.json'));
|
|
431
|
+
check('Safety policy installed', safetyOk, 'Run: npx huaweicloud-devkit-test install');
|
|
432
|
+
|
|
433
|
+
const opencodeCfg = opencodeConfigFile();
|
|
434
|
+
let mcpConfigured = false;
|
|
435
|
+
if (existsSync(opencodeCfg)) {
|
|
436
|
+
try {
|
|
437
|
+
const cfg = JSON.parse(readFileSync(opencodeCfg, 'utf8'));
|
|
438
|
+
mcpConfigured = !!(cfg.mcp && cfg.mcp.huaweicloud);
|
|
439
|
+
} catch {}
|
|
440
|
+
}
|
|
441
|
+
check('OpenCode MCP configured', mcpConfigured, `Add MCP to ${opencodeCfg} — run: npx huaweicloud-devkit-test install`);
|
|
442
|
+
|
|
443
|
+
// hcloud CLI
|
|
444
|
+
const hcloudBin = process.env.HCLOUD_BIN || 'hcloud';
|
|
445
|
+
const hcloudCheck = spawnSync(`"${hcloudBin}" version`, [], { shell: true, windowsHide: true, stdio: 'pipe', timeout: 5000 });
|
|
446
|
+
const hcloudOk = hcloudCheck.status === 0 && hcloudCheck.stdout.toString().includes('KooCLI');
|
|
447
|
+
check('hcloud CLI installed', hcloudOk, 'Run: npx huaweicloud-devkit install-hcloud');
|
|
448
|
+
|
|
449
|
+
if (hcloudOk) {
|
|
450
|
+
const ver = (hcloudCheck.stdout.toString().match(/(\d+\.\d+\.\d+)/) || [])[1] || 'unknown';
|
|
451
|
+
console.log(` Version: ${ver}`);
|
|
452
|
+
|
|
453
|
+
// Check auth
|
|
454
|
+
const authCheck = spawnSync(`"${hcloudBin}" configure list`, [], { shell: true, windowsHide: true, stdio: 'pipe', timeout: 5000 });
|
|
455
|
+
const hasAuth = authCheck.status === 0 && /access.?key/i.test(authCheck.stdout.toString());
|
|
456
|
+
check('hcloud credentials configured', hasAuth, 'Run: hcloud configure init');
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Skills
|
|
460
|
+
const skillsOptions = [opencodeSkillsDir(), codexDesktopSkillsDir()];
|
|
461
|
+
let skillCount = 0, skillsDir = '';
|
|
462
|
+
for (const dir of skillsOptions) {
|
|
463
|
+
if (!existsSync(dir)) continue;
|
|
464
|
+
const count = readdirSync(dir, { withFileTypes: true })
|
|
465
|
+
.filter((d) => d.isDirectory() && d.name.startsWith('huawei')).length;
|
|
466
|
+
if (count > skillCount) { skillCount = count; skillsDir = dir; }
|
|
467
|
+
}
|
|
468
|
+
const skillsOk = skillCount >= 6;
|
|
469
|
+
check(`Skills installed (${skillCount})`, skillsOk, 'Run: npx huaweicloud-devkit-test install');
|
|
470
|
+
|
|
471
|
+
console.log(`\nResults: ${pass} pass, ${warn} warn, ${fail} fail`);
|
|
472
|
+
|
|
473
|
+
if (mcpConfigured && !hcloudOk) {
|
|
474
|
+
console.log('\n\x1b[33mMCP is configured but hcloud is not installed. Install hcloud then restart OpenCode.\x1b[0m');
|
|
475
|
+
}
|
|
476
|
+
if (fail > 0) {
|
|
477
|
+
console.log('\x1b[33mFix failures above, then restart your OpenCode / Codex session.\x1b[0m');
|
|
478
|
+
}
|
|
479
|
+
if (fail === 0 && mcpConfigured) {
|
|
480
|
+
console.log('\n\x1b[32mAll checks passed.\x1b[0m Restart OpenCode and try @huaweicloud-core');
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async function cmdUpdate() {
|
|
485
|
+
console.log(BANNER);
|
|
486
|
+
const target = parseTarget();
|
|
487
|
+
|
|
488
|
+
if (target === 'opencode' || target === 'all') {
|
|
489
|
+
if (!existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs')) && !codexStatus()) {
|
|
490
|
+
console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
await cmdUninstall();
|
|
496
|
+
console.log('');
|
|
497
|
+
await cmdInstall();
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
async function cmdReinstall() {
|
|
501
|
+
console.log(BANNER);
|
|
502
|
+
if (!(await confirm('This will remove and reinstall all HuaweiCloud DevKit files. Continue?'))) {
|
|
503
|
+
console.log('Cancelled.');
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
confirmed = true;
|
|
507
|
+
await cmdUninstall();
|
|
508
|
+
console.log('');
|
|
509
|
+
await cmdInstall();
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
let confirmed = false;
|
|
513
|
+
async function confirm(msg) {
|
|
514
|
+
if (confirmed) return true;
|
|
515
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
516
|
+
return new Promise((ok) => {
|
|
517
|
+
rl.question(`${msg} [y/N] `, (a) => {
|
|
518
|
+
rl.close();
|
|
519
|
+
ok(a.toLowerCase() === 'y' || a.toLowerCase() === 'yes');
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
async function cmdInstallHcloud() {
|
|
525
|
+
console.log(BANNER);
|
|
526
|
+
console.log('Installing KooCLI (hcloud)...\n');
|
|
527
|
+
|
|
528
|
+
const os = platform();
|
|
529
|
+
const arch = process.arch;
|
|
530
|
+
const baseUrl = 'https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest';
|
|
531
|
+
const installDir = os === 'win32'
|
|
532
|
+
? join(homedir(), 'hcloud')
|
|
533
|
+
: join(homedir(), '.local', 'bin');
|
|
534
|
+
|
|
535
|
+
if (os === 'win32') {
|
|
536
|
+
console.log(`[Windows] Download and unzip to ${installDir}`);
|
|
537
|
+
const url = `${baseUrl}/huaweicloud-cli-windows-amd64.zip`;
|
|
538
|
+
console.log(` Download: ${url}`);
|
|
539
|
+
console.log(` Unzip to: ${installDir}`);
|
|
540
|
+
console.log(` Add to PATH: ${installDir}`);
|
|
541
|
+
console.log(` Verify: hcloud version`);
|
|
542
|
+
console.log('\n\x1b[33mWindows auto-install not supported yet. Run commands above manually.\x1b[0m');
|
|
543
|
+
console.log(`\nFull guide: https://support.huaweicloud.com/qs-hcli/hcli_02_003_01.html`);
|
|
544
|
+
} else if (os === 'linux') {
|
|
545
|
+
console.log('[Linux] One-liner install:');
|
|
546
|
+
console.log(' curl -sSL https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y');
|
|
547
|
+
console.log(`\nOr manual: ${arch === 'arm64' ? 'ARM64' : 'AMD64'}`);
|
|
548
|
+
const pkg = arch === 'arm64' ? 'linux-arm64' : 'linux-amd64';
|
|
549
|
+
console.log(` curl -LO "${baseUrl}/huaweicloud-cli-${pkg}.tar.gz"`);
|
|
550
|
+
console.log(` tar -zxvf huaweicloud-cli-${pkg}.tar.gz`);
|
|
551
|
+
console.log(` mv hcloud ~/.local/bin/`);
|
|
552
|
+
console.log(` hcloud version`);
|
|
553
|
+
console.log(`\nFull guide: https://support.huaweicloud.com/qs-hcli/hcli_02_003_02.html`);
|
|
554
|
+
} else if (os === 'darwin') {
|
|
555
|
+
console.log('[macOS] One-liner install:');
|
|
556
|
+
console.log(' curl -sSL https://cn-north-4-hdn-koocli.obs.cn-north-4.myhuaweicloud.com/cli/latest/hcloud_install.sh -o ./hcloud_install.sh && bash ./hcloud_install.sh -y');
|
|
557
|
+
console.log(`\nOr manual: ${arch === 'arm64' ? 'ARM64 (Apple Silicon)' : 'AMD64 (Intel)'}`);
|
|
558
|
+
const pkg = arch === 'arm64' ? 'mac-arm64' : 'mac-amd64';
|
|
559
|
+
console.log(` curl -LO "${baseUrl}/huaweicloud-cli-${pkg}.tar.gz"`);
|
|
560
|
+
console.log(` tar -zxvf huaweicloud-cli-${pkg}.tar.gz`);
|
|
561
|
+
console.log(` mv hcloud /usr/local/bin/`);
|
|
562
|
+
console.log(` hcloud version`);
|
|
563
|
+
console.log(`\nFull guide: https://support.huaweicloud.com/qs-hcli/hcli_02_003_03.html`);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
console.log('\nAfter install, set HCLOUD_BIN if hcloud is not on PATH.');
|
|
567
|
+
console.log('Then run: npx huaweicloud-devkit doctor');
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
async function main() {
|
|
571
|
+
const cmd = process.argv[2] || 'help';
|
|
572
|
+
|
|
573
|
+
switch (cmd) {
|
|
574
|
+
case 'install':
|
|
575
|
+
case 'i':
|
|
576
|
+
await cmdInstall();
|
|
577
|
+
break;
|
|
578
|
+
case 'uninstall':
|
|
579
|
+
case 'remove':
|
|
580
|
+
await cmdUninstall();
|
|
581
|
+
break;
|
|
582
|
+
case 'update':
|
|
583
|
+
case 'upgrade':
|
|
584
|
+
await cmdUpdate();
|
|
585
|
+
break;
|
|
586
|
+
case 'reinstall':
|
|
587
|
+
await cmdReinstall();
|
|
588
|
+
break;
|
|
589
|
+
case 'status':
|
|
590
|
+
case 'info':
|
|
591
|
+
await cmdStatus();
|
|
592
|
+
break;
|
|
593
|
+
case 'doctor':
|
|
594
|
+
case 'check':
|
|
595
|
+
await cmdDoctor();
|
|
596
|
+
break;
|
|
597
|
+
case 'install-hcloud':
|
|
598
|
+
await cmdInstallHcloud();
|
|
599
|
+
break;
|
|
600
|
+
case 'help':
|
|
601
|
+
case '--help':
|
|
602
|
+
case '-h':
|
|
603
|
+
default:
|
|
604
|
+
console.log(BANNER);
|
|
605
|
+
console.log('Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|all>]\n');
|
|
606
|
+
console.log('Commands:');
|
|
607
|
+
console.log(' install Install skills, MCP server, safety policy');
|
|
608
|
+
console.log(' uninstall Remove installed files');
|
|
609
|
+
console.log(' update Update to latest version');
|
|
610
|
+
console.log(' reinstall Full clean reinstall');
|
|
611
|
+
console.log(' status Show installation status');
|
|
612
|
+
console.log(' doctor Self-check: hcloud, MCP, skills, auth');
|
|
613
|
+
console.log(' install-hcloud Show KooCLI install commands for your OS');
|
|
614
|
+
console.log(' help Show this help');
|
|
615
|
+
console.log('\nOptions:');
|
|
616
|
+
console.log(' --target Target agent: opencode (default), codex, all');
|
|
617
|
+
console.log('\nExamples:');
|
|
618
|
+
console.log(' npx huaweicloud-devkit install');
|
|
619
|
+
console.log(' npx huaweicloud-devkit install --target codex');
|
|
620
|
+
console.log(' npx huaweicloud-devkit install --target all');
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
main().catch((e) => {
|
|
626
|
+
console.error(`\x1b[31mError: ${e.message}\x1b[0m`);
|
|
627
|
+
process.exit(1);
|
|
628
|
+
});
|