huaweicloud-devkit 1.1.0-next.8 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -2
- package/README.zh-CN.md +45 -2
- package/package.json +3 -1
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +29 -12
- package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +136 -24
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +15 -14
- package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +1 -1
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +5 -1
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/hc-activity.md +27 -0
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +11 -11
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +6 -5
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +291 -53
- package/plugins/huaweicloud-core/skills/huawei-sandbox/references/nginx-templates.md +4 -0
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +19 -12
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +26 -26
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +12 -6
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +7 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +7 -7
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +14 -0
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +30 -1
- package/plugins/huaweicloud-core/src/auth/service.mjs +33 -0
- package/plugins/huaweicloud-core/src/detect-framework.mjs +25 -3
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +67 -5
- package/plugins/huaweicloud-core/src/mcp-server.mjs +23 -0
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +11 -3
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +365 -11
- package/plugins/huaweicloud-core/src/search-market.mjs +1 -0
- package/plugins/huaweicloud-core/src/setup-cli.mjs +491 -64
- package/plugins/huaweicloud-core/src/tools.mjs +184 -14
|
@@ -6,7 +6,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
6
6
|
|
|
7
7
|
import { evaluateArtifacts, evaluateCommandRisk, evaluateDeployPlan } from './risk-rule-engine.mjs';
|
|
8
8
|
import { classifyTextCommand, redactSecrets } from './safety-policy.mjs';
|
|
9
|
-
import { planHcloudCommand, runHcloud } from './hcloud-cli.mjs';
|
|
9
|
+
import { planHcloudCommand, runHcloud, consumeApprovalToken } from './hcloud-cli.mjs';
|
|
10
10
|
import { searchMarketplace } from './search-market.mjs';
|
|
11
11
|
import { getServiceIcon } from './icon-library.mjs';
|
|
12
12
|
import { detectFramework } from './detect-framework.mjs';
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
closeSession,
|
|
17
17
|
uploadFileWithSession,
|
|
18
18
|
uploadProjectWithSession,
|
|
19
|
+
deployNginx,
|
|
20
|
+
deployCheck,
|
|
19
21
|
getCurrentWorkspaceId,
|
|
20
22
|
setWorkspaceId,
|
|
21
23
|
} from './sandbox/session-manager.mjs';
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
hdkitVoucherStatus,
|
|
28
30
|
hdkitVoucherClaim,
|
|
29
31
|
} from './sandbox/hdkitservice-api.mjs';
|
|
32
|
+
import { getCredentials } from './sandbox/hwlink-api.mjs';
|
|
30
33
|
import { getAuthStatus, syncAuth } from './auth/service.mjs';
|
|
31
34
|
import {
|
|
32
35
|
readGlobalCredentials,
|
|
@@ -45,6 +48,10 @@ function codeartsSkillsDir() {
|
|
|
45
48
|
const home = homedir();
|
|
46
49
|
return join(home, '.codeartsdoer', 'skills');
|
|
47
50
|
}
|
|
51
|
+
function codeartsWorkSkillsDir() {
|
|
52
|
+
const home = homedir();
|
|
53
|
+
return join(home, '.codeartswork', 'skills');
|
|
54
|
+
}
|
|
48
55
|
function workbuddySkillsDir() {
|
|
49
56
|
const home = homedir();
|
|
50
57
|
return join(home, '.workbuddy', 'skills');
|
|
@@ -99,6 +106,15 @@ function hermesSkillsDir() {
|
|
|
99
106
|
return join(home, '.hermes', 'skills');
|
|
100
107
|
}
|
|
101
108
|
|
|
109
|
+
function atomcodeSkillsDir() {
|
|
110
|
+
const home = process.env.ATOMCODE_HOME || homedir();
|
|
111
|
+
return join(home, '.atomcode', 'skills');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function codexDesktopSkillsDir() {
|
|
115
|
+
return join(homedir(), '.agents', 'skills');
|
|
116
|
+
}
|
|
117
|
+
|
|
102
118
|
export function listSkillDirs(root) {
|
|
103
119
|
if (!existsSync(root)) return [];
|
|
104
120
|
try {
|
|
@@ -123,10 +139,13 @@ function resolveSkillsRoot() {
|
|
|
123
139
|
SKILLS_ROOT_DEV,
|
|
124
140
|
dshSkillsDir(),
|
|
125
141
|
codeartsSkillsDir(),
|
|
142
|
+
codeartsWorkSkillsDir(),
|
|
126
143
|
opencodeSkillsDir(),
|
|
127
144
|
workbuddySkillsDir(),
|
|
128
145
|
officeaceSkillsRoot(),
|
|
129
146
|
hermesSkillsDir(),
|
|
147
|
+
atomcodeSkillsDir(),
|
|
148
|
+
codexDesktopSkillsDir(),
|
|
130
149
|
]) || SKILLS_ROOT_DEV
|
|
131
150
|
);
|
|
132
151
|
}
|
|
@@ -211,16 +230,16 @@ export const TOOL_DEFINITIONS = [
|
|
|
211
230
|
'Run a write-capable hcloud command only after the exact command has been shown and explicitly approved by the user.',
|
|
212
231
|
inputSchema: {
|
|
213
232
|
type: 'object',
|
|
214
|
-
required: ['args', '
|
|
233
|
+
required: ['args', 'approvalToken', 'approvedByUser'],
|
|
215
234
|
properties: {
|
|
216
235
|
args: {
|
|
217
236
|
type: 'array',
|
|
218
237
|
items: { type: 'string' },
|
|
219
238
|
description: 'hcloud arguments, excluding the hcloud executable.',
|
|
220
239
|
},
|
|
221
|
-
|
|
240
|
+
approvalToken: {
|
|
222
241
|
type: 'string',
|
|
223
|
-
description: 'The
|
|
242
|
+
description: 'The approvalToken returned by huaweicloud_plan_cli_command.',
|
|
224
243
|
},
|
|
225
244
|
approvedByUser: {
|
|
226
245
|
type: 'boolean',
|
|
@@ -446,7 +465,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
446
465
|
target: {
|
|
447
466
|
type: 'string',
|
|
448
467
|
description:
|
|
449
|
-
'Agent target to check: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, hermes, openclaw, atomcode, or all (default).',
|
|
468
|
+
'Agent target to check: opencode, codex, codex-desktop, codearts, codearts-work, workbuddy, dsh, officeace, hermes, openclaw, atomcode, or all (default).',
|
|
450
469
|
},
|
|
451
470
|
},
|
|
452
471
|
},
|
|
@@ -461,7 +480,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
461
480
|
target: {
|
|
462
481
|
type: 'string',
|
|
463
482
|
description:
|
|
464
|
-
'Agent target to report after sync: opencode, codex, codex-desktop, codearts, workbuddy, dsh, officeace, hermes, or all (default).',
|
|
483
|
+
'Agent target to report after sync: opencode, codex, codex-desktop, codearts, codearts-work, workbuddy, dsh, officeace, hermes, or all (default).',
|
|
465
484
|
},
|
|
466
485
|
},
|
|
467
486
|
},
|
|
@@ -549,7 +568,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
549
568
|
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
550
569
|
},
|
|
551
570
|
username: { type: 'string', description: 'Login username (default: root)' },
|
|
552
|
-
timeout_ms: { type: 'number', description: '
|
|
571
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 60000)' },
|
|
553
572
|
},
|
|
554
573
|
},
|
|
555
574
|
},
|
|
@@ -586,6 +605,72 @@ export const TOOL_DEFINITIONS = [
|
|
|
586
605
|
},
|
|
587
606
|
},
|
|
588
607
|
},
|
|
608
|
+
{
|
|
609
|
+
name: 'huaweicloud_sandbox_deploy_nginx',
|
|
610
|
+
description:
|
|
611
|
+
'Deploy an nginx configuration on the sandbox and reload. Takes nginxType, port, project, outputDir from framework detection and writes the correct template (SPA try_files, SSR reverse proxy, or static). Also fixes directory traverse permissions on the project path. Use this instead of manually constructing nginx config — it handles permissions, template selection, and reload in one call.',
|
|
612
|
+
inputSchema: {
|
|
613
|
+
type: 'object',
|
|
614
|
+
required: ['nginx_type', 'port', 'project', 'output_dir'],
|
|
615
|
+
properties: {
|
|
616
|
+
nginx_type: {
|
|
617
|
+
type: 'string',
|
|
618
|
+
description:
|
|
619
|
+
'Nginx config type from framework detection: spa (try_files fallback for SPA/SSG/cross-platform), proxy (reverse proxy for SSR), or static (plain root for Hugo/Hexo).',
|
|
620
|
+
enum: ['spa', 'proxy', 'static'],
|
|
621
|
+
},
|
|
622
|
+
port: { type: 'number', description: 'Listen port (from framework detection).' },
|
|
623
|
+
project: { type: 'string', description: 'Project directory name under /workspace, e.g. movie-ticket.' },
|
|
624
|
+
output_dir: {
|
|
625
|
+
type: 'string',
|
|
626
|
+
description: 'Build output directory relative to /workspace/<project>, e.g. dist/build/h5.',
|
|
627
|
+
},
|
|
628
|
+
node_port: { type: 'number', description: 'Node.js app port for SSR (required when nginx_type=proxy).' },
|
|
629
|
+
public_port: { type: 'number', description: 'Public listen port for SSR proxy (optional, defaults to port).' },
|
|
630
|
+
config_name: {
|
|
631
|
+
type: 'string',
|
|
632
|
+
description:
|
|
633
|
+
'Config file name (without .conf suffix). Defaults to the project name, ensuring each project gets its own config. Override with distinct names (e.g. admin, docs) for sub-app deployments.',
|
|
634
|
+
},
|
|
635
|
+
workspace_id: {
|
|
636
|
+
type: 'string',
|
|
637
|
+
description:
|
|
638
|
+
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
639
|
+
},
|
|
640
|
+
username: { type: 'string', description: 'Login username (default: root)' },
|
|
641
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 30000)' },
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
name: 'huaweicloud_sandbox_deploy_check',
|
|
647
|
+
description:
|
|
648
|
+
'Run a deployment completeness check on the sandbox. Verifies nginx is serving, output directory exists, DevBridge tunnel is active and accessible, and QR code exists (cross-platform). Returns a score and nextStep to fix any missing items. Call this at the end of a deployment workflow to confirm everything is working before reporting success.',
|
|
649
|
+
inputSchema: {
|
|
650
|
+
type: 'object',
|
|
651
|
+
required: ['port', 'project', 'output_dir'],
|
|
652
|
+
properties: {
|
|
653
|
+
port: { type: 'number', description: 'App listen port (from framework detection).' },
|
|
654
|
+
project: { type: 'string', description: 'Project directory name under /workspace.' },
|
|
655
|
+
output_dir: {
|
|
656
|
+
type: 'string',
|
|
657
|
+
description: 'Build output directory relative to /workspace/<project>, e.g. dist/build/h5.',
|
|
658
|
+
},
|
|
659
|
+
framework_type: {
|
|
660
|
+
type: 'string',
|
|
661
|
+
description: 'Framework type from detect_framework. Set to cross-platform for QR code check.',
|
|
662
|
+
enum: ['spa', 'ssr', 'ssg', 'cross-platform', 'monorepo', 'static'],
|
|
663
|
+
},
|
|
664
|
+
workspace_id: {
|
|
665
|
+
type: 'string',
|
|
666
|
+
description:
|
|
667
|
+
'Workspace ID from huaweicloud_sandbox_connect return value. Required - must be passed explicitly when HW_WORKSPACE_ID is not set.',
|
|
668
|
+
},
|
|
669
|
+
username: { type: 'string', description: 'Login username (default: root)' },
|
|
670
|
+
timeout_ms: { type: 'number', description: 'Execution timeout in milliseconds (default: 30000)' },
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
},
|
|
589
674
|
{
|
|
590
675
|
name: 'huaweicloud_sandbox_check_user',
|
|
591
676
|
description:
|
|
@@ -824,6 +909,59 @@ export async function callTool(name, args = {}) {
|
|
|
824
909
|
},
|
|
825
910
|
);
|
|
826
911
|
}
|
|
912
|
+
case 'huaweicloud_sandbox_deploy_nginx': {
|
|
913
|
+
if (!args.nginx_type || !args.port || !args.project || !args.output_dir) {
|
|
914
|
+
throw new Error('nginx_type, port, project, and output_dir are required.');
|
|
915
|
+
}
|
|
916
|
+
const sandboxWsId7 = args.workspace_id || getCurrentWorkspaceId();
|
|
917
|
+
if (!sandboxWsId7) {
|
|
918
|
+
throw new Error(
|
|
919
|
+
'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
|
|
920
|
+
'or set HW_WORKSPACE_ID environment variable before starting the agent.',
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
const sandboxUser7 = args.username || 'root';
|
|
924
|
+
const sandboxTimeout7 = args.timeout_ms || 60000;
|
|
925
|
+
return await deployNginx(
|
|
926
|
+
sandboxWsId7,
|
|
927
|
+
{
|
|
928
|
+
nginxType: args.nginx_type,
|
|
929
|
+
port: args.port,
|
|
930
|
+
project: args.project,
|
|
931
|
+
outputDir: args.output_dir,
|
|
932
|
+
nodePort: args.node_port,
|
|
933
|
+
publicPort: args.public_port,
|
|
934
|
+
configName: args.config_name,
|
|
935
|
+
},
|
|
936
|
+
sandboxUser7,
|
|
937
|
+
sandboxTimeout7,
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
case 'huaweicloud_sandbox_deploy_check': {
|
|
941
|
+
if (!args.port || !args.project || !args.output_dir) {
|
|
942
|
+
throw new Error('port, project, and output_dir are required.');
|
|
943
|
+
}
|
|
944
|
+
const sandboxWsId8 = args.workspace_id || getCurrentWorkspaceId();
|
|
945
|
+
if (!sandboxWsId8) {
|
|
946
|
+
throw new Error(
|
|
947
|
+
'workspace_id is required. No sandbox connected — call huaweicloud_sandbox_connect first, ' +
|
|
948
|
+
'or set HW_WORKSPACE_ID environment variable before starting the agent.',
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
const sandboxUser8 = args.username || 'root';
|
|
952
|
+
const sandboxTimeout8 = args.timeout_ms || 30000;
|
|
953
|
+
return await deployCheck(
|
|
954
|
+
sandboxWsId8,
|
|
955
|
+
{
|
|
956
|
+
port: args.port,
|
|
957
|
+
project: args.project,
|
|
958
|
+
outputDir: args.output_dir,
|
|
959
|
+
frameworkType: args.framework_type,
|
|
960
|
+
},
|
|
961
|
+
sandboxUser8,
|
|
962
|
+
sandboxTimeout8,
|
|
963
|
+
);
|
|
964
|
+
}
|
|
827
965
|
case 'huaweicloud_sandbox_check_user':
|
|
828
966
|
return await hdkitCheckUser();
|
|
829
967
|
case 'huaweicloud_sandbox_sign_agreement':
|
|
@@ -833,11 +971,38 @@ export async function callTool(name, args = {}) {
|
|
|
833
971
|
const devStageId = connectResult?.dev_stage_id || connectResult?.devStageId;
|
|
834
972
|
if (devStageId) {
|
|
835
973
|
setWorkspaceId(devStageId);
|
|
974
|
+
try {
|
|
975
|
+
await execOneShot(devStageId, 'devbridge delete-all 2>/dev/null || true', 'root', 15000);
|
|
976
|
+
} catch {}
|
|
836
977
|
}
|
|
837
978
|
return connectResult;
|
|
838
979
|
}
|
|
839
|
-
case 'huaweicloud_sandbox_credentials':
|
|
840
|
-
|
|
980
|
+
case 'huaweicloud_sandbox_credentials': {
|
|
981
|
+
const devStageId = args.dev_stage_id || getCurrentWorkspaceId();
|
|
982
|
+
const credResult = await hdkitCredentials(args.session_id, devStageId, args.enable_sts !== false);
|
|
983
|
+
const sandboxWsIdCred = args.dev_stage_id || getCurrentWorkspaceId();
|
|
984
|
+
if (sandboxWsIdCred) {
|
|
985
|
+
try {
|
|
986
|
+
const { ak, sk, securitytoken } = getCredentials();
|
|
987
|
+
const credsScript = [
|
|
988
|
+
`export HW_ACCESS_KEY='${ak}'`,
|
|
989
|
+
`export HW_SECRET_KEY='${sk}'`,
|
|
990
|
+
securitytoken ? `export HW_SECURITY_TOKEN='${securitytoken}'` : '',
|
|
991
|
+
securitytoken ? `export X_HW_SECURITY_TOKEN='${securitytoken}'` : '',
|
|
992
|
+
]
|
|
993
|
+
.filter(Boolean)
|
|
994
|
+
.join('\n');
|
|
995
|
+
const credsFile = '/tmp/hw_creds.sh';
|
|
996
|
+
await execOneShot(
|
|
997
|
+
sandboxWsIdCred,
|
|
998
|
+
`cat > ${credsFile} << 'HWCREDS_EOF'\n${credsScript}\nHWCREDS_EOF\nchmod 600 ${credsFile}`,
|
|
999
|
+
'root',
|
|
1000
|
+
15000,
|
|
1001
|
+
);
|
|
1002
|
+
} catch {}
|
|
1003
|
+
}
|
|
1004
|
+
return credResult;
|
|
1005
|
+
}
|
|
841
1006
|
case 'huaweicloud_voucher_status':
|
|
842
1007
|
return await hdkitVoucherStatus(args.domain_id);
|
|
843
1008
|
case 'huaweicloud_voucher_claim':
|
|
@@ -1072,12 +1237,17 @@ async function runApprovedCommand(args = {}) {
|
|
|
1072
1237
|
if (args.approvedByUser !== true) {
|
|
1073
1238
|
throw new Error('approvedByUser must be true after explicit user approval for this exact command.');
|
|
1074
1239
|
}
|
|
1075
|
-
const
|
|
1076
|
-
const
|
|
1077
|
-
if (
|
|
1078
|
-
throw new Error('
|
|
1240
|
+
const token = String(args.approvalToken || '');
|
|
1241
|
+
const storedArgs = consumeApprovalToken(token);
|
|
1242
|
+
if (!storedArgs || storedArgs.length === 0) {
|
|
1243
|
+
throw new Error('Invalid or expired approval token. Please re-plan the command.');
|
|
1244
|
+
}
|
|
1245
|
+
const providedArgs = Array.isArray(args.args) ? args.args.map(String) : [];
|
|
1246
|
+
if (JSON.stringify(storedArgs) !== JSON.stringify(providedArgs)) {
|
|
1247
|
+
throw new Error('Provided args do not match the approved plan. Use the exact args from the plan.');
|
|
1079
1248
|
}
|
|
1080
|
-
const
|
|
1249
|
+
const strictPlan = planHcloudCommand(providedArgs, { allowWrites: false });
|
|
1250
|
+
const result = await runHcloud(providedArgs, {
|
|
1081
1251
|
allowWrites: true,
|
|
1082
1252
|
timeoutMs: args.timeoutMs,
|
|
1083
1253
|
maxRetries: args.maxRetries,
|