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.
Files changed (35) hide show
  1. package/README.md +46 -2
  2. package/README.zh-CN.md +45 -2
  3. package/package.json +3 -1
  4. package/plugins/huaweicloud-core/.claude-plugin/plugin.json +1 -1
  5. package/plugins/huaweicloud-core/.codex-plugin/plugin.json +1 -1
  6. package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +1 -1
  7. package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +1 -1
  8. package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +1 -1
  9. package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +29 -12
  10. package/plugins/huaweicloud-core/openclaw.plugin.json +1 -1
  11. package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +136 -24
  12. package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +15 -14
  13. package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +1 -1
  14. package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +5 -1
  15. package/plugins/huaweicloud-core/skills/huawei-ecs/references/hc-activity.md +27 -0
  16. package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +11 -11
  17. package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +6 -5
  18. package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +291 -53
  19. package/plugins/huaweicloud-core/skills/huawei-sandbox/references/nginx-templates.md +4 -0
  20. package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +19 -12
  21. package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +26 -26
  22. package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +12 -6
  23. package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +7 -4
  24. package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +7 -7
  25. package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +14 -0
  26. package/plugins/huaweicloud-core/src/auth/credentials.mjs +30 -1
  27. package/plugins/huaweicloud-core/src/auth/service.mjs +33 -0
  28. package/plugins/huaweicloud-core/src/detect-framework.mjs +25 -3
  29. package/plugins/huaweicloud-core/src/hcloud-cli.mjs +67 -5
  30. package/plugins/huaweicloud-core/src/mcp-server.mjs +23 -0
  31. package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +11 -3
  32. package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +365 -11
  33. package/plugins/huaweicloud-core/src/search-market.mjs +1 -0
  34. package/plugins/huaweicloud-core/src/setup-cli.mjs +491 -64
  35. 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', 'approvedCommand', 'approvedByUser'],
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
- approvedCommand: {
240
+ approvalToken: {
222
241
  type: 'string',
223
- description: 'The exact command string previously shown to the user.',
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: 'Per-command execution timeout in milliseconds (default: 120000)' },
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
- return await hdkitCredentials(args.session_id, args.dev_stage_id, args.enable_sts !== false);
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 strictPlan = planHcloudCommand(args.args || [], { allowWrites: false });
1076
- const plan = planHcloudCommand(args.args || [], { allowWrites: true });
1077
- if (String(args.approvedCommand || '') !== plan.command) {
1078
- throw new Error('approvedCommand must exactly match the planned hcloud command.');
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 result = await runHcloud(args.args || [], {
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,