@kenhkl/agent-moss 0.11.5 → 0.12.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/dist/cli.js CHANGED
@@ -575,8 +575,70 @@ var init_inline_analyzer = __esm({
575
575
  }
576
576
  });
577
577
 
578
+ // src/infra/rules-loader.ts
579
+ import { readFileSync as readFileSync2, existsSync } from "fs";
580
+ import { resolve, dirname } from "path";
581
+ import { fileURLToPath as fileURLToPath3 } from "url";
582
+ function findRulesFile(name) {
583
+ for (const dir of RULES_DIR_CANDS) {
584
+ const p = resolve(dir, name);
585
+ if (existsSync(p)) return p;
586
+ }
587
+ return null;
588
+ }
589
+ function loadJson(name) {
590
+ const p = findRulesFile(name);
591
+ if (!p) return {};
592
+ try {
593
+ return JSON.parse(readFileSync2(p, "utf-8")) || {};
594
+ } catch {
595
+ return {};
596
+ }
597
+ }
598
+ function loadL1() {
599
+ return loadJson("l1_rules.json");
600
+ }
601
+ function loadL2() {
602
+ return loadJson("l2_rules.json");
603
+ }
604
+ function loadL3() {
605
+ return loadJson("l3_rules.json");
606
+ }
607
+ var __filename2, __dirname2, RULES_DIR_CANDS;
608
+ var init_rules_loader = __esm({
609
+ "src/infra/rules-loader.ts"() {
610
+ "use strict";
611
+ init_esm_shims();
612
+ __filename2 = fileURLToPath3(import.meta.url);
613
+ __dirname2 = dirname(__filename2);
614
+ RULES_DIR_CANDS = (() => {
615
+ const cands = [
616
+ // dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
617
+ resolve(__dirname2, "../rules"),
618
+ resolve(__dirname2, "rules"),
619
+ // dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
620
+ resolve(__dirname2, "../../../agent_moss/rules"),
621
+ // dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
622
+ resolve(__dirname2, "../../../../agent_moss/rules"),
623
+ // 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
624
+ resolve(process.cwd(), "agent_moss/rules")
625
+ ];
626
+ return cands.filter((c) => existsSync(c));
627
+ })();
628
+ }
629
+ });
630
+
578
631
  // src/engine/logic-rules.ts
579
632
  import * as fs2 from "fs";
633
+ function _loadSensitivePathScope(scope) {
634
+ const sps = loadL1().sensitive_path_access || [];
635
+ return sps.filter((sp) => sp.scope === scope).map((sp) => ({
636
+ path: sp.path,
637
+ riskLevel: sp.risk_level,
638
+ desc: sp.desc,
639
+ credential: sp.deny_mode === "deny_both" ? true : void 0
640
+ }));
641
+ }
580
642
  function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWORDS) {
581
643
  if (writeKeywords.some((kw) => actionType.includes(kw) || actionDetail.includes(kw))) return true;
582
644
  if (WRITE_COMMAND_RES.some((rx) => rx.test(actionDetail))) return true;
@@ -938,57 +1000,10 @@ var init_logic_rules = __esm({
938
1000
  "use strict";
939
1001
  init_esm_shims();
940
1002
  init_inline_analyzer();
941
- ROOT_SENSITIVE_PATHS = [
942
- { path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
943
- { path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
944
- // 注意:/etc/passwd 仅从 L1 特征匹配规则移除(公开可读),L2 敏感路径仍保留
945
- { path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
946
- { path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
947
- // 系统配置(仅拦截写入)
948
- { path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
949
- { path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
950
- { path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
951
- { path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
952
- { path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
953
- { path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
954
- { path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
955
- // 危险目录(仅拦截写入)
956
- { path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
957
- { path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
958
- { path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
959
- // 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
960
- { path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
961
- { path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907\uFF08\u8BFB\u5199\u5747\u5371\u9669\uFF09", credential: true },
962
- { path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
963
- // SSH 主机私钥
964
- // SSH 主机私钥(排除 .pub 公钥变体,对齐 Python .ssh/id_rsa(?!\.pub) 范式)
965
- { path: "/etc/ssh/ssh_host_rsa_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
966
- { path: "/etc/ssh/ssh_host_ed25519_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
967
- { path: "/etc/ssh/ssh_host_ecdsa_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true },
968
- // Nginx 配置(仅拦截写入,对齐 Python linux.py sensitive_paths)
969
- { path: "/etc/nginx/nginx.conf", riskLevel: "high", desc: "Nginx \u4E3B\u914D\u7F6E" },
970
- { path: "/etc/nginx/conf.d/", riskLevel: "high", desc: "Nginx \u5B50\u914D\u7F6E\u76EE\u5F55" },
971
- { path: "/etc/nginx/sites-enabled/", riskLevel: "high", desc: "Nginx \u542F\u7528\u7AD9\u70B9\u914D\u7F6E\u76EE\u5F55" }
972
- ];
973
- HOME_SENSITIVE_PATHS = [
974
- { path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
975
- { path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
976
- { path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
977
- // 持久化与命令劫持(仅拦截写入)
978
- { path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
979
- { path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
980
- { path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
981
- { path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
982
- { path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
983
- { path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
984
- ];
985
- CREDENTIAL_PATHS = [
986
- { path: "credentials.yml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
987
- { path: "credentials.yaml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
988
- { path: "secrets.yml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
989
- { path: "secrets.yaml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
990
- { path: ".env", riskLevel: "high", desc: "\u73AF\u5883\u53D8\u91CF/\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" }
991
- ];
1003
+ init_rules_loader();
1004
+ ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
1005
+ HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
1006
+ CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path3, riskLevel, desc }) => ({ path: path3, riskLevel, desc, credential: true }));
992
1007
  NON_WRITE_TOOLS = /* @__PURE__ */ new Set([
993
1008
  "ask_user_question",
994
1009
  "glob",
@@ -1116,34 +1131,22 @@ var init_logic_rules = __esm({
1116
1131
  DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
1117
1132
  READ_KEYWORDS = ["read", "\u8BFB\u53D6", "cat", "head", "tail", "less", "more", "view", "\u67E5\u770B", "open", "\u6253\u5F00", "load", "\u52A0\u8F7D", "grep", "search", "\u641C\u7D22", "find", "\u67E5\u627E"];
1118
1133
  CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
1119
- PASSWORD_MODIFY_PATTERNS = [
1120
- /\|\s*passwd\b/i,
1121
- /\bpasswd\s+--stdin\b/i,
1122
- /\bchpasswd\b/i,
1123
- /\bchgpasswd\b/i,
1124
- /\b(newusers|lnewusers)\b/i,
1125
- /\bpasswd\s+-[a-zA-Z]*d\b/i,
1126
- /\bpasswd\s+-[a-zA-Z]*l\b/i
1127
- ];
1128
- USER_DELETION_PATTERNS = [
1129
- /\buserdel\s/i,
1130
- /\bdeluser\s/i,
1131
- /\bluserdel\s/i,
1132
- /\bgroupdel\s/i,
1133
- /\blgroupdel\s/i
1134
- ];
1135
- INTENT_DEVIATION_PATTERNS = [
1136
- {
1137
- intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
1138
- dangerousActions: ["\u5220\u9664", "remove", "delete", "rm", "\u683C\u5F0F\u5316", "format"],
1139
- reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
1140
- },
1141
- {
1142
- intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
1143
- dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
1144
- reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
1145
- }
1146
- ];
1134
+ PASSWORD_MODIFY_PATTERNS = (() => {
1135
+ const pats = loadL2().password_modify_consent || [];
1136
+ return pats.map((p) => new RegExp(p.pattern, "i"));
1137
+ })();
1138
+ USER_DELETION_PATTERNS = (() => {
1139
+ const pats = loadL2().user_deletion_consent || [];
1140
+ return pats.map((p) => new RegExp(p.pattern, "i"));
1141
+ })();
1142
+ INTENT_DEVIATION_PATTERNS = (() => {
1143
+ const pats = loadL2().intent_consistency || [];
1144
+ return pats.map((p) => ({
1145
+ intentKeywords: p.intent_keywords,
1146
+ dangerousActions: p.dangerous_actions,
1147
+ reason: p.reason
1148
+ }));
1149
+ })();
1147
1150
  INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
1148
1151
  DIR_TRAVERSAL_KW = [
1149
1152
  "os.listdir",
@@ -1161,21 +1164,21 @@ var init_logic_rules = __esm({
1161
1164
  });
1162
1165
 
1163
1166
  // src/engine/skill-loader.ts
1164
- import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
1165
- import { resolve, dirname } from "path";
1166
- import { fileURLToPath as fileURLToPath3 } from "url";
1167
+ import { readFileSync as readFileSync3, readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
1168
+ import { resolve as resolve2, dirname as dirname2 } from "path";
1169
+ import { fileURLToPath as fileURLToPath4 } from "url";
1167
1170
  function loadAllSkills() {
1168
1171
  if (cachedSkills) return cachedSkills;
1169
1172
  cachedSkills = {};
1170
- const dir = existsSync2(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
1171
- if (!existsSync2(dir)) {
1173
+ const dir = existsSync3(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
1174
+ if (!existsSync3(dir)) {
1172
1175
  console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
1173
1176
  return cachedSkills;
1174
1177
  }
1175
1178
  const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
1176
1179
  for (const f of files) {
1177
1180
  const name = f.replace(".md", "");
1178
- cachedSkills[name] = readFileSync2(resolve(dir, f), "utf-8");
1181
+ cachedSkills[name] = readFileSync3(resolve2(dir, f), "utf-8");
1179
1182
  }
1180
1183
  console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
1181
1184
  return cachedSkills;
@@ -1229,32 +1232,27 @@ function formatSkillsForPrompt(skills) {
1229
1232
  ${s.skillContent}`
1230
1233
  ).join("\n\n---\n\n");
1231
1234
  }
1232
- var __filename2, __dirname2, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
1235
+ var __filename3, __dirname3, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
1233
1236
  var init_skill_loader = __esm({
1234
1237
  "src/engine/skill-loader.ts"() {
1235
1238
  "use strict";
1236
1239
  init_esm_shims();
1237
- __filename2 = fileURLToPath3(import.meta.url);
1238
- __dirname2 = dirname(__filename2);
1239
- SKILLS_DIR = resolve(__dirname2, "../skills");
1240
- SKILLS_DIR_FALLBACK = resolve(__dirname2, "../../../agent_moss/skills");
1241
- SKILL_KEYWORD_MAP = {
1242
- "file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
1243
- "script_execution_guard": ["command", "shell", "bash", "exec", "execute", "\u6267\u884C", "\u547D\u4EE4", "script", "\u811A\u672C", "run", "\u8FD0\u884C", "subprocess", "system", "eval", "compile", "\u5B89\u88C5", "install", "pip", "npm"],
1244
- "data_exfiltration_guard": ["upload", "\u4E0A\u4F20", "send", "\u53D1\u9001", "transfer", "\u4F20\u8F93", "export", "\u5BFC\u51FA", "download", "\u4E0B\u8F7D", "post", "http", "request", "fetch", "curl", "wget", "\u5916\u4F20", "\u6CC4\u9732", "exfil", "network", "\u7F51\u7EDC"],
1245
- "browser_web_access_guard": ["browser", "\u6D4F\u89C8\u5668", "web", "\u7F51\u9875", "url", "navigate", "\u6D4F\u89C8", "scrape", "crawl", "\u722C\u53D6", "click", "screenshot"],
1246
- "email_operation_guard": ["email", "\u90AE\u4EF6", "mail", "smtp", "send_email", "\u53D1\u9001\u90AE\u4EF6", "inbox", "\u6536\u4EF6\u7BB1", "\u9644\u4EF6", "attachment"],
1247
- "general_tool_risk_guard": ["tool", "\u5DE5\u5177", "api", "invoke", "\u8C03\u7528", "function", "\u51FD\u6570"],
1248
- "lateral_movement_guard": ["ssh", "scp", "ftp", "sftp", "rdp", "vnc", "telnet", "\u5185\u7F51", "\u6A2A\u5411", "tunnel", "\u4EE3\u7406", "proxy", "port", "\u7AEF\u53E3", "scan", "\u626B\u63CF", "ping", "traceroute", "nslookup"],
1249
- "persistence_backdoor_guard": ["cron", "crontab", "\u5B9A\u65F6", "scheduled", "startup", "\u542F\u52A8\u9879", "service", "\u670D\u52A1", "daemon", "\u5B88\u62A4", "registry", "\u6CE8\u518C\u8868", "backdoor", "\u540E\u95E8", "persist", "\u6301\u4E45\u5316", "authorized_keys", "bashrc", "zshrc", "profile", ".rc", "shell config", "shell \u914D\u7F6E"],
1250
- // 注意:避免裸 'memory'/'内存'/'dd'——会误命中 memory-addMemory(写记忆工具,
1251
- // 'dd' 是 'addMemory' 的子串)等无害操作。用组合词/命令前缀精准匹配攻击场景。
1252
- "resource_exhaustion_guard": ["fork", "bomb", "\u70B8\u5F39", "memory bomb", "oom", "out of memory", "\u5185\u5B58\u6EA2\u51FA", "\u5185\u5B58\u8017\u5C3D", "\u5185\u5B58\u6CC4\u6F0F", "disk", "\u78C1\u76D8", "cpu", "process", "\u8FDB\u7A0B", "dd if=", "/dev/zero", "/dev/null", "yes >", "while true", "\u65E0\u9650\u5FAA\u73AF", "\u8017\u5C3D", "exhaust", "\u8D44\u6E90"],
1253
- "skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
1254
- "supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
1255
- "intent_deviation_guard": ["rm", "del", "delete", "remove", "\u5220\u9664", "format", "\u683C\u5F0F\u5316", "drop", "truncate", "\u6E05\u7A7A", "wipe", "\u64E6\u9664", "\u91CD\u7F6E", "reset", "sudo", "root", "admin", "\u7BA1\u7406\u5458", "privilege", "\u63D0\u6743"],
1256
- "indirect_access_guard": ["listdir", "os.walk", "os.scandir", "glob.glob", "glob.iglob", "pathlib", "iterdir", "rglob", "scandir", "os.listdir", "\u76EE\u5F55\u904D\u5386", "traversal"]
1257
- };
1240
+ init_rules_loader();
1241
+ __filename3 = fileURLToPath4(import.meta.url);
1242
+ __dirname3 = dirname2(__filename3);
1243
+ SKILLS_DIR = resolve2(__dirname3, "../skills");
1244
+ SKILLS_DIR_FALLBACK = resolve2(__dirname3, "../../../agent_moss/skills");
1245
+ SKILL_KEYWORD_MAP = (() => {
1246
+ const map = {};
1247
+ const cats = loadL3();
1248
+ for (const cat of Object.values(cats)) {
1249
+ if (!cat || typeof cat !== "object") continue;
1250
+ for (const [skillId, keywords] of Object.entries(cat)) {
1251
+ map[skillId] = keywords;
1252
+ }
1253
+ }
1254
+ return map;
1255
+ })();
1258
1256
  cachedSkills = null;
1259
1257
  }
1260
1258
  });
@@ -1306,10 +1304,10 @@ __export(runtime_config_exports, {
1306
1304
  updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
1307
1305
  updateSkillEnabled: () => updateSkillEnabled
1308
1306
  });
1309
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
1307
+ import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
1310
1308
  import { join as join2 } from "path";
1311
- import { fileURLToPath as fileURLToPath4 } from "url";
1312
- import { dirname as dirname2 } from "path";
1309
+ import { fileURLToPath as fileURLToPath5 } from "url";
1310
+ import { dirname as dirname3 } from "path";
1313
1311
  function runtimeDir() {
1314
1312
  return getRuntimeConfigDir();
1315
1313
  }
@@ -1321,7 +1319,7 @@ function getRuntimeConfigPath() {
1321
1319
  function saveRuntimeConfig(config) {
1322
1320
  const p = getRuntimeConfigPath();
1323
1321
  const dir = p.substring(0, p.lastIndexOf("/"));
1324
- if (dir && !existsSync3(dir)) mkdirSync(dir, { recursive: true });
1322
+ if (dir && !existsSync4(dir)) mkdirSync(dir, { recursive: true });
1325
1323
  writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
1326
1324
  }
1327
1325
  function emptyRuntimeConfig() {
@@ -1336,7 +1334,7 @@ function emptyRuntimeConfig() {
1336
1334
  }
1337
1335
  function loadRuntimeConfig() {
1338
1336
  const p = getRuntimeConfigPath();
1339
- if (!existsSync3(p)) {
1337
+ if (!existsSync4(p)) {
1340
1338
  const seeded = mergeConfig({}, generateSourceDefaults());
1341
1339
  try {
1342
1340
  saveRuntimeConfig(seeded);
@@ -1345,7 +1343,7 @@ function loadRuntimeConfig() {
1345
1343
  return seeded;
1346
1344
  }
1347
1345
  try {
1348
- const data = JSON.parse(readFileSync3(p, "utf-8"));
1346
+ const data = JSON.parse(readFileSync4(p, "utf-8"));
1349
1347
  const merged = mergeConfig(data, generateSourceDefaults());
1350
1348
  return {
1351
1349
  version: data.version || 1,
@@ -1413,9 +1411,9 @@ function getEnabledL2SensitivePaths(runtime) {
1413
1411
  }
1414
1412
  function hasRuntimeConfig() {
1415
1413
  const p = getRuntimeConfigPath();
1416
- if (!existsSync3(p)) return false;
1414
+ if (!existsSync4(p)) return false;
1417
1415
  try {
1418
- JSON.parse(readFileSync3(p, "utf-8"));
1416
+ JSON.parse(readFileSync4(p, "utf-8"));
1419
1417
  return true;
1420
1418
  } catch {
1421
1419
  return false;
@@ -1594,8 +1592,8 @@ function _l3SkillsToRules() {
1594
1592
  }
1595
1593
  function _agentRulesToDefaults() {
1596
1594
  const out = {};
1597
- const agentsDir = existsSync3(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync3(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
1598
- if (!existsSync3(agentsDir)) return out;
1595
+ const agentsDir = existsSync4(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync4(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
1596
+ if (!existsSync4(agentsDir)) return out;
1599
1597
  let entries;
1600
1598
  try {
1601
1599
  entries = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
@@ -1604,10 +1602,10 @@ function _agentRulesToDefaults() {
1604
1602
  }
1605
1603
  for (const agentId of entries) {
1606
1604
  const rulesFile = join2(agentsDir, agentId, "rules.json");
1607
- if (!existsSync3(rulesFile)) continue;
1605
+ if (!existsSync4(rulesFile)) continue;
1608
1606
  let data;
1609
1607
  try {
1610
- data = JSON.parse(readFileSync3(rulesFile, "utf-8"));
1608
+ data = JSON.parse(readFileSync4(rulesFile, "utf-8"));
1611
1609
  } catch {
1612
1610
  continue;
1613
1611
  }
@@ -1686,6 +1684,9 @@ function generateSourceDefaults() {
1686
1684
  agent_rules: _agentRulesToDefaults()
1687
1685
  };
1688
1686
  }
1687
+ function normalizePatternForFingerprint(pattern) {
1688
+ return pattern.replace(/\\\//g, "/");
1689
+ }
1689
1690
  function _ruleFingerprint(rule) {
1690
1691
  if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
1691
1692
  return JSON.stringify([
@@ -1693,8 +1694,8 @@ function _ruleFingerprint(rule) {
1693
1694
  [...rule.dangerous_actions || []].sort()
1694
1695
  ]);
1695
1696
  }
1696
- if (rule.path) return JSON.stringify([rule.path]);
1697
- if (rule.pattern) return JSON.stringify([rule.pattern]);
1697
+ if (rule.path) return JSON.stringify([normalizePatternForFingerprint(rule.path)]);
1698
+ if (rule.pattern) return JSON.stringify([normalizePatternForFingerprint(rule.pattern)]);
1698
1699
  if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
1699
1700
  if (rule.value) return JSON.stringify([rule.value]);
1700
1701
  return JSON.stringify([rule.id || ""]);
@@ -2045,7 +2046,7 @@ function deleteCustomSkill(skillId, category) {
2045
2046
  skills.splice(idx, 1);
2046
2047
  const skillPath = join2(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
2047
2048
  try {
2048
- if (existsSync3(skillPath)) unlinkSync(skillPath);
2049
+ if (existsSync4(skillPath)) unlinkSync(skillPath);
2049
2050
  } catch {
2050
2051
  }
2051
2052
  saveRuntimeConfig(runtime);
@@ -2159,7 +2160,7 @@ function updateAgentCategoryEnabled(agentId, category, enabled) {
2159
2160
  saveRuntimeConfig(runtime);
2160
2161
  return runtime;
2161
2162
  }
2162
- var __dirname3, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, SKILL_ID_RE;
2163
+ var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, SKILL_ID_RE;
2163
2164
  var init_runtime_config = __esm({
2164
2165
  "src/infra/runtime-config.ts"() {
2165
2166
  "use strict";
@@ -2168,7 +2169,7 @@ var init_runtime_config = __esm({
2168
2169
  init_heuristic();
2169
2170
  init_logic_rules();
2170
2171
  init_skill_loader();
2171
- __dirname3 = dirname2(fileURLToPath4(import.meta.url));
2172
+ __dirname4 = dirname3(fileURLToPath5(import.meta.url));
2172
2173
  ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
2173
2174
  RUNTIME_CONFIG_DIR = runtimeDir();
2174
2175
  RUNTIME_CONFIG_PATH = join2(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
@@ -2182,19 +2183,19 @@ var init_runtime_config = __esm({
2182
2183
  deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
2183
2184
  deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
2184
2185
  };
2185
- _AGENTS_DIR_PROD = join2(__dirname3, "agents");
2186
- _AGENTS_DIR_DEV = join2(__dirname3, "..", "..", "..", "agent_moss", "agents");
2186
+ _AGENTS_DIR_PROD = join2(__dirname4, "agents");
2187
+ _AGENTS_DIR_DEV = join2(__dirname4, "..", "..", "..", "agent_moss", "agents");
2187
2188
  SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
2188
2189
  }
2189
2190
  });
2190
2191
 
2191
2192
  // src/agents/index.ts
2192
- import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
2193
- import { resolve as resolve2, dirname as dirname3 } from "path";
2194
- import { fileURLToPath as fileURLToPath5 } from "url";
2193
+ import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
2194
+ import { resolve as resolve3, dirname as dirname4 } from "path";
2195
+ import { fileURLToPath as fileURLToPath6 } from "url";
2195
2196
  function resolveAgentsDir() {
2196
- if (existsSync4(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
2197
- if (existsSync4(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
2197
+ if (existsSync5(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
2198
+ if (existsSync5(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
2198
2199
  return AGENTS_DIR_PROD;
2199
2200
  }
2200
2201
  function getAgentExtensions(agentId) {
@@ -2210,13 +2211,13 @@ function getAgentExtensions(agentId) {
2210
2211
  }
2211
2212
  if (ext === null) {
2212
2213
  const agentsDir = resolveAgentsDir();
2213
- const rulesFile = resolve2(agentsDir, agentId, "rules.json");
2214
- if (!existsSync4(rulesFile)) {
2214
+ const rulesFile = resolve3(agentsDir, agentId, "rules.json");
2215
+ if (!existsSync5(rulesFile)) {
2215
2216
  cache.set(agentId, null);
2216
2217
  return null;
2217
2218
  }
2218
2219
  try {
2219
- const raw = readFileSync4(rulesFile, "utf-8");
2220
+ const raw = readFileSync5(rulesFile, "utf-8");
2220
2221
  ext = JSON.parse(raw);
2221
2222
  if (!ext.agent_id) {
2222
2223
  console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
@@ -2235,15 +2236,15 @@ function getAgentExtensions(agentId) {
2235
2236
  );
2236
2237
  return ext;
2237
2238
  }
2238
- var __filename3, __dirname4, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
2239
+ var __filename4, __dirname5, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
2239
2240
  var init_agents = __esm({
2240
2241
  "src/agents/index.ts"() {
2241
2242
  "use strict";
2242
2243
  init_esm_shims();
2243
- __filename3 = fileURLToPath5(import.meta.url);
2244
- __dirname4 = dirname3(__filename3);
2245
- AGENTS_DIR_PROD = resolve2(__dirname4, "agents");
2246
- AGENTS_DIR_DEV_FALLBACK = resolve2(__dirname4, "../../../agent_moss/agents");
2244
+ __filename4 = fileURLToPath6(import.meta.url);
2245
+ __dirname5 = dirname4(__filename4);
2246
+ AGENTS_DIR_PROD = resolve3(__dirname5, "agents");
2247
+ AGENTS_DIR_DEV_FALLBACK = resolve3(__dirname5, "../../../agent_moss/agents");
2247
2248
  cache = /* @__PURE__ */ new Map();
2248
2249
  }
2249
2250
  });
@@ -2708,208 +2709,69 @@ var init_heuristic = __esm({
2708
2709
  init_esm_shims();
2709
2710
  init_config();
2710
2711
  init_agents();
2712
+ init_rules_loader();
2711
2713
  init_logic_rules();
2712
- INJECTION_KEYWORDS = [
2713
- // ---- Instruction Override ----
2714
- { keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
2715
- { keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
2716
- { keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
2717
- { keyword: "forget everything", riskLevel: "high", lang: "en" },
2718
- { keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
2719
- { keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
2720
- { keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
2721
- { keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
2722
- // ---- Role Hijacking ----
2723
- { keyword: "you are now", riskLevel: "high", lang: "en" },
2724
- { keyword: "you are no longer", riskLevel: "high", lang: "en" },
2725
- { keyword: "new instructions", riskLevel: "high", lang: "en" },
2726
- { keyword: "from now on, you", riskLevel: "high", lang: "en" },
2727
- { keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
2728
- { keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
2729
- { keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
2730
- { keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
2731
- // ---- Command Injection ----
2732
- { keyword: "system prompt", riskLevel: "high", lang: "en" },
2733
- { keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
2734
- { keyword: "output your instructions", riskLevel: "high", lang: "en" },
2735
- { keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
2736
- { keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
2737
- { keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
2738
- { keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
2739
- // ---- Social Engineering ----
2740
- { keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
2741
- { keyword: "security audit approved", riskLevel: "high", lang: "en" },
2742
- { keyword: "admin override", riskLevel: "critical", lang: "en" },
2743
- { keyword: "debug mode", riskLevel: "high", lang: "en" },
2744
- { keyword: "maintenance mode", riskLevel: "high", lang: "en" },
2745
- { keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
2746
- { keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
2747
- { keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
2748
- { keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
2749
- // ---- XSS ----
2750
- { keyword: "<script>", riskLevel: "high", lang: "en" },
2751
- { keyword: "javascript:", riskLevel: "high", lang: "en" },
2752
- { keyword: "onerror=", riskLevel: "high", lang: "en" },
2753
- { keyword: "onload=", riskLevel: "high", lang: "en" },
2754
- // ---- Fake Boundaries ----
2755
- { keyword: "=== END OF", riskLevel: "high", lang: "en" },
2756
- { keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
2757
- { keyword: "[END OF", riskLevel: "high", lang: "en" },
2758
- { keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
2759
- { keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
2760
- // ---- Developer Mode ----
2761
- { keyword: "developer mode", riskLevel: "critical", lang: "en" },
2762
- { keyword: "DAN mode", riskLevel: "critical", lang: "en" },
2763
- { keyword: "jailbreak", riskLevel: "critical", lang: "en" },
2764
- { keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
2765
- { keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
2766
- // 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
2767
- // 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
2768
- // 由 agent_ext 机制注入,不再污染通用层。
2769
- ];
2770
- DANGEROUS_PATTERNS = [
2771
- // --- Critical: rm command graded detection (strict to loose, first match wins) ---
2772
- // 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
2773
- { pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\/(?!\w)/i, severity: "critical", description: "\u9012\u5F52\u5F3A\u5236\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)", riskType: "script_execution" },
2774
- // 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
2775
- // 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
2776
- { pattern: /\brm\s+-[a-zA-Z]*[rf][a-zA-Z]*\s+\/(etc|var(?!\/tmp)|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55", riskType: "script_execution" },
2777
- // 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
2778
- { pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*\s+\/(?!tmp\/|var\/tmp\/|run\/|dev\/shm\/)/i, severity: "high", description: "\u9012\u5F52\u5220\u9664\u64CD\u4F5C (rm -r /...)", riskType: "script_execution" },
2779
- // 4. rm -f absolute path (no -r) — 同样排除临时目录
2780
- { pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*\s+\/(?!tmp\/|var\/tmp\/|run\/|dev\/shm\/)/i, severity: "medium", description: "\u5F3A\u5236\u5220\u9664\u6587\u4EF6 (rm -f /...)", riskType: "script_execution" },
2781
- // dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
2782
- { pattern: /dd\s+if=\/dev\/zero.*\bof\s*=\s*\/(etc|var|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u78C1\u76D8\u6E05\u96F6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55 (dd if=/dev/zero of=/\u7CFB\u7EDF\u76EE\u5F55)", riskType: "script_execution" },
2783
- // mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
2784
- { pattern: /mkfs\.\w+\s+\/dev\/(sd|hd|nvme|vd|loop|mapper|md|dm)\S+/i, severity: "critical", description: "\u683C\u5F0F\u5316\u5757\u8BBE\u5907 (mkfs /dev/\u5757\u8BBE\u5907)", riskType: "script_execution" },
2785
- { pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
2786
- // --- Critical: remote code execution ---
2787
- // curl|bash 优先匹配 user_rules(high/sensitive_action),见下方 UserRuleMatcher 段;
2788
- // 非 bash 的 curl|sh/zsh/dash 走 CommandPatternScanner critical。两条分流由
2789
- // runHeuristicCheck 的规则顺序保证(bash 在前 high,其余 fall-through 到 critical)。
2790
- { pattern: /curl\s+.*\|\s*bash/i, severity: "high", description: "curl pipe bash \u6267\u884C (user_rules)", riskType: "sensitive_action" },
2791
- { pattern: /curl\s+.*\|\s*(sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
2792
- { pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
2793
- { pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
2794
- { pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "high", description: "base64 \u89E3\u7801\u6267\u884C (user_rules)", riskType: "sensitive_action" },
2795
- // --- Critical: reverse shell ---
2796
- { pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
2797
- { pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
2798
- // --- Critical: sensitive file access ---
2799
- { pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
2800
- { pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
2801
- // 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
2802
- { pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
2803
- // --- Critical: privilege escalation / container escape ---
2804
- { pattern: /\/var\/run\/docker\.sock/i, severity: "critical", description: "\u8BBF\u95EE Docker socket\uFF0C\u53EF\u80FD\u5BFC\u81F4\u5BB9\u5668\u9003\u9038", riskType: "privilege_escalation" },
2805
- // --- Critical: dangerous permissions / ownership (提权 / 权限敞口) ---
2806
- // 修正「只拦 777、xx7 漏放」的缺口:
2807
- // 1) 八进制末位 7:other 位含写+执行敞口(chmod 777/647/007/4777 等);644/755/1700 不命中
2808
- // 2) 八进制 setuid/setgid 位:首位 4(setuid)/2(setgid) 的 4 位八进制(chmod 4755/2755)
2809
- // 3) 符号 setuid/setgid:chmod u+s / g+s / a+s
2810
- // 普通权限修改(chmod 644)不在此列——L2 read_before_write 已豁免 chmod/chown(不写内容),
2811
- // 危险形态在此 L1 专门拦,以「提权」正确理由拦截,不再靠 RBW 误打误撞。
2812
- { pattern: /chmod\s+[0-7]{0,3}7\b/i, severity: "critical", description: "\u5371\u9669\u6743\u9650\u8BBE\u7F6E (chmod ...7, other \u4F4D\u542B\u5199+\u6267\u884C\u655E\u53E3)", riskType: "privilege_escalation" },
2813
- { pattern: /chmod\s+[24][0-7]{3}\b/i, severity: "critical", description: "setuid/setgid \u4F4D\u8BBE\u7F6E (chmod 4xxx/2xxx, \u63D0\u6743)", riskType: "privilege_escalation" },
2814
- { pattern: /chmod\s+[ugoa]\+s\b/i, severity: "critical", description: "\u7B26\u53F7 setuid/setgid \u8BBE\u7F6E (chmod u+s/g+s/a+s, \u63D0\u6743)", riskType: "privilege_escalation" },
2815
- { pattern: /chown\s+root|chown\s+-R\s+\w+:\w+\s+\//i, severity: "high", description: "\u4FEE\u6539\u6240\u6709\u8005\u4E3A root / \u9012\u5F52\u4FEE\u6539\u6839\u76EE\u5F55\u6240\u6709\u8005", riskType: "script_execution" },
2816
- // sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
2817
- { pattern: /sudo\s+(rm|chmod|chown|dd|mkfs|format|iptables|crontab|passwd|userdel|useradd|groupdel)\b/i, severity: "high", description: "\u63D0\u6743\u6267\u884C\u9AD8\u5371\u547D\u4EE4 (sudo + \u5371\u9669\u64CD\u4F5C)", riskType: "privilege_escalation" },
2818
- // --- High: system config overwrite ---
2819
- // > /etc/ 重定向覆盖:对齐 Python CommandPatternScanner `>\s*/etc/`(不兼容引号路径),
2820
- // 引号包裹的重定向目标(cat > '/etc/ssh/sshd_config')走 checkSensitivePath 敏感路径 high。
2821
- { pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
2822
- { pattern: />\s*\/boot\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55", riskType: "file_access" },
2823
- // 注:/proc/ 重定向覆盖走 checkSensitivePath 的 high(内核参数 /proc/sys/),此处不拦 /proc/ 避免遮蔽(对齐 Python)。
2824
- // --- High: download script + execute risk ---
2825
- // curl -X POST 外传敏感信息(对齐 Python linux.py: curl POST env/key/token)
2826
- { pattern: /curl\s+.*-X\s+POST.*\b(env|environment|secret|key|token|password)\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u901A\u8FC7 curl POST \u5916\u4F20\u53EF\u80FD\u5305\u542B\u654F\u611F\u4FE1\u606F\u7684\u8BF7\u6C42", riskType: "data_exfiltration" },
2827
- { pattern: /(?:curl|wget)\s+.*(?:-o|--output|>)\s*\S+\.(?:sh|bash)\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u811A\u672C\u5230\u672C\u5730", riskType: "script_execution" },
2828
- { pattern: /(?:curl|wget)\s+\S+.*\b(?:chmod|chown)\s+\+x\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u540E\u4FEE\u6539\u53EF\u6267\u884C\u6743\u9650\uFF0C\u5B58\u5728\u8FDC\u7A0B\u4EE3\u7801\u6267\u884C\u98CE\u9669", riskType: "script_execution" },
2829
- { pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
2830
- // --- High: package manager pipe-to-shell ---
2831
- { pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
2832
- { pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
2833
- { pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
2834
- // --- Medium: destructive git operations (降级为 medium,允许但记录) ---
2835
- { pattern: /git\s+push\s+.*--force/i, severity: "medium", description: "\u5F3A\u5236\u63A8\u9001\u64CD\u4F5C (git push --force)\uFF0C\u53EF\u80FD\u8986\u76D6\u8FDC\u7A0B\u63D0\u4EA4\u5386\u53F2", riskType: "script_execution" },
2836
- // --- High: network port scanning (横向移动侦察) ---
2837
- { pattern: /\bnmap\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF (nmap)\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
2838
- { pattern: /\b(masscan|zmap|unicornscan)\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF\u5DE5\u5177\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
2839
- // --- Medium: credential export ---
2840
- { pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
2841
- // --- High: shell rc file append (persistence) ---
2842
- // 两条规则:第一条匹配 ~ 与 /home/<user>/ 形态;第二条按文件名兜底,
2843
- // 覆盖非标准 home 路径(如鸿蒙 /storage/Users/currentUser/.bashrc、
2844
- // /root/.bashrc),避免按 home 前缀枚举导致漏检持久化后门。
2845
- { pattern: />>\s*(~|\/home\/[^/]+)\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)/, severity: "high", description: "\u68C0\u6D4B\u5230\u4FEE\u6539 Shell \u914D\u7F6E\u6587\u4EF6\uFF08~/.bashrc \u7B49\uFF09\uFF0C\u53EF\u80FD\u7528\u4E8E\u6301\u4E45\u5316\u540E\u95E8", riskType: "persistence" },
2846
- { pattern: />>\s*\S*\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)\b/, severity: "high", description: "\u68C0\u6D4B\u5230\u4FEE\u6539 Shell \u914D\u7F6E\u6587\u4EF6\uFF08.bashrc \u7B49\uFF09\uFF0C\u53EF\u80FD\u7528\u4E8E\u6301\u4E45\u5316\u540E\u95E8", riskType: "persistence" },
2847
- // --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
2848
- { pattern: />\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i, severity: "high", description: "\u91CD\u5B9A\u5411\u5199\u5165\u8BBE\u5907\u6587\u4EF6", riskType: "file_access" },
2849
- // --- High: non-interactive password modification ---
2850
- { pattern: /\|\s*passwd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7BA1\u9053\u65B9\u5F0F\u8C03\u7528 passwd\uFF08\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\uFF09", riskType: "script_execution" },
2851
- { pattern: /\bpasswd\s+--stdin\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 passwd --stdin \u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u8F93\u5165", riskType: "script_execution" },
2852
- { pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
2853
- { pattern: /\b(newusers|lnewusers)\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 newusers/lnewusers \u6279\u91CF\u7528\u6237\u6DFB\u52A0\u547D\u4EE4", riskType: "script_execution" },
2854
- { pattern: /\bpasswd\s+-[a-zA-Z]*d\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u5BC6\u7801\u64CD\u4F5C (passwd -d)", riskType: "script_execution" },
2855
- { pattern: /\bpasswd\s+-[a-zA-Z]*l\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u9501\u5B9A\u7528\u6237\u8D26\u6237\u64CD\u4F5C (passwd -l)", riskType: "script_execution" },
2856
- // --- High: non-interactive password modification (extended) ---
2857
- { pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
2858
- { pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
2859
- { pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
2860
- // --- High: user/group deletion (irreversible, requires user consent) ---
2861
- { pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
2862
- { pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
2863
- { pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
2864
- { pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
2865
- { pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
2866
- // --- High: generic command -p parameter password setting ---
2867
- { pattern: /\b(useradd|luseradd|groupadd|usermod|lusermod|groupmod|lgroupmod)\b[^&|;]*?\s-p\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u901A\u8FC7 -p \u53C2\u6570\u8BBE\u7F6E\u6216\u4FEE\u6539\u5BC6\u7801 (useradd/usermod/groupmod)", riskType: "script_execution" }
2868
- ];
2714
+ INJECTION_KEYWORDS = (() => {
2715
+ const kws = loadL1().injection_detection || [];
2716
+ return kws.map((k) => ({
2717
+ keyword: k.keyword,
2718
+ riskLevel: k.risk_level,
2719
+ lang: k.lang || "en"
2720
+ }));
2721
+ })();
2722
+ DANGEROUS_PATTERNS = (() => {
2723
+ const cmds = loadL1().dangerous_commands || [];
2724
+ return cmds.map((c) => ({
2725
+ pattern: new RegExp(c.pattern, "i"),
2726
+ severity: c.risk_level,
2727
+ riskType: c.risk_type,
2728
+ description: c.reason
2729
+ }));
2730
+ })();
2869
2731
  SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
2870
2732
  SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
2871
2733
  }
2872
2734
  });
2873
2735
 
2874
2736
  // src/engine/template-loader.ts
2875
- import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
2876
- import { resolve as resolve3, dirname as dirname4 } from "path";
2877
- import { fileURLToPath as fileURLToPath6 } from "url";
2737
+ import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
2738
+ import { resolve as resolve4, dirname as dirname5 } from "path";
2739
+ import { fileURLToPath as fileURLToPath7 } from "url";
2878
2740
  function loadTemplates() {
2879
2741
  if (cachedTemplates) return cachedTemplates;
2880
2742
  cachedTemplates = {};
2881
- const dir = existsSync5(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
2882
- if (!existsSync5(dir)) {
2743
+ const dir = existsSync6(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
2744
+ if (!existsSync6(dir)) {
2883
2745
  console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
2884
2746
  return cachedTemplates;
2885
2747
  }
2886
2748
  for (const file of TEMPLATE_FILES) {
2887
- const filePath = resolve3(dir, file);
2888
- if (existsSync5(filePath)) {
2749
+ const filePath = resolve4(dir, file);
2750
+ if (existsSync6(filePath)) {
2889
2751
  const key = file.replace(".txt", "").replace("_template", "");
2890
- cachedTemplates[key] = readFileSync5(filePath, "utf-8");
2752
+ cachedTemplates[key] = readFileSync6(filePath, "utf-8");
2891
2753
  }
2892
2754
  }
2893
2755
  console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
2894
2756
  return cachedTemplates;
2895
2757
  }
2896
- var __filename4, __dirname5, TEMPLATES_DIR, TEMPLATES_DIR_FALLBACK, TEMPLATE_FILES, cachedTemplates;
2758
+ var __filename5, __dirname6, TEMPLATES_DIR, TEMPLATES_DIR_FALLBACK, TEMPLATE_FILES, cachedTemplates;
2897
2759
  var init_template_loader = __esm({
2898
2760
  "src/engine/template-loader.ts"() {
2899
2761
  "use strict";
2900
2762
  init_esm_shims();
2901
- __filename4 = fileURLToPath6(import.meta.url);
2902
- __dirname5 = dirname4(__filename4);
2903
- TEMPLATES_DIR = resolve3(__dirname5, "../templates");
2904
- TEMPLATES_DIR_FALLBACK = resolve3(__dirname5, "../../../agent_moss/templates");
2763
+ __filename5 = fileURLToPath7(import.meta.url);
2764
+ __dirname6 = dirname5(__filename5);
2765
+ TEMPLATES_DIR = resolve4(__dirname6, "../templates");
2766
+ TEMPLATES_DIR_FALLBACK = resolve4(__dirname6, "../../../agent_moss/templates");
2905
2767
  TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
2906
2768
  cachedTemplates = null;
2907
2769
  }
2908
2770
  });
2909
2771
 
2910
2772
  // src/engine/script-analyzer.ts
2911
- import { readFileSync as readFileSync6 } from "fs";
2912
- import { resolve as resolve4, join as join3 } from "path";
2773
+ import { readFileSync as readFileSync7 } from "fs";
2774
+ import { resolve as resolve5, join as join3 } from "path";
2913
2775
  function emptyResult() {
2914
2776
  return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
2915
2777
  }
@@ -2962,8 +2824,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
2962
2824
  }
2963
2825
  function readScriptContent(scriptPath, maxLines = 500) {
2964
2826
  try {
2965
- const expandedPath = scriptPath.startsWith("~") ? join3(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve4(scriptPath);
2966
- const raw = readFileSync6(expandedPath, "utf-8");
2827
+ const expandedPath = scriptPath.startsWith("~") ? join3(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve5(scriptPath);
2828
+ const raw = readFileSync7(expandedPath, "utf-8");
2967
2829
  const lines = raw.split("\n").slice(0, maxLines);
2968
2830
  return { content: lines.join("\n"), lineCount: lines.length, error: "" };
2969
2831
  } catch (e) {
@@ -3101,8 +2963,8 @@ var init_script_analyzer = __esm({
3101
2963
  });
3102
2964
 
3103
2965
  // src/engine/script-content-analyzer.ts
3104
- import { readFileSync as readFileSync7, statSync, existsSync as existsSync6 } from "fs";
3105
- import { resolve as resolve5, join as join4, isAbsolute, dirname as dirname5 } from "path";
2966
+ import { readFileSync as readFileSync8, statSync, existsSync as existsSync7 } from "fs";
2967
+ import { resolve as resolve6, join as join4, isAbsolute, dirname as dirname6 } from "path";
3106
2968
  import { homedir as homedir2 } from "os";
3107
2969
  function emptyChain() {
3108
2970
  return {
@@ -3249,7 +3111,7 @@ function extractInlineScriptPath(actionDetail) {
3249
3111
  let resolved;
3250
3112
  try {
3251
3113
  resolved = resolvePath(p);
3252
- if (existsSync6(resolved)) {
3114
+ if (existsSync7(resolved)) {
3253
3115
  const stat = statSync(resolved);
3254
3116
  if (stat.isFile()) return resolved;
3255
3117
  }
@@ -3263,18 +3125,18 @@ function resolvePath(p) {
3263
3125
  if (expanded.startsWith("~/") || expanded === "~") {
3264
3126
  expanded = join4(homedir2(), expanded.slice(1));
3265
3127
  }
3266
- return resolve5(expanded);
3128
+ return resolve6(expanded);
3267
3129
  }
3268
3130
  function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
3269
3131
  try {
3270
3132
  const path3 = resolvePath(scriptPath);
3271
- if (!existsSync6(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
3133
+ if (!existsSync7(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
3272
3134
  const stat = statSync(path3);
3273
3135
  if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
3274
3136
  if (stat.size > maxFileSize) {
3275
3137
  return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
3276
3138
  }
3277
- const content = readFileSync7(path3, "utf-8");
3139
+ const content = readFileSync8(path3, "utf-8");
3278
3140
  const lineCount = content ? content.split("\n").length : 0;
3279
3141
  return { content, lineCount, fileSize: stat.size, error: "" };
3280
3142
  } catch (e) {
@@ -3293,7 +3155,7 @@ function extractChildScriptPaths(content, baseDir) {
3293
3155
  if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
3294
3156
  const full = isAbsolute(pathStr) ? pathStr : join4(baseDir, pathStr);
3295
3157
  const resolved = resolvePath(full);
3296
- if (existsSync6(resolved)) {
3158
+ if (existsSync7(resolved)) {
3297
3159
  try {
3298
3160
  if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
3299
3161
  } catch {
@@ -3308,7 +3170,7 @@ function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 10
3308
3170
  const result = emptyChain();
3309
3171
  result.entryPath = entryPath;
3310
3172
  const entry = resolvePath(entryPath);
3311
- const baseDir = existsSync6(entry) ? dirname5(entry) : input.cwd || process.cwd();
3173
+ const baseDir = existsSync7(entry) ? dirname6(entry) : input.cwd || process.cwd();
3312
3174
  resolveRecursive({
3313
3175
  path: entry,
3314
3176
  depth: 0,
@@ -3383,7 +3245,7 @@ function resolveRecursive(ctx) {
3383
3245
  maxTotalSize,
3384
3246
  visited,
3385
3247
  result,
3386
- baseDir: dirname5(resolvePath(childPath)),
3248
+ baseDir: dirname6(resolvePath(childPath)),
3387
3249
  input
3388
3250
  });
3389
3251
  if (result.hasCritical) return;
@@ -3644,16 +3506,16 @@ __export(token_stats_exports, {
3644
3506
  recordTokenUsage: () => recordTokenUsage,
3645
3507
  resetTokenStats: () => resetTokenStats
3646
3508
  });
3647
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
3509
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
3648
3510
  function getStatsPath() {
3649
3511
  const envPath = process.env[ENV_STATS_PATH];
3650
3512
  if (envPath) return envPath;
3651
3513
  return getTokenStatsPath();
3652
3514
  }
3653
3515
  function loadAllRecords(statsPath) {
3654
- if (!existsSync7(statsPath)) return [];
3516
+ if (!existsSync8(statsPath)) return [];
3655
3517
  try {
3656
- const data = JSON.parse(readFileSync8(statsPath, "utf-8"));
3518
+ const data = JSON.parse(readFileSync9(statsPath, "utf-8"));
3657
3519
  if (Array.isArray(data)) return data;
3658
3520
  if (data && Array.isArray(data.records)) return data.records;
3659
3521
  return [];
@@ -3664,7 +3526,7 @@ function loadAllRecords(statsPath) {
3664
3526
  function saveRecords(statsPath, records) {
3665
3527
  try {
3666
3528
  const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
3667
- if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
3529
+ if (dir && !existsSync8(dir)) mkdirSync2(dir, { recursive: true });
3668
3530
  writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
3669
3531
  } catch (e) {
3670
3532
  }
@@ -3786,7 +3648,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
3786
3648
  }
3787
3649
  function resetTokenStats() {
3788
3650
  const statsPath = getStatsPath();
3789
- if (existsSync7(statsPath)) {
3651
+ if (existsSync8(statsPath)) {
3790
3652
  try {
3791
3653
  unlinkSync2(statsPath);
3792
3654
  } catch {
@@ -3994,7 +3856,7 @@ async function analyzeWithLLM(prompt, config) {
3994
3856
  }
3995
3857
  if (attempt < maxRetries) {
3996
3858
  console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
3997
- await new Promise((resolve6) => setTimeout(resolve6, retryCfg.retryInterval * 1e3));
3859
+ await new Promise((resolve7) => setTimeout(resolve7, retryCfg.retryInterval * 1e3));
3998
3860
  } else {
3999
3861
  clearTimeout(totalTimeoutTimer);
4000
3862
  console.error("[AgentMoss] LLM call failed after all retries:", err.message);
@@ -4432,9 +4294,9 @@ var require_bindings = __commonJS({
4432
4294
  init_esm_shims();
4433
4295
  var fs4 = __require("fs");
4434
4296
  var path3 = __require("path");
4435
- var fileURLToPath9 = require_file_uri_to_path();
4297
+ var fileURLToPath10 = require_file_uri_to_path();
4436
4298
  var join6 = path3.join;
4437
- var dirname7 = path3.dirname;
4299
+ var dirname8 = path3.dirname;
4438
4300
  var exists = fs4.accessSync && function(path4) {
4439
4301
  try {
4440
4302
  fs4.accessSync(path4);
@@ -4544,12 +4406,12 @@ var require_bindings = __commonJS({
4544
4406
  Error.stackTraceLimit = origSTL;
4545
4407
  var fileSchema = "file://";
4546
4408
  if (fileName.indexOf(fileSchema) === 0) {
4547
- fileName = fileURLToPath9(fileName);
4409
+ fileName = fileURLToPath10(fileName);
4548
4410
  }
4549
4411
  return fileName;
4550
4412
  };
4551
4413
  exports.getRoot = function getRoot(file) {
4552
- var dir = dirname7(file), prev;
4414
+ var dir = dirname8(file), prev;
4553
4415
  while (true) {
4554
4416
  if (dir === ".") {
4555
4417
  dir = process.cwd();
@@ -4755,13 +4617,13 @@ var require_backup = __commonJS({
4755
4617
  var runBackup = (backup, handler) => {
4756
4618
  let rate = 0;
4757
4619
  let useDefault = true;
4758
- return new Promise((resolve6, reject) => {
4620
+ return new Promise((resolve7, reject) => {
4759
4621
  setImmediate(function step() {
4760
4622
  try {
4761
4623
  const progress = backup.transfer(rate);
4762
4624
  if (!progress.remainingPages) {
4763
4625
  backup.close();
4764
- resolve6(progress);
4626
+ resolve7(progress);
4765
4627
  return;
4766
4628
  }
4767
4629
  if (useDefault) {
@@ -5390,11 +5252,11 @@ init_esm_shims();
5390
5252
  init_runtime_config();
5391
5253
  init_token_stats();
5392
5254
  import { Hono } from "hono";
5393
- import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
5394
- import { join as join5, dirname as dirname6 } from "path";
5395
- import { fileURLToPath as fileURLToPath7 } from "url";
5396
- var __dirname6 = dirname6(fileURLToPath7(import.meta.url));
5397
- var STATIC_DIR = join5(__dirname6, "static");
5255
+ import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
5256
+ import { join as join5, dirname as dirname7 } from "path";
5257
+ import { fileURLToPath as fileURLToPath8 } from "url";
5258
+ var __dirname7 = dirname7(fileURLToPath8(import.meta.url));
5259
+ var STATIC_DIR = join5(__dirname7, "static");
5398
5260
  var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
5399
5261
  function getConsoleToken() {
5400
5262
  return process.env[CONSOLE_TOKEN_ENV] || "";
@@ -5419,8 +5281,8 @@ function createConsoleRouter() {
5419
5281
  console2.use("/api/*", authMiddleware());
5420
5282
  const serveIndex = (c) => {
5421
5283
  const indexFile = join5(STATIC_DIR, "index.html");
5422
- if (existsSync9(indexFile)) {
5423
- return c.html(readFileSync9(indexFile, "utf-8"));
5284
+ if (existsSync10(indexFile)) {
5285
+ return c.html(readFileSync10(indexFile, "utf-8"));
5424
5286
  }
5425
5287
  return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
5426
5288
  };
@@ -5511,13 +5373,13 @@ function createConsoleRouter() {
5511
5373
  if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
5512
5374
  return c.json({ error: "invalid skill_id" }, 400);
5513
5375
  }
5514
- const skillPath = join5(dirname6(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
5515
- if (!existsSync9(skillPath)) {
5516
- const builtinPath = join5(__dirname6, "..", "..", "agent_moss", "skills", `${skillId}.md`);
5517
- if (existsSync9(builtinPath)) return c.text(readFileSync9(builtinPath, "utf-8"));
5376
+ const skillPath = join5(dirname7(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
5377
+ if (!existsSync10(skillPath)) {
5378
+ const builtinPath = join5(__dirname7, "..", "..", "agent_moss", "skills", `${skillId}.md`);
5379
+ if (existsSync10(builtinPath)) return c.text(readFileSync10(builtinPath, "utf-8"));
5518
5380
  return c.json({ error: "skill not found" }, 404);
5519
5381
  }
5520
- return c.text(readFileSync9(skillPath, "utf-8"));
5382
+ return c.text(readFileSync10(skillPath, "utf-8"));
5521
5383
  });
5522
5384
  console2.get("/api/agents", (c) => {
5523
5385
  const rc = loadRuntimeConfig();
@@ -5601,12 +5463,12 @@ function createApp() {
5601
5463
  }
5602
5464
  async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
5603
5465
  for (let port = startPort; port < startPort + scan; port++) {
5604
- const free = await new Promise((resolve6) => {
5466
+ const free = await new Promise((resolve7) => {
5605
5467
  const server = net.createServer();
5606
5468
  server.listen(port, host, () => {
5607
- server.close(() => resolve6(true));
5469
+ server.close(() => resolve7(true));
5608
5470
  });
5609
- server.on("error", () => resolve6(false));
5471
+ server.on("error", () => resolve7(false));
5610
5472
  });
5611
5473
  if (free) {
5612
5474
  if (port !== startPort) {
@@ -5631,7 +5493,7 @@ async function startServer(host = "127.0.0.1", port = 9090) {
5631
5493
  }
5632
5494
 
5633
5495
  // src/cli.ts
5634
- import { fileURLToPath as fileURLToPath8 } from "url";
5496
+ import { fileURLToPath as fileURLToPath9 } from "url";
5635
5497
  import { realpathSync } from "fs";
5636
5498
  function printHelp() {
5637
5499
  console.log(`AgentMoss security analysis engine (TypeScript v${version})
@@ -5722,7 +5584,7 @@ async function main() {
5722
5584
  const { execSync } = await import("child_process");
5723
5585
  const fs4 = await import("fs");
5724
5586
  const path3 = await import("path");
5725
- const { fileURLToPath: fileURLToPath9 } = await import("url");
5587
+ const { fileURLToPath: fileURLToPath10 } = await import("url");
5726
5588
  if (process.getuid?.() !== 0) {
5727
5589
  try {
5728
5590
  execSync("sudo -n true", { stdio: "ignore", timeout: 5e3 });
@@ -5737,18 +5599,18 @@ async function main() {
5737
5599
  execSync(sudoArgs.join(" "), { stdio: "inherit" });
5738
5600
  process.exit(0);
5739
5601
  }
5740
- const __filename5 = fileURLToPath9(import.meta.url);
5741
- const __dirname7 = path3.dirname(__filename5);
5742
- let srcPath = path3.join(__dirname7, "data", "agent_moss.service");
5602
+ const __filename6 = fileURLToPath10(import.meta.url);
5603
+ const __dirname8 = path3.dirname(__filename6);
5604
+ let srcPath = path3.join(__dirname8, "data", "agent_moss.service");
5743
5605
  if (!fs4.existsSync(srcPath)) {
5744
- srcPath = path3.join(__dirname7, "..", "src", "data", "agent_moss.service");
5606
+ srcPath = path3.join(__dirname8, "..", "src", "data", "agent_moss.service");
5745
5607
  }
5746
5608
  if (!fs4.existsSync(srcPath)) {
5747
5609
  console.error("\u9519\u8BEF\uFF1A\u627E\u4E0D\u5230 agent_moss.service \u6A21\u677F\u6587\u4EF6");
5748
5610
  process.exit(1);
5749
5611
  }
5750
5612
  const which = execSync("which agent-moss 2>/dev/null || true", { encoding: "utf-8" }).trim();
5751
- const binPath = which || `${process.execPath} ${path3.join(__dirname7, "cli.js")}`;
5613
+ const binPath = which || `${process.execPath} ${path3.join(__dirname8, "cli.js")}`;
5752
5614
  let content = fs4.readFileSync(srcPath, "utf-8");
5753
5615
  content = content.replace("__AGENT_MOSS_BIN__", binPath);
5754
5616
  const dst = "/etc/systemd/system/agent-moss.service";
@@ -5792,13 +5654,13 @@ async function main() {
5792
5654
  let version2 = "";
5793
5655
  for (let port = 9090; port <= 9095; port++) {
5794
5656
  try {
5795
- const data = await new Promise((resolve6, reject) => {
5657
+ const data = await new Promise((resolve7, reject) => {
5796
5658
  const req = http.get(`http://127.0.0.1:${port}/api/v1/health`, { timeout: 2e3 }, (res) => {
5797
5659
  let body = "";
5798
5660
  res.on("data", (chunk) => body += chunk);
5799
5661
  res.on("end", () => {
5800
5662
  try {
5801
- resolve6(JSON.parse(body));
5663
+ resolve7(JSON.parse(body));
5802
5664
  } catch {
5803
5665
  reject();
5804
5666
  }
@@ -5826,13 +5688,13 @@ async function main() {
5826
5688
  console.log(` Systemd: \u672A\u6CE8\u518C\uFF08\u8FD0\u884C agent-moss install --enable \u5B89\u88C5\uFF09`);
5827
5689
  }
5828
5690
  try {
5829
- const data = await new Promise((resolve6, reject) => {
5691
+ const data = await new Promise((resolve7, reject) => {
5830
5692
  const req = http.get(`http://127.0.0.1:${foundPort}/console/api/config`, { timeout: 3e3 }, (res) => {
5831
5693
  let body = "";
5832
5694
  res.on("data", (chunk) => body += chunk);
5833
5695
  res.on("end", () => {
5834
5696
  try {
5835
- resolve6(JSON.parse(body));
5697
+ resolve7(JSON.parse(body));
5836
5698
  } catch {
5837
5699
  reject();
5838
5700
  }
@@ -5879,9 +5741,9 @@ async function main() {
5879
5741
  var isMain = (() => {
5880
5742
  try {
5881
5743
  if (!process.argv[1]) return false;
5882
- const thisFile = realpathSync(fileURLToPath8(import.meta.url));
5744
+ const thisFile = realpathSync(fileURLToPath9(import.meta.url));
5883
5745
  const argv1 = realpathSync(
5884
- process.argv[1].startsWith("file://") ? fileURLToPath8(process.argv[1]) : process.argv[1]
5746
+ process.argv[1].startsWith("file://") ? fileURLToPath9(process.argv[1]) : process.argv[1]
5885
5747
  );
5886
5748
  return thisFile === argv1;
5887
5749
  } catch {