@kenhkl/agent-moss 0.16.9 → 0.16.13
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/README.md +3 -0
- package/dist/cli.d.ts +19 -1
- package/dist/cli.js +2605 -478
- package/dist/data/agent_moss.service +4 -0
- package/dist/data/rules_template/README.md +133 -0
- package/dist/data/rules_template/extra.json +14 -0
- package/dist/data/rules_template/override.json +15 -0
- package/dist/engine/coordinator.js +917 -208
- package/dist/server.d.ts +18 -1
- package/dist/server.js +1809 -312
- package/dist/static/index.html +26 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -22,6 +22,99 @@ var init_esm_shims = __esm({
|
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
// src/package-json.ts
|
|
26
|
+
import { createRequire } from "module";
|
|
27
|
+
var require2, pkg, version;
|
|
28
|
+
var init_package_json = __esm({
|
|
29
|
+
"src/package-json.ts"() {
|
|
30
|
+
"use strict";
|
|
31
|
+
init_esm_shims();
|
|
32
|
+
require2 = createRequire(import.meta.url);
|
|
33
|
+
pkg = require2("../package.json");
|
|
34
|
+
version = pkg.version;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// src/models.ts
|
|
39
|
+
import { z } from "zod";
|
|
40
|
+
var ActionItemSchema, PromptTurnSchema, AnalyzeRequestSchema, AnalyzeResponseSchema, HealthResponseSchema, ReviewRequestSchema, ReviewResponseSchema;
|
|
41
|
+
var init_models = __esm({
|
|
42
|
+
"src/models.ts"() {
|
|
43
|
+
"use strict";
|
|
44
|
+
init_esm_shims();
|
|
45
|
+
init_package_json();
|
|
46
|
+
ActionItemSchema = z.object({
|
|
47
|
+
action_type: z.string().describe("Action type, e.g. bash, file_read, file_write"),
|
|
48
|
+
action_detail: z.string().describe("Action detail / command content"),
|
|
49
|
+
// 工具返回给 agent 的内容。仅 L2 的授权(consent)判定用:判断「用户是否已授权」
|
|
50
|
+
// 看的是 ask_user_question 的**回答**,不是提问正文。历史 bug:模型里没有这个字段、
|
|
51
|
+
// 路由也没透传,TS 的授权判定退化成「提问正文非空」,等于问过任何问题即已授权。
|
|
52
|
+
output: z.string().optional().describe("Tool result / user answer (needed by L2 consent checks)")
|
|
53
|
+
});
|
|
54
|
+
PromptTurnSchema = z.object({
|
|
55
|
+
text: z.string().default("").describe("User prompt text at this turn"),
|
|
56
|
+
actions: z.array(ActionItemSchema).default([]).describe("Actions triggered by this prompt, in order, until the next prompt")
|
|
57
|
+
});
|
|
58
|
+
AnalyzeRequestSchema = z.object({
|
|
59
|
+
session_id: z.string().describe("Session / agent unique identifier"),
|
|
60
|
+
prompt_session: z.string().default("").describe("User original prompt (most recent user message; backward compat)"),
|
|
61
|
+
prompt_history: z.array(PromptTurnSchema).default([]).describe("Full interleaved prompt history (ordered turns, each with its triggered actions). Optional; empty = fall back to prompt_session only."),
|
|
62
|
+
action_history: z.array(ActionItemSchema).default([]).describe("Historical action sequence"),
|
|
63
|
+
session_history: z.array(z.object({
|
|
64
|
+
session_id: z.string(),
|
|
65
|
+
action_history: z.array(ActionItemSchema).default([])
|
|
66
|
+
})).default([]).describe("Multi-session historical actions (optional): for L2.2 formal engine cross-session combo-chain / sensitive-data-flow verification. Provided by caller (incl. future agentgate)."),
|
|
67
|
+
a_next: ActionItemSchema.describe("Next action to be executed"),
|
|
68
|
+
reason: z.string().default("").describe("Reason for executing this action"),
|
|
69
|
+
os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
|
|
70
|
+
cwd: z.string().default("").describe("Current working directory"),
|
|
71
|
+
agent_id: z.string().default("").describe("Caller agent identifier, e.g. 'xiaoo' / 'opencode' / 'opendesk'. Empty = generic mode (no agent-specific rules)."),
|
|
72
|
+
metadata: z.record(z.unknown()).optional().describe("Extended metadata")
|
|
73
|
+
});
|
|
74
|
+
AnalyzeResponseSchema = z.object({
|
|
75
|
+
decision: z.enum(["Allow", "Deny"]).describe("Allow or Deny"),
|
|
76
|
+
reason: z.string().describe("Decision reason"),
|
|
77
|
+
risk_level: z.enum(["low", "medium", "high", "critical"]).default("low"),
|
|
78
|
+
risk_type: z.string().default(""),
|
|
79
|
+
violated_layers: z.array(z.string()).default([]),
|
|
80
|
+
policy: z.string().default(""),
|
|
81
|
+
violated_policy: z.string().default(""),
|
|
82
|
+
confidence: z.number().int().min(0).max(100).default(100),
|
|
83
|
+
analysis_duration_ms: z.number().default(0),
|
|
84
|
+
// 组合攻击链反例路径:每步 [session_id, seq, stage, timestamp],含真实发生时间
|
|
85
|
+
violation_chain: z.array(z.array(z.union([z.string(), z.number()]).nullable())).default([])
|
|
86
|
+
});
|
|
87
|
+
HealthResponseSchema = z.object({
|
|
88
|
+
status: z.string().default("healthy"),
|
|
89
|
+
version: z.string().default(version),
|
|
90
|
+
// 归属实例标识:值来自 env AGENT_MOSS_INSTANCE(spawn/systemd 注入),默认空串。
|
|
91
|
+
// 多个 agentmoss 同机并存时,调用方按 instance 过滤避免连到别人的进程。
|
|
92
|
+
// 空=未配置(独立部署或旧版),调用方可选择兼容或严格过滤。
|
|
93
|
+
instance: z.string().default("")
|
|
94
|
+
});
|
|
95
|
+
ReviewRequestSchema = z.object({
|
|
96
|
+
agent_id: z.string().default("").describe("\u76EE\u6807 agent \u6807\u8BC6\uFF08\u51B3\u5B9A\u72B6\u6001\u5E93\u5206\u6876\uFF09"),
|
|
97
|
+
db_path: z.string().default("").describe("AgentGate \u672C\u5730\u5B58\u50A8\u8DEF\u5F84\uFF08SQLite \u6587\u4EF6 \u6216 tool_calls JSON \u76EE\u5F55\uFF09\uFF1B\u4E3A\u7A7A\u5219\u7528 sessions"),
|
|
98
|
+
sessions: z.array(z.object({
|
|
99
|
+
session_id: z.string(),
|
|
100
|
+
action_history: z.array(ActionItemSchema).default([])
|
|
101
|
+
})).default([]).describe("\u591A session \u5386\u53F2\u52A8\u4F5C\uFF08\u66FF\u4EE3/\u515C\u5E95 db_path\uFF09"),
|
|
102
|
+
a_next: ActionItemSchema.optional().describe("\u53EF\u9009\uFF1Aseed \u540E\u8FDE\u540C\u5F53\u524D\u52A8\u4F5C\u4E00\u8D77\u5224\u5B9A"),
|
|
103
|
+
is_error: z.boolean().default(false).describe("\u5F53\u524D\u52A8\u4F5C\u6267\u884C\u662F\u5426\u5931\u8D25"),
|
|
104
|
+
output_pending: z.boolean().default(false).describe("\u8FD4\u56DE\u524D\u5E8F\u94FE\u6E05\u5355 pending_prefixes")
|
|
105
|
+
});
|
|
106
|
+
ReviewResponseSchema = z.object({
|
|
107
|
+
agent_id: z.string().default(""),
|
|
108
|
+
seeded_nodes: z.number().default(0),
|
|
109
|
+
verified: z.boolean().default(true),
|
|
110
|
+
violations: z.array(z.record(z.unknown())).default([]),
|
|
111
|
+
pending_prefixes: z.array(z.record(z.unknown())).default([]),
|
|
112
|
+
state_nodes: z.number().default(0),
|
|
113
|
+
discovered_patterns: z.array(z.record(z.unknown())).default([]).describe("\u6570\u636E\u9A71\u52A8\u53D1\u73B0\uFF1A\u8DE8 session \u9891\u7E41\u5371\u9669\u7EC4\u5408\uFF08\u9884\u7F6E\u94FE\u76F2\u533A\uFF0CPatternMiner\uFF09")
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
25
118
|
// src/infra/paths.ts
|
|
26
119
|
import { join } from "path";
|
|
27
120
|
import { homedir } from "os";
|
|
@@ -395,6 +488,52 @@ var init_runtime_config_bridge = __esm({
|
|
|
395
488
|
}
|
|
396
489
|
});
|
|
397
490
|
|
|
491
|
+
// src/infra/rules-mode.ts
|
|
492
|
+
function truthy(name) {
|
|
493
|
+
return TRUTHY.has((process.env[name] || "").trim().toLowerCase());
|
|
494
|
+
}
|
|
495
|
+
function getRulesMode() {
|
|
496
|
+
if (cached !== null) return cached;
|
|
497
|
+
const raw = (process.env[ENV_RULES_MODE] || "").trim().toLowerCase();
|
|
498
|
+
if (raw) {
|
|
499
|
+
if (VALID_MODES.has(raw)) {
|
|
500
|
+
cached = raw;
|
|
501
|
+
} else {
|
|
502
|
+
console.error(
|
|
503
|
+
`[AgentMoss] ${ENV_RULES_MODE}=${JSON.stringify(raw)} \u975E\u6CD5\uFF08\u53EA\u652F\u6301 hot / cold / builtin\uFF09\uFF1B\u4E3A\u5B89\u5168\u8D77\u89C1\u6309 builtin\uFF08\u4EC5\u5185\u7F6E\u89C4\u5219\uFF09\u5904\u7406`
|
|
504
|
+
);
|
|
505
|
+
cached = "builtin";
|
|
506
|
+
}
|
|
507
|
+
return cached;
|
|
508
|
+
}
|
|
509
|
+
if (truthy(ENV_RULES_BUILTIN_ONLY)) cached = "builtin";
|
|
510
|
+
else if (truthy(ENV_DISABLE_RULE_HOT_RELOAD)) cached = "cold";
|
|
511
|
+
else cached = "hot";
|
|
512
|
+
return cached;
|
|
513
|
+
}
|
|
514
|
+
function isHotReloadEnabled() {
|
|
515
|
+
return getRulesMode() === "hot";
|
|
516
|
+
}
|
|
517
|
+
function isRuntimeConfigDisabled() {
|
|
518
|
+
return getRulesMode() !== "hot";
|
|
519
|
+
}
|
|
520
|
+
function isBuiltinOnly() {
|
|
521
|
+
return getRulesMode() === "builtin";
|
|
522
|
+
}
|
|
523
|
+
var ENV_RULES_MODE, ENV_RULES_BUILTIN_ONLY, ENV_DISABLE_RULE_HOT_RELOAD, TRUTHY, VALID_MODES, cached;
|
|
524
|
+
var init_rules_mode = __esm({
|
|
525
|
+
"src/infra/rules-mode.ts"() {
|
|
526
|
+
"use strict";
|
|
527
|
+
init_esm_shims();
|
|
528
|
+
ENV_RULES_MODE = "AGENT_MOSS_RULES_MODE";
|
|
529
|
+
ENV_RULES_BUILTIN_ONLY = "AGENT_MOSS_RULES_BUILTIN_ONLY";
|
|
530
|
+
ENV_DISABLE_RULE_HOT_RELOAD = "AGENT_MOSS_DISABLE_RULE_HOT_RELOAD";
|
|
531
|
+
TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
532
|
+
VALID_MODES = /* @__PURE__ */ new Set(["hot", "cold", "builtin"]);
|
|
533
|
+
cached = null;
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
|
|
398
537
|
// src/infra/rules-loader.ts
|
|
399
538
|
import { readFileSync as readFileSync2, existsSync, readdirSync, statSync } from "fs";
|
|
400
539
|
import { resolve, dirname, join as join2 } from "path";
|
|
@@ -407,71 +546,67 @@ function parseDirEnv(envName) {
|
|
|
407
546
|
if (!raw) return [];
|
|
408
547
|
return raw.split(/[,;]/).map((s) => s.trim()).filter(Boolean).map((s) => resolve(s));
|
|
409
548
|
}
|
|
410
|
-
function
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
console.warn(
|
|
417
|
-
`[AgentMoss] \u89C4\u5219\u6587\u4EF6\u88AB\u8986\u76D6\u76EE\u5F55\u8986\u76D6: ${path6}\uFF08\u5185\u7F6E\u9ED8\u8BA4\u88AB\u5FFD\u7565\uFF09\u3002\u8BE5\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4 \u2014\u2014 **\u5185\u7F6E\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762**\uFF1B\u5220\u9664\u8BE5\u6587\u4EF6\uFF08\u6216\u6E05\u7A7A AGENT_MOSS_OVERRIDE_RULES_DIR\uFF09\u5373\u6062\u590D\u5185\u7F6E\u89C4\u5219\u3002\u82E5\u53EA\u60F3\u9010\u6761\u542F\u505C/\u589E\u5220\uFF0C\u8BF7\u6539\u7528 runtime config\uFF08Console\uFF09\u3002`
|
|
418
|
-
);
|
|
419
|
-
const missing = (EXPECTED_KEYS[name] || []).filter((k) => !(k in data));
|
|
420
|
-
if (missing.length > 0) {
|
|
421
|
-
console.warn(
|
|
422
|
-
`\u26A0\uFE0F [AgentMoss] \u8986\u76D6\u6587\u4EF6 ${path6} \u7F3A\u5C11\u9876\u5C42\u952E [${missing.join(", ")}] \u2014\u2014 \u5BF9\u5E94\u7C7B\u522B\u4F1A**\u4E00\u6761\u89C4\u5219\u90FD\u4E0D\u8DD1**\uFF08\u9759\u9ED8\u5931\u6548\uFF09\u3002\u82E5\u53EA\u60F3\u6539\u5176\u4E2D\u4E00\u7C7B\uFF0C\u8BF7\u5148\u62F7\u8D1D\u5B8C\u6574\u7684\u5185\u7F6E\u6587\u4EF6\u518D\u6539\u3002`
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
function overriddenRuleFiles() {
|
|
427
|
-
const out = [];
|
|
428
|
-
for (const name of Object.keys(EXPECTED_KEYS)) {
|
|
429
|
-
for (const dir of overrideRulesDirs()) {
|
|
430
|
-
const p = resolve(dir, name);
|
|
431
|
-
if (existsSync(p)) {
|
|
432
|
-
out.push(p);
|
|
433
|
-
break;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
return out;
|
|
549
|
+
function rulesDirs() {
|
|
550
|
+
if (isBuiltinOnly()) return [];
|
|
551
|
+
const dirs = [];
|
|
552
|
+
for (const env of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env));
|
|
553
|
+
dirs.push(resolve(dataRootDir(), "rules"));
|
|
554
|
+
return [...new Set(dirs)];
|
|
438
555
|
}
|
|
439
556
|
function findRulesFile(name) {
|
|
440
|
-
for (const dir of overrideRulesDirs()) {
|
|
441
|
-
const p = resolve(dir, name);
|
|
442
|
-
if (existsSync(p)) return p;
|
|
443
|
-
}
|
|
444
557
|
for (const dir of RULES_DIR_CANDS) {
|
|
445
558
|
const p = resolve(dir, name);
|
|
446
559
|
if (existsSync(p)) return p;
|
|
447
560
|
}
|
|
448
561
|
return null;
|
|
449
562
|
}
|
|
450
|
-
function
|
|
451
|
-
return
|
|
563
|
+
function resolveRulesFile(name) {
|
|
564
|
+
return findRulesFile(name);
|
|
452
565
|
}
|
|
453
566
|
function loadJson(name) {
|
|
567
|
+
if (!isHotReloadEnabled()) {
|
|
568
|
+
const cached2 = frozenJsonCache.get(name);
|
|
569
|
+
if (cached2 !== void 0) return cached2;
|
|
570
|
+
}
|
|
454
571
|
const p = findRulesFile(name);
|
|
455
572
|
if (!p) return {};
|
|
456
|
-
const isOverride = isOverridePath(name, p);
|
|
457
|
-
let data;
|
|
458
573
|
try {
|
|
459
|
-
|
|
574
|
+
const parsed = JSON.parse(readFileSync2(p, "utf-8")) || {};
|
|
575
|
+
if (!isHotReloadEnabled()) frozenJsonCache.set(name, parsed);
|
|
576
|
+
return parsed;
|
|
460
577
|
} catch {
|
|
461
|
-
if (isOverride) warnOverride(name, p, {});
|
|
462
578
|
return {};
|
|
463
579
|
}
|
|
464
|
-
if (isOverride) warnOverride(name, p, data);
|
|
465
|
-
return data;
|
|
466
580
|
}
|
|
467
581
|
function resolveScenarioRulesFile(scenarioId) {
|
|
468
582
|
const rel = join2("scenarios", scenarioId, "rules.json");
|
|
469
|
-
for (const dir of
|
|
583
|
+
for (const dir of rulesDirs()) {
|
|
470
584
|
const p = resolve(dir, rel);
|
|
471
|
-
if (existsSync(p))
|
|
585
|
+
if (!existsSync(p)) continue;
|
|
586
|
+
let reason = "";
|
|
587
|
+
try {
|
|
588
|
+
reason = invalidScenarioRulesReason(JSON.parse(readFileSync2(p, "utf-8")) || {});
|
|
589
|
+
} catch (err) {
|
|
590
|
+
reason = `\u8BFB\u53D6/\u89E3\u6790\u5931\u8D25: ${String(err)}`;
|
|
591
|
+
}
|
|
592
|
+
if (reason) {
|
|
593
|
+
console.error(
|
|
594
|
+
`[AgentMoss] \u274C \u573A\u666F\u89C4\u5219\u8986\u76D6\u6587\u4EF6\u7ED3\u6784\u4E0D\u53EF\u7528\uFF0C**\u5DF2\u5FFD\u7565\u5E76\u56DE\u9000\u5185\u7F6E**: ${p}
|
|
595
|
+
\u539F\u56E0: ${reason}`
|
|
596
|
+
);
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
console.warn(`[AgentMoss] \u26A0\uFE0F \u573A\u666F ${scenarioId} \u89C4\u5219\u88AB\u8986\u76D6: ${p}\uFF08\u5185\u7F6E\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762\uFF09`);
|
|
600
|
+
return p;
|
|
472
601
|
}
|
|
473
602
|
return null;
|
|
474
603
|
}
|
|
604
|
+
function invalidScenarioRulesReason(data) {
|
|
605
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
606
|
+
return `\u9876\u5C42\u5FC5\u987B\u662F JSON \u5BF9\u8C61\uFF0C\u5B9E\u9645\u662F ${Array.isArray(data) ? "array" : typeof data}`;
|
|
607
|
+
}
|
|
608
|
+
return SCENARIO_LIST_KEYS.filter((k) => k in data && !Array.isArray(data[k])).map((k) => `"${k}" \u5E94\u4E3A\u6570\u7EC4\uFF08\u5BF9\u8C61\u6570\u7EC4\uFF09\uFF0C\u5B9E\u9645\u662F ${typeof data[k]}`).join("\uFF1B");
|
|
609
|
+
}
|
|
475
610
|
function loadL1() {
|
|
476
611
|
return loadJson("l1_rules.json");
|
|
477
612
|
}
|
|
@@ -479,10 +614,40 @@ function loadL2() {
|
|
|
479
614
|
return loadJson("l2_rules.json");
|
|
480
615
|
}
|
|
481
616
|
function loadL3() {
|
|
482
|
-
|
|
617
|
+
const data = { ...loadJson("l3_rules.json") };
|
|
618
|
+
try {
|
|
619
|
+
const ov = loadOverrideRules().skills;
|
|
620
|
+
const ex = loadExtraRules().skills || {};
|
|
621
|
+
if (ov && typeof ov === "object") {
|
|
622
|
+
for (const [cat, skills] of Object.entries(ov)) data[cat] = { ...skills };
|
|
623
|
+
}
|
|
624
|
+
if (ex && typeof ex === "object") {
|
|
625
|
+
for (const [cat, skills] of Object.entries(ex)) data[cat] = { ...data[cat] || {}, ...skills };
|
|
626
|
+
}
|
|
627
|
+
} catch {
|
|
628
|
+
}
|
|
629
|
+
return data;
|
|
630
|
+
}
|
|
631
|
+
function loadMCRaw() {
|
|
632
|
+
const mc = { ...loadJson("mc_rules.json") };
|
|
633
|
+
let rules = [...mc.rules || []];
|
|
634
|
+
try {
|
|
635
|
+
const ov = loadOverrideRules().ltl_rules;
|
|
636
|
+
const ex = loadExtraRules().ltl_rules || [];
|
|
637
|
+
if (Array.isArray(ov)) rules = [...ov];
|
|
638
|
+
if (Array.isArray(ex) && ex.length > 0) {
|
|
639
|
+
const byId = /* @__PURE__ */ new Map();
|
|
640
|
+
for (const r of rules) if (r && r.id) byId.set(r.id, r);
|
|
641
|
+
for (const r of ex) if (r && r.id) byId.set(r.id, r);
|
|
642
|
+
rules = [...byId.values()];
|
|
643
|
+
}
|
|
644
|
+
} catch {
|
|
645
|
+
}
|
|
646
|
+
mc.rules = rules;
|
|
647
|
+
return mc;
|
|
483
648
|
}
|
|
484
649
|
function loadMC() {
|
|
485
|
-
const mc =
|
|
650
|
+
const mc = loadMCRaw();
|
|
486
651
|
const rules = mc.rules || [];
|
|
487
652
|
try {
|
|
488
653
|
const rt = runtimeConfigBridge.current;
|
|
@@ -502,166 +667,303 @@ function loadMC() {
|
|
|
502
667
|
return mc;
|
|
503
668
|
}
|
|
504
669
|
function loadToolRegistry() {
|
|
505
|
-
|
|
506
|
-
}
|
|
507
|
-
function extraRulesDirs() {
|
|
508
|
-
return parseDirEnv("AGENT_MOSS_EXTRA_RULES_DIR");
|
|
509
|
-
}
|
|
510
|
-
function listJsonFilesDeep(dir) {
|
|
511
|
-
const out = [];
|
|
512
|
-
let entries;
|
|
670
|
+
const data = { ...loadJson("tool_registry.json") };
|
|
671
|
+
let tools = { ...data.tools || {} };
|
|
513
672
|
try {
|
|
514
|
-
|
|
673
|
+
const ov = loadOverrideRules().tools;
|
|
674
|
+
const ex = loadExtraRules().tools || {};
|
|
675
|
+
if (ov && typeof ov === "object") tools = { ...ov };
|
|
676
|
+
if (ex && typeof ex === "object") tools = { ...tools, ...ex };
|
|
515
677
|
} catch {
|
|
516
|
-
return out;
|
|
517
678
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
679
|
+
data.tools = tools;
|
|
680
|
+
return data;
|
|
681
|
+
}
|
|
682
|
+
function scanStructEntry(cat, value, path6) {
|
|
683
|
+
if (cat === "skills") {
|
|
684
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
685
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 skills \u5E94\u4E3A\u5BF9\u8C61\uFF08{\u7C7B\u522B: {\u6280\u80FD\u540D: [\u5173\u952E\u8BCD]}}\uFF09\u2014\u2014 \u5DF2\u8DF3\u8FC7\u3002`);
|
|
686
|
+
return null;
|
|
525
687
|
}
|
|
526
|
-
|
|
527
|
-
|
|
688
|
+
const out = {};
|
|
689
|
+
for (const [skCat, skills] of Object.entries(value)) {
|
|
690
|
+
if (!skills || typeof skills !== "object" || Array.isArray(skills)) {
|
|
691
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 skills.${skCat} \u5E94\u4E3A\u5BF9\u8C61\uFF08\u6280\u80FD\u540D \u2192 \u5173\u952E\u8BCD\u6570\u7EC4\uFF09\u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u7C7B\u522B\u3002`);
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
const keep = {};
|
|
695
|
+
for (const [name, kws] of Object.entries(skills)) {
|
|
696
|
+
if (Array.isArray(kws)) keep[name] = kws;
|
|
697
|
+
else console.warn(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 skills.${skCat}.${name} \u5173\u952E\u8BCD\u4E0D\u662F\u6570\u7EC4 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u6761\u3002`);
|
|
698
|
+
}
|
|
699
|
+
if (Object.keys(keep).length > 0) out[skCat] = keep;
|
|
700
|
+
}
|
|
701
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
528
702
|
}
|
|
529
|
-
|
|
703
|
+
if (cat === "ltl_rules") {
|
|
704
|
+
if (!Array.isArray(value)) {
|
|
705
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 ltl_rules \u5E94\u4E3A\u5BF9\u8C61\u6570\u7EC4 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u3002`);
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
const keep = value.filter((r) => r && typeof r === "object" && r.id);
|
|
709
|
+
if (keep.length !== value.length) {
|
|
710
|
+
console.warn(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 ltl_rules \u6709\u6761\u76EE\u7F3A id \u6216\u4E0D\u662F\u5BF9\u8C61 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u6761\u3002`);
|
|
711
|
+
}
|
|
712
|
+
return keep.length > 0 ? keep : null;
|
|
713
|
+
}
|
|
714
|
+
if (cat === "tools") {
|
|
715
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
716
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 tools \u5E94\u4E3A\u5BF9\u8C61\uFF08{\u5DE5\u5177\u540D: {actionClass, ...}}\uFF09\u2014\u2014 \u5DF2\u8DF3\u8FC7\u3002`);
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
const keep = {};
|
|
720
|
+
for (const [k, v] of Object.entries(value)) {
|
|
721
|
+
if (v && typeof v === "object" && !Array.isArray(v)) keep[k] = v;
|
|
722
|
+
else console.warn(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 tools.${k} \u4E0D\u662F\u5BF9\u8C61 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u6761\u3002`);
|
|
723
|
+
}
|
|
724
|
+
return Object.keys(keep).length > 0 ? keep : null;
|
|
725
|
+
}
|
|
726
|
+
return null;
|
|
530
727
|
}
|
|
531
|
-
function
|
|
532
|
-
const
|
|
533
|
-
const
|
|
534
|
-
|
|
728
|
+
function scanRulesFile(path6, kind) {
|
|
729
|
+
const out = {};
|
|
730
|
+
const label = kind === "override" ? "\u8986\u76D6" : "\u8FFD\u52A0";
|
|
731
|
+
let parsed;
|
|
732
|
+
try {
|
|
733
|
+
parsed = JSON.parse(readFileSync2(path6, "utf-8"));
|
|
734
|
+
} catch (e) {
|
|
735
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u635F\u574F/\u4E0D\u53EF\u8BFB\uFF0C\u5DF2\u8DF3\u8FC7\uFF08${kind} \u89C4\u5219\u4E0D\u751F\u6548\uFF09: ${e.message}`);
|
|
736
|
+
return out;
|
|
737
|
+
}
|
|
535
738
|
const classifyFlat = (r) => {
|
|
536
739
|
for (const [keys, cat] of EXTRA_FLAT_FIELD) {
|
|
537
740
|
if (r && typeof r === "object" && keys.every((k) => r[k])) return cat;
|
|
538
741
|
}
|
|
539
742
|
return null;
|
|
540
743
|
};
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
744
|
+
let pairs = [];
|
|
745
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
746
|
+
pairs = Object.entries(parsed);
|
|
747
|
+
} else if (Array.isArray(parsed)) {
|
|
748
|
+
let flatBad = 0;
|
|
749
|
+
for (const r of parsed) {
|
|
750
|
+
const cat = classifyFlat(r);
|
|
751
|
+
if (cat === null) {
|
|
752
|
+
flatBad += 1;
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
pairs.push([cat, [r]]);
|
|
756
|
+
}
|
|
757
|
+
if (flatBad > 0) {
|
|
758
|
+
console.warn(
|
|
759
|
+
`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u6709 ${flatBad} \u6761\u5E73\u94FA\u6761\u76EE\u65E0\u6CD5\u5F52\u7C7B\uFF08\u88F8 pattern \u4E0D\u5E26 risk_level/risk_type \u65F6\u533A\u5206\u4E0D\u4E86 L1 \u5371\u9669\u547D\u4EE4\u4E0E L2 \u6388\u6743\u7C7B\uFF09\u2014\u2014 \u8BF7\u6539\u7528\u5BF9\u8C61\u5F62\u5F0F\uFF1A{"\u7C7B\u522B\u952E": [\u6761\u76EE...]}\u3002`
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
} else {
|
|
763
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u9876\u5C42\u5E94\u662F\u5BF9\u8C61\uFF08\u7C7B\u522B\u952E\u2192\u6761\u76EE\u6570\u7EC4\uFF09\u6216\u6570\u7EC4\uFF0C\u5DF2\u8DF3\u8FC7\u3002`);
|
|
764
|
+
return out;
|
|
765
|
+
}
|
|
766
|
+
for (const [cat, rules] of pairs) {
|
|
767
|
+
if (String(cat).startsWith("_")) continue;
|
|
768
|
+
if (STRUCT_KEYS.includes(cat)) {
|
|
769
|
+
const got = scanStructEntry(cat, rules, path6);
|
|
770
|
+
if (got !== null) out[cat] = got;
|
|
547
771
|
continue;
|
|
548
772
|
}
|
|
549
|
-
|
|
550
|
-
if (
|
|
551
|
-
pairs = Object.entries(parsed);
|
|
552
|
-
} else if (Array.isArray(parsed)) {
|
|
553
|
-
let flatBad = 0;
|
|
554
|
-
for (const r of parsed) {
|
|
555
|
-
const cat = classifyFlat(r);
|
|
556
|
-
if (cat === null) {
|
|
557
|
-
flatBad += 1;
|
|
558
|
-
continue;
|
|
559
|
-
}
|
|
560
|
-
pairs.push([cat, [r]]);
|
|
561
|
-
}
|
|
562
|
-
if (flatBad > 0) {
|
|
563
|
-
console.warn(
|
|
564
|
-
`[AgentMoss] \u8FFD\u52A0\u89C4\u5219\u6587\u4EF6 ${p} \u6709 ${flatBad} \u6761\u5E73\u94FA\u6761\u76EE\u65E0\u6CD5\u5F52\u7C7B\uFF08\u88F8 pattern \u4E0D\u5E26 risk_level/risk_type \u65F6\u533A\u5206\u4E0D\u4E86 L1 \u5371\u9669\u547D\u4EE4\u4E0E L2 \u6388\u6743\u7C7B\uFF09\u2014\u2014 \u8BF7\u6539\u7528\u5BF9\u8C61\u5F62\u5F0F\uFF1A{"\u7C7B\u522B\u952E": [\u6761\u76EE...]}\u3002`
|
|
565
|
-
);
|
|
566
|
-
}
|
|
567
|
-
} else {
|
|
773
|
+
const required = EXTRA_CATEGORIES[cat];
|
|
774
|
+
if (!required) {
|
|
568
775
|
console.warn(
|
|
569
|
-
`[AgentMoss] \
|
|
776
|
+
`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u542B\u672A\u77E5\u7C7B\u522B\u952E "${cat}"\uFF08\u53EF\u7528\uFF1A${Object.keys(EXTRA_CATEGORIES).join(", ")}\uFF09\u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u7C7B\u522B\u3002`
|
|
777
|
+
);
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
if (rules && typeof rules === "object" && !Array.isArray(rules)) {
|
|
781
|
+
console.error(
|
|
782
|
+
`[AgentMoss] \u274C \u89C4\u5219\u6587\u4EF6 ${path6} \u7684\u7C7B\u522B "${cat}" \u662F**\u5BF9\u8C61**\uFF0C\u5E94\u4E3A**\u5BF9\u8C61\u6570\u7EC4** \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u7C7B\u522B\u3002
|
|
783
|
+
\u6B63\u786E\u5199\u6CD5: {"${cat}": [{"pattern": "...", "risk_level": "high", ...}]}
|
|
784
|
+
\u4F60\u5199\u7684\u50CF\u662F runtime config \u7684\u683C\u5F0F\uFF08{"category_enabled": ..., "rules": [...]}\uFF09\u2014\u2014\u90A3\u662F ~/.config/agentmoss/agent_moss_runtime.json \u7684\u683C\u5F0F\uFF0C\u4E0D\u80FD\u76F4\u63A5\u5F53\u89C4\u5219\u6587\u4EF6\u7528\u3002`
|
|
570
785
|
);
|
|
571
786
|
continue;
|
|
572
787
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
788
|
+
if (!Array.isArray(rules)) {
|
|
789
|
+
console.warn(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684\u7C7B\u522B "${cat}" \u5E94\u4E3A\u6570\u7EC4\uFF0C\u5B9E\u9645\u662F ${typeof rules} \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u7C7B\u522B\u3002`);
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
const items = [];
|
|
793
|
+
for (const r of rules) {
|
|
794
|
+
if (!r || typeof r !== "object" || !required.every((k) => r[k])) {
|
|
577
795
|
console.warn(
|
|
578
|
-
`[AgentMoss] \
|
|
796
|
+
`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7C7B\u522B ${cat} \u6709\u6761\u76EE\u7F3A\u5FC5\u9700\u5B57\u6BB5 [${required.join(", ")}] \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u6761\u3002`
|
|
579
797
|
);
|
|
580
798
|
continue;
|
|
581
799
|
}
|
|
582
|
-
|
|
583
|
-
|
|
800
|
+
items.push(r);
|
|
801
|
+
}
|
|
802
|
+
if (items.length > 0) out[cat] = (out[cat] || []).concat(items);
|
|
803
|
+
}
|
|
804
|
+
if (Object.keys(out).length > 0) {
|
|
805
|
+
console.info(
|
|
806
|
+
`[AgentMoss] \u5DF2\u52A0\u8F7D${label}\u89C4\u5219\u6587\u4EF6 ${path6}\uFF1A` + Object.entries(out).map(([k, v]) => `${k}\xD7${v.length}`).join(", ")
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
return out;
|
|
810
|
+
}
|
|
811
|
+
function scanRuleDirs() {
|
|
812
|
+
const override = {};
|
|
813
|
+
const extra = {};
|
|
814
|
+
const loaded = [];
|
|
815
|
+
const ignored = [];
|
|
816
|
+
for (const d of rulesDirs()) {
|
|
817
|
+
if (!existsSync(d)) continue;
|
|
818
|
+
let entries = [];
|
|
819
|
+
try {
|
|
820
|
+
entries = readdirSync(d).sort();
|
|
821
|
+
} catch {
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
for (const name of entries) {
|
|
825
|
+
const f = resolve(d, name);
|
|
826
|
+
try {
|
|
827
|
+
if (statSync(f).isDirectory()) continue;
|
|
828
|
+
} catch {
|
|
584
829
|
continue;
|
|
585
830
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
)
|
|
591
|
-
|
|
831
|
+
if (name === OVERRIDE_FILENAME) {
|
|
832
|
+
const got = scanRulesFile(f, "override");
|
|
833
|
+
if (Object.keys(got).length > 0) loaded.push(f);
|
|
834
|
+
for (const [cat, items] of Object.entries(got)) {
|
|
835
|
+
if (Array.isArray(items)) {
|
|
836
|
+
override[cat] = (override[cat] || []).concat(items);
|
|
837
|
+
} else {
|
|
838
|
+
override[cat] = { ...override[cat] || {}, ...items };
|
|
839
|
+
}
|
|
592
840
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
);
|
|
599
|
-
|
|
841
|
+
} else if (name === EXTRA_FILENAME) {
|
|
842
|
+
const got = scanRulesFile(f, "extra");
|
|
843
|
+
if (Object.values(got).some((v) => v.length > 0)) loaded.push(f);
|
|
844
|
+
for (const [cat, items] of Object.entries(got)) {
|
|
845
|
+
if (Array.isArray(items)) {
|
|
846
|
+
extra[cat] = (extra[cat] || []).concat(items);
|
|
847
|
+
} else {
|
|
848
|
+
extra[cat] = { ...extra[cat] || {}, ...items };
|
|
600
849
|
}
|
|
601
|
-
seenIds.set(rid, p);
|
|
602
850
|
}
|
|
603
|
-
|
|
604
|
-
|
|
851
|
+
} else if (name.toLowerCase().endsWith(".json")) {
|
|
852
|
+
ignored.push(f);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
if (Object.keys(override).length > 0 && identicalToBuiltin(override)) {
|
|
857
|
+
const key = "override-identical::" + Object.entries(override).map(([c, v]) => `${c}=${v.length}`).sort().join("|");
|
|
858
|
+
if (!warnedIgnored.has(key)) {
|
|
859
|
+
warnedIgnored.add(key);
|
|
860
|
+
console.warn(
|
|
861
|
+
`[AgentMoss] \u2139\uFE0F ${OVERRIDE_FILENAME} \u4E0E**\u5185\u7F6E\u89C4\u5219\u5B8C\u5168\u4E00\u81F4**\uFF08` + Object.entries(override).sort().map(([c, v]) => `${c}\xD7${v.length}`).join(", ") + `\uFF09\u2014\u2014 \u770B\u8D77\u6765\u662F\u6A21\u677F\u539F\u6837\u62F7\u8FDB\u6765\u6CA1\u6539\u3002
|
|
862
|
+
\u5B83\u73B0\u5728\u7684\u6548\u679C = \u5185\u7F6E\u89C4\u5219\uFF1B\u4F46**\u4EE5\u540E\u5347\u7EA7\u5185\u7F6E\u89C4\u5219\u4E0D\u4F1A\u53CD\u6620\u5230\u8FD9\u4EFD\u4E0A**\uFF08\u88AB\u9489\u4F4F\u4E86\uFF09\u3002
|
|
863
|
+
\u53EA\u60F3\u8FFD\u52A0\u89C4\u5219\uFF1A\u5220\u6389 ${OVERRIDE_FILENAME}\uFF0C\u6539\u7528 ${EXTRA_FILENAME}\u3002`
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (Object.keys(override).length > 0) {
|
|
868
|
+
const key = "override-summary::" + Object.entries(override).map(([c, v]) => `${c}=${v.length}`).sort().join("|");
|
|
869
|
+
if (!warnedIgnored.has(key)) {
|
|
870
|
+
warnedIgnored.add(key);
|
|
871
|
+
const l1 = loadL1();
|
|
872
|
+
const l2 = loadL2();
|
|
873
|
+
const builtinCounts = {
|
|
874
|
+
dangerous_commands: (l1.dangerous_commands || []).length,
|
|
875
|
+
injection_detection: (l1.injection_detection || []).length,
|
|
876
|
+
sensitive_path_access: (l1.sensitive_path_access || []).length,
|
|
877
|
+
intent_consistency: (l2.intent_consistency || []).length,
|
|
878
|
+
password_modify_consent: (l2.password_modify_consent || []).length,
|
|
879
|
+
user_deletion_consent: (l2.user_deletion_consent || []).length
|
|
880
|
+
};
|
|
881
|
+
console.warn(`[AgentMoss] \u26A0\uFE0F ${OVERRIDE_FILENAME} \u751F\u6548\uFF08**\u6574\u4EFD\u66FF\u6362**\uFF0C\u672A\u5217\u51FA\u7684\u5185\u7F6E\u89C4\u5219\u4E0D\u518D\u751F\u6548\uFF09\uFF1A`);
|
|
882
|
+
for (const [cat, items] of Object.entries(override).sort()) {
|
|
883
|
+
console.warn(` \u7C7B\u522B ${cat}\uFF1A\u5185\u7F6E ${builtinCounts[cat] ?? 0} \u6761 \u2192 \u4F60\u7684 ${items.length} \u6761`);
|
|
605
884
|
}
|
|
885
|
+
console.warn(` \u53EA\u60F3**\u8FFD\u52A0**\u89C4\u5219\u8BF7\u6539\u7528 ${EXTRA_FILENAME}\uFF1B\u8981\u6539\u67D0\u4E00\u6761\u5185\u7F6E\u89C4\u5219\uFF0C\u8BF7\u5148\u62F7\u8D1D\u5185\u7F6E\u6587\u4EF6\u518D\u6539\u3002`);
|
|
606
886
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
887
|
+
}
|
|
888
|
+
if (ignored.length > 0) {
|
|
889
|
+
const key = "ignored::" + ignored.join("|");
|
|
890
|
+
if (!warnedIgnored.has(key)) {
|
|
891
|
+
warnedIgnored.add(key);
|
|
892
|
+
console.warn(
|
|
893
|
+
`[AgentMoss] \u26A0\uFE0F \u89C4\u5219\u76EE\u5F55\u91CC\u6709 ${ignored.length} \u4E2A json **\u4E0D\u4F1A\u88AB\u8BFB\u53D6**\uFF08\u53EA\u6709 ${OVERRIDE_FILENAME} \u4E0E ${EXTRA_FILENAME} \u4F1A\u88AB\u8BFB\uFF09: ${ignored.join(", ")}
|
|
894
|
+
\u8981\u8986\u76D6\u5185\u7F6E\u89C4\u5219 \u2192 \u5185\u5BB9\u653E\u8FDB ${OVERRIDE_FILENAME}\uFF1B\u8981\u8FFD\u52A0 \u2192 \u653E\u8FDB ${EXTRA_FILENAME}\u3002`
|
|
895
|
+
);
|
|
610
896
|
}
|
|
611
897
|
}
|
|
612
|
-
return {
|
|
898
|
+
return { override, extra, loaded, ignored };
|
|
613
899
|
}
|
|
614
|
-
function
|
|
615
|
-
|
|
616
|
-
const dirs =
|
|
617
|
-
|
|
900
|
+
function rulesCached() {
|
|
901
|
+
if (!isHotReloadEnabled() && _RULES_CACHE) return _RULES_CACHE;
|
|
902
|
+
const dirs = rulesDirs();
|
|
903
|
+
const sig = RULES_DIR_ENVS.map((e) => `${e}=${process.env[e] || ""}`).join("|") + "|" + dirs.join("|");
|
|
904
|
+
const files = [];
|
|
618
905
|
for (const d of dirs) {
|
|
619
|
-
if (existsSync(d))
|
|
620
|
-
|
|
906
|
+
if (!existsSync(d)) continue;
|
|
907
|
+
try {
|
|
908
|
+
for (const name of readdirSync(d).sort()) {
|
|
909
|
+
if (name.toLowerCase().endsWith(".json")) files.push(resolve(d, name));
|
|
910
|
+
}
|
|
911
|
+
} catch {
|
|
912
|
+
}
|
|
621
913
|
}
|
|
622
|
-
|
|
623
|
-
const fp = files.map((p) => {
|
|
914
|
+
const fp = JSON.stringify(files.map((p) => {
|
|
624
915
|
try {
|
|
625
916
|
const st = statSync(p);
|
|
626
917
|
return [p, st.mtimeMs, st.size];
|
|
627
918
|
} catch {
|
|
628
919
|
return [p, -1, -1];
|
|
629
920
|
}
|
|
630
|
-
});
|
|
631
|
-
if (
|
|
632
|
-
|
|
921
|
+
}));
|
|
922
|
+
if (_RULES_CACHE && _RULES_CACHE.sig === sig && _RULES_CACHE.fp === fp) return _RULES_CACHE;
|
|
923
|
+
_RULES_CACHE = { sig, fp, ...scanRuleDirs() };
|
|
924
|
+
return _RULES_CACHE;
|
|
925
|
+
}
|
|
926
|
+
function identicalToBuiltin(override) {
|
|
927
|
+
const l1 = loadL1();
|
|
928
|
+
const l2 = loadL2();
|
|
929
|
+
for (const [cat, items] of Object.entries(override)) {
|
|
930
|
+
const builtin = l1[cat] ?? l2[cat];
|
|
931
|
+
if (!Array.isArray(builtin) || builtin.length !== items.length) return false;
|
|
932
|
+
if (JSON.stringify(builtin) !== JSON.stringify(items)) return false;
|
|
633
933
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
934
|
+
return Object.keys(override).length > 0;
|
|
935
|
+
}
|
|
936
|
+
function loadOverrideRules() {
|
|
937
|
+
if (isBuiltinOnly()) return {};
|
|
938
|
+
return rulesCached().override;
|
|
637
939
|
}
|
|
638
|
-
function
|
|
639
|
-
|
|
640
|
-
return
|
|
940
|
+
function loadExtraRules() {
|
|
941
|
+
if (isBuiltinOnly()) return {};
|
|
942
|
+
return rulesCached().extra;
|
|
943
|
+
}
|
|
944
|
+
function ruleFilesLoaded() {
|
|
945
|
+
return rulesCached().loaded;
|
|
641
946
|
}
|
|
642
|
-
|
|
947
|
+
function ignoredRuleFiles() {
|
|
948
|
+
return rulesCached().ignored;
|
|
949
|
+
}
|
|
950
|
+
var __filename2, __dirname2, OVERRIDE_FILENAME, EXTRA_FILENAME, RULES_DIR_ENVS, RULES_DIR_CANDS, frozenJsonCache, SCENARIO_LIST_KEYS, EXTRA_CATEGORIES, STRUCT_KEYS, EXTRA_FLAT_FIELD, _RULES_CACHE, warnedIgnored;
|
|
643
951
|
var init_rules_loader = __esm({
|
|
644
952
|
"src/infra/rules-loader.ts"() {
|
|
645
953
|
"use strict";
|
|
646
954
|
init_esm_shims();
|
|
647
955
|
init_paths();
|
|
956
|
+
init_rules_mode();
|
|
648
957
|
init_runtime_config_bridge();
|
|
649
958
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
650
959
|
__dirname2 = dirname(__filename2);
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
"l3_rules.json": [],
|
|
655
|
-
"mc_rules.json": ["rules"],
|
|
656
|
-
"tool_registry.json": ["tools"]
|
|
657
|
-
};
|
|
658
|
-
warnedOverride = /* @__PURE__ */ new Set();
|
|
960
|
+
OVERRIDE_FILENAME = "override.json";
|
|
961
|
+
EXTRA_FILENAME = "extra.json";
|
|
962
|
+
RULES_DIR_ENVS = ["AGENT_MOSS_EXTRA_RULES_DIR", "AGENT_MOSS_OVERRIDE_RULES_DIR"];
|
|
659
963
|
RULES_DIR_CANDS = (() => {
|
|
660
964
|
const cands = [
|
|
661
|
-
//
|
|
662
|
-
//
|
|
663
|
-
// mc_rules.json 放到 <data_root>/rules/ 即可覆盖内置默认,无需改源码。
|
|
664
|
-
resolve(dataRootDir(), "rules"),
|
|
965
|
+
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
966
|
+
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
665
967
|
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
666
968
|
resolve(__dirname2, "../rules"),
|
|
667
969
|
resolve(__dirname2, "rules"),
|
|
@@ -674,6 +976,8 @@ var init_rules_loader = __esm({
|
|
|
674
976
|
];
|
|
675
977
|
return cands.filter((c) => existsSync(c));
|
|
676
978
|
})();
|
|
979
|
+
frozenJsonCache = /* @__PURE__ */ new Map();
|
|
980
|
+
SCENARIO_LIST_KEYS = ["extra_command_patterns", "extra_injection_keywords", "extra_sensitive_paths"];
|
|
677
981
|
EXTRA_CATEGORIES = {
|
|
678
982
|
dangerous_commands: ["pattern"],
|
|
679
983
|
injection_detection: ["keyword", "lang"],
|
|
@@ -682,6 +986,7 @@ var init_rules_loader = __esm({
|
|
|
682
986
|
password_modify_consent: ["pattern"],
|
|
683
987
|
user_deletion_consent: ["pattern"]
|
|
684
988
|
};
|
|
989
|
+
STRUCT_KEYS = ["skills", "ltl_rules", "tools"];
|
|
685
990
|
EXTRA_FLAT_FIELD = [
|
|
686
991
|
[["path"], "sensitive_path_access"],
|
|
687
992
|
[["keyword", "lang"], "injection_detection"],
|
|
@@ -689,7 +994,8 @@ var init_rules_loader = __esm({
|
|
|
689
994
|
[["pattern", "risk_level"], "dangerous_commands"],
|
|
690
995
|
[["pattern", "risk_type"], "dangerous_commands"]
|
|
691
996
|
];
|
|
692
|
-
|
|
997
|
+
_RULES_CACHE = null;
|
|
998
|
+
warnedIgnored = /* @__PURE__ */ new Set();
|
|
693
999
|
}
|
|
694
1000
|
});
|
|
695
1001
|
|
|
@@ -1753,6 +2059,7 @@ function userSkillsDir() {
|
|
|
1753
2059
|
return resolve3(getDataRoot(), "agent_moss_skills");
|
|
1754
2060
|
}
|
|
1755
2061
|
function runtimeCustomSkillKeywords() {
|
|
2062
|
+
if (isRuntimeConfigDisabled()) return {};
|
|
1756
2063
|
try {
|
|
1757
2064
|
const rt = runtimeConfigBridge.current?.loadRuntimeConfig?.();
|
|
1758
2065
|
if (!rt) return {};
|
|
@@ -1770,6 +2077,7 @@ function runtimeCustomSkillKeywords() {
|
|
|
1770
2077
|
}
|
|
1771
2078
|
}
|
|
1772
2079
|
function loadUserSkill(name) {
|
|
2080
|
+
if (isRuntimeConfigDisabled()) return "";
|
|
1773
2081
|
if (userSkillCache[name] !== void 0) return userSkillCache[name];
|
|
1774
2082
|
try {
|
|
1775
2083
|
const p = resolve3(userSkillsDir(), `${name}.md`);
|
|
@@ -1838,6 +2146,7 @@ var init_skill_loader = __esm({
|
|
|
1838
2146
|
init_esm_shims();
|
|
1839
2147
|
init_rules_loader();
|
|
1840
2148
|
init_paths();
|
|
2149
|
+
init_rules_mode();
|
|
1841
2150
|
init_asset_dir();
|
|
1842
2151
|
init_runtime_config_bridge();
|
|
1843
2152
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
@@ -1898,6 +2207,7 @@ __export(runtime_config_exports, {
|
|
|
1898
2207
|
getEnabledScenarioExtensions: () => getEnabledScenarioExtensions,
|
|
1899
2208
|
getEnvOverrides: () => getEnvOverrides,
|
|
1900
2209
|
getProfileOrDetect: () => getProfileOrDetect,
|
|
2210
|
+
getRuleSourceFingerprint: () => getRuleSourceFingerprint,
|
|
1901
2211
|
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
1902
2212
|
getScenarioRules: () => getScenarioRules,
|
|
1903
2213
|
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
@@ -1932,7 +2242,22 @@ function getRuntimeConfigPath() {
|
|
|
1932
2242
|
if (envPath) return envPath;
|
|
1933
2243
|
return RUNTIME_CONFIG_PATH;
|
|
1934
2244
|
}
|
|
2245
|
+
function getRuleSourceFingerprint() {
|
|
2246
|
+
if (isRuntimeConfigDisabled()) return `mode:${getRulesMode()}`;
|
|
2247
|
+
const p = getRuntimeConfigPath();
|
|
2248
|
+
try {
|
|
2249
|
+
const st = statSync2(p);
|
|
2250
|
+
return `${p}|${st.mtimeMs}|${st.size}`;
|
|
2251
|
+
} catch {
|
|
2252
|
+
return `${p}|absent`;
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
1935
2255
|
function saveRuntimeConfig(config) {
|
|
2256
|
+
if (isRuntimeConfigDisabled()) {
|
|
2257
|
+
throw new Error(
|
|
2258
|
+
`\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF1A\u4FEE\u6539\u89C4\u5219\u4E0D\u4F1A\u751F\u6548\uFF0C\u8BF7\u5207\u6362\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u5E76\u91CD\u542F\u670D\u52A1\u3002`
|
|
2259
|
+
);
|
|
2260
|
+
}
|
|
1936
2261
|
const p = getRuntimeConfigPath();
|
|
1937
2262
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1938
2263
|
if (dir && !existsSync5(dir)) mkdirSync(dir, { recursive: true });
|
|
@@ -1956,6 +2281,18 @@ function invalidateRuntimeConfigCache() {
|
|
|
1956
2281
|
hasRuntimeConfigCache.clear();
|
|
1957
2282
|
}
|
|
1958
2283
|
function loadRuntimeConfig() {
|
|
2284
|
+
if (isRuntimeConfigDisabled()) {
|
|
2285
|
+
return {
|
|
2286
|
+
version: 1,
|
|
2287
|
+
layers: { ...DEFAULT_LAYERS },
|
|
2288
|
+
L1_rules: {},
|
|
2289
|
+
L2_rules: {},
|
|
2290
|
+
L3_skills: {},
|
|
2291
|
+
L2_2: {},
|
|
2292
|
+
agent_rules: {},
|
|
2293
|
+
scenario_rules: {}
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
1959
2296
|
const p = getRuntimeConfigPath();
|
|
1960
2297
|
if (!existsSync5(p)) {
|
|
1961
2298
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
@@ -2002,6 +2339,7 @@ function loadRuntimeConfig() {
|
|
|
2002
2339
|
function isLayerEnabled(layerKey) {
|
|
2003
2340
|
const disableKey = LAYER_DISABLE_ENV[layerKey];
|
|
2004
2341
|
if (disableKey && process.env[disableKey] === "1") return false;
|
|
2342
|
+
if (isRuntimeConfigDisabled()) return true;
|
|
2005
2343
|
const runtime = loadRuntimeConfig();
|
|
2006
2344
|
const layers = runtime.layers || {};
|
|
2007
2345
|
if (layerKey in layers) return !!layers[layerKey];
|
|
@@ -2047,18 +2385,34 @@ function _mergeUserCustom(primary, runtime, layers, key) {
|
|
|
2047
2385
|
const extra = _userCustomRules(runtime, layers, key);
|
|
2048
2386
|
return extra.length > 0 ? [...primary, ...extra] : [...primary];
|
|
2049
2387
|
}
|
|
2050
|
-
function
|
|
2388
|
+
function _dedup(rules, category) {
|
|
2389
|
+
const keys = _DEDUP_KEYS[category];
|
|
2390
|
+
if (!keys) return rules;
|
|
2391
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2392
|
+
const out = [];
|
|
2393
|
+
for (const r of rules) {
|
|
2394
|
+
const k = keys.map((x) => String(r?.[x] ?? "")).join("\0");
|
|
2395
|
+
if (seen.has(k)) continue;
|
|
2396
|
+
seen.add(k);
|
|
2397
|
+
out.push(r);
|
|
2398
|
+
}
|
|
2399
|
+
return out;
|
|
2400
|
+
}
|
|
2401
|
+
function _mergeRulesFromDirs(primary, category) {
|
|
2402
|
+
let base = [...primary];
|
|
2051
2403
|
try {
|
|
2404
|
+
const override = loadOverrideRules()[category];
|
|
2405
|
+
if (override !== void 0 && override !== null) base = [...override];
|
|
2052
2406
|
const extra = loadExtraRules()[category] || [];
|
|
2053
|
-
return
|
|
2407
|
+
return _dedup([...base, ...extra], category);
|
|
2054
2408
|
} catch {
|
|
2055
|
-
return
|
|
2409
|
+
return base;
|
|
2056
2410
|
}
|
|
2057
2411
|
}
|
|
2058
2412
|
function getEnabledL1CommandPatterns(runtime, osType) {
|
|
2059
2413
|
const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
|
|
2060
2414
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2061
|
-
rules =
|
|
2415
|
+
rules = _mergeRulesFromDirs(rules, "dangerous_commands");
|
|
2062
2416
|
rules = _mergeUserCustom(rules, runtime, "L1_rules", "pattern");
|
|
2063
2417
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2064
2418
|
return rules.map((r) => ({
|
|
@@ -2074,7 +2428,7 @@ function getEnabledL2SensitivePaths(runtime, osType) {
|
|
|
2074
2428
|
cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
2075
2429
|
}
|
|
2076
2430
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2077
|
-
rules =
|
|
2431
|
+
rules = _mergeRulesFromDirs(rules, "sensitive_path_access");
|
|
2078
2432
|
rules = _mergeUserCustom(rules, runtime, ["L1_rules", "L2_rules"], "path");
|
|
2079
2433
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2080
2434
|
return rules.map((r) => {
|
|
@@ -2086,6 +2440,7 @@ function getEnabledL2SensitivePaths(runtime, osType) {
|
|
|
2086
2440
|
});
|
|
2087
2441
|
}
|
|
2088
2442
|
function hasRuntimeConfig() {
|
|
2443
|
+
if (isRuntimeConfigDisabled()) return false;
|
|
2089
2444
|
const p = getRuntimeConfigPath();
|
|
2090
2445
|
let key;
|
|
2091
2446
|
try {
|
|
@@ -2094,8 +2449,8 @@ function hasRuntimeConfig() {
|
|
|
2094
2449
|
} catch {
|
|
2095
2450
|
return false;
|
|
2096
2451
|
}
|
|
2097
|
-
const
|
|
2098
|
-
if (
|
|
2452
|
+
const cached2 = hasRuntimeConfigCache.get(key);
|
|
2453
|
+
if (cached2 !== void 0) return cached2;
|
|
2099
2454
|
let ok = false;
|
|
2100
2455
|
try {
|
|
2101
2456
|
JSON.parse(readFileSync4(p, "utf-8"));
|
|
@@ -2459,25 +2814,11 @@ function generateSourceDefaults() {
|
|
|
2459
2814
|
};
|
|
2460
2815
|
}
|
|
2461
2816
|
function _ltlRulesToRuntime() {
|
|
2462
|
-
const cands = [
|
|
2463
|
-
getRuntimeConfigDir() && join3(getRuntimeConfigDir(), "rules"),
|
|
2464
|
-
join3(__dirname4, "../rules"),
|
|
2465
|
-
join3(__dirname4, "rules"),
|
|
2466
|
-
join3(__dirname4, "../../../agent_moss/rules"),
|
|
2467
|
-
join3(__dirname4, "../../../../agent_moss/rules"),
|
|
2468
|
-
join3(process.cwd(), "agent_moss/rules")
|
|
2469
|
-
].filter(Boolean);
|
|
2470
2817
|
let mc = {};
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
mc = JSON.parse(readFileSync4(p, "utf-8")) || {};
|
|
2476
|
-
} catch {
|
|
2477
|
-
mc = {};
|
|
2478
|
-
}
|
|
2479
|
-
break;
|
|
2480
|
-
}
|
|
2818
|
+
try {
|
|
2819
|
+
mc = loadMCRaw();
|
|
2820
|
+
} catch {
|
|
2821
|
+
mc = {};
|
|
2481
2822
|
}
|
|
2482
2823
|
const rules = mc.rules || [];
|
|
2483
2824
|
return {
|
|
@@ -2706,7 +3047,7 @@ function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
|
|
|
2706
3047
|
function getEnabledL1InjectionKeywords(runtime, osType) {
|
|
2707
3048
|
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
2708
3049
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2709
|
-
rules =
|
|
3050
|
+
rules = _mergeRulesFromDirs(rules, "injection_detection");
|
|
2710
3051
|
rules = _mergeUserCustom(rules, runtime, "L1_rules", "keyword");
|
|
2711
3052
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2712
3053
|
return rules.map((r) => ({
|
|
@@ -2718,7 +3059,7 @@ function getEnabledL1InjectionKeywords(runtime, osType) {
|
|
|
2718
3059
|
function getEnabledL2IntentPatterns(runtime, osType) {
|
|
2719
3060
|
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
2720
3061
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2721
|
-
rules =
|
|
3062
|
+
rules = _mergeRulesFromDirs(rules, "intent_consistency");
|
|
2722
3063
|
rules = _mergeUserCustom(rules, runtime, "L2_rules", "intent_keywords");
|
|
2723
3064
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2724
3065
|
return rules.map((r) => ({
|
|
@@ -2730,7 +3071,7 @@ function getEnabledL2IntentPatterns(runtime, osType) {
|
|
|
2730
3071
|
function getEnabledL2PasswordPatterns(runtime) {
|
|
2731
3072
|
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
2732
3073
|
const rules = _mergeUserCustom(
|
|
2733
|
-
|
|
3074
|
+
_mergeRulesFromDirs(filterEnabledRules(cat.rules || [], cat.category_enabled), "password_modify_consent"),
|
|
2734
3075
|
runtime,
|
|
2735
3076
|
"L2_rules",
|
|
2736
3077
|
"pattern"
|
|
@@ -2740,7 +3081,7 @@ function getEnabledL2PasswordPatterns(runtime) {
|
|
|
2740
3081
|
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
2741
3082
|
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
2742
3083
|
const rules = _mergeUserCustom(
|
|
2743
|
-
|
|
3084
|
+
_mergeRulesFromDirs(filterEnabledRules(cat.rules || [], cat.category_enabled), "user_deletion_consent"),
|
|
2744
3085
|
runtime,
|
|
2745
3086
|
"L2_rules",
|
|
2746
3087
|
"pattern"
|
|
@@ -2777,7 +3118,7 @@ function getEnabledL1CommandPatternsOrDefault(runtime, osType) {
|
|
|
2777
3118
|
return getEnabledL1CommandPatterns(runtime, osType);
|
|
2778
3119
|
}
|
|
2779
3120
|
return _mergeUserCustom(
|
|
2780
|
-
|
|
3121
|
+
_mergeRulesFromDirs(_filterDefaultsByOs(_l1CommandPatternsToRules(), osType), "dangerous_commands"),
|
|
2781
3122
|
runtime,
|
|
2782
3123
|
"L1_rules",
|
|
2783
3124
|
"pattern"
|
|
@@ -2789,7 +3130,7 @@ function getEnabledL1InjectionKeywordsOrDefault(runtime, osType) {
|
|
|
2789
3130
|
return getEnabledL1InjectionKeywords(runtime, osType);
|
|
2790
3131
|
}
|
|
2791
3132
|
return _mergeUserCustom(
|
|
2792
|
-
|
|
3133
|
+
_mergeRulesFromDirs(_l1InjectionKeywordsToRules(), "injection_detection"),
|
|
2793
3134
|
runtime,
|
|
2794
3135
|
"L1_rules",
|
|
2795
3136
|
"keyword"
|
|
@@ -2801,7 +3142,7 @@ function getEnabledL2SensitivePathsOrDefault(runtime, osType) {
|
|
|
2801
3142
|
return getEnabledL2SensitivePaths(runtime, osType);
|
|
2802
3143
|
}
|
|
2803
3144
|
return _mergeUserCustom(
|
|
2804
|
-
|
|
3145
|
+
_mergeRulesFromDirs(_filterDefaultsByOs(_l2SensitivePathsToRules(), osType), "sensitive_path_access"),
|
|
2805
3146
|
runtime,
|
|
2806
3147
|
["L1_rules", "L2_rules"],
|
|
2807
3148
|
"path"
|
|
@@ -2813,7 +3154,7 @@ function getEnabledL2IntentPatternsOrDefault(runtime, osType) {
|
|
|
2813
3154
|
return getEnabledL2IntentPatterns(runtime, osType);
|
|
2814
3155
|
}
|
|
2815
3156
|
return _mergeUserCustom(
|
|
2816
|
-
|
|
3157
|
+
_mergeRulesFromDirs(_filterDefaultsByOs(_l2IntentPatternsToRules(), osType), "intent_consistency"),
|
|
2817
3158
|
runtime,
|
|
2818
3159
|
"L2_rules",
|
|
2819
3160
|
"intent_keywords"
|
|
@@ -2989,6 +3330,11 @@ function deleteCustomRule(layer, category, ruleId) {
|
|
|
2989
3330
|
}
|
|
2990
3331
|
function addCustomSkill(skillId, category, keywords, content) {
|
|
2991
3332
|
_validateSkillId(skillId);
|
|
3333
|
+
if (isRuntimeConfigDisabled()) {
|
|
3334
|
+
throw new Error(
|
|
3335
|
+
`\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF0C\u4E0D\u5141\u8BB8\u65B0\u589E\u81EA\u5B9A\u4E49 skill\u3002`
|
|
3336
|
+
);
|
|
3337
|
+
}
|
|
2992
3338
|
const runtime = loadRuntimeConfig();
|
|
2993
3339
|
const skillsDir = join3(getRuntimeConfigDir(), "agent_moss_skills");
|
|
2994
3340
|
try {
|
|
@@ -3204,7 +3550,7 @@ function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
|
|
|
3204
3550
|
saveRuntimeConfig(runtime);
|
|
3205
3551
|
return runtime;
|
|
3206
3552
|
}
|
|
3207
|
-
var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, runtimeConfigCache, hasRuntimeConfigCache, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, _effectiveCache, SKILL_ID_RE;
|
|
3553
|
+
var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, runtimeConfigCache, _DEDUP_KEYS, hasRuntimeConfigCache, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, _effectiveCache, SKILL_ID_RE;
|
|
3208
3554
|
var init_runtime_config = __esm({
|
|
3209
3555
|
"src/infra/runtime-config.ts"() {
|
|
3210
3556
|
"use strict";
|
|
@@ -3215,6 +3561,7 @@ var init_runtime_config = __esm({
|
|
|
3215
3561
|
init_skill_loader();
|
|
3216
3562
|
init_rules_loader();
|
|
3217
3563
|
init_runtime_config_bridge();
|
|
3564
|
+
init_rules_mode();
|
|
3218
3565
|
__dirname4 = dirname3(fileURLToPath4(import.meta.url));
|
|
3219
3566
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
3220
3567
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
@@ -3237,6 +3584,14 @@ var init_runtime_config = __esm({
|
|
|
3237
3584
|
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
3238
3585
|
};
|
|
3239
3586
|
runtimeConfigCache = null;
|
|
3587
|
+
_DEDUP_KEYS = {
|
|
3588
|
+
dangerous_commands: ["pattern"],
|
|
3589
|
+
password_modify_consent: ["pattern"],
|
|
3590
|
+
user_deletion_consent: ["pattern"],
|
|
3591
|
+
sensitive_path_access: ["path"],
|
|
3592
|
+
injection_detection: ["keyword", "lang"],
|
|
3593
|
+
intent_consistency: ["intent_keywords", "reason"]
|
|
3594
|
+
};
|
|
3240
3595
|
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
3241
3596
|
_AGENTS_DIR_PROD = join3(__dirname4, "agents");
|
|
3242
3597
|
_AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
@@ -3262,6 +3617,8 @@ var init_runtime_config = __esm({
|
|
|
3262
3617
|
var config_exports = {};
|
|
3263
3618
|
__export(config_exports, {
|
|
3264
3619
|
detectProvider: () => detectProvider,
|
|
3620
|
+
getAgentMossSetting: () => getAgentMossSetting,
|
|
3621
|
+
getCacheOptions: () => getCacheOptions,
|
|
3265
3622
|
getCustomRules: () => getCustomRules,
|
|
3266
3623
|
getLLMConfig: () => getLLMConfig,
|
|
3267
3624
|
getLLMFailMode: () => getLLMFailMode,
|
|
@@ -3273,7 +3630,10 @@ __export(config_exports, {
|
|
|
3273
3630
|
isHeuristicEnabled: () => isHeuristicEnabled,
|
|
3274
3631
|
isLLMAvailable: () => isLLMAvailable,
|
|
3275
3632
|
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
3633
|
+
isPolicyGenEnabled: () => isPolicyGenEnabled,
|
|
3634
|
+
isPolicySaveEnabled: () => isPolicySaveEnabled,
|
|
3276
3635
|
probeLLMConnection: () => probeLLMConnection,
|
|
3636
|
+
reloadCustomRules: () => reloadCustomRules,
|
|
3277
3637
|
updateLLMConfig: () => updateLLMConfig,
|
|
3278
3638
|
userRuleVariantPattern: () => userRuleVariantPattern,
|
|
3279
3639
|
wildcardToRegExp: () => wildcardToRegExp
|
|
@@ -3295,8 +3655,10 @@ function resolveConfigPath() {
|
|
|
3295
3655
|
const userConfig = path2.join(getConfigDir(), "config.json");
|
|
3296
3656
|
if (fs2.existsSync(userConfig)) return userConfig;
|
|
3297
3657
|
const defaultCandidates = [
|
|
3298
|
-
path2.join(
|
|
3299
|
-
path2.join(
|
|
3658
|
+
path2.join(__configModuleDir, "config.json.example"),
|
|
3659
|
+
path2.join(__configModuleDir, "..", "config.json.example"),
|
|
3660
|
+
path2.join(__configModuleDir, "..", "..", "agent_moss", "infra", "config.json.example"),
|
|
3661
|
+
path2.join(__configModuleDir, "..", "..", "..", "agent_moss", "infra", "config.json.example")
|
|
3300
3662
|
];
|
|
3301
3663
|
const defaultConfig = defaultCandidates.find((p) => fs2.existsSync(p));
|
|
3302
3664
|
if (defaultConfig) {
|
|
@@ -3305,8 +3667,8 @@ function resolveConfigPath() {
|
|
|
3305
3667
|
const pkgConfig = loadConfigFile(defaultConfig);
|
|
3306
3668
|
const safeConfig = pkgConfig ? { ...pkgConfig, llm: { ...pkgConfig.llm, api_key: "" } } : {};
|
|
3307
3669
|
fs2.writeFileSync(userConfig, JSON.stringify(safeConfig, null, 4), "utf-8");
|
|
3308
|
-
console.
|
|
3309
|
-
console.
|
|
3670
|
+
console.error(`[AgentMoss] \u5DF2\u521B\u5EFA\u7528\u6237\u914D\u7F6E\u6587\u4EF6: ${userConfig}`);
|
|
3671
|
+
console.error(`[AgentMoss] \u8BF7\u7F16\u8F91\u8BE5\u6587\u4EF6\u914D\u7F6E\u4F60\u7684 LLM API Key`);
|
|
3310
3672
|
}
|
|
3311
3673
|
return userConfig;
|
|
3312
3674
|
}
|
|
@@ -3352,17 +3714,19 @@ function wildcardToRegExp(pattern) {
|
|
|
3352
3714
|
}
|
|
3353
3715
|
function loadCustomRules() {
|
|
3354
3716
|
const rules = [];
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3717
|
+
if (!isBuiltinOnly()) {
|
|
3718
|
+
try {
|
|
3719
|
+
const raw = process.env.AGENT_MOSS_CUSTOM_RULES;
|
|
3720
|
+
if (raw) {
|
|
3721
|
+
const parsed = JSON.parse(raw);
|
|
3722
|
+
if (Array.isArray(parsed)) {
|
|
3723
|
+
for (const r of parsed) {
|
|
3724
|
+
if (r && r.pattern && r.action) rules.push({ pattern: r.pattern, action: r.action, severity: r.severity || "high" });
|
|
3725
|
+
}
|
|
3362
3726
|
}
|
|
3363
3727
|
}
|
|
3728
|
+
} catch {
|
|
3364
3729
|
}
|
|
3365
|
-
} catch {
|
|
3366
3730
|
}
|
|
3367
3731
|
for (const rulesPath of USER_RULES_PATHS) {
|
|
3368
3732
|
try {
|
|
@@ -3391,6 +3755,9 @@ function userRuleVariantPattern(pattern) {
|
|
|
3391
3755
|
}
|
|
3392
3756
|
return null;
|
|
3393
3757
|
}
|
|
3758
|
+
function reloadCustomRules() {
|
|
3759
|
+
customRules = loadCustomRules();
|
|
3760
|
+
}
|
|
3394
3761
|
function getLLMConfig() {
|
|
3395
3762
|
return llmConfig;
|
|
3396
3763
|
}
|
|
@@ -3478,7 +3845,33 @@ function layerEnabledOrDefault(key) {
|
|
|
3478
3845
|
}
|
|
3479
3846
|
}
|
|
3480
3847
|
function isBrainEnabled() {
|
|
3481
|
-
|
|
3848
|
+
if (isRuntimeConfigDisabled()) return false;
|
|
3849
|
+
return getAgentMossSetting("AGENT_MOSS_BRAIN_ENABLED", "") === "1";
|
|
3850
|
+
}
|
|
3851
|
+
function getAgentMossSetting(key, def = "") {
|
|
3852
|
+
const envValue = process.env[key];
|
|
3853
|
+
if (envValue !== void 0) return envValue;
|
|
3854
|
+
const cfg = loadConfigFile(resolveConfigPath());
|
|
3855
|
+
if (cfg && key in cfg) return String(cfg[key]);
|
|
3856
|
+
return def;
|
|
3857
|
+
}
|
|
3858
|
+
function isPolicyGenEnabled() {
|
|
3859
|
+
return getAgentMossSetting("AGENT_MOSS_ENABLE_POLICY_GEN", "") === "1";
|
|
3860
|
+
}
|
|
3861
|
+
function getCacheOptions() {
|
|
3862
|
+
let enabled = true;
|
|
3863
|
+
let maxSize = 1e3;
|
|
3864
|
+
const cfg = loadConfigFile(resolveConfigPath());
|
|
3865
|
+
const cache3 = cfg?.cache;
|
|
3866
|
+
if (cache3 && typeof cache3 === "object") {
|
|
3867
|
+
if (typeof cache3.enabled === "boolean") enabled = cache3.enabled;
|
|
3868
|
+
if (typeof cache3.max_size === "number" && cache3.max_size > 0) maxSize = cache3.max_size;
|
|
3869
|
+
}
|
|
3870
|
+
if (process.env.AGENT_MOSS_DISABLE_CACHE === "1") enabled = false;
|
|
3871
|
+
return { enabled, maxSize };
|
|
3872
|
+
}
|
|
3873
|
+
function isPolicySaveEnabled() {
|
|
3874
|
+
return getAgentMossSetting("AGENT_MOSS_SAVE_POLICY", "1") !== "0";
|
|
3482
3875
|
}
|
|
3483
3876
|
function isLogicRulesEnabled() {
|
|
3484
3877
|
if (process.env.AGENT_MOSS_DISABLE_LOGIC_RULES === "1") return false;
|
|
@@ -3487,13 +3880,21 @@ function isLogicRulesEnabled() {
|
|
|
3487
3880
|
function isFormalObserveMode() {
|
|
3488
3881
|
return process.env.AGENT_MOSS_FORMAL_OBSERVE === "1";
|
|
3489
3882
|
}
|
|
3490
|
-
var timeoutConfig, retryConfig, llmConfig,
|
|
3883
|
+
var __configModuleDir, timeoutConfig, retryConfig, llmConfig, USER_RULES_PATHS, customRules, PROVIDER_HEADERS;
|
|
3491
3884
|
var init_config = __esm({
|
|
3492
3885
|
"src/config.ts"() {
|
|
3493
3886
|
"use strict";
|
|
3494
3887
|
init_esm_shims();
|
|
3495
3888
|
init_paths();
|
|
3496
3889
|
init_runtime_config();
|
|
3890
|
+
init_rules_mode();
|
|
3891
|
+
__configModuleDir = (() => {
|
|
3892
|
+
try {
|
|
3893
|
+
return path2.dirname(fileURLToPath5(import.meta.url));
|
|
3894
|
+
} catch {
|
|
3895
|
+
return typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
3896
|
+
}
|
|
3897
|
+
})();
|
|
3497
3898
|
llmConfig = (() => {
|
|
3498
3899
|
const configPath = resolveConfigPath();
|
|
3499
3900
|
const fileConfig = fs2.existsSync(configPath) ? loadLLMConfigFromFile(configPath) : {};
|
|
@@ -3511,13 +3912,6 @@ var init_config = __esm({
|
|
|
3511
3912
|
timeout: parseInt(process.env.AGENT_MOSS_LLM_TIMEOUT || String(fileConfig.timeout ?? String(tr.timeout.prompt1Timeout)), 10)
|
|
3512
3913
|
};
|
|
3513
3914
|
})();
|
|
3514
|
-
__configModuleDir = (() => {
|
|
3515
|
-
try {
|
|
3516
|
-
return path2.dirname(fileURLToPath5(import.meta.url));
|
|
3517
|
-
} catch {
|
|
3518
|
-
return typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
3519
|
-
}
|
|
3520
|
-
})();
|
|
3521
3915
|
USER_RULES_PATHS = [
|
|
3522
3916
|
path2.join(__configModuleDir, "rules", "user_rules.json"),
|
|
3523
3917
|
// 源在 ts/src/rules(一般不存在)
|
|
@@ -3550,7 +3944,9 @@ function resolveAgentsDir() {
|
|
|
3550
3944
|
}
|
|
3551
3945
|
function getAgentExtensions(agentId) {
|
|
3552
3946
|
if (!agentId) return null;
|
|
3553
|
-
|
|
3947
|
+
const fp = getRuleSourceFingerprint();
|
|
3948
|
+
const cached2 = cache.get(agentId);
|
|
3949
|
+
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
3554
3950
|
let ext = null;
|
|
3555
3951
|
try {
|
|
3556
3952
|
if (hasRuntimeConfig()) {
|
|
@@ -3562,7 +3958,7 @@ function getAgentExtensions(agentId) {
|
|
|
3562
3958
|
const agentsDir = resolveAgentsDir();
|
|
3563
3959
|
const rulesFile = resolve4(agentsDir, agentId, "rules.json");
|
|
3564
3960
|
if (!existsSync6(rulesFile)) {
|
|
3565
|
-
cache.set(agentId, null);
|
|
3961
|
+
cache.set(agentId, { fp, ext: null });
|
|
3566
3962
|
return null;
|
|
3567
3963
|
}
|
|
3568
3964
|
try {
|
|
@@ -3570,16 +3966,16 @@ function getAgentExtensions(agentId) {
|
|
|
3570
3966
|
ext = JSON.parse(raw);
|
|
3571
3967
|
if (!ext.agent_id) {
|
|
3572
3968
|
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
3573
|
-
cache.set(agentId, null);
|
|
3969
|
+
cache.set(agentId, { fp, ext: null });
|
|
3574
3970
|
return null;
|
|
3575
3971
|
}
|
|
3576
3972
|
} catch (e) {
|
|
3577
3973
|
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
3578
|
-
cache.set(agentId, null);
|
|
3974
|
+
cache.set(agentId, { fp, ext: null });
|
|
3579
3975
|
return null;
|
|
3580
3976
|
}
|
|
3581
3977
|
}
|
|
3582
|
-
cache.set(agentId, ext);
|
|
3978
|
+
cache.set(agentId, { fp, ext });
|
|
3583
3979
|
console.error(
|
|
3584
3980
|
`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths, ${ext.builtin_safe_skills?.length ?? 0} builtin safe skills, ${ext.builtin_safe_action_types?.length ?? 0} builtin safe action types`
|
|
3585
3981
|
);
|
|
@@ -3625,7 +4021,9 @@ function resolveScenariosDir() {
|
|
|
3625
4021
|
}
|
|
3626
4022
|
function getScenarioExtensions(scenarioId) {
|
|
3627
4023
|
if (!scenarioId) return null;
|
|
3628
|
-
|
|
4024
|
+
const fp = getRuleSourceFingerprint();
|
|
4025
|
+
const cached2 = cache2.get(scenarioId);
|
|
4026
|
+
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
3629
4027
|
let ext = null;
|
|
3630
4028
|
let overrideHit = null;
|
|
3631
4029
|
try {
|
|
@@ -3649,7 +4047,7 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3649
4047
|
);
|
|
3650
4048
|
}
|
|
3651
4049
|
if (!existsSync7(rulesFile)) {
|
|
3652
|
-
cache2.set(scenarioId, null);
|
|
4050
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3653
4051
|
return null;
|
|
3654
4052
|
}
|
|
3655
4053
|
try {
|
|
@@ -3658,7 +4056,7 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3658
4056
|
const id = data.scenario_id || data.agent_id || "";
|
|
3659
4057
|
if (!id) {
|
|
3660
4058
|
console.warn(`[AgentMoss scenarios] rules.json \u7F3A\u5C11 scenario_id \u5B57\u6BB5: ${rulesFile}`);
|
|
3661
|
-
cache2.set(scenarioId, null);
|
|
4059
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3662
4060
|
return null;
|
|
3663
4061
|
}
|
|
3664
4062
|
ext = {
|
|
@@ -3673,11 +4071,11 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3673
4071
|
};
|
|
3674
4072
|
} catch (e) {
|
|
3675
4073
|
console.error(`[AgentMoss scenarios] \u52A0\u8F7D\u573A\u666F ${scenarioId} rules.json \u5931\u8D25:`, e.message);
|
|
3676
|
-
cache2.set(scenarioId, null);
|
|
4074
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3677
4075
|
return null;
|
|
3678
4076
|
}
|
|
3679
4077
|
}
|
|
3680
|
-
cache2.set(scenarioId, ext);
|
|
4078
|
+
cache2.set(scenarioId, { fp, ext });
|
|
3681
4079
|
console.error(
|
|
3682
4080
|
`[AgentMoss scenarios] \u52A0\u8F7D\u573A\u666F ${scenarioId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths`
|
|
3683
4081
|
);
|
|
@@ -7031,10 +7429,10 @@ async function runLLMAnalysis(input, previous) {
|
|
|
7031
7429
|
const config = input.llmConfig?.apiKey ? { ...globalConfig, ...input.llmConfig } : globalConfig;
|
|
7032
7430
|
const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
|
|
7033
7431
|
const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
|
|
7034
|
-
const
|
|
7035
|
-
if (
|
|
7432
|
+
const cached2 = llmCache.get(cacheKey);
|
|
7433
|
+
if (cached2) {
|
|
7036
7434
|
console.log("[AgentMoss] LLM cache hit");
|
|
7037
|
-
return
|
|
7435
|
+
return cached2;
|
|
7038
7436
|
}
|
|
7039
7437
|
const matchedSkills = matchSkills(input.aNext.actionType, input.aNext.actionDetail, input.reason);
|
|
7040
7438
|
const templates = loadTemplates();
|
|
@@ -7223,7 +7621,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
7223
7621
|
}
|
|
7224
7622
|
if (attempt < maxRetries) {
|
|
7225
7623
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
7226
|
-
await new Promise((
|
|
7624
|
+
await new Promise((resolve11) => setTimeout(resolve11, retryCfg.retryInterval * 1e3));
|
|
7227
7625
|
} else {
|
|
7228
7626
|
clearTimeout(totalTimeoutTimer);
|
|
7229
7627
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
@@ -7346,6 +7744,56 @@ var init_rule_store = __esm({
|
|
|
7346
7744
|
getRule(ruleId) {
|
|
7347
7745
|
return this.data.rules.find((r) => r.id === ruleId);
|
|
7348
7746
|
}
|
|
7747
|
+
/** 质疑:active → disputed,并把 disputed_count +1(镜像 Python dispute_rule)。 */
|
|
7748
|
+
disputeRule(ruleId, _reason = "") {
|
|
7749
|
+
const rule = this.getRule(ruleId);
|
|
7750
|
+
if (!rule) throw new Error(`\u89C4\u5219\u4E0D\u5B58\u5728: ${ruleId}`);
|
|
7751
|
+
if (rule.status !== STATUS_ACTIVE) throw new Error(`\u53EA\u6709 active \u72B6\u6001\u53EF\u8D28\u7591\uFF0C\u5F53\u524D: ${rule.status}`);
|
|
7752
|
+
rule.status = STATUS_DISPUTED;
|
|
7753
|
+
rule.disputedCount += 1;
|
|
7754
|
+
this.save();
|
|
7755
|
+
}
|
|
7756
|
+
// ==================== 生命周期(curator 用,镜像 Python rule_store) ====================
|
|
7757
|
+
/** 长期未触发的 active/disputed 规则 → stale。 */
|
|
7758
|
+
markStale(staleDays = 30) {
|
|
7759
|
+
const cutoff = Date.now() - staleDays * 864e5;
|
|
7760
|
+
for (const r of this.data.rules) {
|
|
7761
|
+
if ((r.status === STATUS_ACTIVE || r.status === STATUS_DISPUTED) && r.lastTriggeredAt > 0 && r.lastTriggeredAt < cutoff) {
|
|
7762
|
+
r.status = STATUS_STALE;
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
this.save();
|
|
7766
|
+
}
|
|
7767
|
+
/** 长期 stale 的规则 → archived。 */
|
|
7768
|
+
markArchived(archiveDays = 90) {
|
|
7769
|
+
const cutoff = Date.now() - archiveDays * 864e5;
|
|
7770
|
+
for (const r of this.data.rules) {
|
|
7771
|
+
if (r.status === STATUS_STALE && r.lastTriggeredAt > 0 && r.lastTriggeredAt < cutoff) {
|
|
7772
|
+
r.status = STATUS_ARCHIVED;
|
|
7773
|
+
}
|
|
7774
|
+
}
|
|
7775
|
+
this.save();
|
|
7776
|
+
}
|
|
7777
|
+
/** disputed 次数达阈值 → 自动降级 stale(自动纠错闭环)。 */
|
|
7778
|
+
autoDemoteDisputed(disputeThreshold = 3) {
|
|
7779
|
+
for (const r of this.data.rules) {
|
|
7780
|
+
if (r.status === STATUS_DISPUTED && r.disputedCount >= disputeThreshold) {
|
|
7781
|
+
r.status = STATUS_STALE;
|
|
7782
|
+
}
|
|
7783
|
+
}
|
|
7784
|
+
this.save();
|
|
7785
|
+
}
|
|
7786
|
+
// ==================== 中心化预留(镜像 Python) ====================
|
|
7787
|
+
/** 导出本地新增(非 central 来源)的 active 规则。 */
|
|
7788
|
+
getRulesForExport() {
|
|
7789
|
+
return this.data.rules.filter((r) => r.source !== "central" && r.status === STATUS_ACTIVE);
|
|
7790
|
+
}
|
|
7791
|
+
/** 导入中心下发规则(强制标 central 来源,避免被再次回流)。 */
|
|
7792
|
+
importRules(rules) {
|
|
7793
|
+
for (const rule of rules) {
|
|
7794
|
+
this.addRule({ ...rule, source: "central", originNode: "central" });
|
|
7795
|
+
}
|
|
7796
|
+
}
|
|
7349
7797
|
getActiveRules(layer) {
|
|
7350
7798
|
return this.data.rules.filter((r) => r.status === STATUS_ACTIVE && (layer === void 0 || r.layer === layer));
|
|
7351
7799
|
}
|
|
@@ -7453,12 +7901,14 @@ var init_pattern_extractor = __esm({
|
|
|
7453
7901
|
minOccurrences;
|
|
7454
7902
|
builtinPatterns;
|
|
7455
7903
|
reviewFn;
|
|
7904
|
+
skillManager;
|
|
7456
7905
|
constructor(ruleStore, opts) {
|
|
7457
7906
|
this.ruleStore = ruleStore;
|
|
7458
7907
|
this.minConfidence = opts?.minConfidence ?? 0.9;
|
|
7459
7908
|
this.minOccurrences = opts?.minOccurrences ?? 3;
|
|
7460
7909
|
this.builtinPatterns = opts?.builtinPatterns ?? [];
|
|
7461
7910
|
this.reviewFn = opts?.review ?? null;
|
|
7911
|
+
this.skillManager = opts?.skillManager ?? null;
|
|
7462
7912
|
}
|
|
7463
7913
|
async extract(event, llmResult) {
|
|
7464
7914
|
const result = { rulesCreated: 0, patternsRecorded: 0, skippedReason: "", deduped: [] };
|
|
@@ -7547,11 +7997,99 @@ var init_pattern_extractor = __esm({
|
|
|
7547
7997
|
result.rulesCreated += 1;
|
|
7548
7998
|
}
|
|
7549
7999
|
}
|
|
8000
|
+
if (this.skillManager) {
|
|
8001
|
+
const skill = this.buildSkill(actionDetail, reason, riskType, eventId);
|
|
8002
|
+
if (skill) {
|
|
8003
|
+
const id = this.skillManager.addSkill(skill);
|
|
8004
|
+
if (id) {
|
|
8005
|
+
result.skillsCreated = (result.skillsCreated ?? 0) + 1;
|
|
8006
|
+
}
|
|
8007
|
+
}
|
|
8008
|
+
}
|
|
7550
8009
|
return result;
|
|
7551
8010
|
}
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
8011
|
+
/**
|
|
8012
|
+
* 生成候选 Skill;不值得沉淀时返回 null(镜像 Python `_extract_skill` 的三道价值评估)。
|
|
8013
|
+
*
|
|
8014
|
+
* 评估 1:reason 太短(<50 字)说明没有可复用的判断逻辑,不沉淀;
|
|
8015
|
+
* 评估 2:同名 skill 已存在 → 跳过;
|
|
8016
|
+
* 评估 3:与已有 active skill 关键词重叠 >= 2 → 跳过(防同类案例重复沉淀,
|
|
8017
|
+
* 如 cat /etc/shadow 与 cat /etc/sudoers 生成两个近乎重复的 skill)。
|
|
8018
|
+
*/
|
|
8019
|
+
buildSkill(actionDetail, reason, riskType, eventId) {
|
|
8020
|
+
if (!reason || reason.length < 50) return null;
|
|
8021
|
+
if (!riskType) return null;
|
|
8022
|
+
const detailWords = (actionDetail.toLowerCase().match(/[a-z]+/g) || []).slice(0, 3);
|
|
8023
|
+
const name = detailWords.length > 0 ? `brain_${riskType}_${detailWords.join("_")}` : `brain_${riskType}`;
|
|
8024
|
+
if (this.skillManager.getSkillMeta(name)) return null;
|
|
8025
|
+
const keywords = this.extractSkillKeywords(actionDetail, reason, riskType);
|
|
8026
|
+
if (this.isCoveredByExistingSkill(keywords)) return null;
|
|
8027
|
+
return {
|
|
8028
|
+
name,
|
|
8029
|
+
category: this.categorizeRisk(riskType),
|
|
8030
|
+
content: this.generateSkillContent(name, actionDetail, reason, riskType),
|
|
8031
|
+
keywords,
|
|
8032
|
+
sourceEventIds: eventId ? [eventId] : [],
|
|
8033
|
+
status: "active"
|
|
8034
|
+
};
|
|
8035
|
+
}
|
|
8036
|
+
/** 关键词提取(镜像 Python `_extract_keywords`)。 */
|
|
8037
|
+
extractSkillKeywords(actionDetail, reason, riskType) {
|
|
8038
|
+
const keywords = /* @__PURE__ */ new Set();
|
|
8039
|
+
if (riskType) keywords.add(riskType);
|
|
8040
|
+
for (const w of (actionDetail.toLowerCase().match(/[a-z_]{3,}/g) || []).slice(0, 10)) keywords.add(w);
|
|
8041
|
+
for (const w of (reason.toLowerCase().match(/[a-z_]{3,}/g) || []).slice(0, 5)) keywords.add(w);
|
|
8042
|
+
return [...keywords];
|
|
8043
|
+
}
|
|
8044
|
+
/** 与已有 active skill 关键词重叠 >= 2 → 视为已覆盖(镜像 `_is_covered_by_existing_skill`)。 */
|
|
8045
|
+
isCoveredByExistingSkill(keywords) {
|
|
8046
|
+
if (keywords.length === 0) return false;
|
|
8047
|
+
let existing = [];
|
|
8048
|
+
try {
|
|
8049
|
+
existing = this.skillManager.getActiveSkills();
|
|
8050
|
+
} catch {
|
|
8051
|
+
return false;
|
|
8052
|
+
}
|
|
8053
|
+
const cand = new Set(keywords.map((k) => k.toLowerCase()));
|
|
8054
|
+
for (const meta of existing) {
|
|
8055
|
+
const metaKeywords = (meta.keywords || []).map((k) => String(k).toLowerCase());
|
|
8056
|
+
if (metaKeywords.length === 0) continue;
|
|
8057
|
+
const overlap = metaKeywords.filter((k) => cand.has(k)).length;
|
|
8058
|
+
if (overlap >= 2) return true;
|
|
8059
|
+
}
|
|
8060
|
+
return false;
|
|
8061
|
+
}
|
|
8062
|
+
/** 风险类型 → skill 分类(镜像 Python `_categorize_risk`)。 */
|
|
8063
|
+
categorizeRisk(riskType) {
|
|
8064
|
+
if (!riskType) return "edge_case";
|
|
8065
|
+
if (["inject", "backdoor", "persist"].some((k) => riskType.includes(k))) return "attack_pattern";
|
|
8066
|
+
if (["exfil", "leak", "export"].some((k) => riskType.includes(k))) return "attack_pattern";
|
|
8067
|
+
return "defense_strategy";
|
|
8068
|
+
}
|
|
8069
|
+
/** skill 正文(与 Python `_generate_skill_content` 同结构,便于双端对拍)。 */
|
|
8070
|
+
generateSkillContent(name, actionDetail, reason, riskType) {
|
|
8071
|
+
return `# ${name}
|
|
8072
|
+
|
|
8073
|
+
## \u6765\u6E90
|
|
8074
|
+
Brain \u81EA\u52A8\u63D0\u53D6\uFF08LLM \u5206\u6790\u6C89\u6DC0\uFF09
|
|
8075
|
+
|
|
8076
|
+
## \u573A\u666F
|
|
8077
|
+
\u5F53 Agent \u6267\u884C\u4EE5\u4E0B\u7C7B\u578B\u64CD\u4F5C\u65F6\u89E6\u53D1\uFF1A
|
|
8078
|
+
- \u98CE\u9669\u7C7B\u578B: ${riskType}
|
|
8079
|
+
- \u64CD\u4F5C\u7279\u5F81: ${actionDetail.slice(0, 200)}
|
|
8080
|
+
|
|
8081
|
+
## \u5224\u65AD\u4F9D\u636E
|
|
8082
|
+
${reason.slice(0, 500)}
|
|
8083
|
+
|
|
8084
|
+
## \u5904\u7406\u5EFA\u8BAE
|
|
8085
|
+
- \u4F18\u5148\u68C0\u67E5\u64CD\u4F5C\u7684\u4E0A\u4E0B\u6587\u548C\u610F\u56FE
|
|
8086
|
+
- \u7ED3\u5408 action_history \u5224\u65AD\u662F\u5426\u5B58\u5728\u6076\u610F\u94FE
|
|
8087
|
+
- \u5FC5\u8981\u65F6 Deny \u5E76\u8BB0\u5F55\u539F\u56E0
|
|
8088
|
+
`;
|
|
8089
|
+
}
|
|
8090
|
+
extractPatterns(actionDetail, reason, riskType) {
|
|
8091
|
+
const patterns = [];
|
|
8092
|
+
const detailLower = actionDetail.toLowerCase();
|
|
7555
8093
|
const seen = /* @__PURE__ */ new Set();
|
|
7556
8094
|
const sensitivePathRe = /(\/(?:etc|root)(?:\/[^\s;|&"'<>`$]*)?|(?:[^\s;|&"'<>`$]*)?\/\.ssh\/[^\s;|&"'<>`$]*|(?:[^\s;|&"'<>`$]*)?\/\.aws\/[^\s;|&"'<>`$]*|\S+\.(?:pem|key|p12|pfx|id_rsa|authorized_keys)\b)/;
|
|
7557
8095
|
const m = detailLower.match(sensitivePathRe);
|
|
@@ -7744,6 +8282,494 @@ var init_reviewer = __esm({
|
|
|
7744
8282
|
}
|
|
7745
8283
|
});
|
|
7746
8284
|
|
|
8285
|
+
// src/brain/skill-manager.ts
|
|
8286
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8287
|
+
import { homedir as homedir5 } from "os";
|
|
8288
|
+
import { join as join8 } from "path";
|
|
8289
|
+
import { randomUUID } from "crypto";
|
|
8290
|
+
function getMetaPath(override) {
|
|
8291
|
+
return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
|
|
8292
|
+
}
|
|
8293
|
+
var DEFAULT_SKILL_META_PATH, ENV_SKILL_META_PATH, BrainSkillManager;
|
|
8294
|
+
var init_skill_manager = __esm({
|
|
8295
|
+
"src/brain/skill-manager.ts"() {
|
|
8296
|
+
"use strict";
|
|
8297
|
+
init_esm_shims();
|
|
8298
|
+
DEFAULT_SKILL_META_PATH = join8(homedir5(), ".agentmoss", "brain", "skill_meta.json");
|
|
8299
|
+
ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
|
|
8300
|
+
BrainSkillManager = class {
|
|
8301
|
+
constructor(skillsDir, metaPath) {
|
|
8302
|
+
this.skillsDir = skillsDir;
|
|
8303
|
+
this.metaPath = getMetaPath(metaPath);
|
|
8304
|
+
try {
|
|
8305
|
+
mkdirSync4(join8(this.metaPath, ".."), { recursive: true });
|
|
8306
|
+
} catch {
|
|
8307
|
+
}
|
|
8308
|
+
this.load();
|
|
8309
|
+
this.cleanupGhostSkills();
|
|
8310
|
+
}
|
|
8311
|
+
skillsDir;
|
|
8312
|
+
skills = [];
|
|
8313
|
+
metaPath;
|
|
8314
|
+
load() {
|
|
8315
|
+
if (!existsSync13(this.metaPath)) return;
|
|
8316
|
+
try {
|
|
8317
|
+
const data = JSON.parse(readFileSync13(this.metaPath, "utf-8"));
|
|
8318
|
+
this.skills = data.skills || [];
|
|
8319
|
+
} catch {
|
|
8320
|
+
this.skills = [];
|
|
8321
|
+
}
|
|
8322
|
+
}
|
|
8323
|
+
save() {
|
|
8324
|
+
const out = { version: 1, skills: this.skills };
|
|
8325
|
+
const tmp = `${this.metaPath}.tmp`;
|
|
8326
|
+
try {
|
|
8327
|
+
writeFileSync4(tmp, JSON.stringify(out, null, 2), "utf-8");
|
|
8328
|
+
renameSync(tmp, this.metaPath);
|
|
8329
|
+
} catch {
|
|
8330
|
+
}
|
|
8331
|
+
}
|
|
8332
|
+
/**
|
|
8333
|
+
* 清理"幽灵 skill":meta 记 active 但磁盘 .md 已不存在。
|
|
8334
|
+
* (沿用 Python 的处理:置 stale,避免污染关键词表并阻塞同名新 skill 生成。)
|
|
8335
|
+
*/
|
|
8336
|
+
cleanupGhostSkills() {
|
|
8337
|
+
let changed = false;
|
|
8338
|
+
for (const sk of this.skills) {
|
|
8339
|
+
if (sk.status !== "active") continue;
|
|
8340
|
+
if (!existsSync13(join8(this.skillsDir, `${sk.name}.md`))) {
|
|
8341
|
+
sk.status = "stale";
|
|
8342
|
+
changed = true;
|
|
8343
|
+
}
|
|
8344
|
+
}
|
|
8345
|
+
if (changed) this.save();
|
|
8346
|
+
}
|
|
8347
|
+
/** 新增 Skill:写 .md + 记元数据。目录缺失或同名已存在时跳过(返回 "")。 */
|
|
8348
|
+
addSkill(skill) {
|
|
8349
|
+
const id = skill.id || randomUUID();
|
|
8350
|
+
const name = skill.name || `brain_${id.slice(0, 8)}`;
|
|
8351
|
+
if (!existsSync13(this.skillsDir)) return "";
|
|
8352
|
+
const mdPath = join8(this.skillsDir, `${name}.md`);
|
|
8353
|
+
if (existsSync13(mdPath)) return "";
|
|
8354
|
+
try {
|
|
8355
|
+
writeFileSync4(mdPath, skill.content, "utf-8");
|
|
8356
|
+
} catch {
|
|
8357
|
+
return "";
|
|
8358
|
+
}
|
|
8359
|
+
this.skills.push({
|
|
8360
|
+
id,
|
|
8361
|
+
name,
|
|
8362
|
+
category: skill.category || "",
|
|
8363
|
+
keywords: skill.keywords || [],
|
|
8364
|
+
source_event_ids: skill.sourceEventIds || [],
|
|
8365
|
+
status: skill.status || "draft",
|
|
8366
|
+
created_at: skill.createdAt || Date.now() / 1e3,
|
|
8367
|
+
usage_count: skill.usageCount || 0,
|
|
8368
|
+
origin_node: skill.originNode || "local"
|
|
8369
|
+
});
|
|
8370
|
+
this.save();
|
|
8371
|
+
return id;
|
|
8372
|
+
}
|
|
8373
|
+
getSkillMeta(name) {
|
|
8374
|
+
return this.skills.find((s) => s.name === name);
|
|
8375
|
+
}
|
|
8376
|
+
getActiveSkills() {
|
|
8377
|
+
return this.skills.filter((s) => s.status === "active").sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
|
|
8378
|
+
}
|
|
8379
|
+
recordUsage(name) {
|
|
8380
|
+
const sk = this.getSkillMeta(name);
|
|
8381
|
+
if (!sk) return;
|
|
8382
|
+
sk.usage_count = (sk.usage_count || 0) + 1;
|
|
8383
|
+
this.save();
|
|
8384
|
+
}
|
|
8385
|
+
/** 长期未使用(usage_count=0)且创建超过 staleDays 的 active skill → stale。 */
|
|
8386
|
+
markStale(staleDays = 60) {
|
|
8387
|
+
const cutoff = Date.now() / 1e3 - staleDays * 86400;
|
|
8388
|
+
let changed = false;
|
|
8389
|
+
for (const sk of this.skills) {
|
|
8390
|
+
if (sk.status === "active" && (sk.usage_count || 0) === 0 && (sk.created_at || 0) < cutoff) {
|
|
8391
|
+
sk.status = "stale";
|
|
8392
|
+
changed = true;
|
|
8393
|
+
}
|
|
8394
|
+
}
|
|
8395
|
+
if (changed) this.save();
|
|
8396
|
+
}
|
|
8397
|
+
/** 导出本地 active skill(含正文),供回流中心。 */
|
|
8398
|
+
exportSkills() {
|
|
8399
|
+
const out = [];
|
|
8400
|
+
for (const sk of this.skills) {
|
|
8401
|
+
if (sk.origin_node !== "local" || sk.status !== "active") continue;
|
|
8402
|
+
const mdPath = join8(this.skillsDir, `${sk.name}.md`);
|
|
8403
|
+
if (!existsSync13(mdPath)) continue;
|
|
8404
|
+
out.push({
|
|
8405
|
+
name: sk.name,
|
|
8406
|
+
category: sk.category || "",
|
|
8407
|
+
content: readFileSync13(mdPath, "utf-8"),
|
|
8408
|
+
keywords: sk.keywords || []
|
|
8409
|
+
});
|
|
8410
|
+
}
|
|
8411
|
+
return out;
|
|
8412
|
+
}
|
|
8413
|
+
/** 导入中心下发 skill(标 central,避免被再次回流)。 */
|
|
8414
|
+
importSkills(skills) {
|
|
8415
|
+
for (const s of skills) {
|
|
8416
|
+
this.addSkill({
|
|
8417
|
+
name: s.name,
|
|
8418
|
+
category: s.category || "",
|
|
8419
|
+
content: s.content,
|
|
8420
|
+
keywords: s.keywords || [],
|
|
8421
|
+
status: "active",
|
|
8422
|
+
originNode: "central"
|
|
8423
|
+
});
|
|
8424
|
+
}
|
|
8425
|
+
}
|
|
8426
|
+
/** 保留 close 签名(JSON 落盘已逐次持久化)。 */
|
|
8427
|
+
close() {
|
|
8428
|
+
this.save();
|
|
8429
|
+
}
|
|
8430
|
+
};
|
|
8431
|
+
}
|
|
8432
|
+
});
|
|
8433
|
+
|
|
8434
|
+
// src/brain/curator.ts
|
|
8435
|
+
var BrainCurator;
|
|
8436
|
+
var init_curator = __esm({
|
|
8437
|
+
"src/brain/curator.ts"() {
|
|
8438
|
+
"use strict";
|
|
8439
|
+
init_esm_shims();
|
|
8440
|
+
BrainCurator = class {
|
|
8441
|
+
constructor(ruleStore, skillManager) {
|
|
8442
|
+
this.ruleStore = ruleStore;
|
|
8443
|
+
this.skillManager = skillManager;
|
|
8444
|
+
}
|
|
8445
|
+
ruleStore;
|
|
8446
|
+
skillManager;
|
|
8447
|
+
/**
|
|
8448
|
+
* 执行一次维护,返回报告。
|
|
8449
|
+
*
|
|
8450
|
+
* autoApprove 默认 true:brain 提取的规则达阈值后自动生效(学习闭环);
|
|
8451
|
+
* 需人工审核时设 false(由 Console 手动 approve)。
|
|
8452
|
+
*/
|
|
8453
|
+
runMaintenance(opts = {}) {
|
|
8454
|
+
const {
|
|
8455
|
+
staleRuleDays = 30,
|
|
8456
|
+
archiveRuleDays = 90,
|
|
8457
|
+
staleSkillDays = 60,
|
|
8458
|
+
disputeThreshold = 3,
|
|
8459
|
+
autoApprove = true
|
|
8460
|
+
} = opts;
|
|
8461
|
+
const autoApproved = autoApprove ? this.autoApproveDrafts() : [];
|
|
8462
|
+
this.ruleStore.markStale(staleRuleDays);
|
|
8463
|
+
this.ruleStore.markArchived(archiveRuleDays);
|
|
8464
|
+
this.ruleStore.autoDemoteDisputed(disputeThreshold);
|
|
8465
|
+
this.skillManager.markStale(staleSkillDays);
|
|
8466
|
+
return {
|
|
8467
|
+
rule_stats: this.ruleStore.getStats(),
|
|
8468
|
+
auto_approved: autoApproved
|
|
8469
|
+
};
|
|
8470
|
+
}
|
|
8471
|
+
/** draft → active(仅未 disputed 的 draft)。返回被激活规则的 pattern 前缀。 */
|
|
8472
|
+
autoApproveDrafts() {
|
|
8473
|
+
const approved = [];
|
|
8474
|
+
let drafts;
|
|
8475
|
+
try {
|
|
8476
|
+
drafts = this.ruleStore.getDraftRules();
|
|
8477
|
+
} catch {
|
|
8478
|
+
return approved;
|
|
8479
|
+
}
|
|
8480
|
+
for (const rule of drafts) {
|
|
8481
|
+
try {
|
|
8482
|
+
this.ruleStore.approveRule(rule.id);
|
|
8483
|
+
approved.push(rule.pattern.slice(0, 60));
|
|
8484
|
+
} catch {
|
|
8485
|
+
}
|
|
8486
|
+
}
|
|
8487
|
+
return approved;
|
|
8488
|
+
}
|
|
8489
|
+
};
|
|
8490
|
+
}
|
|
8491
|
+
});
|
|
8492
|
+
|
|
8493
|
+
// src/brain/contributor.ts
|
|
8494
|
+
var contributor_exports = {};
|
|
8495
|
+
__export(contributor_exports, {
|
|
8496
|
+
CONTACT_EMAIL: () => CONTACT_EMAIL,
|
|
8497
|
+
ContributionError: () => ContributionError,
|
|
8498
|
+
DEFAULT_TARGET_REPO: () => DEFAULT_TARGET_REPO,
|
|
8499
|
+
EXPORT_RULE_SOURCES: () => EXPORT_RULE_SOURCES,
|
|
8500
|
+
PLATFORM_SSH_HOST: () => PLATFORM_SSH_HOST,
|
|
8501
|
+
TOKEN_ENV: () => TOKEN_ENV,
|
|
8502
|
+
buildCommitMessage: () => buildCommitMessage,
|
|
8503
|
+
buildForkSshUrl: () => buildForkSshUrl,
|
|
8504
|
+
collectRules: () => collectRules,
|
|
8505
|
+
collectSkills: () => collectSkills,
|
|
8506
|
+
contribute: () => contribute,
|
|
8507
|
+
createMrViaApi: () => createMrViaApi,
|
|
8508
|
+
extractMrUrl: () => extractMrUrl,
|
|
8509
|
+
extractRepoPath: () => extractRepoPath,
|
|
8510
|
+
getCurrentUsername: () => getCurrentUsername,
|
|
8511
|
+
gitCommitAndPush: () => gitCommitAndPush,
|
|
8512
|
+
probeAccess: () => probeAccess,
|
|
8513
|
+
resolvePushTarget: () => resolvePushTarget,
|
|
8514
|
+
runGit: () => runGit,
|
|
8515
|
+
writeExportFiles: () => writeExportFiles
|
|
8516
|
+
});
|
|
8517
|
+
import { mkdirSync as mkdirSync5, mkdtempSync, rmSync, writeFileSync as writeFileSync5 } from "fs";
|
|
8518
|
+
import { tmpdir } from "os";
|
|
8519
|
+
import { join as join9 } from "path";
|
|
8520
|
+
import { spawnSync } from "child_process";
|
|
8521
|
+
import { dirname as dirname9 } from "path";
|
|
8522
|
+
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
8523
|
+
function collectRules(store) {
|
|
8524
|
+
return store.getActiveRules().filter((r) => EXPORT_RULE_SOURCES.includes(r.source)).map((r) => ({
|
|
8525
|
+
layer: r.layer,
|
|
8526
|
+
rule_type: r.ruleType,
|
|
8527
|
+
pattern: r.pattern,
|
|
8528
|
+
scope: r.scope,
|
|
8529
|
+
confidence: r.confidence,
|
|
8530
|
+
explanation: r.explanation,
|
|
8531
|
+
source_event_count: (r.sourceEventIds || []).length,
|
|
8532
|
+
trigger_count: r.triggerCount
|
|
8533
|
+
}));
|
|
8534
|
+
}
|
|
8535
|
+
function collectSkills(skillMgr) {
|
|
8536
|
+
return skillMgr.exportSkills();
|
|
8537
|
+
}
|
|
8538
|
+
function writeExportFiles(outDir, rules, skills) {
|
|
8539
|
+
const written = [];
|
|
8540
|
+
const rulesDir = join9(outDir, "rules");
|
|
8541
|
+
mkdirSync5(rulesDir, { recursive: true });
|
|
8542
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
8543
|
+
for (const r of rules) {
|
|
8544
|
+
if (!byLayer.has(r.layer)) byLayer.set(r.layer, []);
|
|
8545
|
+
byLayer.get(r.layer).push(r);
|
|
8546
|
+
}
|
|
8547
|
+
for (const layer of [...byLayer.keys()].sort((a, b) => a - b)) {
|
|
8548
|
+
const path6 = join9(rulesDir, `l${layer}_brain_rules.json`);
|
|
8549
|
+
writeFileSync5(path6, JSON.stringify(byLayer.get(layer), null, 2), "utf-8");
|
|
8550
|
+
written.push(path6);
|
|
8551
|
+
}
|
|
8552
|
+
if (rules.length === 0) {
|
|
8553
|
+
const path6 = join9(rulesDir, "empty.txt");
|
|
8554
|
+
writeFileSync5(path6, "(no brain rules to contribute)", "utf-8");
|
|
8555
|
+
written.push(path6);
|
|
8556
|
+
}
|
|
8557
|
+
const skillsDir = join9(outDir, "skills");
|
|
8558
|
+
mkdirSync5(skillsDir, { recursive: true });
|
|
8559
|
+
for (const sk of skills) {
|
|
8560
|
+
const path6 = join9(skillsDir, `${sk.name}.md`);
|
|
8561
|
+
writeFileSync5(path6, sk.content || "", "utf-8");
|
|
8562
|
+
written.push(path6);
|
|
8563
|
+
}
|
|
8564
|
+
if (skills.length === 0) {
|
|
8565
|
+
const path6 = join9(skillsDir, "empty.txt");
|
|
8566
|
+
writeFileSync5(path6, "(no brain skills to contribute)", "utf-8");
|
|
8567
|
+
written.push(path6);
|
|
8568
|
+
}
|
|
8569
|
+
return written;
|
|
8570
|
+
}
|
|
8571
|
+
function runGit(args, cwd) {
|
|
8572
|
+
const res = spawnSync("git", args, { cwd, encoding: "utf-8", timeout: 6e4 });
|
|
8573
|
+
return {
|
|
8574
|
+
code: res.status ?? 1,
|
|
8575
|
+
stdout: res.stdout ?? "",
|
|
8576
|
+
stderr: res.stderr ?? String(res.error ?? "")
|
|
8577
|
+
};
|
|
8578
|
+
}
|
|
8579
|
+
function probeAccess(targetRepo) {
|
|
8580
|
+
return runGit(["ls-remote", "--heads", targetRepo]).code === 0;
|
|
8581
|
+
}
|
|
8582
|
+
function buildCommitMessage(rules, skills) {
|
|
8583
|
+
const lines = [
|
|
8584
|
+
"feat(brain): \u8D21\u732E\u672C\u5730 LLM \u81EA\u5B66\u4E60\u6C89\u6DC0\u7684\u89C4\u5219\u4E0E Skill",
|
|
8585
|
+
"",
|
|
8586
|
+
`\u672C\u6B21\u8D21\u732E\u7531 AgentMoss Brain \u4ECE ${rules.length} \u6761\u89C4\u5219\u4E0E ${skills.length} \u4E2A Skill \u7EC4\u6210\uFF0C`,
|
|
8587
|
+
"\u5747\u4E3A\u672C\u5730 LLM \u63D0\u53D6 + \u5BA1\u6838\u901A\u8FC7\u7684 active \u9879\u3002"
|
|
8588
|
+
];
|
|
8589
|
+
if (rules.length > 0) {
|
|
8590
|
+
lines.push("", "## \u89C4\u5219");
|
|
8591
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
8592
|
+
for (const r of rules) byLayer.set(r.layer, (byLayer.get(r.layer) ?? 0) + 1);
|
|
8593
|
+
for (const [layer, cnt] of [...byLayer.entries()].sort((a, b) => a[0] - b[0])) {
|
|
8594
|
+
lines.push(`- Layer ${layer}: ${cnt} \u6761`);
|
|
8595
|
+
}
|
|
8596
|
+
for (const r of rules.slice(0, 5)) {
|
|
8597
|
+
lines.push(` - [${r.rule_type}] ${r.pattern.slice(0, 80)} \u2014 ${r.explanation.slice(0, 60)}`);
|
|
8598
|
+
}
|
|
8599
|
+
}
|
|
8600
|
+
if (skills.length > 0) {
|
|
8601
|
+
lines.push("", "## Skill");
|
|
8602
|
+
for (const sk of skills.slice(0, 5)) lines.push(`- ${sk.name}: ${sk.category || ""}`);
|
|
8603
|
+
}
|
|
8604
|
+
lines.push("", "Co-Authored-By: AgentMoss Brain <brain@agentmoss.local>");
|
|
8605
|
+
return lines.join("\n");
|
|
8606
|
+
}
|
|
8607
|
+
function gitCommitAndPush(workdir, targetRepo, branch, rules, skills) {
|
|
8608
|
+
runGit(["init", "-q"], workdir);
|
|
8609
|
+
runGit(["remote", "add", "origin", targetRepo], workdir);
|
|
8610
|
+
runGit(["fetch", "--depth", "1", "origin", "master"], workdir);
|
|
8611
|
+
runGit(["checkout", "-q", "-b", branch, "FETCH_HEAD"], workdir);
|
|
8612
|
+
runGit(["add", "-A"], workdir);
|
|
8613
|
+
runGit(["commit", "-q", "-m", buildCommitMessage(rules, skills)], workdir);
|
|
8614
|
+
const push = runGit(["push", "origin", branch], workdir);
|
|
8615
|
+
if (push.code !== 0) {
|
|
8616
|
+
throw new ContributionError(`push \u5230 ${targetRepo} \u5931\u8D25\uFF1A${push.stderr.trim().slice(0, 300)}`);
|
|
8617
|
+
}
|
|
8618
|
+
return push.stdout + push.stderr;
|
|
8619
|
+
}
|
|
8620
|
+
function extractMrUrl(pushOutput, targetRepo) {
|
|
8621
|
+
for (const rawLine of pushOutput.split("\n")) {
|
|
8622
|
+
const line = rawLine.trim();
|
|
8623
|
+
if (line.includes("merge_requests/new") || line.includes("pull/new") || line.includes("new?source_branch")) {
|
|
8624
|
+
return line;
|
|
8625
|
+
}
|
|
8626
|
+
}
|
|
8627
|
+
if (targetRepo.includes("gitcode.com") || targetRepo.includes("atomgit.com")) {
|
|
8628
|
+
return `https://gitcode.com/${extractRepoPath(targetRepo)}/branches`;
|
|
8629
|
+
}
|
|
8630
|
+
return "";
|
|
8631
|
+
}
|
|
8632
|
+
function extractRepoPath(repoUrl) {
|
|
8633
|
+
let url = repoUrl;
|
|
8634
|
+
if (url.startsWith("ssh://")) {
|
|
8635
|
+
url = url.split("://")[1];
|
|
8636
|
+
const first = url.split("/")[0];
|
|
8637
|
+
if (first.includes("@")) url = url.slice(url.indexOf("/") + 1);
|
|
8638
|
+
} else if (url.startsWith("git@") && url.includes(":")) {
|
|
8639
|
+
url = url.split(":")[1];
|
|
8640
|
+
} else if (url.startsWith("http")) {
|
|
8641
|
+
url = url.split("://")[1];
|
|
8642
|
+
const slash = url.indexOf("/");
|
|
8643
|
+
if (slash >= 0) url = url.slice(slash + 1);
|
|
8644
|
+
}
|
|
8645
|
+
return url.replace(/\.git$/, "").replace(/^\/+|\/+$/g, "");
|
|
8646
|
+
}
|
|
8647
|
+
async function createMrViaApi(targetRepo, branch, title, desc) {
|
|
8648
|
+
const token = process.env[TOKEN_ENV] || "";
|
|
8649
|
+
if (!token) return false;
|
|
8650
|
+
const repoPath = extractRepoPath(targetRepo);
|
|
8651
|
+
const apiUrl = `https://gitcode.com/api/v4/projects/${encodeURIComponent(repoPath)}/merge_requests`;
|
|
8652
|
+
try {
|
|
8653
|
+
const resp = await fetch(apiUrl, {
|
|
8654
|
+
method: "POST",
|
|
8655
|
+
headers: { "Content-Type": "application/json", "PRIVATE-TOKEN": token },
|
|
8656
|
+
body: JSON.stringify({ source_branch: branch, target_branch: "master", title, description: desc }),
|
|
8657
|
+
signal: AbortSignal.timeout(2e4)
|
|
8658
|
+
});
|
|
8659
|
+
return resp.status === 200 || resp.status === 201;
|
|
8660
|
+
} catch {
|
|
8661
|
+
return false;
|
|
8662
|
+
}
|
|
8663
|
+
}
|
|
8664
|
+
function getCurrentUsername() {
|
|
8665
|
+
const res = runGit(["config", "--get", "user.name"]);
|
|
8666
|
+
return res.code === 0 ? res.stdout.trim() : "";
|
|
8667
|
+
}
|
|
8668
|
+
function buildForkSshUrl(username) {
|
|
8669
|
+
return `ssh://git@${PLATFORM_SSH_HOST}/${username}/AgentMoss.git`;
|
|
8670
|
+
}
|
|
8671
|
+
function resolvePushTarget(opts) {
|
|
8672
|
+
if (opts.target) return opts.target;
|
|
8673
|
+
if (opts.forkUser) return buildForkSshUrl(opts.forkUser);
|
|
8674
|
+
const username = getCurrentUsername();
|
|
8675
|
+
if (username) return buildForkSshUrl(username);
|
|
8676
|
+
throw new ContributionError(
|
|
8677
|
+
"\u65E0\u6CD5\u786E\u5B9A push \u76EE\u6807\uFF1A\u8BF7\u5728\u7F51\u9875\u7AEF fork \u76EE\u6807\u4ED3\u5E93\u540E\u4F7F\u7528 --fork-user <\u7528\u6237\u540D>\uFF08\u6216\u76F4\u63A5 --target <\u4F60\u7684 fork SSH URL>\uFF09"
|
|
8678
|
+
);
|
|
8679
|
+
}
|
|
8680
|
+
async function contribute(opts = {}) {
|
|
8681
|
+
if (runGit(["version"]).code !== 0) {
|
|
8682
|
+
console.log("\u9519\u8BEF\uFF1A\u672A\u68C0\u6D4B\u5230 git\uFF0C\u8D21\u732E\u529F\u80FD\u9700\u8981 git CLI\u3002");
|
|
8683
|
+
return 2;
|
|
8684
|
+
}
|
|
8685
|
+
let pushTarget;
|
|
8686
|
+
try {
|
|
8687
|
+
pushTarget = resolvePushTarget(opts);
|
|
8688
|
+
} catch (err) {
|
|
8689
|
+
console.log(`[Brain] ${err.message}`);
|
|
8690
|
+
return 2;
|
|
8691
|
+
}
|
|
8692
|
+
console.log(`[Brain] push \u76EE\u6807: ${pushTarget}`);
|
|
8693
|
+
console.log(`[Brain] MR \u6307\u5411: ${DEFAULT_TARGET_REPO}`);
|
|
8694
|
+
const store = new BrainRuleStore();
|
|
8695
|
+
const skillsDir = resolveAssetDir(__dirname8, "skills") ?? join9(__dirname8, "..", "..", "agent_moss", "skills");
|
|
8696
|
+
const skillMgr = new BrainSkillManager(skillsDir);
|
|
8697
|
+
let rules;
|
|
8698
|
+
let skills;
|
|
8699
|
+
try {
|
|
8700
|
+
rules = collectRules(store);
|
|
8701
|
+
skills = collectSkills(skillMgr);
|
|
8702
|
+
} finally {
|
|
8703
|
+
skillMgr.close();
|
|
8704
|
+
}
|
|
8705
|
+
if (rules.length === 0 && skills.length === 0) {
|
|
8706
|
+
console.log("[Brain] \u672C\u5730\u6CA1\u6709 active \u7684 brain \u89C4\u5219\u6216 skill\uFF0C\u65E0\u53EF\u8D21\u732E\u5185\u5BB9\u3002");
|
|
8707
|
+
return 0;
|
|
8708
|
+
}
|
|
8709
|
+
console.log(`[Brain] \u5BFC\u51FA: ${rules.length} \u6761\u89C4\u5219, ${skills.length} \u4E2A Skill`);
|
|
8710
|
+
const workdir = mkdtempSync(join9(tmpdir(), "agentmoss-contrib-"));
|
|
8711
|
+
try {
|
|
8712
|
+
writeExportFiles(join9(workdir, "agent_moss", "brain_contrib"), rules, skills);
|
|
8713
|
+
console.log(`[Brain] \u63A2\u6D4B ${pushTarget} \u8BBF\u95EE\u6743\u9650 ...`);
|
|
8714
|
+
if (!probeAccess(pushTarget)) {
|
|
8715
|
+
const username = getCurrentUsername();
|
|
8716
|
+
console.log(`[Brain] \u65E0\u6CD5\u8BBF\u95EE ${pushTarget}`, "");
|
|
8717
|
+
if (username) {
|
|
8718
|
+
console.log(` \u5F53\u524D git \u7528\u6237: ${username}`);
|
|
8719
|
+
console.log(" \u53EF\u80FD\u539F\u56E0\uFF1A\u4F60\u8FD8\u6CA1\u6709 fork \u76EE\u6807\u4ED3\u5E93\uFF0C\u6216 fork \u540D\u4E0D\u5339\u914D\u3002");
|
|
8720
|
+
console.log(` \u8BF7\u5148\u5728\u7F51\u9875\u7AEF fork: ${DEFAULT_TARGET_REPO}`);
|
|
8721
|
+
console.log(` \uFF08fork \u540E\u4F60\u7684\u4ED3\u5E94\u8BE5\u662F: ${buildForkSshUrl(username)}\uFF09`);
|
|
8722
|
+
}
|
|
8723
|
+
console.log("");
|
|
8724
|
+
console.log(" \u5982\u679C\u4F60\u786E\u8BA4\u5DF2 fork \u4F46\u5BF9\u79C1\u4ED3\u8BBF\u95EE\u53D7\u9650\uFF1A");
|
|
8725
|
+
console.log(` \u6536\u4EF6\u4EBA: ${CONTACT_EMAIL}`);
|
|
8726
|
+
console.log(" \u90AE\u4EF6\u6CE8\u660E: \u4F60\u7684 gitcode \u8D26\u53F7 + \u7533\u8BF7\u7406\u7531\uFF08\u8BBF\u95EE AgentMoss \u4ED3\u5E93\u4EE5\u63D0\u4EA4 PR\uFF09");
|
|
8727
|
+
console.log(" fork \u540E\u91CD\u8BD5\u672C\u547D\u4EE4\uFF0C\u6216\u7528 --target <\u4F60\u7684 fork URL> \u663E\u5F0F\u6307\u5B9A\u3002");
|
|
8728
|
+
return 1;
|
|
8729
|
+
}
|
|
8730
|
+
const branch = opts.branch || `brain-contrib/${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 15)}`;
|
|
8731
|
+
console.log(`[Brain] \u63A8\u9001\u5206\u652F ${branch} \u2192 ${pushTarget}`);
|
|
8732
|
+
const pushOutput = gitCommitAndPush(workdir, pushTarget, branch, rules, skills);
|
|
8733
|
+
console.log("[Brain] \u63A8\u9001\u6210\u529F\u3002");
|
|
8734
|
+
const title = `[Brain] \u8D21\u732E ${rules.length} \u6761\u89C4\u5219 + ${skills.length} \u4E2A Skill`;
|
|
8735
|
+
const desc = buildCommitMessage(rules, skills);
|
|
8736
|
+
if (await createMrViaApi(pushTarget, branch, title, desc)) {
|
|
8737
|
+
console.log("[Brain] \u5DF2\u901A\u8FC7 API \u521B\u5EFA Merge Request\uFF1A");
|
|
8738
|
+
console.log(` ${branch} \u2192 master (MR \u6307\u5411 ${DEFAULT_TARGET_REPO})`);
|
|
8739
|
+
} else {
|
|
8740
|
+
const mrUrl = extractMrUrl(pushOutput, pushTarget);
|
|
8741
|
+
console.log("");
|
|
8742
|
+
console.log("[Brain] \u8BF7\u5728\u7F51\u9875\u7AEF\u521B\u5EFA Merge Request\uFF08from \u4F60\u7684 fork \u2192 shared \u4ED3 master\uFF09\uFF1A");
|
|
8743
|
+
if (mrUrl) console.log(` ${mrUrl}`);
|
|
8744
|
+
console.log(` \u5206\u652F: ${branch} \u2192 master (\u76EE\u6807\u4ED3: ${DEFAULT_TARGET_REPO})`);
|
|
8745
|
+
if (!process.env[TOKEN_ENV]) {
|
|
8746
|
+
console.log(` \uFF08\u63D0\u793A\uFF1A\u8BBE\u7F6E env ${TOKEN_ENV} \u53EF\u8BA9\u672C\u547D\u4EE4\u81EA\u52A8\u521B\u5EFA MR\uFF09`);
|
|
8747
|
+
}
|
|
8748
|
+
}
|
|
8749
|
+
return 0;
|
|
8750
|
+
} finally {
|
|
8751
|
+
rmSync(workdir, { recursive: true, force: true });
|
|
8752
|
+
}
|
|
8753
|
+
}
|
|
8754
|
+
var __dirname8, DEFAULT_TARGET_REPO, CONTACT_EMAIL, TOKEN_ENV, PLATFORM_SSH_HOST, EXPORT_RULE_SOURCES, ContributionError;
|
|
8755
|
+
var init_contributor = __esm({
|
|
8756
|
+
"src/brain/contributor.ts"() {
|
|
8757
|
+
"use strict";
|
|
8758
|
+
init_esm_shims();
|
|
8759
|
+
init_rule_store();
|
|
8760
|
+
init_skill_manager();
|
|
8761
|
+
init_asset_dir();
|
|
8762
|
+
__dirname8 = dirname9(fileURLToPath9(import.meta.url));
|
|
8763
|
+
DEFAULT_TARGET_REPO = "ssh://git@atomgit.com/kenhkl/AgentMoss.git";
|
|
8764
|
+
CONTACT_EMAIL = "kenhkl11@hotmail.com";
|
|
8765
|
+
TOKEN_ENV = "AGENT_MOSS_CONTRIBUTE_TOKEN";
|
|
8766
|
+
PLATFORM_SSH_HOST = "atomgit.com";
|
|
8767
|
+
EXPORT_RULE_SOURCES = ["llm_extracted", "user"];
|
|
8768
|
+
ContributionError = class extends Error {
|
|
8769
|
+
};
|
|
8770
|
+
}
|
|
8771
|
+
});
|
|
8772
|
+
|
|
7747
8773
|
// src/brain/index.ts
|
|
7748
8774
|
function checkBrainRules(store, opts) {
|
|
7749
8775
|
if (!store) return null;
|
|
@@ -7796,6 +8822,9 @@ var init_brain = __esm({
|
|
|
7796
8822
|
init_rule_store();
|
|
7797
8823
|
init_pattern_extractor();
|
|
7798
8824
|
init_reviewer();
|
|
8825
|
+
init_skill_manager();
|
|
8826
|
+
init_curator();
|
|
8827
|
+
init_contributor();
|
|
7799
8828
|
}
|
|
7800
8829
|
});
|
|
7801
8830
|
|
|
@@ -9325,19 +10354,64 @@ function extractRawCommand(detail) {
|
|
|
9325
10354
|
function matchesAny(cmd, patterns) {
|
|
9326
10355
|
return patterns.some((p) => p.test(cmd));
|
|
9327
10356
|
}
|
|
9328
|
-
function getBrainStore() {
|
|
10357
|
+
function getBrainStore(osType) {
|
|
9329
10358
|
if (!isBrainEnabled()) return null;
|
|
9330
10359
|
if (!_brainInitialized) {
|
|
9331
10360
|
_brainStore = new BrainRuleStore();
|
|
9332
|
-
|
|
10361
|
+
const skillsDir = resolveAssetDir(__dirname, "skills");
|
|
10362
|
+
_brainSkillManager = skillsDir ? new BrainSkillManager(skillsDir) : null;
|
|
10363
|
+
_brainExtractor = new PatternExtractor(_brainStore, {
|
|
10364
|
+
review: createBrainReviewer(),
|
|
10365
|
+
// 内置规则去重集(对齐 Python coordinator._ensure_brain 收集的 builtin_patterns):
|
|
10366
|
+
// 危险命令正则 + 敏感路径(deny_* 或 credential)。
|
|
10367
|
+
// 历史 bug:TS 这里只传 review,不传 builtinPatterns → 提取器拿不到内置规则,
|
|
10368
|
+
// `isCoveredByBuiltin` 恒 False → 把 L1/L2 本来就能拦的案例重复沉淀成 Brain 规则。
|
|
10369
|
+
builtinPatterns: collectBrainBuiltinPatterns(osType),
|
|
10370
|
+
skillManager: _brainSkillManager
|
|
10371
|
+
});
|
|
10372
|
+
if (_brainSkillManager) {
|
|
10373
|
+
_brainCurator = new BrainCurator(_brainStore, _brainSkillManager);
|
|
10374
|
+
}
|
|
9333
10375
|
_brainInitialized = true;
|
|
9334
10376
|
}
|
|
10377
|
+
const now = Date.now();
|
|
10378
|
+
if (_brainCurator && now - _brainLastMaintenance >= BRAIN_MAINT_INTERVAL_MS) {
|
|
10379
|
+
try {
|
|
10380
|
+
_brainCurator.runMaintenance({ autoApprove: true });
|
|
10381
|
+
} catch {
|
|
10382
|
+
}
|
|
10383
|
+
_brainLastMaintenance = now;
|
|
10384
|
+
}
|
|
9335
10385
|
return _brainStore;
|
|
9336
10386
|
}
|
|
10387
|
+
function collectBrainBuiltinPatterns(osType) {
|
|
10388
|
+
const bridge = runtimeConfigBridge.current;
|
|
10389
|
+
if (!bridge) return [];
|
|
10390
|
+
const out = [];
|
|
10391
|
+
try {
|
|
10392
|
+
for (const cp of bridge.effectiveDangerousPatterns(osType)) {
|
|
10393
|
+
if (cp?.pattern?.source) out.push(cp.pattern.source);
|
|
10394
|
+
}
|
|
10395
|
+
} catch {
|
|
10396
|
+
}
|
|
10397
|
+
try {
|
|
10398
|
+
for (const sp of bridge.effectiveSensitivePaths(osType)) {
|
|
10399
|
+
const denyMode = String(sp?.denyMode || "");
|
|
10400
|
+
if (["deny_both", "deny_write", "deny_read"].includes(denyMode) || sp?.credential) {
|
|
10401
|
+
if (sp?.path) out.push(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
10402
|
+
}
|
|
10403
|
+
}
|
|
10404
|
+
} catch {
|
|
10405
|
+
}
|
|
10406
|
+
return out.filter(Boolean);
|
|
10407
|
+
}
|
|
9337
10408
|
function _resetBrainForTest() {
|
|
9338
10409
|
_brainStore = null;
|
|
9339
10410
|
_brainExtractor = null;
|
|
10411
|
+
_brainSkillManager = null;
|
|
10412
|
+
_brainCurator = null;
|
|
9340
10413
|
_brainInitialized = false;
|
|
10414
|
+
_brainLastMaintenance = 0;
|
|
9341
10415
|
}
|
|
9342
10416
|
async function runAnalysis(input) {
|
|
9343
10417
|
const observedFormalLayers = [];
|
|
@@ -9389,7 +10463,7 @@ async function runAnalysis(input) {
|
|
|
9389
10463
|
}
|
|
9390
10464
|
}
|
|
9391
10465
|
}
|
|
9392
|
-
const brainStore = getBrainStore();
|
|
10466
|
+
const brainStore = getBrainStore(input.osType);
|
|
9393
10467
|
if (brainStore) {
|
|
9394
10468
|
const brainL1 = checkBrainRules(brainStore, {
|
|
9395
10469
|
layer: 1,
|
|
@@ -9686,7 +10760,7 @@ async function runAnalysis(input) {
|
|
|
9686
10760
|
}
|
|
9687
10761
|
return llmResult;
|
|
9688
10762
|
}
|
|
9689
|
-
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS, _brainStore, _brainExtractor, _brainInitialized, formalEngine;
|
|
10763
|
+
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS, _brainStore, _brainExtractor, _brainSkillManager, _brainCurator, _brainInitialized, _brainLastMaintenance, BRAIN_MAINT_INTERVAL_MS, formalEngine;
|
|
9690
10764
|
var init_coordinator = __esm({
|
|
9691
10765
|
"src/engine/coordinator.ts"() {
|
|
9692
10766
|
"use strict";
|
|
@@ -9703,7 +10777,11 @@ var init_coordinator = __esm({
|
|
|
9703
10777
|
init_agents();
|
|
9704
10778
|
init_scenarios();
|
|
9705
10779
|
init_brain();
|
|
10780
|
+
init_skill_manager();
|
|
10781
|
+
init_curator();
|
|
9706
10782
|
init_reviewer();
|
|
10783
|
+
init_asset_dir();
|
|
10784
|
+
init_runtime_config_bridge();
|
|
9707
10785
|
init_formal_engine();
|
|
9708
10786
|
FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
9709
10787
|
"ask_user_question",
|
|
@@ -9726,122 +10804,495 @@ var init_coordinator = __esm({
|
|
|
9726
10804
|
]);
|
|
9727
10805
|
_brainStore = null;
|
|
9728
10806
|
_brainExtractor = null;
|
|
10807
|
+
_brainSkillManager = null;
|
|
10808
|
+
_brainCurator = null;
|
|
9729
10809
|
_brainInitialized = false;
|
|
10810
|
+
_brainLastMaintenance = 0;
|
|
10811
|
+
BRAIN_MAINT_INTERVAL_MS = 3e5;
|
|
9730
10812
|
formalEngine = new FormalEngine();
|
|
9731
10813
|
}
|
|
9732
10814
|
});
|
|
9733
10815
|
|
|
9734
|
-
// src/
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
}
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
|
|
9791
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
}
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
10816
|
+
// src/infra/policy-cache.ts
|
|
10817
|
+
import { createHash as createHash2 } from "crypto";
|
|
10818
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync5, readFileSync as readFileSync15, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
10819
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
10820
|
+
import { join as join11 } from "path";
|
|
10821
|
+
function cacheDir() {
|
|
10822
|
+
return cacheDirOverride ?? CACHE_DIR;
|
|
10823
|
+
}
|
|
10824
|
+
function getDefaultCache() {
|
|
10825
|
+
if (!defaultCache) defaultCache = new PolicyCache(getCacheOptions());
|
|
10826
|
+
return defaultCache;
|
|
10827
|
+
}
|
|
10828
|
+
var CACHE_DIR, cacheDirOverride, PolicyCache, defaultCache;
|
|
10829
|
+
var init_policy_cache = __esm({
|
|
10830
|
+
"src/infra/policy-cache.ts"() {
|
|
10831
|
+
"use strict";
|
|
10832
|
+
init_esm_shims();
|
|
10833
|
+
init_config();
|
|
10834
|
+
CACHE_DIR = join11(tmpdir2(), "agent_moss_policy_cache");
|
|
10835
|
+
cacheDirOverride = null;
|
|
10836
|
+
PolicyCache = class {
|
|
10837
|
+
// Map 的插入序即 LRU 序(get 时重新 set 到末尾)
|
|
10838
|
+
constructor(options) {
|
|
10839
|
+
this.options = options;
|
|
10840
|
+
}
|
|
10841
|
+
options;
|
|
10842
|
+
memory = /* @__PURE__ */ new Map();
|
|
10843
|
+
cacheKey(sessionId, promptSession) {
|
|
10844
|
+
return `${sessionId}::${promptSession}`;
|
|
10845
|
+
}
|
|
10846
|
+
cacheFilePath(sessionId, promptSession) {
|
|
10847
|
+
const promptHash = createHash2("md5").update(promptSession).digest("hex").slice(0, 12);
|
|
10848
|
+
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10849
|
+
return join11(cacheDir(), safeSessionId, `${promptHash}.toml`);
|
|
10850
|
+
}
|
|
10851
|
+
/** 取缓存:内存 → 文件;未命中返回 null。 */
|
|
10852
|
+
get(sessionId, promptSession) {
|
|
10853
|
+
if (!this.options.enabled) return null;
|
|
10854
|
+
const key = this.cacheKey(sessionId, promptSession);
|
|
10855
|
+
const hit = this.memory.get(key);
|
|
10856
|
+
if (hit !== void 0) {
|
|
10857
|
+
this.memory.delete(key);
|
|
10858
|
+
this.memory.set(key, hit);
|
|
10859
|
+
return hit;
|
|
10860
|
+
}
|
|
10861
|
+
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10862
|
+
if (existsSync15(file)) {
|
|
10863
|
+
try {
|
|
10864
|
+
const policy = readFileSync15(file, "utf-8");
|
|
10865
|
+
this.remember(key, policy);
|
|
10866
|
+
return policy;
|
|
10867
|
+
} catch {
|
|
10868
|
+
}
|
|
10869
|
+
}
|
|
10870
|
+
return null;
|
|
10871
|
+
}
|
|
10872
|
+
/** 写缓存(内存 + 文件)。落盘失败不影响判定(缓存是旁路产物)。 */
|
|
10873
|
+
put(sessionId, promptSession, policy) {
|
|
10874
|
+
if (!this.options.enabled) return;
|
|
10875
|
+
this.remember(this.cacheKey(sessionId, promptSession), policy);
|
|
10876
|
+
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10877
|
+
try {
|
|
10878
|
+
mkdirSync7(join11(cacheDir(), sessionId.replace(/\//g, "_").replace(/\\/g, "_")), { recursive: true });
|
|
10879
|
+
writeFileSync7(file, policy, "utf-8");
|
|
10880
|
+
} catch {
|
|
10881
|
+
}
|
|
10882
|
+
}
|
|
10883
|
+
remember(key, policy) {
|
|
10884
|
+
if (this.memory.has(key)) this.memory.delete(key);
|
|
10885
|
+
this.memory.set(key, policy);
|
|
10886
|
+
while (this.memory.size > this.options.maxSize) {
|
|
10887
|
+
const oldest = this.memory.keys().next().value;
|
|
10888
|
+
if (oldest === void 0) break;
|
|
10889
|
+
this.memory.delete(oldest);
|
|
10890
|
+
}
|
|
10891
|
+
}
|
|
10892
|
+
invalidate(sessionId, promptSession) {
|
|
10893
|
+
this.memory.delete(this.cacheKey(sessionId, promptSession));
|
|
10894
|
+
try {
|
|
10895
|
+
unlinkSync3(this.cacheFilePath(sessionId, promptSession));
|
|
10896
|
+
} catch {
|
|
10897
|
+
}
|
|
10898
|
+
}
|
|
10899
|
+
invalidateSession(sessionId) {
|
|
10900
|
+
for (const k of [...this.memory.keys()]) {
|
|
10901
|
+
if (k.startsWith(`${sessionId}::`)) this.memory.delete(k);
|
|
10902
|
+
}
|
|
10903
|
+
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10904
|
+
const dir = join11(cacheDir(), safeSessionId);
|
|
10905
|
+
try {
|
|
10906
|
+
if (existsSync15(dir)) {
|
|
10907
|
+
for (const f of readdirSync5(dir)) {
|
|
10908
|
+
try {
|
|
10909
|
+
unlinkSync3(join11(dir, f));
|
|
10910
|
+
} catch {
|
|
10911
|
+
}
|
|
10912
|
+
}
|
|
10913
|
+
rmdirSync(dir);
|
|
10914
|
+
}
|
|
10915
|
+
} catch {
|
|
10916
|
+
}
|
|
10917
|
+
}
|
|
10918
|
+
clear() {
|
|
10919
|
+
this.memory.clear();
|
|
10920
|
+
try {
|
|
10921
|
+
if (existsSync15(cacheDir())) {
|
|
10922
|
+
for (const sessionDir of readdirSync5(cacheDir())) {
|
|
10923
|
+
const dir = join11(cacheDir(), sessionDir);
|
|
10924
|
+
try {
|
|
10925
|
+
for (const f of readdirSync5(dir)) unlinkSync3(join11(dir, f));
|
|
10926
|
+
rmdirSync(dir);
|
|
10927
|
+
} catch {
|
|
10928
|
+
}
|
|
10929
|
+
}
|
|
10930
|
+
}
|
|
10931
|
+
} catch {
|
|
10932
|
+
}
|
|
10933
|
+
}
|
|
10934
|
+
/** 当前内存缓存条目数。 */
|
|
10935
|
+
size() {
|
|
10936
|
+
return this.memory.size;
|
|
10937
|
+
}
|
|
10938
|
+
};
|
|
10939
|
+
defaultCache = null;
|
|
10940
|
+
}
|
|
9818
10941
|
});
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
10942
|
+
|
|
10943
|
+
// src/infra/logging.ts
|
|
10944
|
+
function safe(value, limit = 200) {
|
|
10945
|
+
const s = String(value ?? "");
|
|
10946
|
+
return s.length > limit ? `${s.slice(0, limit)}\u2026` : s;
|
|
10947
|
+
}
|
|
10948
|
+
var MossLogger;
|
|
10949
|
+
var init_logging = __esm({
|
|
10950
|
+
"src/infra/logging.ts"() {
|
|
10951
|
+
"use strict";
|
|
10952
|
+
init_esm_shims();
|
|
10953
|
+
MossLogger = class {
|
|
10954
|
+
constructor(sessionId) {
|
|
10955
|
+
this.sessionId = sessionId;
|
|
10956
|
+
}
|
|
10957
|
+
sessionId;
|
|
10958
|
+
stepTimes = {};
|
|
10959
|
+
stepStart = null;
|
|
10960
|
+
startTime = Date.now();
|
|
10961
|
+
prefix() {
|
|
10962
|
+
return `[${safe(this.sessionId, 64)}]`;
|
|
10963
|
+
}
|
|
10964
|
+
logInput(promptSession, actionHistory, aNext, reason) {
|
|
10965
|
+
console.error(
|
|
10966
|
+
`${this.prefix()} agent_moss \u8F93\u5165 | prompt=${safe(promptSession, 100)}\u2026 | \u5386\u53F2\u52A8\u4F5C\u6570=${actionHistory.length} | \u4E0B\u4E00\u6B65\u52A8\u4F5C=${safe(aNext?.action_type ?? "?", 32)}:${safe(aNext?.action_detail ?? "?", 80)} | \u7406\u7531=${safe(reason, 80)}`
|
|
10967
|
+
);
|
|
10968
|
+
}
|
|
10969
|
+
startStep(stepName) {
|
|
10970
|
+
this.stepStart = Date.now();
|
|
10971
|
+
}
|
|
10972
|
+
endStep(stepName, detail = "") {
|
|
10973
|
+
if (this.stepStart === null) return;
|
|
10974
|
+
const elapsed = (Date.now() - this.stepStart) / 1e3;
|
|
10975
|
+
this.stepTimes[stepName] = elapsed;
|
|
10976
|
+
const detailStr = detail ? ` | ${safe(detail)}` : "";
|
|
10977
|
+
console.error(
|
|
10978
|
+
`${this.prefix()} \u6B65\u9AA4\u5B8C\u6210: ${safe(stepName, 48)} | \u8017\u65F6=${elapsed.toFixed(3)}s${detailStr}`
|
|
10979
|
+
);
|
|
10980
|
+
this.stepStart = null;
|
|
10981
|
+
}
|
|
10982
|
+
logLlmInteraction(step, promptSummary, responseSummary) {
|
|
10983
|
+
console.error(
|
|
10984
|
+
`${this.prefix()} LLM\u4EA4\u4E92 | \u6B65\u9AA4=${safe(step, 48)} | PROMPT\u6458\u8981=${safe(promptSummary, 200)} | \u54CD\u5E94\u6458\u8981=${safe(responseSummary, 200)}`
|
|
10985
|
+
);
|
|
10986
|
+
}
|
|
10987
|
+
logResult(decision, reason, policy = "") {
|
|
10988
|
+
const total = (Date.now() - this.startTime) / 1e3;
|
|
10989
|
+
const steps = Object.entries(this.stepTimes).map(([k, v]) => `${safe(k, 48)}=${v.toFixed(3)}s`).join(", ");
|
|
10990
|
+
console.error(
|
|
10991
|
+
`${this.prefix()} agent_moss \u7ED3\u679C | \u51B3\u7B56=${safe(decision, 16)} | \u7406\u7531=${safe(reason, 150)} | \u6B65\u9AA4\u8017\u65F6=[${steps}] | \u603B\u8017\u65F6=${total.toFixed(3)}s`
|
|
10992
|
+
);
|
|
10993
|
+
if (policy) {
|
|
10994
|
+
console.error(`${this.prefix()} \u751F\u6210\u7684Policy:
|
|
10995
|
+
${safe(policy, 2e3)}`);
|
|
10996
|
+
}
|
|
10997
|
+
}
|
|
10998
|
+
logError(step, error) {
|
|
10999
|
+
const name = error?.name ?? typeof error;
|
|
11000
|
+
console.error(
|
|
11001
|
+
`${this.prefix()} \u6B65\u9AA4\u5F02\u5E38: ${safe(step, 48)} | \u9519\u8BEF=${name}: ${safe(error?.message ?? error)}`
|
|
11002
|
+
);
|
|
11003
|
+
}
|
|
11004
|
+
};
|
|
11005
|
+
}
|
|
9827
11006
|
});
|
|
9828
11007
|
|
|
9829
|
-
// src/
|
|
9830
|
-
|
|
11008
|
+
// src/engine/analyzer.ts
|
|
11009
|
+
var analyzer_exports = {};
|
|
11010
|
+
__export(analyzer_exports, {
|
|
11011
|
+
DEFAULT_READONLY_POLICY: () => DEFAULT_READONLY_POLICY,
|
|
11012
|
+
POLICY_OUTPUT_DIR: () => POLICY_OUTPUT_DIR,
|
|
11013
|
+
_setPolicyOutputDirForTest: () => _setPolicyOutputDirForTest,
|
|
11014
|
+
analyze: () => analyze,
|
|
11015
|
+
buildPolicyPrompt: () => buildPolicyPrompt,
|
|
11016
|
+
parsePolicyFromLlm: () => parsePolicyFromLlm,
|
|
11017
|
+
policyOutputDir: () => policyOutputDir,
|
|
11018
|
+
savePolicyToFile: () => savePolicyToFile,
|
|
11019
|
+
validateToml: () => validateToml
|
|
11020
|
+
});
|
|
11021
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "fs";
|
|
11022
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
11023
|
+
import { join as join12, resolve as resolve10 } from "path";
|
|
11024
|
+
import { parse as parseToml } from "smol-toml";
|
|
11025
|
+
function _setPolicyOutputDirForTest(dir) {
|
|
11026
|
+
policyOutputDirOverride = dir;
|
|
11027
|
+
}
|
|
11028
|
+
function policyOutputDir() {
|
|
11029
|
+
return policyOutputDirOverride ?? POLICY_OUTPUT_DIR;
|
|
11030
|
+
}
|
|
11031
|
+
function savePolicyToFile(sessionId, policyToml) {
|
|
11032
|
+
if (!isPolicySaveEnabled()) return null;
|
|
11033
|
+
try {
|
|
11034
|
+
const outDir = policyOutputDir();
|
|
11035
|
+
mkdirSync8(outDir, { recursive: true });
|
|
11036
|
+
const safeName = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
11037
|
+
const filepath = join12(outDir, `${safeName}.toml`);
|
|
11038
|
+
writeFileSync8(filepath, policyToml, "utf-8");
|
|
11039
|
+
return filepath;
|
|
11040
|
+
} catch (err) {
|
|
11041
|
+
console.warn(`[AgentMoss] \u7B56\u7565\u6587\u4EF6\u843D\u76D8\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u5224\u5B9A\uFF09: ${policyOutputDir()}: ${String(err)}`);
|
|
11042
|
+
return null;
|
|
11043
|
+
}
|
|
11044
|
+
}
|
|
11045
|
+
function parsePolicyFromLlm(response) {
|
|
11046
|
+
const tomlBlock = response.match(/```toml\s*\n([\s\S]*?)```/);
|
|
11047
|
+
if (tomlBlock) {
|
|
11048
|
+
const s = tomlBlock[1].trim();
|
|
11049
|
+
validateToml(s);
|
|
11050
|
+
return s;
|
|
11051
|
+
}
|
|
11052
|
+
const anyBlock = response.match(/```\s*\n([\s\S]*?)```/);
|
|
11053
|
+
if (anyBlock) {
|
|
11054
|
+
const s = anyBlock[1].trim();
|
|
11055
|
+
try {
|
|
11056
|
+
validateToml(s);
|
|
11057
|
+
return s;
|
|
11058
|
+
} catch {
|
|
11059
|
+
}
|
|
11060
|
+
}
|
|
11061
|
+
const whole = response.trim();
|
|
11062
|
+
validateToml(whole);
|
|
11063
|
+
return whole;
|
|
11064
|
+
}
|
|
11065
|
+
function validateToml(tomlStr) {
|
|
11066
|
+
try {
|
|
11067
|
+
parseToml(tomlStr);
|
|
11068
|
+
} catch (err) {
|
|
11069
|
+
throw new Error(`TOML \u89E3\u6790\u5931\u8D25: ${err.message}`);
|
|
11070
|
+
}
|
|
11071
|
+
}
|
|
11072
|
+
function loadPolicyMappingDoc() {
|
|
11073
|
+
const dir = resolveAssetDir(__dirname, "templates");
|
|
11074
|
+
if (dir) {
|
|
11075
|
+
const p = resolve10(dir, "policy_mapping.md");
|
|
11076
|
+
if (existsSync16(p)) return readFileSync16(p, "utf-8");
|
|
11077
|
+
}
|
|
11078
|
+
for (const cand of assetDirCandidates(__dirname, "templates")) {
|
|
11079
|
+
const p = resolve10(cand, "policy_mapping.md");
|
|
11080
|
+
if (existsSync16(p)) return readFileSync16(p, "utf-8");
|
|
11081
|
+
}
|
|
11082
|
+
return "";
|
|
11083
|
+
}
|
|
11084
|
+
function buildPolicyPrompt(promptSession) {
|
|
11085
|
+
const template = loadTemplates()["prompt1"] || "";
|
|
11086
|
+
return template.replaceAll("{prompt_policy_mapping_doc}", loadPolicyMappingDoc()).replaceAll("{prompt_session}", promptSession);
|
|
11087
|
+
}
|
|
11088
|
+
async function generatePolicyWithLLM(sessionId, promptSession) {
|
|
11089
|
+
const { default: OpenAI3 } = await import("openai");
|
|
11090
|
+
const cfg = getLLMConfig();
|
|
11091
|
+
const timeoutCfg = getTimeoutConfig();
|
|
11092
|
+
const retryCfg = getRetryConfig();
|
|
11093
|
+
const client = new OpenAI3({
|
|
11094
|
+
baseURL: cfg.baseUrl,
|
|
11095
|
+
apiKey: cfg.apiKey,
|
|
11096
|
+
timeout: (cfg.timeout || timeoutCfg.prompt1Timeout) * 1e3
|
|
11097
|
+
});
|
|
11098
|
+
const prompt1 = buildPolicyPrompt(promptSession);
|
|
11099
|
+
let lastErr = null;
|
|
11100
|
+
for (let attempt = 1; attempt <= Math.max(1, retryCfg.maxRetries); attempt++) {
|
|
11101
|
+
try {
|
|
11102
|
+
const resp = await client.chat.completions.create({
|
|
11103
|
+
model: cfg.model,
|
|
11104
|
+
temperature: 0.1,
|
|
11105
|
+
messages: [{ role: "user", content: prompt1 }]
|
|
11106
|
+
});
|
|
11107
|
+
try {
|
|
11108
|
+
const usage = resp.usage || {};
|
|
11109
|
+
recordTokenUsage({
|
|
11110
|
+
sessionId,
|
|
11111
|
+
step: "step2_policy_gen",
|
|
11112
|
+
model: cfg.model,
|
|
11113
|
+
promptTokens: usage.prompt_tokens || 0,
|
|
11114
|
+
completionTokens: usage.completion_tokens || 0,
|
|
11115
|
+
totalTokens: usage.total_tokens || 0
|
|
11116
|
+
});
|
|
11117
|
+
} catch {
|
|
11118
|
+
}
|
|
11119
|
+
return parsePolicyFromLlm(resp.choices?.[0]?.message?.content || "");
|
|
11120
|
+
} catch (err) {
|
|
11121
|
+
lastErr = err;
|
|
11122
|
+
if (attempt < Math.max(1, retryCfg.maxRetries)) {
|
|
11123
|
+
await new Promise((r) => setTimeout(r, retryCfg.retryInterval * 1e3));
|
|
11124
|
+
}
|
|
11125
|
+
}
|
|
11126
|
+
}
|
|
11127
|
+
throw lastErr ?? new Error("\u7B56\u7565\u751F\u6210\u5931\u8D25");
|
|
11128
|
+
}
|
|
11129
|
+
function allowResult(policy, judgment) {
|
|
11130
|
+
return {
|
|
11131
|
+
decision: "Allow",
|
|
11132
|
+
reason: judgment.reason,
|
|
11133
|
+
risk_level: judgment.riskLevel,
|
|
11134
|
+
risk_type: judgment.riskType,
|
|
11135
|
+
violated_layers: judgment.violatedLayers || [],
|
|
11136
|
+
policy,
|
|
11137
|
+
violated_policy: "",
|
|
11138
|
+
confidence: judgment.confidence
|
|
11139
|
+
};
|
|
11140
|
+
}
|
|
11141
|
+
async function analyze(input) {
|
|
11142
|
+
const mossLog = new MossLogger(input.sessionId);
|
|
11143
|
+
try {
|
|
11144
|
+
return await analyzeInner(input, mossLog);
|
|
11145
|
+
} catch (err) {
|
|
11146
|
+
mossLog.logError("unknown", err);
|
|
11147
|
+
const result = {
|
|
11148
|
+
decision: "Deny",
|
|
11149
|
+
reason: `\u5206\u6790\u8FC7\u7A0B\u53D1\u751F\u672A\u9884\u671F\u5F02\u5E38\uFF1A${String(err)}`,
|
|
11150
|
+
risk_level: "high",
|
|
11151
|
+
risk_type: "",
|
|
11152
|
+
violated_layers: [],
|
|
11153
|
+
policy: "",
|
|
11154
|
+
violated_policy: "\u672A\u77E5\u5F02\u5E38",
|
|
11155
|
+
confidence: 100
|
|
11156
|
+
};
|
|
11157
|
+
mossLog.logResult("Deny", result.reason);
|
|
11158
|
+
return result;
|
|
11159
|
+
}
|
|
11160
|
+
}
|
|
11161
|
+
async function analyzeInner(input, mossLog) {
|
|
11162
|
+
mossLog.logInput(input.promptSession, input.actionHistory || [], input.aNext, input.reason || "");
|
|
11163
|
+
mossLog.startStep("step1_security_judge");
|
|
11164
|
+
const judgment = await runAnalysis(input);
|
|
11165
|
+
mossLog.endStep(
|
|
11166
|
+
"step1_security_judge",
|
|
11167
|
+
`allowed=${judgment.allowed}, risk_level=${judgment.riskLevel}, source=${judgment.source}`
|
|
11168
|
+
);
|
|
11169
|
+
if (!judgment.allowed) {
|
|
11170
|
+
const denied = {
|
|
11171
|
+
decision: "Deny",
|
|
11172
|
+
reason: judgment.reason,
|
|
11173
|
+
risk_level: judgment.riskLevel,
|
|
11174
|
+
risk_type: judgment.riskType,
|
|
11175
|
+
violated_layers: judgment.violatedLayers || [],
|
|
11176
|
+
policy: "",
|
|
11177
|
+
violated_policy: `[${judgment.riskType}] ${judgment.reason}`,
|
|
11178
|
+
confidence: judgment.confidence
|
|
11179
|
+
};
|
|
11180
|
+
mossLog.logResult("Deny", denied.reason);
|
|
11181
|
+
return denied;
|
|
11182
|
+
}
|
|
11183
|
+
const cache3 = getDefaultCache();
|
|
11184
|
+
mossLog.startStep("step2_cache_lookup");
|
|
11185
|
+
const cached2 = cache3.get(input.sessionId, input.promptSession);
|
|
11186
|
+
if (cached2 !== null) {
|
|
11187
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u547D\u4E2D");
|
|
11188
|
+
savePolicyToFile(input.sessionId, cached2);
|
|
11189
|
+
const res = allowResult(cached2, judgment);
|
|
11190
|
+
mossLog.logResult("Allow", res.reason, cached2);
|
|
11191
|
+
return res;
|
|
11192
|
+
}
|
|
11193
|
+
if (!isPolicyGenEnabled()) {
|
|
11194
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u672A\u547D\u4E2D\uFF0CPolicy \u751F\u6210\u5DF2\u7981\u7528");
|
|
11195
|
+
mossLog.startStep("step2_default_policy");
|
|
11196
|
+
cache3.put(input.sessionId, input.promptSession, DEFAULT_READONLY_POLICY);
|
|
11197
|
+
savePolicyToFile(input.sessionId, DEFAULT_READONLY_POLICY);
|
|
11198
|
+
const res = allowResult(DEFAULT_READONLY_POLICY, judgment);
|
|
11199
|
+
mossLog.endStep("step2_default_policy", "\u4F7F\u7528\u9ED8\u8BA4\u6700\u5C0F\u6743\u9650 Policy");
|
|
11200
|
+
mossLog.logResult("Allow", res.reason, DEFAULT_READONLY_POLICY);
|
|
11201
|
+
return res;
|
|
11202
|
+
}
|
|
11203
|
+
if (judgment.source === "whitelist_bypass") {
|
|
11204
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u672A\u547D\u4E2D\uFF0C\u767D\u540D\u5355\u5DE5\u5177");
|
|
11205
|
+
cache3.put(input.sessionId, input.promptSession, DEFAULT_READONLY_POLICY);
|
|
11206
|
+
savePolicyToFile(input.sessionId, DEFAULT_READONLY_POLICY);
|
|
11207
|
+
const res = allowResult(DEFAULT_READONLY_POLICY, judgment);
|
|
11208
|
+
mossLog.logResult("Allow", res.reason, DEFAULT_READONLY_POLICY);
|
|
11209
|
+
return res;
|
|
11210
|
+
}
|
|
11211
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u672A\u547D\u4E2D\uFF0C\u542F\u7528 LLM \u751F\u6210");
|
|
11212
|
+
mossLog.startStep("step2_generate_policy");
|
|
11213
|
+
try {
|
|
11214
|
+
const policy = await generatePolicyWithLLM(input.sessionId, input.promptSession);
|
|
11215
|
+
cache3.put(input.sessionId, input.promptSession, policy);
|
|
11216
|
+
savePolicyToFile(input.sessionId, policy);
|
|
11217
|
+
mossLog.endStep("step2_generate_policy", "Policy \u751F\u6210\u5E76\u7F13\u5B58\u6210\u529F");
|
|
11218
|
+
const res = allowResult(policy, judgment);
|
|
11219
|
+
mossLog.logResult("Allow", res.reason, policy);
|
|
11220
|
+
return res;
|
|
11221
|
+
} catch (err) {
|
|
11222
|
+
mossLog.endStep("step2_generate_policy", `\u5931\u8D25: ${String(err)}`);
|
|
11223
|
+
mossLog.logError("step2_generate_policy", err);
|
|
11224
|
+
const failed = {
|
|
11225
|
+
decision: "Deny",
|
|
11226
|
+
reason: `\u5B89\u5168\u68C0\u6D4B\u901A\u8FC7\uFF0C\u4F46\u7B56\u7565\u751F\u6210\u5931\u8D25\uFF1A${String(err)}`,
|
|
11227
|
+
risk_level: judgment.riskLevel,
|
|
11228
|
+
risk_type: judgment.riskType,
|
|
11229
|
+
violated_layers: judgment.violatedLayers || [],
|
|
11230
|
+
policy: "",
|
|
11231
|
+
violated_policy: "\u65E0\u6CD5\u751F\u6210\u7B56\u7565\uFF1ALLM \u670D\u52A1\u5F02\u5E38\u6216\u8FD4\u56DE\u683C\u5F0F\u65E0\u6548",
|
|
11232
|
+
confidence: judgment.confidence
|
|
11233
|
+
};
|
|
11234
|
+
mossLog.logResult("Deny", failed.reason);
|
|
11235
|
+
return failed;
|
|
11236
|
+
}
|
|
11237
|
+
}
|
|
11238
|
+
var POLICY_OUTPUT_DIR, policyOutputDirOverride, DEFAULT_READONLY_POLICY;
|
|
11239
|
+
var init_analyzer = __esm({
|
|
11240
|
+
"src/engine/analyzer.ts"() {
|
|
11241
|
+
"use strict";
|
|
11242
|
+
init_esm_shims();
|
|
11243
|
+
init_coordinator();
|
|
11244
|
+
init_policy_cache();
|
|
11245
|
+
init_config();
|
|
11246
|
+
init_template_loader();
|
|
11247
|
+
init_asset_dir();
|
|
11248
|
+
init_token_stats();
|
|
11249
|
+
init_logging();
|
|
11250
|
+
POLICY_OUTPUT_DIR = join12(tmpdir3(), "agent_moss");
|
|
11251
|
+
policyOutputDirOverride = null;
|
|
11252
|
+
DEFAULT_READONLY_POLICY = `landlock_optional = false
|
|
11253
|
+
mount_isolation_fallback = false
|
|
9831
11254
|
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
11255
|
+
[path_groups]
|
|
11256
|
+
system_binaries = true
|
|
11257
|
+
system_libraries = true
|
|
11258
|
+
temp_directories = true
|
|
11259
|
+
device_files = false
|
|
11260
|
+
proc_filesystem = false
|
|
11261
|
+
network_config = false
|
|
11262
|
+
wsl_paths = false
|
|
11263
|
+
|
|
11264
|
+
[namespaces]
|
|
11265
|
+
mount = true
|
|
11266
|
+
pid = false
|
|
11267
|
+
network = true
|
|
11268
|
+
user = false
|
|
11269
|
+
|
|
11270
|
+
[resources]
|
|
11271
|
+
timeout_secs = 30
|
|
11272
|
+
max_memory_bytes = 268435456
|
|
11273
|
+
|
|
11274
|
+
[environment]
|
|
11275
|
+
whitelist = ["PATH", "LANG", "HOME", "USER", "HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy", "ALL_PROXY", "all_proxy", "NO_PROXY", "no_proxy"]
|
|
11276
|
+
`;
|
|
11277
|
+
}
|
|
11278
|
+
});
|
|
9835
11279
|
|
|
9836
11280
|
// src/infra/esm-require.ts
|
|
9837
|
-
init_esm_shims();
|
|
9838
11281
|
import { createRequire as createRequire2 } from "module";
|
|
9839
|
-
var requireFromHere = createRequire2(import.meta.url);
|
|
9840
11282
|
function esmRequire(id) {
|
|
9841
11283
|
return requireFromHere(id);
|
|
9842
11284
|
}
|
|
11285
|
+
var requireFromHere;
|
|
11286
|
+
var init_esm_require = __esm({
|
|
11287
|
+
"src/infra/esm-require.ts"() {
|
|
11288
|
+
"use strict";
|
|
11289
|
+
init_esm_shims();
|
|
11290
|
+
requireFromHere = createRequire2(import.meta.url);
|
|
11291
|
+
}
|
|
11292
|
+
});
|
|
9843
11293
|
|
|
9844
11294
|
// src/actrail.ts
|
|
11295
|
+
import * as fs5 from "fs";
|
|
9845
11296
|
function escape(value) {
|
|
9846
11297
|
return value.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/=/g, "\\e");
|
|
9847
11298
|
}
|
|
@@ -9939,15 +11390,15 @@ function reportEnforcementEvent(opts) {
|
|
|
9939
11390
|
return false;
|
|
9940
11391
|
}
|
|
9941
11392
|
}
|
|
9942
|
-
|
|
9943
|
-
|
|
9944
|
-
|
|
11393
|
+
var init_actrail = __esm({
|
|
11394
|
+
"src/actrail.ts"() {
|
|
11395
|
+
"use strict";
|
|
11396
|
+
init_esm_shims();
|
|
11397
|
+
init_esm_require();
|
|
11398
|
+
}
|
|
11399
|
+
});
|
|
9945
11400
|
|
|
9946
11401
|
// src/engine/formal-engine/pattern_miner.ts
|
|
9947
|
-
init_esm_shims();
|
|
9948
|
-
init_builder();
|
|
9949
|
-
var DANGER_STAGES = /* @__PURE__ */ new Set(["PRIVESC", "EXFIL", "LISTEN"]);
|
|
9950
|
-
var SENSITIVE_PAIRS = /* @__PURE__ */ new Set(["CRED|LISTEN", "LISTEN|CRED"]);
|
|
9951
11402
|
function projectStages(actionHistory) {
|
|
9952
11403
|
const stages = [];
|
|
9953
11404
|
let prev = null;
|
|
@@ -10019,19 +11470,21 @@ function mineDangerousPatterns(sessions, minSup = 2, maxLen = 4) {
|
|
|
10019
11470
|
out.sort((a, b) => b.support - a.support || b.pattern.length - a.pattern.length);
|
|
10020
11471
|
return out;
|
|
10021
11472
|
}
|
|
11473
|
+
var DANGER_STAGES, SENSITIVE_PAIRS;
|
|
11474
|
+
var init_pattern_miner = __esm({
|
|
11475
|
+
"src/engine/formal-engine/pattern_miner.ts"() {
|
|
11476
|
+
"use strict";
|
|
11477
|
+
init_esm_shims();
|
|
11478
|
+
init_builder();
|
|
11479
|
+
DANGER_STAGES = /* @__PURE__ */ new Set(["PRIVESC", "EXFIL", "LISTEN"]);
|
|
11480
|
+
SENSITIVE_PAIRS = /* @__PURE__ */ new Set(["CRED|LISTEN", "LISTEN|CRED"]);
|
|
11481
|
+
}
|
|
11482
|
+
});
|
|
10022
11483
|
|
|
10023
11484
|
// src/agent-gate-reader.ts
|
|
10024
|
-
init_esm_shims();
|
|
10025
11485
|
import * as fs6 from "fs";
|
|
10026
11486
|
import * as os3 from "os";
|
|
10027
11487
|
import * as path5 from "path";
|
|
10028
|
-
var DEFAULT_AGENTGATE_SQLITE = path5.join(
|
|
10029
|
-
os3.homedir(),
|
|
10030
|
-
".local",
|
|
10031
|
-
"share",
|
|
10032
|
-
"agent_gate",
|
|
10033
|
-
"agent_gate_ts.db"
|
|
10034
|
-
);
|
|
10035
11488
|
function normalizeAction(row) {
|
|
10036
11489
|
return {
|
|
10037
11490
|
actionType: String(row.action_type || row.tool_name || row.name || ""),
|
|
@@ -10115,8 +11568,97 @@ function loadAgentGateSessions(dbPath = "") {
|
|
|
10115
11568
|
const jsonDefault = path5.join(os3.homedir(), ".local", "share", "agent_gate", "tool_calls");
|
|
10116
11569
|
return readJsonDir(jsonDefault);
|
|
10117
11570
|
}
|
|
11571
|
+
var DEFAULT_AGENTGATE_SQLITE;
|
|
11572
|
+
var init_agent_gate_reader = __esm({
|
|
11573
|
+
"src/agent-gate-reader.ts"() {
|
|
11574
|
+
"use strict";
|
|
11575
|
+
init_esm_shims();
|
|
11576
|
+
init_esm_require();
|
|
11577
|
+
DEFAULT_AGENTGATE_SQLITE = path5.join(
|
|
11578
|
+
os3.homedir(),
|
|
11579
|
+
".local",
|
|
11580
|
+
"share",
|
|
11581
|
+
"agent_gate",
|
|
11582
|
+
"agent_gate_ts.db"
|
|
11583
|
+
);
|
|
11584
|
+
}
|
|
11585
|
+
});
|
|
11586
|
+
|
|
11587
|
+
// src/adapters/observable.ts
|
|
11588
|
+
function isRawFormat(data) {
|
|
11589
|
+
if (!data || typeof data !== "object") return false;
|
|
11590
|
+
const d = data;
|
|
11591
|
+
if ("current_action" in d) return true;
|
|
11592
|
+
if (!("a_next" in d)) return true;
|
|
11593
|
+
if (!("session_id" in d) && "agent_id" in d) return true;
|
|
11594
|
+
return false;
|
|
11595
|
+
}
|
|
11596
|
+
function adaptObservable(rawData) {
|
|
11597
|
+
const sessionId = rawData.session_id || rawData.agent_id || "unknown";
|
|
11598
|
+
const actionRaw = rawData.a_next || rawData.current_action || {};
|
|
11599
|
+
const aNext = actionRaw && typeof actionRaw === "object" ? {
|
|
11600
|
+
action_type: actionRaw.action_type || actionRaw.tool || "unknown",
|
|
11601
|
+
action_detail: actionRaw.action_detail || actionRaw.command || ""
|
|
11602
|
+
} : { action_type: "unknown", action_detail: "" };
|
|
11603
|
+
const historyRaw = Array.isArray(rawData.action_history) ? rawData.action_history : [];
|
|
11604
|
+
const actionHistory = historyRaw.filter((h) => h && typeof h === "object").map((h) => {
|
|
11605
|
+
const item = {
|
|
11606
|
+
action_type: String(h.action_type || h.name || h.tool || ""),
|
|
11607
|
+
action_detail: String(h.action_detail || h.command || "")
|
|
11608
|
+
};
|
|
11609
|
+
if ("output" in h) item.output = h.output ?? "";
|
|
11610
|
+
return item;
|
|
11611
|
+
});
|
|
11612
|
+
const result = {
|
|
11613
|
+
session_id: sessionId,
|
|
11614
|
+
prompt_session: rawData.prompt_session || rawData.prompt || "",
|
|
11615
|
+
action_history: actionHistory,
|
|
11616
|
+
a_next: aNext,
|
|
11617
|
+
reason: rawData.reason || (actionRaw && typeof actionRaw === "object" ? actionRaw.description : "") || "",
|
|
11618
|
+
os_type: rawData.os_type || "",
|
|
11619
|
+
cwd: rawData.cwd || "",
|
|
11620
|
+
agent_id: rawData.agent_id || ""
|
|
11621
|
+
};
|
|
11622
|
+
if (!result.reason && actionRaw && typeof actionRaw === "object") {
|
|
11623
|
+
result.reason = actionRaw.description || "";
|
|
11624
|
+
}
|
|
11625
|
+
let metadata = rawData.metadata;
|
|
11626
|
+
if (!metadata || typeof metadata !== "object" || Object.keys(metadata).length === 0) {
|
|
11627
|
+
metadata = {};
|
|
11628
|
+
for (const k of ["agent_id", "sandbox_id", "trace_id"]) {
|
|
11629
|
+
if (k in rawData && !(k in result)) metadata[k] = rawData[k];
|
|
11630
|
+
}
|
|
11631
|
+
}
|
|
11632
|
+
if (metadata && Object.keys(metadata).length > 0) result.metadata = metadata;
|
|
11633
|
+
return result;
|
|
11634
|
+
}
|
|
11635
|
+
function normalizeObservablePayload(body) {
|
|
11636
|
+
if (!isRawFormat(body)) return body;
|
|
11637
|
+
const adapted = adaptObservable(body);
|
|
11638
|
+
const actionType = String(adapted.a_next?.action_type || "").trim().toLowerCase();
|
|
11639
|
+
const actionDetail = String(adapted.a_next?.action_detail || "").trim();
|
|
11640
|
+
if (!actionDetail && (actionType === "" || actionType === "unknown")) {
|
|
11641
|
+
const err = new Error(
|
|
11642
|
+
"\u65E0\u6CD5\u8BC6\u522B\u672C\u6B21\u52A8\u4F5C\uFF1A\u65E2\u6CA1\u6709 a_next\uFF08\u6807\u51C6\u683C\u5F0F\uFF09\uFF0C\u4E5F\u6CA1\u6709 current_action\uFF08\u53EF\u89C2\u6D4B\u670D\u52A1\u539F\u59CB\u683C\u5F0F\uFF09"
|
|
11643
|
+
);
|
|
11644
|
+
err.issues = [{
|
|
11645
|
+
path: ["a_next"],
|
|
11646
|
+
message: err.message
|
|
11647
|
+
}];
|
|
11648
|
+
throw err;
|
|
11649
|
+
}
|
|
11650
|
+
return adapted;
|
|
11651
|
+
}
|
|
11652
|
+
var init_observable = __esm({
|
|
11653
|
+
"src/adapters/observable.ts"() {
|
|
11654
|
+
"use strict";
|
|
11655
|
+
init_esm_shims();
|
|
11656
|
+
}
|
|
11657
|
+
});
|
|
10118
11658
|
|
|
10119
11659
|
// src/routes.ts
|
|
11660
|
+
import { join as join14, dirname as dirname11 } from "path";
|
|
11661
|
+
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
10120
11662
|
async function healthRoute(c) {
|
|
10121
11663
|
return c.json(HealthResponseSchema.parse({
|
|
10122
11664
|
instance: process.env.AGENT_MOSS_INSTANCE || ""
|
|
@@ -10126,13 +11668,13 @@ async function analyzeRoute(c) {
|
|
|
10126
11668
|
const t0 = performance.now();
|
|
10127
11669
|
try {
|
|
10128
11670
|
const body = await c.req.json();
|
|
10129
|
-
const req = AnalyzeRequestSchema.parse(body);
|
|
11671
|
+
const req = AnalyzeRequestSchema.parse(normalizeObservablePayload(body));
|
|
10130
11672
|
let osType = req.os_type;
|
|
10131
11673
|
if (!osType) {
|
|
10132
11674
|
osType = process.platform === "win32" ? "windows" : "linux";
|
|
10133
11675
|
}
|
|
10134
11676
|
const agentId = req.agent_id || req.metadata?.agent_type || "";
|
|
10135
|
-
const result = await
|
|
11677
|
+
const result = await analyze({
|
|
10136
11678
|
sessionId: req.session_id,
|
|
10137
11679
|
promptSession: req.prompt_session,
|
|
10138
11680
|
// output 必须透传:L2 的「授权(consent)」判定看的是**用户对提问的回答**,
|
|
@@ -10152,6 +11694,20 @@ async function analyzeRoute(c) {
|
|
|
10152
11694
|
output: h.output
|
|
10153
11695
|
}))
|
|
10154
11696
|
})),
|
|
11697
|
+
// prompt_history 必须透传:L2 意图一致性(logic-rules.ts 的
|
|
11698
|
+
// checkIntentConsistency)与 L3 语义分析(llm-analyzer.ts 的
|
|
11699
|
+
// formatPromptHistory)都靠它看**完整的交错历史**;只传 prompt_session
|
|
11700
|
+
// (最后一条用户消息)会让这两处在压缩/续跑场景下失明。
|
|
11701
|
+
// 历史 bug:请求模型里有这个字段,但路由的 runAnalysis 调用没映射它 →
|
|
11702
|
+
// 经 TS HTTP 入口的判定与 Python HTTP(routes.py 有透传)、与库内直调
|
|
11703
|
+
// runAnalysis 的结果都不一致(2026-10 修正,映射口径对齐 Python)。
|
|
11704
|
+
promptHistory: (req.prompt_history || []).map((t) => ({
|
|
11705
|
+
text: t.text,
|
|
11706
|
+
actions: t.actions.map((a) => ({
|
|
11707
|
+
actionType: a.action_type,
|
|
11708
|
+
actionDetail: a.action_detail
|
|
11709
|
+
}))
|
|
11710
|
+
})),
|
|
10155
11711
|
aNext: {
|
|
10156
11712
|
actionType: req.a_next.action_type,
|
|
10157
11713
|
actionDetail: req.a_next.action_detail
|
|
@@ -10169,15 +11725,15 @@ async function analyzeRoute(c) {
|
|
|
10169
11725
|
try {
|
|
10170
11726
|
reportEnforcementEvent({
|
|
10171
11727
|
traceId: req.session_id,
|
|
10172
|
-
decision: result.
|
|
11728
|
+
decision: result.decision === "Allow" ? "allow" : "deny",
|
|
10173
11729
|
toolName: req.a_next.action_type,
|
|
10174
11730
|
toolCommand: req.a_next.action_detail,
|
|
10175
11731
|
sessionId: req.session_id,
|
|
10176
11732
|
prompt: req.prompt_session,
|
|
10177
|
-
riskLevel: result.
|
|
10178
|
-
riskType: result.
|
|
11733
|
+
riskLevel: result.risk_level,
|
|
11734
|
+
riskType: result.risk_type,
|
|
10179
11735
|
reason: result.reason,
|
|
10180
|
-
violatedLayers: result.
|
|
11736
|
+
violatedLayers: result.violated_layers,
|
|
10181
11737
|
confidence: result.confidence,
|
|
10182
11738
|
durationMs: Math.round(durationMs * 10) / 10
|
|
10183
11739
|
});
|
|
@@ -10194,7 +11750,7 @@ async function analyzeRoute(c) {
|
|
|
10194
11750
|
}, req.session_id, Boolean(meta.is_error));
|
|
10195
11751
|
const judgment = incMatchedToDeny(inc);
|
|
10196
11752
|
if (judgment) {
|
|
10197
|
-
const layers = [...result.
|
|
11753
|
+
const layers = [...result.violated_layers];
|
|
10198
11754
|
if (!layers.includes("L2.2-stateful")) layers.push("L2.2-stateful");
|
|
10199
11755
|
console.error(
|
|
10200
11756
|
`[AgentMoss] stateful \u589E\u91CF\u62E6\u622A: agent=${agentId || "-"} property=${judgment.property_id} risk=${judgment.risk_level}`
|
|
@@ -10220,13 +11776,15 @@ async function analyzeRoute(c) {
|
|
|
10220
11776
|
}
|
|
10221
11777
|
return c.json(
|
|
10222
11778
|
AnalyzeResponseSchema.parse({
|
|
10223
|
-
decision: result.
|
|
11779
|
+
decision: result.decision,
|
|
10224
11780
|
reason: result.reason,
|
|
10225
|
-
risk_level: result.
|
|
10226
|
-
risk_type: result.
|
|
10227
|
-
violated_layers: result.
|
|
10228
|
-
policy
|
|
10229
|
-
violated_policy
|
|
11781
|
+
risk_level: result.risk_level,
|
|
11782
|
+
risk_type: result.risk_type,
|
|
11783
|
+
violated_layers: result.violated_layers,
|
|
11784
|
+
// policy / violated_policy 现在真的有值(镜像 Python analyze()):
|
|
11785
|
+
// Allow → 最小权限 TOML;Deny → 空 policy + violated_policy 描述
|
|
11786
|
+
policy: result.policy,
|
|
11787
|
+
violated_policy: result.violated_policy,
|
|
10230
11788
|
confidence: result.confidence,
|
|
10231
11789
|
analysis_duration_ms: Math.round(durationMs * 10) / 10
|
|
10232
11790
|
})
|
|
@@ -10234,15 +11792,14 @@ async function analyzeRoute(c) {
|
|
|
10234
11792
|
} catch (err) {
|
|
10235
11793
|
console.error("[AgentMoss] Analysis error:", err);
|
|
10236
11794
|
if (err instanceof SyntaxError) {
|
|
10237
|
-
return c.json({ error: "Invalid JSON"
|
|
11795
|
+
return c.json({ detail: err.message, error: "Invalid JSON" }, 422);
|
|
10238
11796
|
}
|
|
10239
11797
|
if (err?.issues) {
|
|
10240
|
-
return c.json({ error: "Validation error"
|
|
11798
|
+
return c.json({ detail: err.issues, error: "Validation error" }, 422);
|
|
10241
11799
|
}
|
|
10242
|
-
return c.json({ error: "Internal error"
|
|
11800
|
+
return c.json({ detail: String(err), error: "Internal error" }, 500);
|
|
10243
11801
|
}
|
|
10244
11802
|
}
|
|
10245
|
-
var reviewStore = null;
|
|
10246
11803
|
function getChainStateStoreFor(_agentId) {
|
|
10247
11804
|
if (!reviewStore) reviewStore = new ChainStateStore();
|
|
10248
11805
|
return reviewStore;
|
|
@@ -10297,13 +11854,25 @@ async function reviewRoute(c) {
|
|
|
10297
11854
|
const violations = [];
|
|
10298
11855
|
if (sessions.length > 0 || tracker.nodes.length > 0) {
|
|
10299
11856
|
const eng = new FormalEngine();
|
|
11857
|
+
const verifySessions = sessions.length > 0 ? sessions.map((s) => ({ sessionId: s.sessionId, actionHistory: s.actionHistory })) : tracker.nodes.length > 0 ? [{
|
|
11858
|
+
sessionId: tracker.agentId,
|
|
11859
|
+
actionHistory: tracker.nodes.map((n) => ({
|
|
11860
|
+
actionType: n.actionType,
|
|
11861
|
+
actionDetail: n.actionDetail
|
|
11862
|
+
}))
|
|
11863
|
+
}] : [];
|
|
10300
11864
|
const full = eng.verify(
|
|
10301
|
-
|
|
11865
|
+
verifySessions,
|
|
10302
11866
|
req.a_next ? { actionType: req.a_next.action_type, actionDetail: req.a_next.action_detail } : void 0
|
|
10303
11867
|
);
|
|
10304
11868
|
verified = full.verified;
|
|
10305
11869
|
for (const v of full.violations) {
|
|
10306
|
-
violations.push({
|
|
11870
|
+
violations.push({
|
|
11871
|
+
property_id: v.propertyId,
|
|
11872
|
+
reason: v.reason,
|
|
11873
|
+
risk_level: v.riskLevel,
|
|
11874
|
+
path: v.path
|
|
11875
|
+
});
|
|
10307
11876
|
}
|
|
10308
11877
|
}
|
|
10309
11878
|
const pending = tracker.pendingPrefixes.map((p) => ({
|
|
@@ -10333,22 +11902,126 @@ async function reviewRoute(c) {
|
|
|
10333
11902
|
return c.json({ error: "Internal error", detail: String(err) }, 500);
|
|
10334
11903
|
}
|
|
10335
11904
|
}
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
11905
|
+
function getBrainRuleStore() {
|
|
11906
|
+
return new BrainRuleStore();
|
|
11907
|
+
}
|
|
11908
|
+
function getBrainSkillManager() {
|
|
11909
|
+
const dir = resolveAssetDir(__dirname9, "skills") ?? join14(__dirname9, "..", "..", "agent_moss", "skills");
|
|
11910
|
+
return new BrainSkillManager(dir);
|
|
11911
|
+
}
|
|
11912
|
+
async function brainRulesRoute(c) {
|
|
11913
|
+
const store = getBrainRuleStore();
|
|
11914
|
+
const layerRaw = c.req.query("layer");
|
|
11915
|
+
const layer = layerRaw ? parseInt(layerRaw, 10) : void 0;
|
|
11916
|
+
const rules = store.getActiveRules(layer);
|
|
11917
|
+
return c.json({
|
|
11918
|
+
rules: rules.map((r) => ({
|
|
11919
|
+
id: r.id,
|
|
11920
|
+
layer: r.layer,
|
|
11921
|
+
rule_type: r.ruleType,
|
|
11922
|
+
pattern: r.pattern,
|
|
11923
|
+
scope: r.scope,
|
|
11924
|
+
confidence: r.confidence,
|
|
11925
|
+
explanation: r.explanation
|
|
11926
|
+
}))
|
|
11927
|
+
});
|
|
11928
|
+
}
|
|
11929
|
+
async function brainDraftsRoute(c) {
|
|
11930
|
+
const store = getBrainRuleStore();
|
|
11931
|
+
return c.json({
|
|
11932
|
+
drafts: store.getDraftRules().map((r) => ({
|
|
11933
|
+
id: r.id,
|
|
11934
|
+
layer: r.layer,
|
|
11935
|
+
rule_type: r.ruleType,
|
|
11936
|
+
pattern: r.pattern,
|
|
11937
|
+
scope: r.scope,
|
|
11938
|
+
confidence: r.confidence,
|
|
11939
|
+
explanation: r.explanation,
|
|
11940
|
+
source_event_ids: r.sourceEventIds
|
|
11941
|
+
}))
|
|
11942
|
+
});
|
|
11943
|
+
}
|
|
11944
|
+
async function brainApproveRoute(c) {
|
|
11945
|
+
const ruleId = c.req.param("rule_id") ?? "";
|
|
11946
|
+
try {
|
|
11947
|
+
getBrainRuleStore().approveRule(ruleId);
|
|
11948
|
+
return c.json({ status: "approved" });
|
|
11949
|
+
} catch (err) {
|
|
11950
|
+
return c.json({ detail: String(err.message) }, 400);
|
|
11951
|
+
}
|
|
11952
|
+
}
|
|
11953
|
+
async function brainRejectRoute(c) {
|
|
11954
|
+
const ruleId = c.req.param("rule_id") ?? "";
|
|
11955
|
+
const reason = c.req.query("reason") || "";
|
|
11956
|
+
try {
|
|
11957
|
+
getBrainRuleStore().rejectRule(ruleId);
|
|
11958
|
+
return c.json({ status: "rejected" });
|
|
11959
|
+
} catch (err) {
|
|
11960
|
+
return c.json({ detail: String(err.message) }, 400);
|
|
11961
|
+
}
|
|
11962
|
+
}
|
|
11963
|
+
async function brainStatsRoute(c) {
|
|
11964
|
+
return c.json(getBrainRuleStore().getStats());
|
|
11965
|
+
}
|
|
11966
|
+
async function brainSkillsRoute(c) {
|
|
11967
|
+
const mgr = getBrainSkillManager();
|
|
11968
|
+
const skills = mgr.getActiveSkills();
|
|
11969
|
+
mgr.close();
|
|
11970
|
+
return c.json({ skills });
|
|
11971
|
+
}
|
|
11972
|
+
async function brainExportRoute(c) {
|
|
11973
|
+
const store = getBrainRuleStore();
|
|
11974
|
+
return c.json({
|
|
11975
|
+
rules: store.getRulesForExport().map((r) => ({
|
|
11976
|
+
id: r.id,
|
|
11977
|
+
layer: r.layer,
|
|
11978
|
+
rule_type: r.ruleType,
|
|
11979
|
+
pattern: r.pattern,
|
|
11980
|
+
scope: r.scope,
|
|
11981
|
+
confidence: r.confidence,
|
|
11982
|
+
explanation: r.explanation,
|
|
11983
|
+
origin_node: r.originNode
|
|
11984
|
+
}))
|
|
11985
|
+
});
|
|
11986
|
+
}
|
|
11987
|
+
async function brainImportRoute(c) {
|
|
11988
|
+
const body = await c.req.json();
|
|
11989
|
+
const rules = Array.isArray(body) ? body : [];
|
|
11990
|
+
getBrainRuleStore().importRules(rules.map((r) => ({
|
|
11991
|
+
layer: r.layer ?? 1,
|
|
11992
|
+
ruleType: r.rule_type ?? "",
|
|
11993
|
+
pattern: r.pattern ?? "",
|
|
11994
|
+
scope: r.scope ?? "all",
|
|
11995
|
+
confidence: r.confidence ?? 0.9,
|
|
11996
|
+
explanation: r.explanation ?? ""
|
|
11997
|
+
})));
|
|
11998
|
+
return c.json({ imported: rules.length });
|
|
11999
|
+
}
|
|
12000
|
+
var __dirname9, reviewStore;
|
|
12001
|
+
var init_routes = __esm({
|
|
12002
|
+
"src/routes.ts"() {
|
|
12003
|
+
"use strict";
|
|
12004
|
+
init_esm_shims();
|
|
12005
|
+
init_models();
|
|
12006
|
+
init_analyzer();
|
|
12007
|
+
init_actrail();
|
|
12008
|
+
init_formal_engine();
|
|
12009
|
+
init_pattern_miner();
|
|
12010
|
+
init_agent_gate_reader();
|
|
12011
|
+
init_observable();
|
|
12012
|
+
init_rule_store();
|
|
12013
|
+
init_skill_manager();
|
|
12014
|
+
init_asset_dir();
|
|
12015
|
+
__dirname9 = dirname11(fileURLToPath10(import.meta.url));
|
|
12016
|
+
reviewStore = null;
|
|
12017
|
+
}
|
|
12018
|
+
});
|
|
10339
12019
|
|
|
10340
12020
|
// src/console/router.ts
|
|
10341
|
-
init_esm_shims();
|
|
10342
|
-
init_runtime_config();
|
|
10343
|
-
init_token_stats();
|
|
10344
|
-
init_rules_loader();
|
|
10345
12021
|
import { Hono } from "hono";
|
|
10346
|
-
import { readFileSync as
|
|
10347
|
-
import { join as
|
|
10348
|
-
import { fileURLToPath as
|
|
10349
|
-
var __dirname8 = dirname10(fileURLToPath9(import.meta.url));
|
|
10350
|
-
var STATIC_DIR = join10(__dirname8, "static");
|
|
10351
|
-
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
12022
|
+
import { readFileSync as readFileSync18, existsSync as existsSync18, unlinkSync as unlinkSync4 } from "fs";
|
|
12023
|
+
import { join as join15, dirname as dirname12 } from "path";
|
|
12024
|
+
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
10352
12025
|
function getConsoleToken() {
|
|
10353
12026
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
10354
12027
|
}
|
|
@@ -10362,7 +12035,20 @@ function verifyAuth(req) {
|
|
|
10362
12035
|
function authMiddleware() {
|
|
10363
12036
|
return async (c, next) => {
|
|
10364
12037
|
if (!verifyAuth(c.req.raw)) {
|
|
10365
|
-
return c.json({
|
|
12038
|
+
return c.json({ detail: "Unauthorized: invalid or missing Bearer token" }, 401);
|
|
12039
|
+
}
|
|
12040
|
+
await next();
|
|
12041
|
+
};
|
|
12042
|
+
}
|
|
12043
|
+
function displayRuntimeConfig() {
|
|
12044
|
+
return isRuntimeConfigDisabled() ? generateSourceDefaults() : loadRuntimeConfig();
|
|
12045
|
+
}
|
|
12046
|
+
function builtinOnlyReadOnlyMiddleware() {
|
|
12047
|
+
return async (c, next) => {
|
|
12048
|
+
if (isRuntimeConfigDisabled() && !["GET", "HEAD", "OPTIONS"].includes(c.req.method)) {
|
|
12049
|
+
return c.json({
|
|
12050
|
+
detail: `\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF1AConsole \u53EA\u8BFB\uFF0C\u4FEE\u6539\u4E0D\u4F1A\u4FDD\u5B58\u3002\u8BF7\u5207\u6362\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u5E76\u91CD\u542F\u670D\u52A1\u540E\u518D\u6539\u89C4\u5219\u3002`
|
|
12051
|
+
}, 403);
|
|
10366
12052
|
}
|
|
10367
12053
|
await next();
|
|
10368
12054
|
};
|
|
@@ -10370,23 +12056,34 @@ function authMiddleware() {
|
|
|
10370
12056
|
function createConsoleRouter() {
|
|
10371
12057
|
const console2 = new Hono();
|
|
10372
12058
|
console2.use("/api/*", authMiddleware());
|
|
12059
|
+
console2.use("/api/*", builtinOnlyReadOnlyMiddleware());
|
|
10373
12060
|
const serveIndex = (c) => {
|
|
10374
|
-
const indexFile =
|
|
10375
|
-
if (
|
|
10376
|
-
return c.html(
|
|
12061
|
+
const indexFile = join15(STATIC_DIR, "index.html");
|
|
12062
|
+
if (existsSync18(indexFile)) {
|
|
12063
|
+
return c.html(readFileSync18(indexFile, "utf-8"));
|
|
10377
12064
|
}
|
|
10378
12065
|
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
10379
12066
|
};
|
|
10380
12067
|
console2.get("/", serveIndex);
|
|
10381
12068
|
console2.get("", serveIndex);
|
|
10382
12069
|
console2.get("/api/layers", (c) => {
|
|
10383
|
-
const rc =
|
|
10384
|
-
return c.json({ layers: rc.layers, env_overrides: getEnvOverrides() });
|
|
12070
|
+
const rc = displayRuntimeConfig();
|
|
12071
|
+
return c.json({ layers: rc.layers, env_overrides: getEnvOverrides(), builtin_only: isBuiltinOnly(), rules_mode: getRulesMode() });
|
|
10385
12072
|
});
|
|
10386
12073
|
console2.put("/api/layers", async (c) => {
|
|
10387
12074
|
const body = await c.req.json();
|
|
12075
|
+
const validKeys = /* @__PURE__ */ new Set(["L1_heuristic", "L2_logic_rules", "L3_llm_analysis"]);
|
|
12076
|
+
if (!validKeys.has(body.layer_key)) {
|
|
12077
|
+
return c.json({ detail: `Invalid layer_key: ${body.layer_key}` }, 400);
|
|
12078
|
+
}
|
|
12079
|
+
const envOverrides = getEnvOverrides();
|
|
12080
|
+
if (envOverrides[body.layer_key]?.overridden) {
|
|
12081
|
+
return c.json({
|
|
12082
|
+
detail: `\u5C42\u7EA7 ${body.layer_key} \u88AB\u73AF\u5883\u53D8\u91CF ${envOverrides[body.layer_key].env_var}=1 \u5F3A\u5236\u8986\u76D6\uFF0CWeb \u8BBE\u7F6E\u65E0\u6548`
|
|
12083
|
+
}, 409);
|
|
12084
|
+
}
|
|
10388
12085
|
const rc = updateLayerEnabled(body.layer_key, body.enabled);
|
|
10389
|
-
return c.json({ layers: rc.layers, env_overrides:
|
|
12086
|
+
return c.json({ layers: rc.layers, env_overrides: envOverrides });
|
|
10390
12087
|
});
|
|
10391
12088
|
function getL22View() {
|
|
10392
12089
|
const ltlHuman = {
|
|
@@ -10406,7 +12103,7 @@ function createConsoleRouter() {
|
|
|
10406
12103
|
const rules = mc.rules || [];
|
|
10407
12104
|
let catEnabled = true;
|
|
10408
12105
|
try {
|
|
10409
|
-
const rt =
|
|
12106
|
+
const rt = displayRuntimeConfig();
|
|
10410
12107
|
catEnabled = rt.L2_2?.ltl_rules?.category_enabled !== false;
|
|
10411
12108
|
} catch {
|
|
10412
12109
|
}
|
|
@@ -10462,8 +12159,22 @@ function createConsoleRouter() {
|
|
|
10462
12159
|
return rc;
|
|
10463
12160
|
}
|
|
10464
12161
|
console2.get("/api/rules", (c) => {
|
|
10465
|
-
const rc =
|
|
10466
|
-
|
|
12162
|
+
const rc = displayRuntimeConfig();
|
|
12163
|
+
const layer = c.req.query("layer");
|
|
12164
|
+
const category = c.req.query("category");
|
|
12165
|
+
const out = {};
|
|
12166
|
+
for (const layerKey of ["L1_rules", "L2_rules"]) {
|
|
12167
|
+
if (layer && layer !== layerKey) continue;
|
|
12168
|
+
const layerData = rc[layerKey] || {};
|
|
12169
|
+
const picked = {};
|
|
12170
|
+
for (const [catName, catData] of Object.entries(layerData)) {
|
|
12171
|
+
if (category && category !== catName) continue;
|
|
12172
|
+
picked[catName] = catData;
|
|
12173
|
+
}
|
|
12174
|
+
out[layerKey] = picked;
|
|
12175
|
+
}
|
|
12176
|
+
if (!category) out.L2_2 = getL22View();
|
|
12177
|
+
return c.json(out);
|
|
10467
12178
|
});
|
|
10468
12179
|
console2.put("/api/rules/enabled", async (c) => {
|
|
10469
12180
|
const b = await c.req.json();
|
|
@@ -10472,6 +12183,9 @@ function createConsoleRouter() {
|
|
|
10472
12183
|
});
|
|
10473
12184
|
console2.put("/api/rules/deny_mode", async (c) => {
|
|
10474
12185
|
const b = await c.req.json();
|
|
12186
|
+
if (!["deny_write", "deny_read", "deny_both"].includes(b.deny_mode)) {
|
|
12187
|
+
return c.json({ detail: `\u65E0\u6548\u7684 deny_mode: ${b.deny_mode}` }, 400);
|
|
12188
|
+
}
|
|
10475
12189
|
const rc = updateRuleDenyMode(b.layer, b.category, b.rule_id, b.deny_mode);
|
|
10476
12190
|
return c.json(withL22View(rc));
|
|
10477
12191
|
});
|
|
@@ -10503,13 +12217,15 @@ function createConsoleRouter() {
|
|
|
10503
12217
|
});
|
|
10504
12218
|
console2.delete("/api/rules", async (c) => {
|
|
10505
12219
|
const b = await c.req.json();
|
|
10506
|
-
deleteCustomRule(b.layer, b.category, b.rule_id);
|
|
10507
|
-
|
|
12220
|
+
const rc = deleteCustomRule(b.layer, b.category, b.rule_id);
|
|
12221
|
+
if (rc === null) {
|
|
12222
|
+
return c.json({ detail: "\u5185\u7F6E\u89C4\u5219\u4E0D\u53EF\u5220\u9664\uFF0C\u53EA\u80FD\u7981\u7528" }, 403);
|
|
12223
|
+
}
|
|
10508
12224
|
return c.json(withL22View(rc));
|
|
10509
12225
|
});
|
|
10510
12226
|
console2.get("/api/skills", (c) => {
|
|
10511
|
-
const rc =
|
|
10512
|
-
return c.json(rc.L3_skills);
|
|
12227
|
+
const rc = displayRuntimeConfig();
|
|
12228
|
+
return c.json(rc.L3_skills || {});
|
|
10513
12229
|
});
|
|
10514
12230
|
console2.put("/api/skills/enabled", async (c) => {
|
|
10515
12231
|
const b = await c.req.json();
|
|
@@ -10528,25 +12244,32 @@ function createConsoleRouter() {
|
|
|
10528
12244
|
});
|
|
10529
12245
|
console2.delete("/api/skills", async (c) => {
|
|
10530
12246
|
const b = await c.req.json();
|
|
10531
|
-
deleteCustomSkill(b.skill_id, b.category);
|
|
10532
|
-
|
|
12247
|
+
const rc = deleteCustomSkill(b.skill_id, b.category);
|
|
12248
|
+
if (rc === null) {
|
|
12249
|
+
return c.json({ detail: "\u5185\u7F6E skill \u4E0D\u53EF\u5220\u9664\uFF0C\u53EA\u80FD\u7981\u7528" }, 403);
|
|
12250
|
+
}
|
|
10533
12251
|
return c.json(withL22View(rc));
|
|
10534
12252
|
});
|
|
10535
12253
|
console2.get("/api/skills/:skill_id/content", async (c) => {
|
|
10536
12254
|
const skillId = c.req.param("skill_id");
|
|
10537
12255
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
10538
|
-
return c.json({
|
|
12256
|
+
return c.json({ detail: "\u975E\u6CD5 skill id" }, 400);
|
|
10539
12257
|
}
|
|
10540
|
-
const
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
|
|
12258
|
+
const userPath = join15(dirname12(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
12259
|
+
const builtinDir = resolveAssetDir(__dirname10, "skills");
|
|
12260
|
+
const candidates = [
|
|
12261
|
+
...isRuntimeConfigDisabled() ? [] : [userPath],
|
|
12262
|
+
builtinDir ? join15(builtinDir, `${skillId}.md`) : ""
|
|
12263
|
+
].filter(Boolean);
|
|
12264
|
+
for (const p of candidates) {
|
|
12265
|
+
if (existsSync18(p)) {
|
|
12266
|
+
return c.json({ skill_id: skillId, path: p, content: readFileSync18(p, "utf-8") });
|
|
12267
|
+
}
|
|
10545
12268
|
}
|
|
10546
|
-
return c.
|
|
12269
|
+
return c.json({ detail: `skill ${skillId} \u4E0D\u5B58\u5728` }, 404);
|
|
10547
12270
|
});
|
|
10548
12271
|
console2.get("/api/agents", (c) => {
|
|
10549
|
-
const rc =
|
|
12272
|
+
const rc = displayRuntimeConfig();
|
|
10550
12273
|
return c.json({ agents: getAgentRules(rc) });
|
|
10551
12274
|
});
|
|
10552
12275
|
console2.put("/api/agents/rules/enabled", async (c) => {
|
|
@@ -10585,7 +12308,7 @@ function createConsoleRouter() {
|
|
|
10585
12308
|
});
|
|
10586
12309
|
const enabledScenarioIds = () => (process.env.AGENT_MOSS_SCENARIO || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
10587
12310
|
console2.get("/api/scenarios", (c) => {
|
|
10588
|
-
const all = getScenarioRules(
|
|
12311
|
+
const all = getScenarioRules(displayRuntimeConfig());
|
|
10589
12312
|
const enabled = enabledScenarioIds();
|
|
10590
12313
|
if (!enabled.length) return c.json({ scenarios: {}, enabled: [] });
|
|
10591
12314
|
const shown = {};
|
|
@@ -10627,20 +12350,27 @@ function createConsoleRouter() {
|
|
|
10627
12350
|
return c.json({ scenarios: getScenarioRules(rc), enabled: enabledScenarioIds() });
|
|
10628
12351
|
});
|
|
10629
12352
|
console2.get("/api/config", (c) => {
|
|
10630
|
-
const rc =
|
|
10631
|
-
return c.json({ ...rc, L2_2: getL22View() });
|
|
12353
|
+
const rc = displayRuntimeConfig();
|
|
12354
|
+
return c.json({ ...rc, L2_2: getL22View(), builtin_only: isBuiltinOnly(), rules_mode: getRulesMode() });
|
|
10632
12355
|
});
|
|
10633
12356
|
console2.get("/api/env-overrides", (c) => {
|
|
10634
12357
|
return c.json({ overrides: getEnvOverrides() });
|
|
10635
12358
|
});
|
|
10636
12359
|
console2.post("/api/reset", (c) => {
|
|
10637
12360
|
const defaults = generateSourceDefaults();
|
|
10638
|
-
|
|
12361
|
+
try {
|
|
12362
|
+
const p = getRuntimeConfigPath();
|
|
12363
|
+
if (existsSync18(p)) unlinkSync4(p);
|
|
12364
|
+
} catch {
|
|
12365
|
+
}
|
|
10639
12366
|
return c.json(withL22View(defaults));
|
|
10640
12367
|
});
|
|
12368
|
+
const parseDays = (v) => {
|
|
12369
|
+
const n = parseInt(v || "0", 10);
|
|
12370
|
+
return Number.isFinite(n) ? n : 0;
|
|
12371
|
+
};
|
|
10641
12372
|
console2.get("/api/token-stats", (c) => {
|
|
10642
|
-
|
|
10643
|
-
return c.json(getTokenStats(days));
|
|
12373
|
+
return c.json(getTokenStats(parseDays(c.req.query("days"))));
|
|
10644
12374
|
});
|
|
10645
12375
|
console2.get("/api/token-stats/recent", (c) => {
|
|
10646
12376
|
const limit = parseInt(c.req.query("limit") || "20", 10);
|
|
@@ -10648,47 +12378,103 @@ function createConsoleRouter() {
|
|
|
10648
12378
|
return c.json({ records: getRecentRecords(limit, offset), total: getRecentRecordsCount() });
|
|
10649
12379
|
});
|
|
10650
12380
|
console2.get("/api/token-stats/trend", (c) => {
|
|
10651
|
-
|
|
10652
|
-
return c.json(getTokenTrend(days));
|
|
12381
|
+
return c.json(getTokenTrend(parseDays(c.req.query("days"))));
|
|
10653
12382
|
});
|
|
10654
12383
|
console2.post("/api/token-stats/reset", (c) => {
|
|
10655
12384
|
resetTokenStats();
|
|
10656
|
-
return c.json({
|
|
12385
|
+
return c.json({ detail: "Token stats reset successfully" });
|
|
10657
12386
|
});
|
|
10658
12387
|
return console2;
|
|
10659
12388
|
}
|
|
12389
|
+
var __dirname10, STATIC_DIR, CONSOLE_TOKEN_ENV;
|
|
12390
|
+
var init_router = __esm({
|
|
12391
|
+
"src/console/router.ts"() {
|
|
12392
|
+
"use strict";
|
|
12393
|
+
init_esm_shims();
|
|
12394
|
+
init_runtime_config();
|
|
12395
|
+
init_token_stats();
|
|
12396
|
+
init_rules_loader();
|
|
12397
|
+
init_asset_dir();
|
|
12398
|
+
init_rules_mode();
|
|
12399
|
+
__dirname10 = dirname12(fileURLToPath11(import.meta.url));
|
|
12400
|
+
STATIC_DIR = join15(__dirname10, "static");
|
|
12401
|
+
CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
12402
|
+
}
|
|
12403
|
+
});
|
|
12404
|
+
|
|
12405
|
+
// src/console/index.ts
|
|
12406
|
+
var init_console = __esm({
|
|
12407
|
+
"src/console/index.ts"() {
|
|
12408
|
+
"use strict";
|
|
12409
|
+
init_esm_shims();
|
|
12410
|
+
init_router();
|
|
12411
|
+
}
|
|
12412
|
+
});
|
|
10660
12413
|
|
|
10661
12414
|
// src/server.ts
|
|
10662
|
-
|
|
10663
|
-
|
|
12415
|
+
var server_exports = {};
|
|
12416
|
+
__export(server_exports, {
|
|
12417
|
+
createApp: () => createApp,
|
|
12418
|
+
findFreePort: () => findFreePort,
|
|
12419
|
+
logDeploymentSelfCheck: () => logDeploymentSelfCheck,
|
|
12420
|
+
startServer: () => startServer,
|
|
12421
|
+
startUnixSocketServer: () => startUnixSocketServer
|
|
12422
|
+
});
|
|
12423
|
+
import { Hono as Hono2 } from "hono";
|
|
12424
|
+
import { cors } from "hono/cors";
|
|
12425
|
+
import { logger } from "hono/logger";
|
|
12426
|
+
import net from "net";
|
|
12427
|
+
import { existsSync as existsSync19 } from "fs";
|
|
10664
12428
|
function logDeploymentSelfCheck() {
|
|
10665
12429
|
try {
|
|
12430
|
+
const mode = getRulesMode();
|
|
12431
|
+
const builtinOnly = isBuiltinOnly();
|
|
12432
|
+
const runtimeDisabled = isRuntimeConfigDisabled();
|
|
10666
12433
|
console.log("================ AgentMoss \u90E8\u7F72\u81EA\u68C0 ================");
|
|
10667
|
-
|
|
10668
|
-
|
|
10669
|
-
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
|
|
10673
|
-
|
|
10674
|
-
for (const p of overrides) console.error(` ${p}`);
|
|
12434
|
+
if (runtimeDisabled) {
|
|
12435
|
+
console.log(`runtime \u914D\u7F6E: ${getRuntimeConfigPath()}\uFF08**\u5DF2\u5FFD\u7565**\uFF1A\u89C4\u5219\u6765\u6E90\u6A21\u5F0F=${mode}\uFF09`);
|
|
12436
|
+
if (builtinOnly) {
|
|
12437
|
+
console.error(
|
|
12438
|
+
"\u89C4\u5219\u6765\u6E90: **\u4EC5\u5185\u7F6E** \u2014\u2014 \u4E0D\u8BFB runtime \u914D\u7F6E / Console \u81EA\u5B9A\u4E49 / \u89C4\u5219\u76EE\u5F55 / AGENT_MOSS_CUSTOM_RULES\uFF1B\u4E0D seed\u3001\u4E0D merge\u3001\u4E0D\u5199\u56DE\u3002"
|
|
12439
|
+
);
|
|
12440
|
+
} else {
|
|
10675
12441
|
console.error(
|
|
10676
|
-
"
|
|
12442
|
+
"\u89C4\u5219\u6765\u6E90: **\u51B7\u542F\u52A8\uFF08cold\uFF09** \u2014\u2014 \u542F\u52A8\u65F6\u8BFB\u53D6\u5185\u7F6E\u89C4\u5219 + \u89C4\u5219\u76EE\u5F55 / AGENT_MOSS_CUSTOM_RULES / \u573A\u666F\uFF0C\u4E4B\u540E\u51BB\u7ED3\uFF1B\u4E0D\u8BFB runtime \u914D\u7F6E / Console\uFF0C\u4E0D seed\u3001\u4E0D merge\u3001\u4E0D\u5199\u56DE\u3002"
|
|
10677
12443
|
);
|
|
12444
|
+
console.error("\u89C4\u5219\u76EE\u5F55\u5185\u5BB9\u6539\u52A8\u9700\u91CD\u542F\u670D\u52A1\u624D\u751F\u6548\u3002");
|
|
10678
12445
|
}
|
|
10679
|
-
|
|
12446
|
+
console.error("Console: \u53EA\u8BFB\uFF08\u89C4\u5219\u5199\u63A5\u53E3\u8FD4\u56DE 403\uFF09\uFF1B\u4FEE\u6539\u89C4\u5219\u9700\u91CD\u542F\u670D\u52A1\u5E76\u5207\u6362\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u3002");
|
|
12447
|
+
} else {
|
|
12448
|
+
console.log(`runtime \u914D\u7F6E: ${getRuntimeConfigPath()}`);
|
|
10680
12449
|
}
|
|
10681
|
-
|
|
10682
|
-
|
|
10683
|
-
|
|
10684
|
-
|
|
10685
|
-
const
|
|
10686
|
-
|
|
10687
|
-
|
|
10688
|
-
|
|
10689
|
-
);
|
|
12450
|
+
const layerKeys = ["L1_heuristic", "L2_logic_rules", "L3_llm_analysis", "agent_rules_enabled", "scenario_rules_enabled"];
|
|
12451
|
+
console.log("\u5C42\u7EA7\u5F00\u5173: " + layerKeys.map((k) => `${k}=${isLayerEnabled(k) ? "on" : "OFF"}`).join(" "));
|
|
12452
|
+
if (!builtinOnly) {
|
|
12453
|
+
try {
|
|
12454
|
+
const dirs = rulesDirs().filter((d) => existsSync19(d));
|
|
12455
|
+
if (dirs.length > 0) {
|
|
12456
|
+
console.log(`\u89C4\u5219\u76EE\u5F55: ${dirs.join(", ")}\uFF08\u53EA\u8BFB\u5176\u4E2D ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09`);
|
|
12457
|
+
}
|
|
12458
|
+
const loaded = ruleFilesLoaded();
|
|
12459
|
+
if (loaded.length > 0) {
|
|
12460
|
+
const ov = loadOverrideRules();
|
|
12461
|
+
const ex = loadExtraRules();
|
|
12462
|
+
console.error(`\u26A0\uFE0F \u5DF2\u4ECE\u89C4\u5219\u76EE\u5F55\u52A0\u8F7D ${loaded.length} \u4E2A\u6587\u4EF6\uFF08\u6574\u4EFD\u66FF\u6362 / \u8FFD\u52A0\uFF0C\u5185\u7F6E\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4EEC\u4E0A\u9762\uFF09:`);
|
|
12463
|
+
for (const f of loaded) console.error(` ${f}`);
|
|
12464
|
+
const ovs = Object.entries(ov).sort().map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
12465
|
+
if (ovs) console.error(` \u8986\u76D6\u7C7B\u522B: ${ovs}\uFF08**\u6574\u4EFD\u66FF\u6362**\uFF1A\u672A\u5217\u51FA\u7684\u5185\u7F6E\u89C4\u5219\u4E0D\u518D\u751F\u6548\uFF09`);
|
|
12466
|
+
const exs = Object.entries(ex).sort().map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
12467
|
+
if (exs) console.log(` \u8FFD\u52A0\u7C7B\u522B: ${exs}\uFF08\u5185\u7F6E\u4FDD\u7559\uFF0C\u8FFD\u52A0\u5728\u5176\u540E\u4E00\u8D77\u5224\u5B9A\uFF09`);
|
|
12468
|
+
}
|
|
12469
|
+
const ignored = ignoredRuleFiles();
|
|
12470
|
+
if (ignored.length > 0) {
|
|
12471
|
+
console.error(
|
|
12472
|
+
`\u26A0\uFE0F \u89C4\u5219\u76EE\u5F55\u91CC\u6709 ${ignored.length} \u4E2A json **\u4E0D\u4F1A\u88AB\u8BFB\u53D6**\uFF08\u53EA\u8BA4 ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09:`
|
|
12473
|
+
);
|
|
12474
|
+
for (const f of ignored) console.error(` ${f}`);
|
|
12475
|
+
}
|
|
12476
|
+
} catch {
|
|
10690
12477
|
}
|
|
10691
|
-
} catch {
|
|
10692
12478
|
}
|
|
10693
12479
|
const scenarioRaw = (process.env.AGENT_MOSS_SCENARIO || "").trim();
|
|
10694
12480
|
if (scenarioRaw) {
|
|
@@ -10710,18 +12496,26 @@ function createApp() {
|
|
|
10710
12496
|
app.get("/api/v1/health", healthRoute);
|
|
10711
12497
|
app.post("/api/v1/analyze", analyzeRoute);
|
|
10712
12498
|
app.post("/api/v1/review", reviewRoute);
|
|
12499
|
+
app.get("/api/v1/brain/rules", brainRulesRoute);
|
|
12500
|
+
app.get("/api/v1/brain/rules/drafts", brainDraftsRoute);
|
|
12501
|
+
app.post("/api/v1/brain/rules/:rule_id/approve", brainApproveRoute);
|
|
12502
|
+
app.post("/api/v1/brain/rules/:rule_id/reject", brainRejectRoute);
|
|
12503
|
+
app.get("/api/v1/brain/stats", brainStatsRoute);
|
|
12504
|
+
app.get("/api/v1/brain/skills", brainSkillsRoute);
|
|
12505
|
+
app.get("/api/v1/brain/rules/export", brainExportRoute);
|
|
12506
|
+
app.post("/api/v1/brain/rules/import", brainImportRoute);
|
|
10713
12507
|
app.route("/console", createConsoleRouter());
|
|
10714
12508
|
app.get("/console/", (c) => c.redirect("/console"));
|
|
10715
12509
|
return app;
|
|
10716
12510
|
}
|
|
10717
12511
|
async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
10718
12512
|
for (let port = startPort; port < startPort + scan; port++) {
|
|
10719
|
-
const free = await new Promise((
|
|
12513
|
+
const free = await new Promise((resolve11) => {
|
|
10720
12514
|
const server = net.createServer();
|
|
10721
12515
|
server.listen(port, host, () => {
|
|
10722
|
-
server.close(() =>
|
|
12516
|
+
server.close(() => resolve11(true));
|
|
10723
12517
|
});
|
|
10724
|
-
server.on("error", () =>
|
|
12518
|
+
server.on("error", () => resolve11(false));
|
|
10725
12519
|
});
|
|
10726
12520
|
if (free) {
|
|
10727
12521
|
if (port !== startPort) {
|
|
@@ -10732,12 +12526,6 @@ async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
|
10732
12526
|
}
|
|
10733
12527
|
return startPort;
|
|
10734
12528
|
}
|
|
10735
|
-
var WARMUP_COMMANDS = {
|
|
10736
|
-
linux: "whoami",
|
|
10737
|
-
macos: "whoami",
|
|
10738
|
-
harmony: "whoami",
|
|
10739
|
-
windows: "dir"
|
|
10740
|
-
};
|
|
10741
12529
|
async function warmUpEngine() {
|
|
10742
12530
|
const prevSavePolicy = process.env.AGENT_MOSS_SAVE_POLICY;
|
|
10743
12531
|
try {
|
|
@@ -10777,9 +12565,241 @@ async function startServer(host = "127.0.0.1", port = 9090) {
|
|
|
10777
12565
|
}
|
|
10778
12566
|
};
|
|
10779
12567
|
}
|
|
12568
|
+
async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_moss.sock") {
|
|
12569
|
+
const app = createApp();
|
|
12570
|
+
let server = null;
|
|
12571
|
+
return {
|
|
12572
|
+
socketPath,
|
|
12573
|
+
close: async () => {
|
|
12574
|
+
if (!server) return;
|
|
12575
|
+
await new Promise((resolve11) => {
|
|
12576
|
+
server.close(() => resolve11());
|
|
12577
|
+
});
|
|
12578
|
+
server = null;
|
|
12579
|
+
},
|
|
12580
|
+
serve: async () => {
|
|
12581
|
+
const { mkdirSync: mkdirSync10, existsSync: existsSync21, unlinkSync: unlinkSync5 } = await import("fs");
|
|
12582
|
+
const { dirname: dirname14 } = await import("path");
|
|
12583
|
+
const dir = dirname14(socketPath);
|
|
12584
|
+
if (dir) {
|
|
12585
|
+
try {
|
|
12586
|
+
mkdirSync10(dir, { recursive: true });
|
|
12587
|
+
} catch {
|
|
12588
|
+
}
|
|
12589
|
+
}
|
|
12590
|
+
if (existsSync21(socketPath)) {
|
|
12591
|
+
const { connect } = await import("net");
|
|
12592
|
+
const inUse = await new Promise((resolve11) => {
|
|
12593
|
+
const probe = connect(socketPath);
|
|
12594
|
+
const done = (v) => {
|
|
12595
|
+
probe.destroy();
|
|
12596
|
+
resolve11(v);
|
|
12597
|
+
};
|
|
12598
|
+
probe.once("connect", () => done(true));
|
|
12599
|
+
probe.once("error", () => done(false));
|
|
12600
|
+
probe.setTimeout(500, () => done(false));
|
|
12601
|
+
});
|
|
12602
|
+
if (inUse) {
|
|
12603
|
+
console.warn(
|
|
12604
|
+
`[AgentMoss] socket ${socketPath} \u5DF2\u6709\u670D\u52A1\u5728\u76D1\u542C\uFF0C\u4E0D\u6E05\u7406\uFF08bind \u4F1A\u56E0\u5360\u7528\u800C\u5931\u8D25\uFF09`
|
|
12605
|
+
);
|
|
12606
|
+
} else {
|
|
12607
|
+
try {
|
|
12608
|
+
unlinkSync5(socketPath);
|
|
12609
|
+
console.log(`[AgentMoss] \u6E05\u7406\u6B8B\u7559 socket \u6587\u4EF6: ${socketPath}`);
|
|
12610
|
+
} catch {
|
|
12611
|
+
}
|
|
12612
|
+
}
|
|
12613
|
+
}
|
|
12614
|
+
logDeploymentSelfCheck();
|
|
12615
|
+
await warmUpEngine();
|
|
12616
|
+
const { getRequestListener } = await import("@hono/node-server");
|
|
12617
|
+
const { createServer } = await import("http");
|
|
12618
|
+
server = createServer(getRequestListener(app.fetch));
|
|
12619
|
+
await new Promise((resolve11, reject) => {
|
|
12620
|
+
server.once("error", reject);
|
|
12621
|
+
server.listen(socketPath, () => resolve11());
|
|
12622
|
+
});
|
|
12623
|
+
console.log(`AgentMoss HTTP server on unix socket: ${socketPath}`);
|
|
12624
|
+
}
|
|
12625
|
+
};
|
|
12626
|
+
}
|
|
12627
|
+
var WARMUP_COMMANDS;
|
|
12628
|
+
var init_server = __esm({
|
|
12629
|
+
"src/server.ts"() {
|
|
12630
|
+
"use strict";
|
|
12631
|
+
init_esm_shims();
|
|
12632
|
+
init_routes();
|
|
12633
|
+
init_console();
|
|
12634
|
+
init_runtime_config();
|
|
12635
|
+
init_rules_mode();
|
|
12636
|
+
init_rules_loader();
|
|
12637
|
+
WARMUP_COMMANDS = {
|
|
12638
|
+
linux: "whoami",
|
|
12639
|
+
macos: "whoami",
|
|
12640
|
+
harmony: "whoami",
|
|
12641
|
+
windows: "dir"
|
|
12642
|
+
};
|
|
12643
|
+
}
|
|
12644
|
+
});
|
|
12645
|
+
|
|
12646
|
+
// src/rules-tools.ts
|
|
12647
|
+
var rules_tools_exports = {};
|
|
12648
|
+
__export(rules_tools_exports, {
|
|
12649
|
+
EXTRA_TEMPLATE: () => EXTRA_TEMPLATE,
|
|
12650
|
+
OVERRIDE_TEMPLATE: () => OVERRIDE_TEMPLATE,
|
|
12651
|
+
initRulesDir: () => initRulesDir,
|
|
12652
|
+
rulesStatusText: () => rulesStatusText,
|
|
12653
|
+
templateSourceDir: () => templateSourceDir
|
|
12654
|
+
});
|
|
12655
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "fs";
|
|
12656
|
+
import { dirname as dirname13, join as join16 } from "path";
|
|
12657
|
+
import { fileURLToPath as fileURLToPath12 } from "url";
|
|
12658
|
+
function initRulesDir(targetDir, opts = {}) {
|
|
12659
|
+
mkdirSync9(targetDir, { recursive: true });
|
|
12660
|
+
const written = [];
|
|
12661
|
+
const extraPath = join16(targetDir, EXTRA_FILENAME);
|
|
12662
|
+
if (opts.force || !existsSync20(extraPath)) {
|
|
12663
|
+
writeFileSync9(extraPath, JSON.stringify(EXTRA_TEMPLATE, null, 2), "utf-8");
|
|
12664
|
+
written.push(extraPath);
|
|
12665
|
+
}
|
|
12666
|
+
const samplePath = join16(targetDir, OVERRIDE_FILENAME);
|
|
12667
|
+
if (opts.force || !existsSync20(samplePath)) {
|
|
12668
|
+
let payload = OVERRIDE_TEMPLATE;
|
|
12669
|
+
if (opts.withBuiltin) {
|
|
12670
|
+
let data = {};
|
|
12671
|
+
const builtin = resolveRulesFile("l1_rules.json");
|
|
12672
|
+
if (builtin && existsSync20(builtin)) {
|
|
12673
|
+
try {
|
|
12674
|
+
data = JSON.parse(readFileSync19(builtin, "utf-8")) || {};
|
|
12675
|
+
} catch {
|
|
12676
|
+
data = {};
|
|
12677
|
+
}
|
|
12678
|
+
}
|
|
12679
|
+
payload = { _\u8BF4\u660E: OVERRIDE_BUILTIN_NOTE };
|
|
12680
|
+
for (const k of L1_KEYS) payload[k] = data[k] || [];
|
|
12681
|
+
}
|
|
12682
|
+
writeFileSync9(samplePath, JSON.stringify(payload, null, 2), "utf-8");
|
|
12683
|
+
written.push(samplePath);
|
|
12684
|
+
}
|
|
12685
|
+
const readmeSrc = join16(templateSourceDir(), "README.md");
|
|
12686
|
+
const readmeDst = join16(targetDir, "README.md");
|
|
12687
|
+
if (existsSync20(readmeSrc) && (opts.force || !existsSync20(readmeDst))) {
|
|
12688
|
+
writeFileSync9(readmeDst, readFileSync19(readmeSrc, "utf-8"), "utf-8");
|
|
12689
|
+
written.push(readmeDst);
|
|
12690
|
+
}
|
|
12691
|
+
return written;
|
|
12692
|
+
}
|
|
12693
|
+
function templateSourceDir() {
|
|
12694
|
+
const cands = [join16(__dirname11, "data", "rules_template"), join16(__dirname11, "..", "..", "agent_moss", "data", "rules_template")];
|
|
12695
|
+
for (const c of cands) if (existsSync20(c)) return c;
|
|
12696
|
+
return cands[0];
|
|
12697
|
+
}
|
|
12698
|
+
function templateFiles(dirs) {
|
|
12699
|
+
const out = [];
|
|
12700
|
+
for (const d of dirs) {
|
|
12701
|
+
try {
|
|
12702
|
+
for (const name of readdirSync7(d)) {
|
|
12703
|
+
if (name.endsWith(".json.\u793A\u4F8B")) out.push(join16(d, name));
|
|
12704
|
+
}
|
|
12705
|
+
} catch {
|
|
12706
|
+
}
|
|
12707
|
+
}
|
|
12708
|
+
return out.sort();
|
|
12709
|
+
}
|
|
12710
|
+
function rulesStatusText() {
|
|
12711
|
+
const lines = [];
|
|
12712
|
+
const existing = rulesDirs().filter((d) => existsSync20(d));
|
|
12713
|
+
lines.push("\u89C4\u5219\u76EE\u5F55: " + (existing.length > 0 ? existing.join(", ") : "\uFF08\u672A\u8BBE\u7F6E / \u4E0D\u5B58\u5728\uFF09"));
|
|
12714
|
+
if (existing.length > 0) {
|
|
12715
|
+
lines.push(` \uFF08\u53EA\u8BFB\u5176\u4E2D\u7684 ${OVERRIDE_FILENAME} \u4E0E ${EXTRA_FILENAME}\uFF1B\u5176\u4ED6\u6587\u4EF6\u540D\u4E00\u5F8B\u4E0D\u8BFB\uFF09`);
|
|
12716
|
+
}
|
|
12717
|
+
const override = loadOverrideRules();
|
|
12718
|
+
const extraAll = loadExtraRules();
|
|
12719
|
+
const extra = {};
|
|
12720
|
+
for (const [k, v] of Object.entries(extraAll)) if (v.length > 0) extra[k] = v;
|
|
12721
|
+
const loaded = ruleFilesLoaded();
|
|
12722
|
+
if (Object.keys(override).length > 0) {
|
|
12723
|
+
lines.push(`\u2705 \u8986\u76D6\u751F\u6548\uFF08${OVERRIDE_FILENAME}\uFF0C**\u6574\u4EFD\u66FF\u6362**\uFF1A\u4E0B\u9762\u8FD9\u4E9B\u7C7B\u522B\u7684\u5185\u7F6E\u89C4\u5219\u4E0D\u518D\u751F\u6548\uFF09:`);
|
|
12724
|
+
for (const [cat, items] of Object.entries(override).sort()) lines.push(` ${cat}: \u4F60\u7684 ${items.length} \u6761`);
|
|
12725
|
+
}
|
|
12726
|
+
if (Object.keys(extra).length > 0) {
|
|
12727
|
+
lines.push(`\u2705 \u8FFD\u52A0\u751F\u6548\uFF08${EXTRA_FILENAME}\uFF0C\u5185\u7F6E\u89C4\u5219\u4FDD\u7559\uFF0C\u8FD9\u4E9B\u6302\u5728\u5176\u540E\uFF09:`);
|
|
12728
|
+
for (const [cat, items] of Object.entries(extra).sort()) lines.push(` ${cat}: ${items.length} \u6761`);
|
|
12729
|
+
}
|
|
12730
|
+
if (Object.keys(override).length === 0 && Object.keys(extra).length === 0) {
|
|
12731
|
+
if (loaded.length > 0 || templateFiles(existing).length > 0) {
|
|
12732
|
+
lines.push("\u2139\uFE0F \u89C4\u5219\u6587\u4EF6\u5DF2\u627E\u5230\uFF0C\u4F46\u91CC\u9762**\u8FD8\u6CA1\u6709\u4EFB\u4F55\u89C4\u5219**\uFF08\u7C7B\u522B\u90FD\u662F\u7A7A\u6570\u7EC4\uFF09\u2014\u2014 \u52A0\u4E0A\u6761\u76EE\u540E\u91CD\u542F\u5373\u53EF\u751F\u6548\u3002");
|
|
12733
|
+
} else {
|
|
12734
|
+
lines.push(`\u2139\uFE0F \u8FD9\u4E2A\u76EE\u5F55\u91CC\u6CA1\u6709 ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF0C\u5F53\u524D\u7528\u7684\u662F\u5185\u7F6E\u89C4\u5219\u3002`);
|
|
12735
|
+
lines.push(` \u6A21\u677F\u968F\u5305\u5206\u53D1\uFF08\u4E0D\u7528\u8DD1\u547D\u4EE4\uFF09: ${templateSourceDir()}`);
|
|
12736
|
+
lines.push(" \u6216\u4E00\u884C\u547D\u4EE4\u751F\u6210\u5230\u76EE\u6807\u76EE\u5F55: agent-moss rules init --dir <\u76EE\u5F55>");
|
|
12737
|
+
}
|
|
12738
|
+
}
|
|
12739
|
+
const templates = templateFiles(existing);
|
|
12740
|
+
if (templates.length > 0) {
|
|
12741
|
+
lines.push("\u2139\uFE0F \u53D1\u73B0 `*.json.\u793A\u4F8B` \u6587\u4EF6\uFF08**\u4E0D\u4F1A\u88AB\u8BFB\u53D6**\uFF1B\u82E5\u786E\u5B9E\u8981\u7528\uFF0C\u53BB\u6389 .\u793A\u4F8B \u540E\u7F00\uFF09:");
|
|
12742
|
+
for (const p of templates) lines.push(` ${p}`);
|
|
12743
|
+
}
|
|
12744
|
+
const ignored = ignoredRuleFiles();
|
|
12745
|
+
if (ignored.length > 0) {
|
|
12746
|
+
lines.push(`\u26A0\uFE0F \u4EE5\u4E0B ${ignored.length} \u4E2A\u6587\u4EF6**\u4E0D\u4F1A\u88AB\u8BFB\u53D6**\uFF08\u540D\u5B57\u4E0D\u5BF9\uFF1B\u53EA\u8BA4 ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09:`);
|
|
12747
|
+
for (const p of ignored) lines.push(` ${p}`);
|
|
12748
|
+
}
|
|
12749
|
+
lines.push("");
|
|
12750
|
+
lines.push("\u63D0\u793A\uFF1A\u8FFD\u52A0\u7528 extra.json\uFF1B\u8981\u6539/\u5220\u5185\u7F6E\u7684\u67D0\u4E00\u6761 \u2192 \u7528 override.json\uFF08\u5185\u7F6E L1 \u5168\u91CF\u62F7\u8D1D\uFF0C\u5220\u6389\u4E0D\u8981\u7684\u90A3\u6761\u5373\u53EF\uFF09\u3002");
|
|
12751
|
+
return lines.join("\n");
|
|
12752
|
+
}
|
|
12753
|
+
var __dirname11, EXTRA_TEMPLATE, OVERRIDE_TEMPLATE, OVERRIDE_BUILTIN_NOTE, L1_KEYS;
|
|
12754
|
+
var init_rules_tools = __esm({
|
|
12755
|
+
"src/rules-tools.ts"() {
|
|
12756
|
+
"use strict";
|
|
12757
|
+
init_esm_shims();
|
|
12758
|
+
init_rules_loader();
|
|
12759
|
+
__dirname11 = dirname13(fileURLToPath12(import.meta.url));
|
|
12760
|
+
EXTRA_TEMPLATE = {
|
|
12761
|
+
_\u8BF4\u660E: [
|
|
12762
|
+
"\u8FFD\u52A0\u89C4\u5219\uFF1A\u5185\u7F6E\u89C4\u5219\u5168\u90E8\u4FDD\u7559\uFF0C\u4F60\u5199\u5728\u8FD9\u91CC\u7684\u89C4\u5219\u52A0\u5728\u5185\u7F6E\u4E4B\u540E\u4E00\u8D77\u5224\u5B9A\u3002",
|
|
12763
|
+
"\u53EA\u8DD1 L1 \u7684\u90E8\u7F72\u53EA\u9700\u8981\u524D\u4E09\u4E2A\u7C7B\u522B\uFF08\u540E\u4E09\u4E2A\u5C5E\u4E8E L2\uFF0C\u5199\u4E86\u4E5F\u4E0D\u751F\u6548\uFF09\u3002",
|
|
12764
|
+
"\u600E\u4E48\u586B\u3001\u53EF\u76F4\u63A5\u7167\u6284\u7684\u6837\u4F8B\uFF1A\u89C1\u540C\u76EE\u5F55 README.md\u3002",
|
|
12765
|
+
"\u6539\u5B8C\u81EA\u67E5\uFF1Aagent-moss rules status"
|
|
12766
|
+
],
|
|
12767
|
+
dangerous_commands: [],
|
|
12768
|
+
injection_detection: [],
|
|
12769
|
+
sensitive_path_access: [],
|
|
12770
|
+
intent_consistency: [],
|
|
12771
|
+
password_modify_consent: [],
|
|
12772
|
+
user_deletion_consent: []
|
|
12773
|
+
};
|
|
12774
|
+
OVERRIDE_TEMPLATE = {
|
|
12775
|
+
_\u8BF4\u660E: [
|
|
12776
|
+
"\u8986\u76D6\u89C4\u5219\uFF1A\u4F60\u5199\u4E86\u54EA\u4E2A\u7C7B\u522B\uFF0C\u90A3\u4E2A\u7C7B\u522B\u5C31\u6574\u4EFD\u4EE5\u4F60\u7684\u4E3A\u51C6\uFF08\u5185\u7F6E\u540C\u540D\u7C7B\u522B\u4E0D\u518D\u751F\u6548\uFF09\uFF1B",
|
|
12777
|
+
"\u6CA1\u5199\u7684\u7C7B\u522B\u7167\u5E38\u7528\u5185\u7F6E\u3002\u7A7A\u6570\u7EC4 = \u4E0D\u8986\u76D6\uFF0C\u6240\u4EE5\u672C\u6587\u4EF6**\u539F\u6837\u653E\u7740 = \u4EC0\u4E48\u90FD\u4E0D\u6539**\u3002",
|
|
12778
|
+
"\u600E\u4E48\u586B\u3001\u53EF\u76F4\u63A5\u7167\u6284\u7684\u6837\u4F8B\uFF1A\u89C1\u540C\u76EE\u5F55 README.md\u3002",
|
|
12779
|
+
"\u8981\u5220\u6389\u5185\u7F6E\u7684\u67D0\u4E00\u6761\uFF08\u800C\u4E0D\u662F\u6574\u7C7B\u66FF\u6362\uFF09\uFF1A\u7528 agent-moss rules init --with-builtin\u3002",
|
|
12780
|
+
"\u6539\u5B8C\u81EA\u67E5\uFF1Aagent-moss rules status"
|
|
12781
|
+
],
|
|
12782
|
+
dangerous_commands: [],
|
|
12783
|
+
injection_detection: [],
|
|
12784
|
+
sensitive_path_access: [],
|
|
12785
|
+
intent_consistency: [],
|
|
12786
|
+
password_modify_consent: [],
|
|
12787
|
+
user_deletion_consent: []
|
|
12788
|
+
};
|
|
12789
|
+
OVERRIDE_BUILTIN_NOTE = [
|
|
12790
|
+
"\u8FD9\u662F\u3010\u8986\u76D6\u3011\u89C4\u5219\u6587\u4EF6\uFF0C\u5185\u5BB9 = \u5185\u7F6E L1 \u89C4\u5219\u7684**\u5B8C\u6574\u62F7\u8D1D**\uFF08\u5C31\u5730\u5220/\u6539\u5185\u7F6E\u67D0\u4E00\u6761\u65F6\u7528\uFF09\u3002",
|
|
12791
|
+
"\u26A0\uFE0F \u5B83\u653E\u8FDB\u76EE\u5F55\u5373\u751F\u6548\uFF0C\u7B49\u4E8E\u628A\u5185\u7F6E L1 \u89C4\u5219**\u9489\u5728**\u8FD9\u4E00\u7248\uFF08\u4EE5\u540E\u5347\u7EA7\u4E0D\u8DDF\u968F\uFF09\u2014\u2014 \u53EA\u505A\u4E34\u65F6\u6539\u52A8\u7528\u3002",
|
|
12792
|
+
"\u5220\u6389/\u4FEE\u6539\u4F60\u4E0D\u8981\u7684\u90A3\u6761\u5373\u53EF\uFF1B\u6539\u5B8C\u81EA\u67E5\uFF1Aagent-moss rules status"
|
|
12793
|
+
];
|
|
12794
|
+
L1_KEYS = ["dangerous_commands", "injection_detection", "sensitive_path_access"];
|
|
12795
|
+
}
|
|
12796
|
+
});
|
|
10780
12797
|
|
|
10781
12798
|
// src/cli.ts
|
|
10782
|
-
|
|
12799
|
+
init_esm_shims();
|
|
12800
|
+
init_server();
|
|
12801
|
+
init_package_json();
|
|
12802
|
+
import { fileURLToPath as fileURLToPath13 } from "url";
|
|
10783
12803
|
import { realpathSync } from "fs";
|
|
10784
12804
|
function printHelp() {
|
|
10785
12805
|
console.log(`AgentMoss security analysis engine (TypeScript v${version})
|
|
@@ -10802,9 +12822,29 @@ Install options:
|
|
|
10802
12822
|
--enable Enable service on boot (systemctl enable)
|
|
10803
12823
|
`);
|
|
10804
12824
|
}
|
|
12825
|
+
function buildAnalysisInputFromFile(data) {
|
|
12826
|
+
return {
|
|
12827
|
+
sessionId: data.session_id || "unknown",
|
|
12828
|
+
promptSession: data.prompt_session || "",
|
|
12829
|
+
actionHistory: (data.action_history || []).map((h) => ({
|
|
12830
|
+
actionType: h.action_type || h.name || "",
|
|
12831
|
+
actionDetail: h.action_detail || "",
|
|
12832
|
+
output: h.output
|
|
12833
|
+
})),
|
|
12834
|
+
aNext: {
|
|
12835
|
+
actionType: data.a_next?.action_type || "",
|
|
12836
|
+
actionDetail: data.a_next?.action_detail || ""
|
|
12837
|
+
},
|
|
12838
|
+
reason: data.reason || "",
|
|
12839
|
+
osType: data.os_type || (process.platform === "win32" ? "windows" : "linux"),
|
|
12840
|
+
cwd: data.cwd || process.cwd(),
|
|
12841
|
+
agentId: data.agent_id || "",
|
|
12842
|
+
scenarioId: data.scenario_id || ""
|
|
12843
|
+
};
|
|
12844
|
+
}
|
|
10805
12845
|
async function analyzeRawCommand(command, useJson = false) {
|
|
10806
|
-
const {
|
|
10807
|
-
const result = await
|
|
12846
|
+
const { analyze: analyze2 } = await Promise.resolve().then(() => (init_analyzer(), analyzer_exports));
|
|
12847
|
+
const result = await analyze2({
|
|
10808
12848
|
sessionId: "cli-command",
|
|
10809
12849
|
promptSession: "",
|
|
10810
12850
|
actionHistory: [],
|
|
@@ -10815,19 +12855,24 @@ async function analyzeRawCommand(command, useJson = false) {
|
|
|
10815
12855
|
agentId: ""
|
|
10816
12856
|
});
|
|
10817
12857
|
if (useJson) console.log(JSON.stringify(result, null, 2));
|
|
10818
|
-
else console.log(result.
|
|
10819
|
-
return result.
|
|
12858
|
+
else console.log(result.decision);
|
|
12859
|
+
return result.decision === "Allow" ? 0 : 1;
|
|
10820
12860
|
}
|
|
10821
12861
|
async function main() {
|
|
10822
12862
|
const args = process.argv.slice(2);
|
|
10823
12863
|
const command = args[0];
|
|
12864
|
+
if (command === "--version" || command === "-V") {
|
|
12865
|
+
console.log(`agent-moss ${version}`);
|
|
12866
|
+
process.exit(0);
|
|
12867
|
+
}
|
|
10824
12868
|
if (!command || command === "--help" || command === "-h") {
|
|
10825
12869
|
printHelp();
|
|
10826
12870
|
process.exit(command ? 0 : 1);
|
|
10827
12871
|
}
|
|
10828
12872
|
switch (command) {
|
|
10829
12873
|
case "init": {
|
|
10830
|
-
const
|
|
12874
|
+
const flagIdx = args.findIndex((a) => a === "--output" || a === "-o");
|
|
12875
|
+
const output = (flagIdx >= 0 ? args[flagIdx + 1] : void 0) || args.slice(1).find((a) => !a.startsWith("-")) || "input.json";
|
|
10831
12876
|
const fs7 = await import("fs");
|
|
10832
12877
|
const template = {
|
|
10833
12878
|
session_id: "my-session-001",
|
|
@@ -10850,7 +12895,6 @@ async function main() {
|
|
|
10850
12895
|
}
|
|
10851
12896
|
const fs7 = await import("fs");
|
|
10852
12897
|
const path6 = await import("path");
|
|
10853
|
-
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_coordinator(), coordinator_exports));
|
|
10854
12898
|
const useJson = args.includes("--json");
|
|
10855
12899
|
const osType = process.platform === "win32" ? "windows" : "linux";
|
|
10856
12900
|
const isFile = fs7.existsSync(target) && fs7.statSync(target).isFile();
|
|
@@ -10871,34 +12915,102 @@ async function main() {
|
|
|
10871
12915
|
}
|
|
10872
12916
|
const filePath = path6.resolve(target);
|
|
10873
12917
|
const data = JSON.parse(fs7.readFileSync(filePath, "utf-8"));
|
|
10874
|
-
const
|
|
10875
|
-
|
|
10876
|
-
|
|
10877
|
-
actionHistory: (data.action_history || []).map((h) => ({
|
|
10878
|
-
actionType: h.action_type || h.name || "",
|
|
10879
|
-
actionDetail: h.action_detail || ""
|
|
10880
|
-
})),
|
|
10881
|
-
aNext: {
|
|
10882
|
-
actionType: data.a_next?.action_type || "",
|
|
10883
|
-
actionDetail: data.a_next?.action_detail || ""
|
|
10884
|
-
},
|
|
10885
|
-
reason: data.reason || "",
|
|
10886
|
-
osType: data.os_type || (process.platform === "win32" ? "windows" : "linux"),
|
|
10887
|
-
cwd: data.cwd || process.cwd(),
|
|
10888
|
-
agentId: data.agent_id || ""
|
|
10889
|
-
});
|
|
12918
|
+
const { analyze: analyze2 } = await Promise.resolve().then(() => (init_analyzer(), analyzer_exports));
|
|
12919
|
+
const sessionId = String(data.session_id || "unknown");
|
|
12920
|
+
const result = await analyze2(buildAnalysisInputFromFile(data));
|
|
10890
12921
|
console.log(JSON.stringify(result, null, 2));
|
|
10891
|
-
|
|
12922
|
+
if (!args.includes("--no-file")) {
|
|
12923
|
+
const outDirIdx = args.findIndex((a) => a === "--output-dir" || a === "-o");
|
|
12924
|
+
const outDir = outDirIdx >= 0 && args[outDirIdx + 1] ? args[outDirIdx + 1] : process.cwd();
|
|
12925
|
+
fs7.mkdirSync(outDir, { recursive: true });
|
|
12926
|
+
const safeSession = sessionId.replace(/\//g, "_");
|
|
12927
|
+
if (result.decision === "Allow") {
|
|
12928
|
+
const policyPath = path6.join(outDir, `${safeSession}_policy.toml`);
|
|
12929
|
+
fs7.writeFileSync(policyPath, result.policy || "", "utf-8");
|
|
12930
|
+
console.log(`Policy written to ${policyPath}`);
|
|
12931
|
+
} else {
|
|
12932
|
+
const denyPath = path6.join(outDir, `${safeSession}_deny_reason.txt`);
|
|
12933
|
+
fs7.writeFileSync(denyPath, result.violated_policy || result.reason || "", "utf-8");
|
|
12934
|
+
console.log(`Deny reason written to ${denyPath}`);
|
|
12935
|
+
}
|
|
12936
|
+
}
|
|
12937
|
+
process.exit(result.decision === "Allow" ? 0 : 1);
|
|
12938
|
+
break;
|
|
12939
|
+
}
|
|
12940
|
+
case "rules": {
|
|
12941
|
+
const sub = args[1];
|
|
12942
|
+
const dirIdx = args.findIndex((a) => a === "--dir" || a === "-d");
|
|
12943
|
+
const dir = dirIdx >= 0 ? args[dirIdx + 1] : void 0;
|
|
12944
|
+
const { initRulesDir: initRulesDir2, rulesStatusText: rulesStatusText2 } = await Promise.resolve().then(() => (init_rules_tools(), rules_tools_exports));
|
|
12945
|
+
if (sub === "init") {
|
|
12946
|
+
if (!dir) {
|
|
12947
|
+
console.error("\u7528\u6CD5: agent-moss rules init --dir <\u76EE\u5F55>");
|
|
12948
|
+
process.exit(2);
|
|
12949
|
+
}
|
|
12950
|
+
const written = initRulesDir2(dir, { force: args.includes("--force"), withBuiltin: args.includes("--with-builtin") });
|
|
12951
|
+
for (const p of written) console.log(`\u5DF2\u751F\u6210: ${p}`);
|
|
12952
|
+
if (written.length === 0) console.log("\uFF08\u6A21\u677F\u5DF2\u5B58\u5728\uFF0C\u672A\u8986\u76D6\uFF1B\u8981\u91CD\u65B0\u751F\u6210\u52A0 --force\uFF09");
|
|
12953
|
+
console.log("");
|
|
12954
|
+
console.log("\u4E0B\u4E00\u6B65\uFF1A");
|
|
12955
|
+
console.log(` 1) \u8FFD\u52A0\u89C4\u5219\uFF1A\u7F16\u8F91 ${dir}/extra.json\uFF0C\u628A\u6761\u76EE\u52A0\u8FDB\u5BF9\u5E94\u7C7B\u522B\u7684\u6570\u7EC4\u91CC`);
|
|
12956
|
+
console.log(` 2) \u8986\u76D6\u5185\u7F6E\uFF1A\u7F16\u8F91 ${dir}/override.json.\u793A\u4F8B\uFF08\u5185\u7F6E L1 \u5168\u91CF\u62F7\u8D1D\uFF09\u2014\u2014 \u5220\u6389/\u6539\u6389\u4E0D\u8981\u7684\u90A3\u6761\uFF0C\u7136\u540E\u6539\u540D\u4E3A override.json`);
|
|
12957
|
+
console.log(` 3) \u8BA9 AgentMoss \u8BFB\u8FD9\u4E2A\u76EE\u5F55\uFF1Aexport AGENT_MOSS_EXTRA_RULES_DIR=${dir}\uFF08\u91CD\u542F\u8FDB\u7A0B\uFF09`);
|
|
12958
|
+
console.log(" 4) \u81EA\u67E5\uFF1Aagent-moss rules status");
|
|
12959
|
+
process.exit(0);
|
|
12960
|
+
}
|
|
12961
|
+
if (sub === "status") {
|
|
12962
|
+
if (dir) process.env.AGENT_MOSS_EXTRA_RULES_DIR = dir;
|
|
12963
|
+
console.log(rulesStatusText2());
|
|
12964
|
+
process.exit(0);
|
|
12965
|
+
}
|
|
12966
|
+
console.log("rules \u5B50\u547D\u4EE4\uFF1Astatus\uFF08\u81EA\u67E5\u89C4\u5219\u76EE\u5F55\uFF09/ init\uFF08\u751F\u6210\u6A21\u677F\uFF09");
|
|
12967
|
+
process.exit(1);
|
|
12968
|
+
break;
|
|
12969
|
+
}
|
|
12970
|
+
case "brain": {
|
|
12971
|
+
const sub = args[1];
|
|
12972
|
+
if (sub !== "contribute") {
|
|
12973
|
+
console.log("Brain \u5B50\u547D\u4EE4\uFF1Acontribute\uFF08\u8D21\u732E\u672C\u5730\u89C4\u5219/Skill \u4E3A MR\uFF09");
|
|
12974
|
+
process.exit(1);
|
|
12975
|
+
}
|
|
12976
|
+
let target;
|
|
12977
|
+
let forkUser;
|
|
12978
|
+
let branch;
|
|
12979
|
+
for (let i = 2; i < args.length; i++) {
|
|
12980
|
+
if ((args[i] === "--target" || args[i] === "-t") && args[i + 1]) target = args[++i];
|
|
12981
|
+
else if (args[i] === "--fork-user" && args[i + 1]) forkUser = args[++i];
|
|
12982
|
+
else if (args[i] === "--branch" && args[i + 1]) branch = args[++i];
|
|
12983
|
+
}
|
|
12984
|
+
const { contribute: contribute2 } = await Promise.resolve().then(() => (init_contributor(), contributor_exports));
|
|
12985
|
+
process.exit(await contribute2({ target, forkUser, branch }));
|
|
10892
12986
|
break;
|
|
10893
12987
|
}
|
|
10894
12988
|
case "server": {
|
|
10895
12989
|
let host = "127.0.0.1";
|
|
10896
12990
|
let port = 9090;
|
|
12991
|
+
let mode = "http";
|
|
12992
|
+
let socketPath = "/var/run/agent_moss/agent_moss.sock";
|
|
12993
|
+
let configPath = "";
|
|
10897
12994
|
for (let i = 1; i < args.length; i++) {
|
|
10898
12995
|
if (args[i] === "--host" && args[i + 1]) host = args[++i];
|
|
10899
12996
|
else if (args[i] === "--port" && args[i + 1]) port = parseInt(args[++i], 10);
|
|
12997
|
+
else if (args[i] === "--mode" && args[i + 1]) mode = args[++i];
|
|
12998
|
+
else if ((args[i] === "--socket" || args[i] === "-s") && args[i + 1]) socketPath = args[++i];
|
|
12999
|
+
else if ((args[i] === "--config" || args[i] === "-c") && args[i + 1]) configPath = args[++i];
|
|
10900
13000
|
}
|
|
13001
|
+
if (configPath) process.env.AGENT_MOSS_CONFIG_PATH = configPath;
|
|
10901
13002
|
if (port === 0) port = 9090;
|
|
13003
|
+
if (mode === "socket") {
|
|
13004
|
+
const { startUnixSocketServer: startUnixSocketServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
13005
|
+
const srv = await startUnixSocketServer2(socketPath);
|
|
13006
|
+
console.log(`Listening on unix socket ${srv.socketPath}`);
|
|
13007
|
+
await srv.serve();
|
|
13008
|
+
break;
|
|
13009
|
+
}
|
|
13010
|
+
if (mode !== "http") {
|
|
13011
|
+
console.error(`\u9519\u8BEF: \u672A\u77E5\u7684 --mode ${mode}\uFF08\u53EF\u9009 http / socket\uFF09`);
|
|
13012
|
+
process.exit(2);
|
|
13013
|
+
}
|
|
10902
13014
|
const server = await startServer(host, port);
|
|
10903
13015
|
console.log(`Listening on http://${host}:${server.port}`);
|
|
10904
13016
|
await server.serve();
|
|
@@ -10909,7 +13021,7 @@ async function main() {
|
|
|
10909
13021
|
const { execSync } = await import("child_process");
|
|
10910
13022
|
const fs7 = await import("fs");
|
|
10911
13023
|
const path6 = await import("path");
|
|
10912
|
-
const { fileURLToPath:
|
|
13024
|
+
const { fileURLToPath: fileURLToPath14 } = await import("url");
|
|
10913
13025
|
if (process.getuid?.() !== 0) {
|
|
10914
13026
|
try {
|
|
10915
13027
|
execSync("sudo -n true", { stdio: "ignore", timeout: 5e3 });
|
|
@@ -10924,18 +13036,18 @@ async function main() {
|
|
|
10924
13036
|
execSync(sudoArgs.join(" "), { stdio: "inherit" });
|
|
10925
13037
|
process.exit(0);
|
|
10926
13038
|
}
|
|
10927
|
-
const __filename7 =
|
|
10928
|
-
const
|
|
10929
|
-
let srcPath = path6.join(
|
|
13039
|
+
const __filename7 = fileURLToPath14(import.meta.url);
|
|
13040
|
+
const __dirname12 = path6.dirname(__filename7);
|
|
13041
|
+
let srcPath = path6.join(__dirname12, "data", "agent_moss.service");
|
|
10930
13042
|
if (!fs7.existsSync(srcPath)) {
|
|
10931
|
-
srcPath = path6.join(
|
|
13043
|
+
srcPath = path6.join(__dirname12, "..", "src", "data", "agent_moss.service");
|
|
10932
13044
|
}
|
|
10933
13045
|
if (!fs7.existsSync(srcPath)) {
|
|
10934
13046
|
console.error("\u9519\u8BEF\uFF1A\u627E\u4E0D\u5230 agent_moss.service \u6A21\u677F\u6587\u4EF6");
|
|
10935
13047
|
process.exit(1);
|
|
10936
13048
|
}
|
|
10937
13049
|
const which = execSync("which agent-moss 2>/dev/null || true", { encoding: "utf-8" }).trim();
|
|
10938
|
-
const binPath = which || `${process.execPath} ${path6.join(
|
|
13050
|
+
const binPath = which || `${process.execPath} ${path6.join(__dirname12, "cli.js")}`;
|
|
10939
13051
|
let content = fs7.readFileSync(srcPath, "utf-8");
|
|
10940
13052
|
content = content.replace("__AGENT_MOSS_BIN__", binPath);
|
|
10941
13053
|
const dst = "/etc/systemd/system/agent-moss.service";
|
|
@@ -10975,17 +13087,19 @@ async function main() {
|
|
|
10975
13087
|
}
|
|
10976
13088
|
} catch {
|
|
10977
13089
|
}
|
|
13090
|
+
const expectInstance = process.env.AGENT_MOSS_INSTANCE || "";
|
|
10978
13091
|
let foundPort = 0;
|
|
10979
13092
|
let version2 = "";
|
|
13093
|
+
const otherInstances = [];
|
|
10980
13094
|
for (let port = 9090; port <= 9095; port++) {
|
|
10981
13095
|
try {
|
|
10982
|
-
const data = await new Promise((
|
|
13096
|
+
const data = await new Promise((resolve11, reject) => {
|
|
10983
13097
|
const req = http.get(`http://127.0.0.1:${port}/api/v1/health`, { timeout: 2e3 }, (res) => {
|
|
10984
13098
|
let body = "";
|
|
10985
13099
|
res.on("data", (chunk) => body += chunk);
|
|
10986
13100
|
res.on("end", () => {
|
|
10987
13101
|
try {
|
|
10988
|
-
|
|
13102
|
+
resolve11(JSON.parse(body));
|
|
10989
13103
|
} catch {
|
|
10990
13104
|
reject();
|
|
10991
13105
|
}
|
|
@@ -10994,9 +13108,13 @@ async function main() {
|
|
|
10994
13108
|
req.on("error", reject);
|
|
10995
13109
|
});
|
|
10996
13110
|
if (data.status === "healthy") {
|
|
10997
|
-
|
|
10998
|
-
|
|
10999
|
-
|
|
13111
|
+
const inst = data.instance || "";
|
|
13112
|
+
if (inst === expectInstance) {
|
|
13113
|
+
foundPort = port;
|
|
13114
|
+
version2 = data.version || "";
|
|
13115
|
+
break;
|
|
13116
|
+
}
|
|
13117
|
+
otherInstances.push({ port, instance: inst });
|
|
11000
13118
|
}
|
|
11001
13119
|
} catch {
|
|
11002
13120
|
}
|
|
@@ -11013,13 +13131,13 @@ async function main() {
|
|
|
11013
13131
|
console.log(` Systemd: \u672A\u6CE8\u518C\uFF08\u8FD0\u884C agent-moss install --enable \u5B89\u88C5\uFF09`);
|
|
11014
13132
|
}
|
|
11015
13133
|
try {
|
|
11016
|
-
const data = await new Promise((
|
|
13134
|
+
const data = await new Promise((resolve11, reject) => {
|
|
11017
13135
|
const req = http.get(`http://127.0.0.1:${foundPort}/console/api/config`, { timeout: 3e3 }, (res) => {
|
|
11018
13136
|
let body = "";
|
|
11019
13137
|
res.on("data", (chunk) => body += chunk);
|
|
11020
13138
|
res.on("end", () => {
|
|
11021
13139
|
try {
|
|
11022
|
-
|
|
13140
|
+
resolve11(JSON.parse(body));
|
|
11023
13141
|
} catch {
|
|
11024
13142
|
reject();
|
|
11025
13143
|
}
|
|
@@ -11043,6 +13161,14 @@ async function main() {
|
|
|
11043
13161
|
console.log(" systemctl restart agent-moss \u91CD\u542F");
|
|
11044
13162
|
console.log(" systemctl status agent-moss \u72B6\u6001");
|
|
11045
13163
|
console.log(" journalctl -u agent_moss -f \u65E5\u5FD7");
|
|
13164
|
+
} else if (otherInstances.length > 0) {
|
|
13165
|
+
console.log(`\u274C AgentMoss \u670D\u52A1\u672A\u8FD0\u884C\uFF08instance=${expectInstance || "(\u7A7A)"}\uFF09`);
|
|
13166
|
+
console.log(` Systemd: ${systemdInfo ? "active" : "inactive"}`);
|
|
13167
|
+
const detail = otherInstances.map((o) => `\u7AEF\u53E3 ${o.port} (instance=${o.instance || "(\u7A7A)"})`).join("; ");
|
|
13168
|
+
console.log(` \u26A0\uFE0F \u68C0\u6D4B\u5230\u5176\u4ED6 agent \u7684 agentmoss \u5B9E\u4F8B: ${detail}`);
|
|
13169
|
+
console.log(` \u8FD9\u4E9B\u4E0D\u5C5E\u4E8E\u672C\u5B9E\u4F8B\uFF08instance=${expectInstance || "(\u7A7A)"}\uFF09\uFF0C\u672A\u8BA1\u5165\u8FD0\u884C\u72B6\u6001\u3002`);
|
|
13170
|
+
console.log(` \u542F\u52A8\u672C\u5B9E\u4F8B: AGENT_MOSS_INSTANCE=${expectInstance || "xiaoo"} agent-moss server`);
|
|
13171
|
+
process.exit(1);
|
|
11046
13172
|
} else {
|
|
11047
13173
|
console.log(`\u274C AgentMoss \u670D\u52A1\u672A\u8FD0\u884C`);
|
|
11048
13174
|
if (systemdInfo) {
|
|
@@ -11066,9 +13192,9 @@ async function main() {
|
|
|
11066
13192
|
var isMain = (() => {
|
|
11067
13193
|
try {
|
|
11068
13194
|
if (!process.argv[1]) return false;
|
|
11069
|
-
const thisFile = realpathSync(
|
|
13195
|
+
const thisFile = realpathSync(fileURLToPath13(import.meta.url));
|
|
11070
13196
|
const argv1 = realpathSync(
|
|
11071
|
-
process.argv[1].startsWith("file://") ?
|
|
13197
|
+
process.argv[1].startsWith("file://") ? fileURLToPath13(process.argv[1]) : process.argv[1]
|
|
11072
13198
|
);
|
|
11073
13199
|
return thisFile === argv1;
|
|
11074
13200
|
} catch {
|
|
@@ -11082,7 +13208,8 @@ if (isMain) {
|
|
|
11082
13208
|
});
|
|
11083
13209
|
}
|
|
11084
13210
|
export {
|
|
11085
|
-
analyzeRawCommand
|
|
13211
|
+
analyzeRawCommand,
|
|
13212
|
+
buildAnalysisInputFromFile
|
|
11086
13213
|
};
|
|
11087
13214
|
//! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
|
|
11088
13215
|
//! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
|