@kenhkl/agent-moss 0.15.2 → 0.15.4

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/dist/cli.d.ts CHANGED
@@ -1 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * AgentMoss CLI — TypeScript version.
4
+ * Supports: init | analyze | server
5
+ */
6
+ /** 命令模式核心:把一条 shell 命令当 a_next 分析,输出 Deny/Allow(--json 时完整
7
+ * JSON),返回退出码(Allow=0 / Deny=1)。导出供测试直接调用(CLI 分支再 process.exit)。
8
+ * 零文件读写——镜像 Python cli.py 的 _analyze_command。 */
9
+ declare function analyzeRawCommand(command: string, useJson?: boolean): Promise<number>;
10
+
11
+ export { analyzeRawCommand };
package/dist/cli.js CHANGED
@@ -1439,6 +1439,7 @@ __export(runtime_config_exports, {
1439
1439
  getRuntimeConfigPath: () => getRuntimeConfigPath,
1440
1440
  getScenarioRules: () => getScenarioRules,
1441
1441
  hasRuntimeConfig: () => hasRuntimeConfig,
1442
+ invalidateRuntimeConfigCache: () => invalidateRuntimeConfigCache,
1442
1443
  isL2CategoryEnabled: () => isL2CategoryEnabled,
1443
1444
  isLayerEnabled: () => isLayerEnabled,
1444
1445
  loadRuntimeConfig: () => loadRuntimeConfig,
@@ -1457,7 +1458,7 @@ __export(runtime_config_exports, {
1457
1458
  updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
1458
1459
  updateSkillEnabled: () => updateSkillEnabled
1459
1460
  });
1460
- import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
1461
+ import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3, statSync } from "fs";
1461
1462
  import { join as join3 } from "path";
1462
1463
  import { fileURLToPath as fileURLToPath5 } from "url";
1463
1464
  import { dirname as dirname3 } from "path";
@@ -1474,6 +1475,7 @@ function saveRuntimeConfig(config) {
1474
1475
  const dir = p.substring(0, p.lastIndexOf("/"));
1475
1476
  if (dir && !existsSync4(dir)) mkdirSync(dir, { recursive: true });
1476
1477
  writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
1478
+ invalidateRuntimeConfigCache();
1477
1479
  }
1478
1480
  function emptyRuntimeConfig() {
1479
1481
  return {
@@ -1487,6 +1489,9 @@ function emptyRuntimeConfig() {
1487
1489
  scenario_rules: {}
1488
1490
  };
1489
1491
  }
1492
+ function invalidateRuntimeConfigCache() {
1493
+ runtimeConfigCache = null;
1494
+ }
1490
1495
  function loadRuntimeConfig() {
1491
1496
  const p = getRuntimeConfigPath();
1492
1497
  if (!existsSync4(p)) {
@@ -1498,9 +1503,14 @@ function loadRuntimeConfig() {
1498
1503
  return seeded;
1499
1504
  }
1500
1505
  try {
1506
+ const st = statSync(p);
1507
+ const cacheKey = `${p}|${st.mtimeMs}|${st.size}`;
1508
+ if (runtimeConfigCache && runtimeConfigCache.key === cacheKey) {
1509
+ return runtimeConfigCache.value;
1510
+ }
1501
1511
  const data = JSON.parse(readFileSync4(p, "utf-8"));
1502
1512
  const merged = mergeConfig(data, generateSourceDefaults());
1503
- return {
1513
+ const result = {
1504
1514
  version: data.version || 1,
1505
1515
  layers: {
1506
1516
  L1_heuristic: data.layers?.L1_heuristic ?? true,
@@ -1514,6 +1524,8 @@ function loadRuntimeConfig() {
1514
1524
  agent_rules: merged.agent_rules || {},
1515
1525
  scenario_rules: merged.scenario_rules || {}
1516
1526
  };
1527
+ runtimeConfigCache = { key: cacheKey, value: result };
1528
+ return result;
1517
1529
  } catch {
1518
1530
  return emptyRuntimeConfig();
1519
1531
  }
@@ -2503,7 +2515,7 @@ function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
2503
2515
  saveRuntimeConfig(runtime);
2504
2516
  return runtime;
2505
2517
  }
2506
- var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
2518
+ var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, runtimeConfigCache, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
2507
2519
  var init_runtime_config = __esm({
2508
2520
  "src/infra/runtime-config.ts"() {
2509
2521
  "use strict";
@@ -2533,6 +2545,7 @@ var init_runtime_config = __esm({
2533
2545
  deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
2534
2546
  deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
2535
2547
  };
2548
+ runtimeConfigCache = null;
2536
2549
  _AGENTS_DIR_PROD = join3(__dirname4, "agents");
2537
2550
  _AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
2538
2551
  _SCENARIOS_DIR_PROD = join3(__dirname4, "scenarios");
@@ -2827,6 +2840,56 @@ function isInlineScriptCommand(actionDetail) {
2827
2840
  const re = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
2828
2841
  return re.test(actionDetail);
2829
2842
  }
2843
+ function resolvePathTokens(segment) {
2844
+ const out = [];
2845
+ const low = segment.toLowerCase();
2846
+ for (const m of low.matchAll(HOME_PREFIX_RE)) {
2847
+ const prefix = m[0];
2848
+ const endIdx = (m.index ?? 0) + m[0].length;
2849
+ const tail = low.slice(endIdx);
2850
+ if (!tail || !(tail[0] === "/" || tail[0] === " ")) {
2851
+ out.push([prefix, prefix]);
2852
+ continue;
2853
+ }
2854
+ const token = tail.trim().split(/\s+/)[0] || "";
2855
+ if (!token.startsWith("/")) continue;
2856
+ const cleaned = token.replace(/\/+$/, "").replace(/^['"]|['"]$/g, "");
2857
+ const segments = cleaned.split("/").filter((s) => s && s !== ".");
2858
+ if (!segments.some((s) => s === "..")) continue;
2859
+ let depth = 0;
2860
+ const kept = [];
2861
+ let escaped = false;
2862
+ for (const s of segments) {
2863
+ if (s === "..") {
2864
+ depth -= 1;
2865
+ if (kept.length) kept.pop();
2866
+ if (depth < 0) {
2867
+ escaped = true;
2868
+ depth = 0;
2869
+ }
2870
+ } else {
2871
+ depth += 1;
2872
+ kept.push(s);
2873
+ }
2874
+ }
2875
+ let resolved = kept.length ? "~/" + kept.join("/") : "~";
2876
+ if (escaped) resolved += "\uFF08\u26A0\uFE0F .. \u5F39\u6808\u8D85\u51FA\u4E3B\u76EE\u5F55\u6839\uFF0C\u5B9E\u9645\u76EE\u6807\u5728\u4E3B\u76EE\u5F55\u4E4B\u5916\uFF09";
2877
+ out.push([prefix + token, resolved]);
2878
+ }
2879
+ return out;
2880
+ }
2881
+ function buildPathContext(actionDetail) {
2882
+ if (!PATH_CONTEXT_TRIGGER_RE.test(actionDetail)) return "";
2883
+ const stripped = stripLiteralTextSegments(actionDetail);
2884
+ const lines = [];
2885
+ for (const seg of splitCommandSegments(stripped)) {
2886
+ for (const [raw, lex] of resolvePathTokens(seg)) {
2887
+ lines.push(`- \u547D\u4EE4\u6BB5 \`${seg.trim().slice(0, 60)}\`\uFF1A\`${raw}\` \u8BCD\u6CD5\u89E3\u6790\u4E3A \`${lex}\``);
2888
+ }
2889
+ }
2890
+ if (!lines.length) return "";
2891
+ return "## \u8DEF\u5F84\u89E3\u6790\u4E8B\u5B9E\uFF08\u8BCD\u6CD5\u5206\u6790\uFF0C\u786E\u5B9A\u6027\u7ED3\u8BBA\uFF09\n" + lines.join("\n") + "\n\n## \u8FD0\u884C\u65F6\u672A\u9A8C\u8BC1\uFF08\u4F9B\u53C2\u8003\uFF0C\u975E\u4E8B\u5B9E\uFF09\n- \u5206\u6790\u5668\u4E0D\u8BFB\u6587\u4EF6\u7CFB\u7EDF\uFF1A\u65E0\u6CD5\u786E\u8BA4\u8DEF\u5F84\u4E2D\u7684\u4E2D\u95F4\u76EE\u5F55\u662F\u5426\u4E3A\u7B26\u53F7\u94FE\u63A5\uFF1B\u82E5\u4E2D\u95F4\u6BB5\u5728\u6267\u884C\u65F6\u662F\uFF08\u6216\u88AB\u66FF\u6362\u4E3A\uFF09\u6307\u5411\u4E3B\u76EE\u5F55\u5916\u7684\u8F6F\u94FE\uFF0C\u5B9E\u9645\u64CD\u4F5C\u76EE\u6807\u53EF\u80FD\u968F\u4E4B\u6539\u53D8\u3002\u8BCD\u6CD5\u89E3\u6790\u7ED3\u679C\u57FA\u4E8E\u6807\u51C6\u8DEF\u5F84\u8BED\u4E49\uFF0C\u4E0D\u5E94\u56E0\u731C\u6D4B\u300C\u89E3\u6790\u4F9D\u8D56\u67D0\u6BB5\u662F\u5426\u5B58\u5728\u300D\u800C\u5426\u5B9A\u5B83\u3002";
2892
+ }
2830
2893
  function stripLiteralTextSegments(actionDetail) {
2831
2894
  const writeScriptExec = />\s*[/~]\S*\.(sh|py|pl|rb)\b/.test(actionDetail) && /(?:&&|\|\||;|\n|\|)\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)/.test(actionDetail);
2832
2895
  const pipeToInterpreter = /\|\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)\b/.test(actionDetail);
@@ -3353,7 +3416,7 @@ function checkLateralMovement(actionDetail) {
3353
3416
  }
3354
3417
  return null;
3355
3418
  }
3356
- var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
3419
+ var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
3357
3420
  var init_heuristic = __esm({
3358
3421
  "src/engine/heuristic.ts"() {
3359
3422
  "use strict";
@@ -3381,6 +3444,8 @@ var init_heuristic = __esm({
3381
3444
  description: c.reason
3382
3445
  }));
3383
3446
  })();
3447
+ PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
3448
+ HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
3384
3449
  SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
3385
3450
  SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
3386
3451
  HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
@@ -5112,7 +5177,7 @@ var init_tracker = __esm({
5112
5177
  });
5113
5178
 
5114
5179
  // src/engine/script-content-analyzer.ts
5115
- import { statSync as statSync2, existsSync as existsSync9 } from "fs";
5180
+ import { statSync as statSync3, existsSync as existsSync9 } from "fs";
5116
5181
  import { resolve as resolve8, join as join6, isAbsolute as isAbsolute2 } from "path";
5117
5182
  import { homedir as homedir4 } from "os";
5118
5183
  function emptyChain() {
@@ -5226,7 +5291,7 @@ function extractInlineScriptPath(actionDetail) {
5226
5291
  try {
5227
5292
  const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
5228
5293
  if (existsSync9(resolved)) {
5229
- const stat = statSync2(resolved);
5294
+ const stat = statSync3(resolved);
5230
5295
  if (stat.isFile()) return resolved;
5231
5296
  }
5232
5297
  } catch {
@@ -5240,7 +5305,7 @@ function extractInlineScriptPath(actionDetail) {
5240
5305
  try {
5241
5306
  const resolved = resolvePath2(p);
5242
5307
  if (existsSync9(resolved)) {
5243
- const stat = statSync2(resolved);
5308
+ const stat = statSync3(resolved);
5244
5309
  if (stat.isFile()) return resolved;
5245
5310
  }
5246
5311
  } catch {
@@ -5256,7 +5321,7 @@ function extractInlineScriptPath(actionDetail) {
5256
5321
  try {
5257
5322
  resolved = resolvePath2(p);
5258
5323
  if (existsSync9(resolved)) {
5259
- const stat = statSync2(resolved);
5324
+ const stat = statSync3(resolved);
5260
5325
  if (stat.isFile()) return resolved;
5261
5326
  }
5262
5327
  } catch {
@@ -5733,7 +5798,7 @@ async function runLLMAnalysis(input, previous) {
5733
5798
  } else {
5734
5799
  scriptAnalysis = analyzeScriptContent(input.aNext.actionType, input.aNext.actionDetail);
5735
5800
  }
5736
- const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText);
5801
+ const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText, previous.pathContext ?? "");
5737
5802
  logPrompt(prompt, input.aNext, previous.llmLogPath);
5738
5803
  const result = await analyzeWithLLM(prompt, config);
5739
5804
  if (llmCache.size >= LLM_CACHE_MAX) {
@@ -5757,7 +5822,7 @@ ${prompt}
5757
5822
  console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
5758
5823
  }
5759
5824
  }
5760
- function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
5825
+ function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "", pathContext = "") {
5761
5826
  const skillsText = formatSkillsForPrompt(matchedSkills);
5762
5827
  const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
5763
5828
  const promptHistoryText = formatPromptHistory(input.promptHistory);
@@ -5765,7 +5830,7 @@ function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis,
5765
5830
  const hintsText = formatHints(previous);
5766
5831
  const securityJudgeTemplate = templates["security_judge"];
5767
5832
  if (securityJudgeTemplate) {
5768
- return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{prompt_history_text}", promptHistoryText).replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{{", "{").replaceAll("}}", "}");
5833
+ return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{prompt_history_text}", promptHistoryText).replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{path_context}", pathContext || "\uFF08\u672C\u547D\u4EE4\u65E0\u4E3B\u76EE\u5F55\u76F8\u5173\u8DEF\u5F84\u6BB5\uFF0C\u4E0D\u9002\u7528\uFF09").replaceAll("{{", "{").replaceAll("}}", "}");
5769
5834
  }
5770
5835
  console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
5771
5836
  return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
@@ -8186,6 +8251,21 @@ async function runAnalysis(input) {
8186
8251
  if ((effectiveResult.riskLevel === "high" || effectiveResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
8187
8252
  return effectiveResult;
8188
8253
  }
8254
+ } else {
8255
+ if (isInlineScriptCmd || isLiteralTextCmd) {
8256
+ const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
8257
+ if (inlineJudgment.actionable && !inlineJudgment.allowed) {
8258
+ return {
8259
+ allowed: false,
8260
+ reason: inlineJudgment.reason,
8261
+ riskLevel: inlineJudgment.riskLevel,
8262
+ riskType: inlineJudgment.riskType,
8263
+ confidence: 90,
8264
+ source: "logic_rule",
8265
+ violatedLayers: ["L2.1"]
8266
+ };
8267
+ }
8268
+ }
8189
8269
  }
8190
8270
  if (isLogicRulesEnabled()) {
8191
8271
  const formalSessions = [];
@@ -8287,12 +8367,14 @@ async function runAnalysis(input) {
8287
8367
  }
8288
8368
  let llmResult;
8289
8369
  try {
8370
+ const pathContext = buildPathContext(input.aNext.actionDetail);
8290
8371
  llmResult = await runLLMAnalysis(input, {
8291
8372
  heuristic: heuristicResult,
8292
8373
  logic: logicResult,
8293
8374
  skipL3Hints,
8294
8375
  scriptChain: scriptChain ?? void 0,
8295
- llmLogPath: input.llmLogPath
8376
+ llmLogPath: input.llmLogPath,
8377
+ pathContext
8296
8378
  });
8297
8379
  } catch (e) {
8298
8380
  const { getLLMFailMode: getLLMFailMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -10174,10 +10256,14 @@ function printHelp() {
10174
10256
 
10175
10257
  Commands:
10176
10258
  init Generate input.json template
10177
- analyze <input> Run security analysis on input JSON
10259
+ analyze <input> Analyze an input JSON file, or a raw shell command string
10260
+ e.g. agent-moss analyze "cat ~/.ssh/id_rsa" \u2192 prints Deny/Allow
10178
10261
  server Start HTTP API server
10179
10262
  install Install systemd service for AgentMoss
10180
10263
 
10264
+ Analyze options:
10265
+ --json Command mode: print the full JSON result instead of plain Deny/Allow
10266
+
10181
10267
  Server options:
10182
10268
  --host <host> Listen host (default: 127.0.0.1)
10183
10269
  --port <port> Listen port (default: 9090, 0=auto assign)
@@ -10186,6 +10272,22 @@ Install options:
10186
10272
  --enable Enable service on boot (systemctl enable)
10187
10273
  `);
10188
10274
  }
10275
+ async function analyzeRawCommand(command, useJson = false) {
10276
+ const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_coordinator(), coordinator_exports));
10277
+ const result = await runAnalysis2({
10278
+ sessionId: "cli-command",
10279
+ promptSession: "",
10280
+ actionHistory: [],
10281
+ aNext: { actionType: "bash", actionDetail: command },
10282
+ reason: "",
10283
+ osType: process.platform === "win32" ? "windows" : "linux",
10284
+ cwd: process.cwd(),
10285
+ agentId: ""
10286
+ });
10287
+ if (useJson) console.log(JSON.stringify(result, null, 2));
10288
+ else console.log(result.allowed ? "Allow" : "Deny");
10289
+ return result.allowed ? 0 : 1;
10290
+ }
10189
10291
  async function main() {
10190
10292
  const args = process.argv.slice(2);
10191
10293
  const command = args[0];
@@ -10211,14 +10313,33 @@ async function main() {
10211
10313
  break;
10212
10314
  }
10213
10315
  case "analyze": {
10214
- if (!args[1]) {
10215
- console.error("Usage: agent-moss analyze <input.json>");
10316
+ const target = args[1];
10317
+ if (!target) {
10318
+ console.error('Usage: agent-moss analyze <input.json | "shell command">');
10216
10319
  process.exit(1);
10217
10320
  }
10218
10321
  const fs7 = await import("fs");
10219
10322
  const path6 = await import("path");
10220
10323
  const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_coordinator(), coordinator_exports));
10221
- const filePath = path6.resolve(args[1]);
10324
+ const useJson = args.includes("--json");
10325
+ const osType = process.platform === "win32" ? "windows" : "linux";
10326
+ const isFile = fs7.existsSync(target) && fs7.statSync(target).isFile();
10327
+ if (!isFile) {
10328
+ if (fs7.existsSync(target)) {
10329
+ console.error(`\u9519\u8BEF: \u4E0D\u662F\u6587\u4EF6: ${target}`);
10330
+ process.exit(2);
10331
+ }
10332
+ if (/\.(json|ya?ml|toml)$/i.test(target)) {
10333
+ console.error(`\u9519\u8BEF: \u6587\u4EF6\u4E0D\u5B58\u5728: ${target}`);
10334
+ process.exit(2);
10335
+ }
10336
+ if (!target.trim()) {
10337
+ console.error("\u9519\u8BEF: \u8F93\u5165\u4E3A\u7A7A\uFF08\u8BF7\u7ED9 JSON \u6587\u4EF6\u8DEF\u5F84\u6216 shell \u547D\u4EE4\uFF09");
10338
+ process.exit(2);
10339
+ }
10340
+ process.exit(await analyzeRawCommand(target, useJson));
10341
+ }
10342
+ const filePath = path6.resolve(target);
10222
10343
  const data = JSON.parse(fs7.readFileSync(filePath, "utf-8"));
10223
10344
  const result = await runAnalysis2({
10224
10345
  sessionId: data.session_id || "unknown",
@@ -10430,6 +10551,9 @@ if (isMain) {
10430
10551
  process.exit(1);
10431
10552
  });
10432
10553
  }
10554
+ export {
10555
+ analyzeRawCommand
10556
+ };
10433
10557
  //! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
10434
10558
  //! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
10435
10559
  //! properties.py ALL_PROPERTIES 保持一致并双端测试确认。