huaweicloud-devkit 0.1.25-dev.0 → 0.1.26-dev.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 +5 -2
- package/integrations/opencode/opencode.json +1 -1
- package/integrations/opencode/skills/huaweicloud-core/SKILL.md +2 -0
- package/package.json +1 -1
- package/plugins/huaweicloud-core/.mcp.json +1 -1
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +48 -0
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +178 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +4 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +20 -3
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +28 -1
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +0 -1
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +2 -2
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +2 -11
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +4 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +14 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +5 -3
- package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +137 -0
- package/plugins/huaweicloud-core/src/safety-policy.mjs +26 -13
- package/plugins/huaweicloud-core/src/setup-cli.mjs +126 -65
- package/plugins/huaweicloud-core/src/tools.mjs +156 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { planHcloudCommand, runHcloud } from './hcloud-cli.mjs';
|
|
2
2
|
import { classifyTextCommand, redactSecrets } from './safety-policy.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import { evaluateArtifacts, evaluateCommandRisk, evaluateDeployPlan } from './risk-rule-engine.mjs';
|
|
4
|
+
import { readFileSync, readdirSync, existsSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
4
5
|
import { join, dirname } from 'node:path';
|
|
5
6
|
import { fileURLToPath } from 'node:url';
|
|
6
7
|
import { homedir } from 'node:os';
|
|
@@ -144,6 +145,51 @@ export const TOOL_DEFINITIONS = [
|
|
|
144
145
|
},
|
|
145
146
|
},
|
|
146
147
|
},
|
|
148
|
+
{
|
|
149
|
+
name: 'huaweicloud_hook_check_command',
|
|
150
|
+
description: 'Check a planned shell or hcloud command against Huawei Cloud hook risk rules without executing it.',
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: 'object',
|
|
153
|
+
required: ['command'],
|
|
154
|
+
properties: {
|
|
155
|
+
command: { type: 'string', description: 'The exact command text to inspect.' },
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'huaweicloud_hook_check_artifacts',
|
|
161
|
+
description: 'Check generated code, IaC, policy, or config artifacts against Huawei Cloud hook risk rules.',
|
|
162
|
+
inputSchema: {
|
|
163
|
+
type: 'object',
|
|
164
|
+
required: ['artifacts'],
|
|
165
|
+
properties: {
|
|
166
|
+
artifacts: {
|
|
167
|
+
type: 'array',
|
|
168
|
+
items: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
required: ['path', 'content'],
|
|
171
|
+
properties: {
|
|
172
|
+
path: { type: 'string' },
|
|
173
|
+
content: { type: 'string' },
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: 'huaweicloud_hook_check_deploy_plan',
|
|
182
|
+
description: 'Check a structured or textual deployment plan for Huawei Cloud sandbox, exposure, IAM, and cost risks.',
|
|
183
|
+
inputSchema: {
|
|
184
|
+
type: 'object',
|
|
185
|
+
required: ['plan'],
|
|
186
|
+
properties: {
|
|
187
|
+
plan: {
|
|
188
|
+
description: 'Deployment plan as an object, array, or string.',
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
147
193
|
{
|
|
148
194
|
name: 'huaweicloud_service_catalog',
|
|
149
195
|
description: 'Return the recommended capability sources for Huawei Cloud agent tasks.',
|
|
@@ -224,6 +270,16 @@ export const TOOL_DEFINITIONS = [
|
|
|
224
270
|
},
|
|
225
271
|
},
|
|
226
272
|
},
|
|
273
|
+
{
|
|
274
|
+
name: 'huaweicloud_setup_obs_config',
|
|
275
|
+
description: 'Synchronize KooCLI credentials to OBS config (~/.obsutilconfig). KooCLI and OBS use separate credential stores — hcloud commands work fine but OBS commands fail with "Please set ak, sk" unless this sync is done. Run this once to enable OBS operations; re-run after changing hcloud credentials.',
|
|
276
|
+
inputSchema: {
|
|
277
|
+
type: 'object',
|
|
278
|
+
properties: {
|
|
279
|
+
profile: { type: 'string', description: 'Optional KooCLI profile name. Uses the active profile by default.' },
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
},
|
|
227
283
|
];
|
|
228
284
|
|
|
229
285
|
export async function callTool(name, args = {}) {
|
|
@@ -245,6 +301,12 @@ export async function callTool(name, args = {}) {
|
|
|
245
301
|
return runApprovedCommand(args);
|
|
246
302
|
case 'huaweicloud_show_profile_redacted':
|
|
247
303
|
return showProfileRedacted(args.profile);
|
|
304
|
+
case 'huaweicloud_hook_check_command':
|
|
305
|
+
return hookResult(evaluateCommandRisk(args.command || ''));
|
|
306
|
+
case 'huaweicloud_hook_check_artifacts':
|
|
307
|
+
return hookResult(evaluateArtifacts(args.artifacts || []));
|
|
308
|
+
case 'huaweicloud_hook_check_deploy_plan':
|
|
309
|
+
return hookResult(evaluateDeployPlan(args.plan || {}));
|
|
248
310
|
case 'huaweicloud_service_catalog':
|
|
249
311
|
return serviceCatalog(args.intent);
|
|
250
312
|
case 'huaweicloud_search_docs':
|
|
@@ -259,11 +321,26 @@ export async function callTool(name, args = {}) {
|
|
|
259
321
|
return explainError(args);
|
|
260
322
|
case 'huaweicloud_search_marketplace':
|
|
261
323
|
return searchMarketplace(args.query || '', args.category || '');
|
|
324
|
+
case 'huaweicloud_setup_obs_config':
|
|
325
|
+
return setupObsConfig(args.profile);
|
|
262
326
|
default:
|
|
263
327
|
throw new Error(`Unknown tool: ${name}`);
|
|
264
328
|
}
|
|
265
329
|
}
|
|
266
330
|
|
|
331
|
+
function hookResult(result) {
|
|
332
|
+
return {
|
|
333
|
+
ok: result.decision !== 'deny',
|
|
334
|
+
decision: result.decision,
|
|
335
|
+
findings: result.findings,
|
|
336
|
+
nextStep: result.decision === 'deny'
|
|
337
|
+
? 'Revise the command, artifact, or deployment plan before execution.'
|
|
338
|
+
: result.decision === 'warn'
|
|
339
|
+
? 'Review the warnings with the user before proceeding.'
|
|
340
|
+
: 'No Huawei Cloud hook risk rule matched.',
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
267
344
|
export async function runVersionCheck(options = {}) {
|
|
268
345
|
const result = await runHcloud(['version'], {
|
|
269
346
|
...options,
|
|
@@ -312,6 +389,84 @@ async function showProfileRedacted(profile) {
|
|
|
312
389
|
};
|
|
313
390
|
}
|
|
314
391
|
|
|
392
|
+
async function setupObsConfig(profile) {
|
|
393
|
+
const obsConfigPath = join(homedir(), '.obsutilconfig');
|
|
394
|
+
if (existsSync(obsConfigPath)) {
|
|
395
|
+
return { ok: true, existed: true, path: obsConfigPath, note: 'OBS config already exists. Delete ~/.obsutilconfig first if you need to re-sync.' };
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const args = ['configure', 'show'];
|
|
399
|
+
if (profile) args.push('--cli-profile', String(profile));
|
|
400
|
+
const result = await runHcloud(args, { allowWrites: false, allowCredentialRead: true });
|
|
401
|
+
|
|
402
|
+
if (!result.ok) {
|
|
403
|
+
return {
|
|
404
|
+
ok: false,
|
|
405
|
+
error: 'Failed to read hcloud profile.',
|
|
406
|
+
detail: result.error || result.stderr || 'hcloud not installed or not configured',
|
|
407
|
+
nextStep: 'Run "hcloud configure init" outside agent chat, then retry.',
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
let accessKeyId = '';
|
|
412
|
+
let secretAccessKey = '';
|
|
413
|
+
let region = '';
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
const parsed = typeof result.stdout === 'string' ? JSON.parse(result.stdout) : result.stdout;
|
|
417
|
+
const cred = parsed.currentCredential || {};
|
|
418
|
+
accessKeyId = cred.accessKeyId || cred.ak || cred.access_key || '';
|
|
419
|
+
secretAccessKey = cred.secretAccessKey || cred.sk || cred.secret_key || '';
|
|
420
|
+
region = parsed.currentRegion || parsed.region || '';
|
|
421
|
+
} catch {
|
|
422
|
+
return {
|
|
423
|
+
ok: false,
|
|
424
|
+
error: 'Failed to parse hcloud profile output.',
|
|
425
|
+
detail: 'hcloud configure show returned unexpected format',
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
430
|
+
return {
|
|
431
|
+
ok: false,
|
|
432
|
+
error: 'No credentials found in hcloud profile.',
|
|
433
|
+
nextStep: 'Run "hcloud configure init" outside agent chat to set up credentials first.',
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (!region) {
|
|
438
|
+
return {
|
|
439
|
+
ok: false,
|
|
440
|
+
error: 'No region found in hcloud profile.',
|
|
441
|
+
nextStep: 'Run "hcloud configure set --cli-region=<region>" outside agent chat to set a default region.',
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const endpoint = `https://obs.${region}.myhuaweicloud.com`;
|
|
446
|
+
const configContent = `[default]\r\nendpoint=${endpoint}\r\nak=${accessKeyId}\r\nsk=${secretAccessKey}\r\n`;
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
writeFileSync(obsConfigPath, configContent, { encoding: 'utf8', mode: 0o600 });
|
|
450
|
+
} catch (e) {
|
|
451
|
+
return {
|
|
452
|
+
ok: false,
|
|
453
|
+
error: 'Failed to write OBS config file.',
|
|
454
|
+
detail: e.message,
|
|
455
|
+
path: obsConfigPath,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
ok: true,
|
|
461
|
+
existed: false,
|
|
462
|
+
created: true,
|
|
463
|
+
path: obsConfigPath,
|
|
464
|
+
region,
|
|
465
|
+
endpoint,
|
|
466
|
+
note: 'OBS credentials synced from hcloud profile. OBS commands (hcloud OBS ls, mb, cp, etc.) should now work.',
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
315
470
|
const SERVICE_EXAMPLES = {
|
|
316
471
|
ECS: { list: 'ECS ListServersDetails', create: 'ECS CreateServers', show: 'IMS GlanceShowImage' },
|
|
317
472
|
VPC: { list: 'VPC ListVpcs', create: 'VPC CreateVpc', show: 'VPC ShowVpc' },
|