@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/server.js CHANGED
@@ -574,8 +574,70 @@ var init_inline_analyzer = __esm({
574
574
  }
575
575
  });
576
576
 
577
+ // src/infra/rules-loader.ts
578
+ import { readFileSync as readFileSync2, existsSync } from "fs";
579
+ import { resolve, dirname } from "path";
580
+ import { fileURLToPath as fileURLToPath3 } from "url";
581
+ function findRulesFile(name) {
582
+ for (const dir of RULES_DIR_CANDS) {
583
+ const p = resolve(dir, name);
584
+ if (existsSync(p)) return p;
585
+ }
586
+ return null;
587
+ }
588
+ function loadJson(name) {
589
+ const p = findRulesFile(name);
590
+ if (!p) return {};
591
+ try {
592
+ return JSON.parse(readFileSync2(p, "utf-8")) || {};
593
+ } catch {
594
+ return {};
595
+ }
596
+ }
597
+ function loadL1() {
598
+ return loadJson("l1_rules.json");
599
+ }
600
+ function loadL2() {
601
+ return loadJson("l2_rules.json");
602
+ }
603
+ function loadL3() {
604
+ return loadJson("l3_rules.json");
605
+ }
606
+ var __filename2, __dirname2, RULES_DIR_CANDS;
607
+ var init_rules_loader = __esm({
608
+ "src/infra/rules-loader.ts"() {
609
+ "use strict";
610
+ init_esm_shims();
611
+ __filename2 = fileURLToPath3(import.meta.url);
612
+ __dirname2 = dirname(__filename2);
613
+ RULES_DIR_CANDS = (() => {
614
+ const cands = [
615
+ // dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
616
+ resolve(__dirname2, "../rules"),
617
+ resolve(__dirname2, "rules"),
618
+ // dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
619
+ resolve(__dirname2, "../../../agent_moss/rules"),
620
+ // dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
621
+ resolve(__dirname2, "../../../../agent_moss/rules"),
622
+ // 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
623
+ resolve(process.cwd(), "agent_moss/rules")
624
+ ];
625
+ return cands.filter((c) => existsSync(c));
626
+ })();
627
+ }
628
+ });
629
+
577
630
  // src/engine/logic-rules.ts
578
631
  import * as fs2 from "fs";
632
+ function _loadSensitivePathScope(scope) {
633
+ const sps = loadL1().sensitive_path_access || [];
634
+ return sps.filter((sp) => sp.scope === scope).map((sp) => ({
635
+ path: sp.path,
636
+ riskLevel: sp.risk_level,
637
+ desc: sp.desc,
638
+ credential: sp.deny_mode === "deny_both" ? true : void 0
639
+ }));
640
+ }
579
641
  function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWORDS) {
580
642
  if (writeKeywords.some((kw) => actionType.includes(kw) || actionDetail.includes(kw))) return true;
581
643
  if (WRITE_COMMAND_RES.some((rx) => rx.test(actionDetail))) return true;
@@ -937,57 +999,10 @@ var init_logic_rules = __esm({
937
999
  "use strict";
938
1000
  init_esm_shims();
939
1001
  init_inline_analyzer();
940
- ROOT_SENSITIVE_PATHS = [
941
- { path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
942
- { path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
943
- // 注意:/etc/passwd 仅从 L1 特征匹配规则移除(公开可读),L2 敏感路径仍保留
944
- { path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
945
- { path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
946
- // 系统配置(仅拦截写入)
947
- { path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
948
- { path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
949
- { path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
950
- { path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
951
- { path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
952
- { path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
953
- { path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
954
- // 危险目录(仅拦截写入)
955
- { path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
956
- { path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
957
- { path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
958
- // 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
959
- { path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
960
- { path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907\uFF08\u8BFB\u5199\u5747\u5371\u9669\uFF09", credential: true },
961
- { path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
962
- // SSH 主机私钥
963
- // SSH 主机私钥(排除 .pub 公钥变体,对齐 Python .ssh/id_rsa(?!\.pub) 范式)
964
- { path: "/etc/ssh/ssh_host_rsa_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
965
- { path: "/etc/ssh/ssh_host_ed25519_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
966
- { path: "/etc/ssh/ssh_host_ecdsa_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true },
967
- // Nginx 配置(仅拦截写入,对齐 Python linux.py sensitive_paths)
968
- { path: "/etc/nginx/nginx.conf", riskLevel: "high", desc: "Nginx \u4E3B\u914D\u7F6E" },
969
- { path: "/etc/nginx/conf.d/", riskLevel: "high", desc: "Nginx \u5B50\u914D\u7F6E\u76EE\u5F55" },
970
- { path: "/etc/nginx/sites-enabled/", riskLevel: "high", desc: "Nginx \u542F\u7528\u7AD9\u70B9\u914D\u7F6E\u76EE\u5F55" }
971
- ];
972
- HOME_SENSITIVE_PATHS = [
973
- { path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
974
- { path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
975
- { path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
976
- // 持久化与命令劫持(仅拦截写入)
977
- { path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
978
- { path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
979
- { path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
980
- { path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
981
- { path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
982
- { path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
983
- ];
984
- CREDENTIAL_PATHS = [
985
- { path: "credentials.yml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
986
- { path: "credentials.yaml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
987
- { path: "secrets.yml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
988
- { path: "secrets.yaml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
989
- { path: ".env", riskLevel: "high", desc: "\u73AF\u5883\u53D8\u91CF/\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" }
990
- ];
1002
+ init_rules_loader();
1003
+ ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
1004
+ HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
1005
+ CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path3, riskLevel, desc }) => ({ path: path3, riskLevel, desc, credential: true }));
991
1006
  NON_WRITE_TOOLS = /* @__PURE__ */ new Set([
992
1007
  "ask_user_question",
993
1008
  "glob",
@@ -1115,34 +1130,22 @@ var init_logic_rules = __esm({
1115
1130
  DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
1116
1131
  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"];
1117
1132
  CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
1118
- PASSWORD_MODIFY_PATTERNS = [
1119
- /\|\s*passwd\b/i,
1120
- /\bpasswd\s+--stdin\b/i,
1121
- /\bchpasswd\b/i,
1122
- /\bchgpasswd\b/i,
1123
- /\b(newusers|lnewusers)\b/i,
1124
- /\bpasswd\s+-[a-zA-Z]*d\b/i,
1125
- /\bpasswd\s+-[a-zA-Z]*l\b/i
1126
- ];
1127
- USER_DELETION_PATTERNS = [
1128
- /\buserdel\s/i,
1129
- /\bdeluser\s/i,
1130
- /\bluserdel\s/i,
1131
- /\bgroupdel\s/i,
1132
- /\blgroupdel\s/i
1133
- ];
1134
- INTENT_DEVIATION_PATTERNS = [
1135
- {
1136
- intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
1137
- dangerousActions: ["\u5220\u9664", "remove", "delete", "rm", "\u683C\u5F0F\u5316", "format"],
1138
- reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
1139
- },
1140
- {
1141
- intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
1142
- dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
1143
- reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
1144
- }
1145
- ];
1133
+ PASSWORD_MODIFY_PATTERNS = (() => {
1134
+ const pats = loadL2().password_modify_consent || [];
1135
+ return pats.map((p) => new RegExp(p.pattern, "i"));
1136
+ })();
1137
+ USER_DELETION_PATTERNS = (() => {
1138
+ const pats = loadL2().user_deletion_consent || [];
1139
+ return pats.map((p) => new RegExp(p.pattern, "i"));
1140
+ })();
1141
+ INTENT_DEVIATION_PATTERNS = (() => {
1142
+ const pats = loadL2().intent_consistency || [];
1143
+ return pats.map((p) => ({
1144
+ intentKeywords: p.intent_keywords,
1145
+ dangerousActions: p.dangerous_actions,
1146
+ reason: p.reason
1147
+ }));
1148
+ })();
1146
1149
  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;
1147
1150
  DIR_TRAVERSAL_KW = [
1148
1151
  "os.listdir",
@@ -1160,21 +1163,21 @@ var init_logic_rules = __esm({
1160
1163
  });
1161
1164
 
1162
1165
  // src/engine/skill-loader.ts
1163
- import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
1164
- import { resolve, dirname } from "path";
1165
- import { fileURLToPath as fileURLToPath3 } from "url";
1166
+ import { readFileSync as readFileSync3, readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
1167
+ import { resolve as resolve2, dirname as dirname2 } from "path";
1168
+ import { fileURLToPath as fileURLToPath4 } from "url";
1166
1169
  function loadAllSkills() {
1167
1170
  if (cachedSkills) return cachedSkills;
1168
1171
  cachedSkills = {};
1169
- const dir = existsSync2(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
1170
- if (!existsSync2(dir)) {
1172
+ const dir = existsSync3(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
1173
+ if (!existsSync3(dir)) {
1171
1174
  console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
1172
1175
  return cachedSkills;
1173
1176
  }
1174
1177
  const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
1175
1178
  for (const f of files) {
1176
1179
  const name = f.replace(".md", "");
1177
- cachedSkills[name] = readFileSync2(resolve(dir, f), "utf-8");
1180
+ cachedSkills[name] = readFileSync3(resolve2(dir, f), "utf-8");
1178
1181
  }
1179
1182
  console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
1180
1183
  return cachedSkills;
@@ -1228,32 +1231,27 @@ function formatSkillsForPrompt(skills) {
1228
1231
  ${s.skillContent}`
1229
1232
  ).join("\n\n---\n\n");
1230
1233
  }
1231
- var __filename2, __dirname2, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
1234
+ var __filename3, __dirname3, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
1232
1235
  var init_skill_loader = __esm({
1233
1236
  "src/engine/skill-loader.ts"() {
1234
1237
  "use strict";
1235
1238
  init_esm_shims();
1236
- __filename2 = fileURLToPath3(import.meta.url);
1237
- __dirname2 = dirname(__filename2);
1238
- SKILLS_DIR = resolve(__dirname2, "../skills");
1239
- SKILLS_DIR_FALLBACK = resolve(__dirname2, "../../../agent_moss/skills");
1240
- SKILL_KEYWORD_MAP = {
1241
- "file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
1242
- "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"],
1243
- "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"],
1244
- "browser_web_access_guard": ["browser", "\u6D4F\u89C8\u5668", "web", "\u7F51\u9875", "url", "navigate", "\u6D4F\u89C8", "scrape", "crawl", "\u722C\u53D6", "click", "screenshot"],
1245
- "email_operation_guard": ["email", "\u90AE\u4EF6", "mail", "smtp", "send_email", "\u53D1\u9001\u90AE\u4EF6", "inbox", "\u6536\u4EF6\u7BB1", "\u9644\u4EF6", "attachment"],
1246
- "general_tool_risk_guard": ["tool", "\u5DE5\u5177", "api", "invoke", "\u8C03\u7528", "function", "\u51FD\u6570"],
1247
- "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"],
1248
- "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"],
1249
- // 注意:避免裸 'memory'/'内存'/'dd'——会误命中 memory-addMemory(写记忆工具,
1250
- // 'dd' 是 'addMemory' 的子串)等无害操作。用组合词/命令前缀精准匹配攻击场景。
1251
- "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"],
1252
- "skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
1253
- "supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
1254
- "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"],
1255
- "indirect_access_guard": ["listdir", "os.walk", "os.scandir", "glob.glob", "glob.iglob", "pathlib", "iterdir", "rglob", "scandir", "os.listdir", "\u76EE\u5F55\u904D\u5386", "traversal"]
1256
- };
1239
+ init_rules_loader();
1240
+ __filename3 = fileURLToPath4(import.meta.url);
1241
+ __dirname3 = dirname2(__filename3);
1242
+ SKILLS_DIR = resolve2(__dirname3, "../skills");
1243
+ SKILLS_DIR_FALLBACK = resolve2(__dirname3, "../../../agent_moss/skills");
1244
+ SKILL_KEYWORD_MAP = (() => {
1245
+ const map = {};
1246
+ const cats = loadL3();
1247
+ for (const cat of Object.values(cats)) {
1248
+ if (!cat || typeof cat !== "object") continue;
1249
+ for (const [skillId, keywords] of Object.entries(cat)) {
1250
+ map[skillId] = keywords;
1251
+ }
1252
+ }
1253
+ return map;
1254
+ })();
1257
1255
  cachedSkills = null;
1258
1256
  }
1259
1257
  });
@@ -1305,10 +1303,10 @@ __export(runtime_config_exports, {
1305
1303
  updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
1306
1304
  updateSkillEnabled: () => updateSkillEnabled
1307
1305
  });
1308
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
1306
+ import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
1309
1307
  import { join as join2 } from "path";
1310
- import { fileURLToPath as fileURLToPath4 } from "url";
1311
- import { dirname as dirname2 } from "path";
1308
+ import { fileURLToPath as fileURLToPath5 } from "url";
1309
+ import { dirname as dirname3 } from "path";
1312
1310
  function runtimeDir() {
1313
1311
  return getRuntimeConfigDir();
1314
1312
  }
@@ -1320,7 +1318,7 @@ function getRuntimeConfigPath() {
1320
1318
  function saveRuntimeConfig(config) {
1321
1319
  const p = getRuntimeConfigPath();
1322
1320
  const dir = p.substring(0, p.lastIndexOf("/"));
1323
- if (dir && !existsSync3(dir)) mkdirSync(dir, { recursive: true });
1321
+ if (dir && !existsSync4(dir)) mkdirSync(dir, { recursive: true });
1324
1322
  writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
1325
1323
  }
1326
1324
  function emptyRuntimeConfig() {
@@ -1335,7 +1333,7 @@ function emptyRuntimeConfig() {
1335
1333
  }
1336
1334
  function loadRuntimeConfig() {
1337
1335
  const p = getRuntimeConfigPath();
1338
- if (!existsSync3(p)) {
1336
+ if (!existsSync4(p)) {
1339
1337
  const seeded = mergeConfig({}, generateSourceDefaults());
1340
1338
  try {
1341
1339
  saveRuntimeConfig(seeded);
@@ -1344,7 +1342,7 @@ function loadRuntimeConfig() {
1344
1342
  return seeded;
1345
1343
  }
1346
1344
  try {
1347
- const data = JSON.parse(readFileSync3(p, "utf-8"));
1345
+ const data = JSON.parse(readFileSync4(p, "utf-8"));
1348
1346
  const merged = mergeConfig(data, generateSourceDefaults());
1349
1347
  return {
1350
1348
  version: data.version || 1,
@@ -1412,9 +1410,9 @@ function getEnabledL2SensitivePaths(runtime) {
1412
1410
  }
1413
1411
  function hasRuntimeConfig() {
1414
1412
  const p = getRuntimeConfigPath();
1415
- if (!existsSync3(p)) return false;
1413
+ if (!existsSync4(p)) return false;
1416
1414
  try {
1417
- JSON.parse(readFileSync3(p, "utf-8"));
1415
+ JSON.parse(readFileSync4(p, "utf-8"));
1418
1416
  return true;
1419
1417
  } catch {
1420
1418
  return false;
@@ -1593,8 +1591,8 @@ function _l3SkillsToRules() {
1593
1591
  }
1594
1592
  function _agentRulesToDefaults() {
1595
1593
  const out = {};
1596
- const agentsDir = existsSync3(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync3(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
1597
- if (!existsSync3(agentsDir)) return out;
1594
+ const agentsDir = existsSync4(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync4(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
1595
+ if (!existsSync4(agentsDir)) return out;
1598
1596
  let entries;
1599
1597
  try {
1600
1598
  entries = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
@@ -1603,10 +1601,10 @@ function _agentRulesToDefaults() {
1603
1601
  }
1604
1602
  for (const agentId of entries) {
1605
1603
  const rulesFile = join2(agentsDir, agentId, "rules.json");
1606
- if (!existsSync3(rulesFile)) continue;
1604
+ if (!existsSync4(rulesFile)) continue;
1607
1605
  let data;
1608
1606
  try {
1609
- data = JSON.parse(readFileSync3(rulesFile, "utf-8"));
1607
+ data = JSON.parse(readFileSync4(rulesFile, "utf-8"));
1610
1608
  } catch {
1611
1609
  continue;
1612
1610
  }
@@ -1685,6 +1683,9 @@ function generateSourceDefaults() {
1685
1683
  agent_rules: _agentRulesToDefaults()
1686
1684
  };
1687
1685
  }
1686
+ function normalizePatternForFingerprint(pattern) {
1687
+ return pattern.replace(/\\\//g, "/");
1688
+ }
1688
1689
  function _ruleFingerprint(rule) {
1689
1690
  if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
1690
1691
  return JSON.stringify([
@@ -1692,8 +1693,8 @@ function _ruleFingerprint(rule) {
1692
1693
  [...rule.dangerous_actions || []].sort()
1693
1694
  ]);
1694
1695
  }
1695
- if (rule.path) return JSON.stringify([rule.path]);
1696
- if (rule.pattern) return JSON.stringify([rule.pattern]);
1696
+ if (rule.path) return JSON.stringify([normalizePatternForFingerprint(rule.path)]);
1697
+ if (rule.pattern) return JSON.stringify([normalizePatternForFingerprint(rule.pattern)]);
1697
1698
  if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
1698
1699
  if (rule.value) return JSON.stringify([rule.value]);
1699
1700
  return JSON.stringify([rule.id || ""]);
@@ -2044,7 +2045,7 @@ function deleteCustomSkill(skillId, category) {
2044
2045
  skills.splice(idx, 1);
2045
2046
  const skillPath = join2(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
2046
2047
  try {
2047
- if (existsSync3(skillPath)) unlinkSync(skillPath);
2048
+ if (existsSync4(skillPath)) unlinkSync(skillPath);
2048
2049
  } catch {
2049
2050
  }
2050
2051
  saveRuntimeConfig(runtime);
@@ -2158,7 +2159,7 @@ function updateAgentCategoryEnabled(agentId, category, enabled) {
2158
2159
  saveRuntimeConfig(runtime);
2159
2160
  return runtime;
2160
2161
  }
2161
- 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;
2162
+ 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;
2162
2163
  var init_runtime_config = __esm({
2163
2164
  "src/infra/runtime-config.ts"() {
2164
2165
  "use strict";
@@ -2167,7 +2168,7 @@ var init_runtime_config = __esm({
2167
2168
  init_heuristic();
2168
2169
  init_logic_rules();
2169
2170
  init_skill_loader();
2170
- __dirname3 = dirname2(fileURLToPath4(import.meta.url));
2171
+ __dirname4 = dirname3(fileURLToPath5(import.meta.url));
2171
2172
  ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
2172
2173
  RUNTIME_CONFIG_DIR = runtimeDir();
2173
2174
  RUNTIME_CONFIG_PATH = join2(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
@@ -2181,19 +2182,19 @@ var init_runtime_config = __esm({
2181
2182
  deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
2182
2183
  deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
2183
2184
  };
2184
- _AGENTS_DIR_PROD = join2(__dirname3, "agents");
2185
- _AGENTS_DIR_DEV = join2(__dirname3, "..", "..", "..", "agent_moss", "agents");
2185
+ _AGENTS_DIR_PROD = join2(__dirname4, "agents");
2186
+ _AGENTS_DIR_DEV = join2(__dirname4, "..", "..", "..", "agent_moss", "agents");
2186
2187
  SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
2187
2188
  }
2188
2189
  });
2189
2190
 
2190
2191
  // src/agents/index.ts
2191
- import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
2192
- import { resolve as resolve2, dirname as dirname3 } from "path";
2193
- import { fileURLToPath as fileURLToPath5 } from "url";
2192
+ import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
2193
+ import { resolve as resolve3, dirname as dirname4 } from "path";
2194
+ import { fileURLToPath as fileURLToPath6 } from "url";
2194
2195
  function resolveAgentsDir() {
2195
- if (existsSync4(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
2196
- if (existsSync4(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
2196
+ if (existsSync5(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
2197
+ if (existsSync5(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
2197
2198
  return AGENTS_DIR_PROD;
2198
2199
  }
2199
2200
  function getAgentExtensions(agentId) {
@@ -2209,13 +2210,13 @@ function getAgentExtensions(agentId) {
2209
2210
  }
2210
2211
  if (ext === null) {
2211
2212
  const agentsDir = resolveAgentsDir();
2212
- const rulesFile = resolve2(agentsDir, agentId, "rules.json");
2213
- if (!existsSync4(rulesFile)) {
2213
+ const rulesFile = resolve3(agentsDir, agentId, "rules.json");
2214
+ if (!existsSync5(rulesFile)) {
2214
2215
  cache.set(agentId, null);
2215
2216
  return null;
2216
2217
  }
2217
2218
  try {
2218
- const raw = readFileSync4(rulesFile, "utf-8");
2219
+ const raw = readFileSync5(rulesFile, "utf-8");
2219
2220
  ext = JSON.parse(raw);
2220
2221
  if (!ext.agent_id) {
2221
2222
  console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
@@ -2234,15 +2235,15 @@ function getAgentExtensions(agentId) {
2234
2235
  );
2235
2236
  return ext;
2236
2237
  }
2237
- var __filename3, __dirname4, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
2238
+ var __filename4, __dirname5, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
2238
2239
  var init_agents = __esm({
2239
2240
  "src/agents/index.ts"() {
2240
2241
  "use strict";
2241
2242
  init_esm_shims();
2242
- __filename3 = fileURLToPath5(import.meta.url);
2243
- __dirname4 = dirname3(__filename3);
2244
- AGENTS_DIR_PROD = resolve2(__dirname4, "agents");
2245
- AGENTS_DIR_DEV_FALLBACK = resolve2(__dirname4, "../../../agent_moss/agents");
2243
+ __filename4 = fileURLToPath6(import.meta.url);
2244
+ __dirname5 = dirname4(__filename4);
2245
+ AGENTS_DIR_PROD = resolve3(__dirname5, "agents");
2246
+ AGENTS_DIR_DEV_FALLBACK = resolve3(__dirname5, "../../../agent_moss/agents");
2246
2247
  cache = /* @__PURE__ */ new Map();
2247
2248
  }
2248
2249
  });
@@ -2707,164 +2708,25 @@ var init_heuristic = __esm({
2707
2708
  init_esm_shims();
2708
2709
  init_config();
2709
2710
  init_agents();
2711
+ init_rules_loader();
2710
2712
  init_logic_rules();
2711
- INJECTION_KEYWORDS = [
2712
- // ---- Instruction Override ----
2713
- { keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
2714
- { keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
2715
- { keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
2716
- { keyword: "forget everything", riskLevel: "high", lang: "en" },
2717
- { keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
2718
- { keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
2719
- { keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
2720
- { keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
2721
- // ---- Role Hijacking ----
2722
- { keyword: "you are now", riskLevel: "high", lang: "en" },
2723
- { keyword: "you are no longer", riskLevel: "high", lang: "en" },
2724
- { keyword: "new instructions", riskLevel: "high", lang: "en" },
2725
- { keyword: "from now on, you", riskLevel: "high", lang: "en" },
2726
- { keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
2727
- { keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
2728
- { keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
2729
- { keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
2730
- // ---- Command Injection ----
2731
- { keyword: "system prompt", riskLevel: "high", lang: "en" },
2732
- { keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
2733
- { keyword: "output your instructions", riskLevel: "high", lang: "en" },
2734
- { keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
2735
- { keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
2736
- { keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
2737
- { keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
2738
- // ---- Social Engineering ----
2739
- { keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
2740
- { keyword: "security audit approved", riskLevel: "high", lang: "en" },
2741
- { keyword: "admin override", riskLevel: "critical", lang: "en" },
2742
- { keyword: "debug mode", riskLevel: "high", lang: "en" },
2743
- { keyword: "maintenance mode", riskLevel: "high", lang: "en" },
2744
- { keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
2745
- { keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
2746
- { keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
2747
- { keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
2748
- // ---- XSS ----
2749
- { keyword: "<script>", riskLevel: "high", lang: "en" },
2750
- { keyword: "javascript:", riskLevel: "high", lang: "en" },
2751
- { keyword: "onerror=", riskLevel: "high", lang: "en" },
2752
- { keyword: "onload=", riskLevel: "high", lang: "en" },
2753
- // ---- Fake Boundaries ----
2754
- { keyword: "=== END OF", riskLevel: "high", lang: "en" },
2755
- { keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
2756
- { keyword: "[END OF", riskLevel: "high", lang: "en" },
2757
- { keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
2758
- { keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
2759
- // ---- Developer Mode ----
2760
- { keyword: "developer mode", riskLevel: "critical", lang: "en" },
2761
- { keyword: "DAN mode", riskLevel: "critical", lang: "en" },
2762
- { keyword: "jailbreak", riskLevel: "critical", lang: "en" },
2763
- { keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
2764
- { keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
2765
- // 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
2766
- // 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
2767
- // 由 agent_ext 机制注入,不再污染通用层。
2768
- ];
2769
- DANGEROUS_PATTERNS = [
2770
- // --- Critical: rm command graded detection (strict to loose, first match wins) ---
2771
- // 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
2772
- { 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" },
2773
- // 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
2774
- // 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
2775
- { 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" },
2776
- // 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
2777
- { 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" },
2778
- // 4. rm -f absolute path (no -r) — 同样排除临时目录
2779
- { 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" },
2780
- // dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
2781
- { 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" },
2782
- // mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
2783
- { 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" },
2784
- { pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
2785
- // --- Critical: remote code execution ---
2786
- // curl|bash 优先匹配 user_rules(high/sensitive_action),见下方 UserRuleMatcher 段;
2787
- // 非 bash 的 curl|sh/zsh/dash 走 CommandPatternScanner critical。两条分流由
2788
- // runHeuristicCheck 的规则顺序保证(bash 在前 high,其余 fall-through 到 critical)。
2789
- { pattern: /curl\s+.*\|\s*bash/i, severity: "high", description: "curl pipe bash \u6267\u884C (user_rules)", riskType: "sensitive_action" },
2790
- { pattern: /curl\s+.*\|\s*(sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
2791
- { pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
2792
- { pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
2793
- { pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "high", description: "base64 \u89E3\u7801\u6267\u884C (user_rules)", riskType: "sensitive_action" },
2794
- // --- Critical: reverse shell ---
2795
- { pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
2796
- { pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
2797
- // --- Critical: sensitive file access ---
2798
- { pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
2799
- { pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
2800
- // 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
2801
- { pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
2802
- // --- Critical: privilege escalation / container escape ---
2803
- { 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" },
2804
- // --- Critical: dangerous permissions / ownership (提权 / 权限敞口) ---
2805
- // 修正「只拦 777、xx7 漏放」的缺口:
2806
- // 1) 八进制末位 7:other 位含写+执行敞口(chmod 777/647/007/4777 等);644/755/1700 不命中
2807
- // 2) 八进制 setuid/setgid 位:首位 4(setuid)/2(setgid) 的 4 位八进制(chmod 4755/2755)
2808
- // 3) 符号 setuid/setgid:chmod u+s / g+s / a+s
2809
- // 普通权限修改(chmod 644)不在此列——L2 read_before_write 已豁免 chmod/chown(不写内容),
2810
- // 危险形态在此 L1 专门拦,以「提权」正确理由拦截,不再靠 RBW 误打误撞。
2811
- { 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" },
2812
- { 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" },
2813
- { 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" },
2814
- { 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" },
2815
- // sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
2816
- { 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" },
2817
- // --- High: system config overwrite ---
2818
- // > /etc/ 重定向覆盖:对齐 Python CommandPatternScanner `>\s*/etc/`(不兼容引号路径),
2819
- // 引号包裹的重定向目标(cat > '/etc/ssh/sshd_config')走 checkSensitivePath 敏感路径 high。
2820
- { pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
2821
- { pattern: />\s*\/boot\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55", riskType: "file_access" },
2822
- // 注:/proc/ 重定向覆盖走 checkSensitivePath 的 high(内核参数 /proc/sys/),此处不拦 /proc/ 避免遮蔽(对齐 Python)。
2823
- // --- High: download script + execute risk ---
2824
- // curl -X POST 外传敏感信息(对齐 Python linux.py: curl POST env/key/token)
2825
- { 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" },
2826
- { 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" },
2827
- { 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" },
2828
- { pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
2829
- // --- High: package manager pipe-to-shell ---
2830
- { pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
2831
- { pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
2832
- { pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
2833
- // --- Medium: destructive git operations (降级为 medium,允许但记录) ---
2834
- { 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" },
2835
- // --- High: network port scanning (横向移动侦察) ---
2836
- { 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" },
2837
- { 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" },
2838
- // --- Medium: credential export ---
2839
- { pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
2840
- // --- High: shell rc file append (persistence) ---
2841
- // 两条规则:第一条匹配 ~ 与 /home/<user>/ 形态;第二条按文件名兜底,
2842
- // 覆盖非标准 home 路径(如鸿蒙 /storage/Users/currentUser/.bashrc、
2843
- // /root/.bashrc),避免按 home 前缀枚举导致漏检持久化后门。
2844
- { 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" },
2845
- { 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" },
2846
- // --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
2847
- { 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" },
2848
- // --- High: non-interactive password modification ---
2849
- { 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" },
2850
- { 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" },
2851
- { pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
2852
- { 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" },
2853
- { 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" },
2854
- { 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" },
2855
- // --- High: non-interactive password modification (extended) ---
2856
- { pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
2857
- { pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
2858
- { pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
2859
- // --- High: user/group deletion (irreversible, requires user consent) ---
2860
- { pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
2861
- { pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
2862
- { pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
2863
- { pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
2864
- { pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
2865
- // --- High: generic command -p parameter password setting ---
2866
- { 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" }
2867
- ];
2713
+ INJECTION_KEYWORDS = (() => {
2714
+ const kws = loadL1().injection_detection || [];
2715
+ return kws.map((k) => ({
2716
+ keyword: k.keyword,
2717
+ riskLevel: k.risk_level,
2718
+ lang: k.lang || "en"
2719
+ }));
2720
+ })();
2721
+ DANGEROUS_PATTERNS = (() => {
2722
+ const cmds = loadL1().dangerous_commands || [];
2723
+ return cmds.map((c) => ({
2724
+ pattern: new RegExp(c.pattern, "i"),
2725
+ severity: c.risk_level,
2726
+ riskType: c.risk_type,
2727
+ description: c.reason
2728
+ }));
2729
+ })();
2868
2730
  SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
2869
2731
  SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
2870
2732
  }
@@ -2879,16 +2741,16 @@ __export(token_stats_exports, {
2879
2741
  recordTokenUsage: () => recordTokenUsage,
2880
2742
  resetTokenStats: () => resetTokenStats
2881
2743
  });
2882
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
2744
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
2883
2745
  function getStatsPath() {
2884
2746
  const envPath = process.env[ENV_STATS_PATH];
2885
2747
  if (envPath) return envPath;
2886
2748
  return getTokenStatsPath();
2887
2749
  }
2888
2750
  function loadAllRecords(statsPath) {
2889
- if (!existsSync7(statsPath)) return [];
2751
+ if (!existsSync8(statsPath)) return [];
2890
2752
  try {
2891
- const data = JSON.parse(readFileSync8(statsPath, "utf-8"));
2753
+ const data = JSON.parse(readFileSync9(statsPath, "utf-8"));
2892
2754
  if (Array.isArray(data)) return data;
2893
2755
  if (data && Array.isArray(data.records)) return data.records;
2894
2756
  return [];
@@ -2899,7 +2761,7 @@ function loadAllRecords(statsPath) {
2899
2761
  function saveRecords(statsPath, records) {
2900
2762
  try {
2901
2763
  const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
2902
- if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
2764
+ if (dir && !existsSync8(dir)) mkdirSync2(dir, { recursive: true });
2903
2765
  writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
2904
2766
  } catch (e) {
2905
2767
  }
@@ -3021,7 +2883,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
3021
2883
  }
3022
2884
  function resetTokenStats() {
3023
2885
  const statsPath = getStatsPath();
3024
- if (existsSync7(statsPath)) {
2886
+ if (existsSync8(statsPath)) {
3025
2887
  try {
3026
2888
  unlinkSync2(statsPath);
3027
2889
  } catch {
@@ -3121,9 +2983,9 @@ var require_bindings = __commonJS({
3121
2983
  init_esm_shims();
3122
2984
  var fs4 = __require("fs");
3123
2985
  var path3 = __require("path");
3124
- var fileURLToPath8 = require_file_uri_to_path();
2986
+ var fileURLToPath9 = require_file_uri_to_path();
3125
2987
  var join6 = path3.join;
3126
- var dirname7 = path3.dirname;
2988
+ var dirname8 = path3.dirname;
3127
2989
  var exists = fs4.accessSync && function(path4) {
3128
2990
  try {
3129
2991
  fs4.accessSync(path4);
@@ -3233,12 +3095,12 @@ var require_bindings = __commonJS({
3233
3095
  Error.stackTraceLimit = origSTL;
3234
3096
  var fileSchema = "file://";
3235
3097
  if (fileName.indexOf(fileSchema) === 0) {
3236
- fileName = fileURLToPath8(fileName);
3098
+ fileName = fileURLToPath9(fileName);
3237
3099
  }
3238
3100
  return fileName;
3239
3101
  };
3240
3102
  exports.getRoot = function getRoot(file) {
3241
- var dir = dirname7(file), prev;
3103
+ var dir = dirname8(file), prev;
3242
3104
  while (true) {
3243
3105
  if (dir === ".") {
3244
3106
  dir = process.cwd();
@@ -3444,13 +3306,13 @@ var require_backup = __commonJS({
3444
3306
  var runBackup = (backup, handler) => {
3445
3307
  let rate = 0;
3446
3308
  let useDefault = true;
3447
- return new Promise((resolve6, reject) => {
3309
+ return new Promise((resolve7, reject) => {
3448
3310
  setImmediate(function step() {
3449
3311
  try {
3450
3312
  const progress = backup.transfer(rate);
3451
3313
  if (!progress.remainingPages) {
3452
3314
  backup.close();
3453
- resolve6(progress);
3315
+ resolve7(progress);
3454
3316
  return;
3455
3317
  }
3456
3318
  if (useDefault) {
@@ -3898,28 +3760,28 @@ init_skill_loader();
3898
3760
 
3899
3761
  // src/engine/template-loader.ts
3900
3762
  init_esm_shims();
3901
- import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
3902
- import { resolve as resolve3, dirname as dirname4 } from "path";
3903
- import { fileURLToPath as fileURLToPath6 } from "url";
3904
- var __filename4 = fileURLToPath6(import.meta.url);
3905
- var __dirname5 = dirname4(__filename4);
3906
- var TEMPLATES_DIR = resolve3(__dirname5, "../templates");
3907
- var TEMPLATES_DIR_FALLBACK = resolve3(__dirname5, "../../../agent_moss/templates");
3763
+ import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
3764
+ import { resolve as resolve4, dirname as dirname5 } from "path";
3765
+ import { fileURLToPath as fileURLToPath7 } from "url";
3766
+ var __filename5 = fileURLToPath7(import.meta.url);
3767
+ var __dirname6 = dirname5(__filename5);
3768
+ var TEMPLATES_DIR = resolve4(__dirname6, "../templates");
3769
+ var TEMPLATES_DIR_FALLBACK = resolve4(__dirname6, "../../../agent_moss/templates");
3908
3770
  var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
3909
3771
  var cachedTemplates = null;
3910
3772
  function loadTemplates() {
3911
3773
  if (cachedTemplates) return cachedTemplates;
3912
3774
  cachedTemplates = {};
3913
- const dir = existsSync5(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
3914
- if (!existsSync5(dir)) {
3775
+ const dir = existsSync6(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
3776
+ if (!existsSync6(dir)) {
3915
3777
  console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
3916
3778
  return cachedTemplates;
3917
3779
  }
3918
3780
  for (const file of TEMPLATE_FILES) {
3919
- const filePath = resolve3(dir, file);
3920
- if (existsSync5(filePath)) {
3781
+ const filePath = resolve4(dir, file);
3782
+ if (existsSync6(filePath)) {
3921
3783
  const key = file.replace(".txt", "").replace("_template", "");
3922
- cachedTemplates[key] = readFileSync5(filePath, "utf-8");
3784
+ cachedTemplates[key] = readFileSync6(filePath, "utf-8");
3923
3785
  }
3924
3786
  }
3925
3787
  console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
@@ -3928,8 +3790,8 @@ function loadTemplates() {
3928
3790
 
3929
3791
  // src/engine/script-analyzer.ts
3930
3792
  init_esm_shims();
3931
- import { readFileSync as readFileSync6 } from "fs";
3932
- import { resolve as resolve4, join as join3 } from "path";
3793
+ import { readFileSync as readFileSync7 } from "fs";
3794
+ import { resolve as resolve5, join as join3 } from "path";
3933
3795
  var SUSPICIOUS_PATTERNS = [
3934
3796
  // Network exfiltration (curl/wget POST in scripts is almost never legitimate)
3935
3797
  { name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
@@ -4062,8 +3924,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
4062
3924
  }
4063
3925
  function readScriptContent(scriptPath, maxLines = 500) {
4064
3926
  try {
4065
- const expandedPath = scriptPath.startsWith("~") ? join3(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve4(scriptPath);
4066
- const raw = readFileSync6(expandedPath, "utf-8");
3927
+ const expandedPath = scriptPath.startsWith("~") ? join3(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve5(scriptPath);
3928
+ const raw = readFileSync7(expandedPath, "utf-8");
4067
3929
  const lines = raw.split("\n").slice(0, maxLines);
4068
3930
  return { content: lines.join("\n"), lineCount: lines.length, error: "" };
4069
3931
  } catch (e) {
@@ -4117,8 +3979,8 @@ ${result.scriptContent}
4117
3979
  init_esm_shims();
4118
3980
  init_heuristic();
4119
3981
  init_logic_rules();
4120
- import { readFileSync as readFileSync7, statSync, existsSync as existsSync6 } from "fs";
4121
- import { resolve as resolve5, join as join4, isAbsolute, dirname as dirname5 } from "path";
3982
+ import { readFileSync as readFileSync8, statSync, existsSync as existsSync7 } from "fs";
3983
+ import { resolve as resolve6, join as join4, isAbsolute, dirname as dirname6 } from "path";
4122
3984
  import { homedir as homedir2 } from "os";
4123
3985
  function emptyChain() {
4124
3986
  return {
@@ -4347,7 +4209,7 @@ function extractInlineScriptPath(actionDetail) {
4347
4209
  let resolved;
4348
4210
  try {
4349
4211
  resolved = resolvePath(p);
4350
- if (existsSync6(resolved)) {
4212
+ if (existsSync7(resolved)) {
4351
4213
  const stat = statSync(resolved);
4352
4214
  if (stat.isFile()) return resolved;
4353
4215
  }
@@ -4361,18 +4223,18 @@ function resolvePath(p) {
4361
4223
  if (expanded.startsWith("~/") || expanded === "~") {
4362
4224
  expanded = join4(homedir2(), expanded.slice(1));
4363
4225
  }
4364
- return resolve5(expanded);
4226
+ return resolve6(expanded);
4365
4227
  }
4366
4228
  function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
4367
4229
  try {
4368
4230
  const path3 = resolvePath(scriptPath);
4369
- if (!existsSync6(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
4231
+ if (!existsSync7(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
4370
4232
  const stat = statSync(path3);
4371
4233
  if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
4372
4234
  if (stat.size > maxFileSize) {
4373
4235
  return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
4374
4236
  }
4375
- const content = readFileSync7(path3, "utf-8");
4237
+ const content = readFileSync8(path3, "utf-8");
4376
4238
  const lineCount = content ? content.split("\n").length : 0;
4377
4239
  return { content, lineCount, fileSize: stat.size, error: "" };
4378
4240
  } catch (e) {
@@ -4391,7 +4253,7 @@ function extractChildScriptPaths(content, baseDir) {
4391
4253
  if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
4392
4254
  const full = isAbsolute(pathStr) ? pathStr : join4(baseDir, pathStr);
4393
4255
  const resolved = resolvePath(full);
4394
- if (existsSync6(resolved)) {
4256
+ if (existsSync7(resolved)) {
4395
4257
  try {
4396
4258
  if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
4397
4259
  } catch {
@@ -4406,7 +4268,7 @@ function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 10
4406
4268
  const result = emptyChain();
4407
4269
  result.entryPath = entryPath;
4408
4270
  const entry = resolvePath(entryPath);
4409
- const baseDir = existsSync6(entry) ? dirname5(entry) : input.cwd || process.cwd();
4271
+ const baseDir = existsSync7(entry) ? dirname6(entry) : input.cwd || process.cwd();
4410
4272
  resolveRecursive({
4411
4273
  path: entry,
4412
4274
  depth: 0,
@@ -4481,7 +4343,7 @@ function resolveRecursive(ctx) {
4481
4343
  maxTotalSize,
4482
4344
  visited,
4483
4345
  result,
4484
- baseDir: dirname5(resolvePath(childPath)),
4346
+ baseDir: dirname6(resolvePath(childPath)),
4485
4347
  input
4486
4348
  });
4487
4349
  if (result.hasCritical) return;
@@ -4834,7 +4696,7 @@ async function analyzeWithLLM(prompt, config) {
4834
4696
  }
4835
4697
  if (attempt < maxRetries) {
4836
4698
  console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
4837
- await new Promise((resolve6) => setTimeout(resolve6, retryCfg.retryInterval * 1e3));
4699
+ await new Promise((resolve7) => setTimeout(resolve7, retryCfg.retryInterval * 1e3));
4838
4700
  } else {
4839
4701
  clearTimeout(totalTimeoutTimer);
4840
4702
  console.error("[AgentMoss] LLM call failed after all retries:", err.message);
@@ -5347,11 +5209,11 @@ init_esm_shims();
5347
5209
  init_runtime_config();
5348
5210
  init_token_stats();
5349
5211
  import { Hono } from "hono";
5350
- import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
5351
- import { join as join5, dirname as dirname6 } from "path";
5352
- import { fileURLToPath as fileURLToPath7 } from "url";
5353
- var __dirname6 = dirname6(fileURLToPath7(import.meta.url));
5354
- var STATIC_DIR = join5(__dirname6, "static");
5212
+ import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
5213
+ import { join as join5, dirname as dirname7 } from "path";
5214
+ import { fileURLToPath as fileURLToPath8 } from "url";
5215
+ var __dirname7 = dirname7(fileURLToPath8(import.meta.url));
5216
+ var STATIC_DIR = join5(__dirname7, "static");
5355
5217
  var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
5356
5218
  function getConsoleToken() {
5357
5219
  return process.env[CONSOLE_TOKEN_ENV] || "";
@@ -5376,8 +5238,8 @@ function createConsoleRouter() {
5376
5238
  console2.use("/api/*", authMiddleware());
5377
5239
  const serveIndex = (c) => {
5378
5240
  const indexFile = join5(STATIC_DIR, "index.html");
5379
- if (existsSync9(indexFile)) {
5380
- return c.html(readFileSync9(indexFile, "utf-8"));
5241
+ if (existsSync10(indexFile)) {
5242
+ return c.html(readFileSync10(indexFile, "utf-8"));
5381
5243
  }
5382
5244
  return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
5383
5245
  };
@@ -5468,13 +5330,13 @@ function createConsoleRouter() {
5468
5330
  if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
5469
5331
  return c.json({ error: "invalid skill_id" }, 400);
5470
5332
  }
5471
- const skillPath = join5(dirname6(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
5472
- if (!existsSync9(skillPath)) {
5473
- const builtinPath = join5(__dirname6, "..", "..", "agent_moss", "skills", `${skillId}.md`);
5474
- if (existsSync9(builtinPath)) return c.text(readFileSync9(builtinPath, "utf-8"));
5333
+ const skillPath = join5(dirname7(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
5334
+ if (!existsSync10(skillPath)) {
5335
+ const builtinPath = join5(__dirname7, "..", "..", "agent_moss", "skills", `${skillId}.md`);
5336
+ if (existsSync10(builtinPath)) return c.text(readFileSync10(builtinPath, "utf-8"));
5475
5337
  return c.json({ error: "skill not found" }, 404);
5476
5338
  }
5477
- return c.text(readFileSync9(skillPath, "utf-8"));
5339
+ return c.text(readFileSync10(skillPath, "utf-8"));
5478
5340
  });
5479
5341
  console2.get("/api/agents", (c) => {
5480
5342
  const rc = loadRuntimeConfig();
@@ -5558,12 +5420,12 @@ function createApp() {
5558
5420
  }
5559
5421
  async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
5560
5422
  for (let port = startPort; port < startPort + scan; port++) {
5561
- const free = await new Promise((resolve6) => {
5423
+ const free = await new Promise((resolve7) => {
5562
5424
  const server = net.createServer();
5563
5425
  server.listen(port, host, () => {
5564
- server.close(() => resolve6(true));
5426
+ server.close(() => resolve7(true));
5565
5427
  });
5566
- server.on("error", () => resolve6(false));
5428
+ server.on("error", () => resolve7(false));
5567
5429
  });
5568
5430
  if (free) {
5569
5431
  if (port !== startPort) {