huaweicloud-devkit 1.1.0-next.19 → 1.1.0-next.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.0-next.19",
3
+ "version": "1.1.0-next.20",
4
4
  "description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
5
5
  "type": "module",
6
6
  "files": [
@@ -17,7 +17,7 @@
17
17
  "mcpServers": "./.mcp.json",
18
18
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
19
19
  "skills": "./skills/",
20
- "version": "1.1.0-next.19",
20
+ "version": "1.1.0-next.20",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
@@ -20,7 +20,7 @@
20
20
  "mcpServers": "./.mcp.json",
21
21
  "description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
22
22
  "skills": "./skills/",
23
- "version": "1.1.0-next.19",
23
+ "version": "1.1.0-next.20",
24
24
  "author": {
25
25
  "name": "HuaweiCloud Mate",
26
26
  "url": "https://github.com/huaweicloud"
@@ -17,7 +17,7 @@
17
17
  "mcpServers": "./.mcp.json",
18
18
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
19
19
  "skills": "./skills/",
20
- "version": "1.1.0-next.19",
20
+ "version": "1.1.0-next.20",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.0-next.19",
3
+ "version": "1.1.0-next.20",
4
4
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
5
  "author": {
6
6
  "name": "HuaweiCloud Mate",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.0-next.19",
3
+ "version": "1.1.0-next.20",
4
4
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
5
  "author": {
6
6
  "name": "HuaweiCloud Mate",
@@ -2,7 +2,7 @@
2
2
  "name": "huaweicloud-devkit",
3
3
  "id": "huaweicloud-devkit",
4
4
  "displayName": "HuaweiCloud DevKit",
5
- "version": "1.1.0-next.19",
5
+ "version": "1.1.0-next.20",
6
6
  "family": "bundle-plugin",
7
7
  "bundleFormat": "codex",
8
8
  "description": "Guide coding agents to use Huawei Cloud safely — KooCLI, APIs, SDKs, 28 MCP tools, skills, and safety guardrails.",
@@ -1,4 +1,5 @@
1
1
  import { spawn } from 'node:child_process';
2
+ import { randomUUID } from 'node:crypto';
2
3
  import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
3
4
  import { homedir } from 'node:os';
4
5
  import { join } from 'node:path';
@@ -9,9 +10,35 @@ import { getProxySettings } from './proxy/proxy-config.mjs';
9
10
  const DEFAULT_TIMEOUT_MS = 60_000;
10
11
  const DEFAULT_FORCE_KILL_AFTER_MS = 2_000;
11
12
  const DEFAULT_MAX_RETRIES = 1;
13
+ const APPROVAL_TTL_MS = 5 * 60_000;
12
14
  const LARGE_OUTPUT_THRESHOLD = 50_000;
13
15
  const OUTPUT_DIR = join('/tmp', 'huaweicloud-devkit');
14
16
 
17
+ const approvalStore = new Map();
18
+
19
+ export function createApprovalToken(rawArgs) {
20
+ const token = randomUUID();
21
+ approvalStore.set(token, { rawArgs, createdAt: Date.now() });
22
+ if (approvalStore.size % 20 === 0) {
23
+ const now = Date.now();
24
+ for (const [k, v] of approvalStore) {
25
+ if (now - v.createdAt > APPROVAL_TTL_MS) approvalStore.delete(k);
26
+ }
27
+ }
28
+ return token;
29
+ }
30
+
31
+ export function consumeApprovalToken(token) {
32
+ const entry = approvalStore.get(token);
33
+ if (!entry) return null;
34
+ if (Date.now() - entry.createdAt > APPROVAL_TTL_MS) {
35
+ approvalStore.delete(token);
36
+ return null;
37
+ }
38
+ approvalStore.delete(token);
39
+ return entry.rawArgs;
40
+ }
41
+
15
42
  function saveLargeOutput(rawStdout) {
16
43
  if (rawStdout.length <= LARGE_OUTPUT_THRESHOLD) return null;
17
44
  try {
@@ -43,6 +70,7 @@ export function planHcloudCommand(args, options = {}) {
43
70
  executableBlock: redactOutput(command),
44
71
  warnings,
45
72
  classification,
73
+ approvalToken: createApprovalToken(normalizedArgs),
46
74
  safeToRun: classification.decision === 'allow',
47
75
  };
48
76
  }
@@ -940,7 +940,24 @@ if [ -f "${outputPath}/qr.png" ]; then
940
940
  echo "qr_code:PASS"
941
941
  PASS=$((PASS+1))
942
942
  else
943
- echo "qr_code:FAIL (no QR code — generate one for cross-platform H5 apps)"
943
+ ${
944
+ isCrossPlatform
945
+ ? `
946
+ if [ -n "$TUNNEL_URL" ]; then
947
+ curl -s "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$TUNNEL_URL" 2>/dev/null)" -o "${outputPath}/qr.png" 2>/dev/null
948
+ chmod o+r "${outputPath}/qr.png" 2>/dev/null || true
949
+ if [ -f "${outputPath}/qr.png" ] && [ -s "${outputPath}/qr.png" ]; then
950
+ echo "qr_code:PASS (auto-generated)"
951
+ PASS=$((PASS+1))
952
+ else
953
+ echo "qr_code:FAIL (QR generation failed)"
954
+ fi
955
+ else
956
+ echo "qr_code:FAIL (no tunnel URL for QR generation)"
957
+ fi
958
+ `
959
+ : `echo "qr_code:FAIL (no QR code — generate one for cross-platform H5 apps)"`
960
+ }
944
961
  fi
945
962
  `}`,
946
963
  `echo "SCORE:\${PASS}/\${TOTAL}"`,
@@ -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';
@@ -224,16 +224,16 @@ export const TOOL_DEFINITIONS = [
224
224
  'Run a write-capable hcloud command only after the exact command has been shown and explicitly approved by the user.',
225
225
  inputSchema: {
226
226
  type: 'object',
227
- required: ['args', 'approvedCommand', 'approvedByUser'],
227
+ required: ['args', 'approvalToken', 'approvedByUser'],
228
228
  properties: {
229
229
  args: {
230
230
  type: 'array',
231
231
  items: { type: 'string' },
232
232
  description: 'hcloud arguments, excluding the hcloud executable.',
233
233
  },
234
- approvedCommand: {
234
+ approvalToken: {
235
235
  type: 'string',
236
- description: 'The exact command string previously shown to the user.',
236
+ description: 'The approvalToken returned by huaweicloud_plan_cli_command.',
237
237
  },
238
238
  approvedByUser: {
239
239
  type: 'boolean',
@@ -1231,15 +1231,17 @@ async function runApprovedCommand(args = {}) {
1231
1231
  if (args.approvedByUser !== true) {
1232
1232
  throw new Error('approvedByUser must be true after explicit user approval for this exact command.');
1233
1233
  }
1234
- const strictPlan = planHcloudCommand(args.args || [], { allowWrites: false });
1235
- const plan = planHcloudCommand(args.args || [], { allowWrites: true });
1236
- const approved = String(args.approvedCommand || '');
1237
- const planned = String(plan.command || '');
1238
- const normalized = (s) => s.replace(/--\S+[= ]<redacted>/g, '--<placeholder>');
1239
- if (normalized(approved) !== normalized(planned)) {
1240
- throw new Error('approvedCommand must exactly match the planned hcloud command.');
1234
+ const token = String(args.approvalToken || '');
1235
+ const storedArgs = consumeApprovalToken(token);
1236
+ if (!storedArgs || storedArgs.length === 0) {
1237
+ throw new Error('Invalid or expired approval token. Please re-plan the command.');
1241
1238
  }
1242
- const result = await runHcloud(args.args || [], {
1239
+ const providedArgs = Array.isArray(args.args) ? args.args.map(String) : [];
1240
+ if (JSON.stringify(storedArgs) !== JSON.stringify(providedArgs)) {
1241
+ throw new Error('Provided args do not match the approved plan. Use the exact args from the plan.');
1242
+ }
1243
+ const strictPlan = planHcloudCommand(providedArgs, { allowWrites: false });
1244
+ const result = await runHcloud(providedArgs, {
1243
1245
  allowWrites: true,
1244
1246
  timeoutMs: args.timeoutMs,
1245
1247
  maxRetries: args.maxRetries,