@kenhkl/agent-moss 0.16.9 → 0.16.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +19 -1
- package/dist/cli.js +2438 -459
- 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 +785 -185
- package/dist/server.d.ts +18 -1
- package/dist/server.js +1620 -271
- 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";
|
|
@@ -407,71 +500,60 @@ function parseDirEnv(envName) {
|
|
|
407
500
|
if (!raw) return [];
|
|
408
501
|
return raw.split(/[,;]/).map((s) => s.trim()).filter(Boolean).map((s) => resolve(s));
|
|
409
502
|
}
|
|
410
|
-
function
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
warnedOverride.add(path6);
|
|
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;
|
|
503
|
+
function rulesDirs() {
|
|
504
|
+
const dirs = [];
|
|
505
|
+
for (const env of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env));
|
|
506
|
+
dirs.push(resolve(dataRootDir(), "rules"));
|
|
507
|
+
return [...new Set(dirs)];
|
|
438
508
|
}
|
|
439
509
|
function findRulesFile(name) {
|
|
440
|
-
for (const dir of overrideRulesDirs()) {
|
|
441
|
-
const p = resolve(dir, name);
|
|
442
|
-
if (existsSync(p)) return p;
|
|
443
|
-
}
|
|
444
510
|
for (const dir of RULES_DIR_CANDS) {
|
|
445
511
|
const p = resolve(dir, name);
|
|
446
512
|
if (existsSync(p)) return p;
|
|
447
513
|
}
|
|
448
514
|
return null;
|
|
449
515
|
}
|
|
450
|
-
function
|
|
451
|
-
return
|
|
516
|
+
function resolveRulesFile(name) {
|
|
517
|
+
return findRulesFile(name);
|
|
452
518
|
}
|
|
453
519
|
function loadJson(name) {
|
|
454
520
|
const p = findRulesFile(name);
|
|
455
521
|
if (!p) return {};
|
|
456
|
-
const isOverride = isOverridePath(name, p);
|
|
457
|
-
let data;
|
|
458
522
|
try {
|
|
459
|
-
|
|
523
|
+
return JSON.parse(readFileSync2(p, "utf-8")) || {};
|
|
460
524
|
} catch {
|
|
461
|
-
if (isOverride) warnOverride(name, p, {});
|
|
462
525
|
return {};
|
|
463
526
|
}
|
|
464
|
-
if (isOverride) warnOverride(name, p, data);
|
|
465
|
-
return data;
|
|
466
527
|
}
|
|
467
528
|
function resolveScenarioRulesFile(scenarioId) {
|
|
468
529
|
const rel = join2("scenarios", scenarioId, "rules.json");
|
|
469
|
-
for (const dir of
|
|
530
|
+
for (const dir of rulesDirs()) {
|
|
470
531
|
const p = resolve(dir, rel);
|
|
471
|
-
if (existsSync(p))
|
|
532
|
+
if (!existsSync(p)) continue;
|
|
533
|
+
let reason = "";
|
|
534
|
+
try {
|
|
535
|
+
reason = invalidScenarioRulesReason(JSON.parse(readFileSync2(p, "utf-8")) || {});
|
|
536
|
+
} catch (err) {
|
|
537
|
+
reason = `\u8BFB\u53D6/\u89E3\u6790\u5931\u8D25: ${String(err)}`;
|
|
538
|
+
}
|
|
539
|
+
if (reason) {
|
|
540
|
+
console.error(
|
|
541
|
+
`[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}
|
|
542
|
+
\u539F\u56E0: ${reason}`
|
|
543
|
+
);
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
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`);
|
|
547
|
+
return p;
|
|
472
548
|
}
|
|
473
549
|
return null;
|
|
474
550
|
}
|
|
551
|
+
function invalidScenarioRulesReason(data) {
|
|
552
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
553
|
+
return `\u9876\u5C42\u5FC5\u987B\u662F JSON \u5BF9\u8C61\uFF0C\u5B9E\u9645\u662F ${Array.isArray(data) ? "array" : typeof data}`;
|
|
554
|
+
}
|
|
555
|
+
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");
|
|
556
|
+
}
|
|
475
557
|
function loadL1() {
|
|
476
558
|
return loadJson("l1_rules.json");
|
|
477
559
|
}
|
|
@@ -479,10 +561,40 @@ function loadL2() {
|
|
|
479
561
|
return loadJson("l2_rules.json");
|
|
480
562
|
}
|
|
481
563
|
function loadL3() {
|
|
482
|
-
|
|
564
|
+
const data = { ...loadJson("l3_rules.json") };
|
|
565
|
+
try {
|
|
566
|
+
const ov = loadOverrideRules().skills;
|
|
567
|
+
const ex = loadExtraRules().skills || {};
|
|
568
|
+
if (ov && typeof ov === "object") {
|
|
569
|
+
for (const [cat, skills] of Object.entries(ov)) data[cat] = { ...skills };
|
|
570
|
+
}
|
|
571
|
+
if (ex && typeof ex === "object") {
|
|
572
|
+
for (const [cat, skills] of Object.entries(ex)) data[cat] = { ...data[cat] || {}, ...skills };
|
|
573
|
+
}
|
|
574
|
+
} catch {
|
|
575
|
+
}
|
|
576
|
+
return data;
|
|
577
|
+
}
|
|
578
|
+
function loadMCRaw() {
|
|
579
|
+
const mc = { ...loadJson("mc_rules.json") };
|
|
580
|
+
let rules = [...mc.rules || []];
|
|
581
|
+
try {
|
|
582
|
+
const ov = loadOverrideRules().ltl_rules;
|
|
583
|
+
const ex = loadExtraRules().ltl_rules || [];
|
|
584
|
+
if (Array.isArray(ov)) rules = [...ov];
|
|
585
|
+
if (Array.isArray(ex) && ex.length > 0) {
|
|
586
|
+
const byId = /* @__PURE__ */ new Map();
|
|
587
|
+
for (const r of rules) if (r && r.id) byId.set(r.id, r);
|
|
588
|
+
for (const r of ex) if (r && r.id) byId.set(r.id, r);
|
|
589
|
+
rules = [...byId.values()];
|
|
590
|
+
}
|
|
591
|
+
} catch {
|
|
592
|
+
}
|
|
593
|
+
mc.rules = rules;
|
|
594
|
+
return mc;
|
|
483
595
|
}
|
|
484
596
|
function loadMC() {
|
|
485
|
-
const mc =
|
|
597
|
+
const mc = loadMCRaw();
|
|
486
598
|
const rules = mc.rules || [];
|
|
487
599
|
try {
|
|
488
600
|
const rt = runtimeConfigBridge.current;
|
|
@@ -502,144 +614,284 @@ function loadMC() {
|
|
|
502
614
|
return mc;
|
|
503
615
|
}
|
|
504
616
|
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;
|
|
617
|
+
const data = { ...loadJson("tool_registry.json") };
|
|
618
|
+
let tools = { ...data.tools || {} };
|
|
513
619
|
try {
|
|
514
|
-
|
|
620
|
+
const ov = loadOverrideRules().tools;
|
|
621
|
+
const ex = loadExtraRules().tools || {};
|
|
622
|
+
if (ov && typeof ov === "object") tools = { ...ov };
|
|
623
|
+
if (ex && typeof ex === "object") tools = { ...tools, ...ex };
|
|
515
624
|
} catch {
|
|
516
|
-
return out;
|
|
517
625
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
626
|
+
data.tools = tools;
|
|
627
|
+
return data;
|
|
628
|
+
}
|
|
629
|
+
function scanStructEntry(cat, value, path6) {
|
|
630
|
+
if (cat === "skills") {
|
|
631
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
632
|
+
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`);
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
const out = {};
|
|
636
|
+
for (const [skCat, skills] of Object.entries(value)) {
|
|
637
|
+
if (!skills || typeof skills !== "object" || Array.isArray(skills)) {
|
|
638
|
+
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`);
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
const keep = {};
|
|
642
|
+
for (const [name, kws] of Object.entries(skills)) {
|
|
643
|
+
if (Array.isArray(kws)) keep[name] = kws;
|
|
644
|
+
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`);
|
|
645
|
+
}
|
|
646
|
+
if (Object.keys(keep).length > 0) out[skCat] = keep;
|
|
525
647
|
}
|
|
526
|
-
|
|
527
|
-
else if (name.toLowerCase().endsWith(".json")) out.push(p);
|
|
648
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
528
649
|
}
|
|
529
|
-
|
|
650
|
+
if (cat === "ltl_rules") {
|
|
651
|
+
if (!Array.isArray(value)) {
|
|
652
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 ltl_rules \u5E94\u4E3A\u5BF9\u8C61\u6570\u7EC4 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u3002`);
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
const keep = value.filter((r) => r && typeof r === "object" && r.id);
|
|
656
|
+
if (keep.length !== value.length) {
|
|
657
|
+
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`);
|
|
658
|
+
}
|
|
659
|
+
return keep.length > 0 ? keep : null;
|
|
660
|
+
}
|
|
661
|
+
if (cat === "tools") {
|
|
662
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
663
|
+
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`);
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
const keep = {};
|
|
667
|
+
for (const [k, v] of Object.entries(value)) {
|
|
668
|
+
if (v && typeof v === "object" && !Array.isArray(v)) keep[k] = v;
|
|
669
|
+
else console.warn(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 tools.${k} \u4E0D\u662F\u5BF9\u8C61 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u6761\u3002`);
|
|
670
|
+
}
|
|
671
|
+
return Object.keys(keep).length > 0 ? keep : null;
|
|
672
|
+
}
|
|
673
|
+
return null;
|
|
530
674
|
}
|
|
531
|
-
function
|
|
532
|
-
const
|
|
533
|
-
const
|
|
534
|
-
|
|
675
|
+
function scanRulesFile(path6, kind) {
|
|
676
|
+
const out = {};
|
|
677
|
+
const label = kind === "override" ? "\u8986\u76D6" : "\u8FFD\u52A0";
|
|
678
|
+
let parsed;
|
|
679
|
+
try {
|
|
680
|
+
parsed = JSON.parse(readFileSync2(path6, "utf-8"));
|
|
681
|
+
} catch (e) {
|
|
682
|
+
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}`);
|
|
683
|
+
return out;
|
|
684
|
+
}
|
|
535
685
|
const classifyFlat = (r) => {
|
|
536
686
|
for (const [keys, cat] of EXTRA_FLAT_FIELD) {
|
|
537
687
|
if (r && typeof r === "object" && keys.every((k) => r[k])) return cat;
|
|
538
688
|
}
|
|
539
689
|
return null;
|
|
540
690
|
};
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
691
|
+
let pairs = [];
|
|
692
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
693
|
+
pairs = Object.entries(parsed);
|
|
694
|
+
} else if (Array.isArray(parsed)) {
|
|
695
|
+
let flatBad = 0;
|
|
696
|
+
for (const r of parsed) {
|
|
697
|
+
const cat = classifyFlat(r);
|
|
698
|
+
if (cat === null) {
|
|
699
|
+
flatBad += 1;
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
pairs.push([cat, [r]]);
|
|
703
|
+
}
|
|
704
|
+
if (flatBad > 0) {
|
|
705
|
+
console.warn(
|
|
706
|
+
`[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`
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
} else {
|
|
710
|
+
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`);
|
|
711
|
+
return out;
|
|
712
|
+
}
|
|
713
|
+
for (const [cat, rules] of pairs) {
|
|
714
|
+
if (String(cat).startsWith("_")) continue;
|
|
715
|
+
if (STRUCT_KEYS.includes(cat)) {
|
|
716
|
+
const got = scanStructEntry(cat, rules, path6);
|
|
717
|
+
if (got !== null) out[cat] = got;
|
|
547
718
|
continue;
|
|
548
719
|
}
|
|
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 {
|
|
720
|
+
const required = EXTRA_CATEGORIES[cat];
|
|
721
|
+
if (!required) {
|
|
568
722
|
console.warn(
|
|
569
|
-
`[AgentMoss] \
|
|
723
|
+
`[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`
|
|
724
|
+
);
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
if (rules && typeof rules === "object" && !Array.isArray(rules)) {
|
|
728
|
+
console.error(
|
|
729
|
+
`[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
|
|
730
|
+
\u6B63\u786E\u5199\u6CD5: {"${cat}": [{"pattern": "...", "risk_level": "high", ...}]}
|
|
731
|
+
\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
732
|
);
|
|
571
733
|
continue;
|
|
572
734
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
735
|
+
if (!Array.isArray(rules)) {
|
|
736
|
+
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`);
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
const items = [];
|
|
740
|
+
for (const r of rules) {
|
|
741
|
+
if (!r || typeof r !== "object" || !required.every((k) => r[k])) {
|
|
577
742
|
console.warn(
|
|
578
|
-
`[AgentMoss] \
|
|
743
|
+
`[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
744
|
);
|
|
580
745
|
continue;
|
|
581
746
|
}
|
|
582
|
-
|
|
583
|
-
|
|
747
|
+
items.push(r);
|
|
748
|
+
}
|
|
749
|
+
if (items.length > 0) out[cat] = (out[cat] || []).concat(items);
|
|
750
|
+
}
|
|
751
|
+
if (Object.keys(out).length > 0) {
|
|
752
|
+
console.info(
|
|
753
|
+
`[AgentMoss] \u5DF2\u52A0\u8F7D${label}\u89C4\u5219\u6587\u4EF6 ${path6}\uFF1A` + Object.entries(out).map(([k, v]) => `${k}\xD7${v.length}`).join(", ")
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
return out;
|
|
757
|
+
}
|
|
758
|
+
function scanRuleDirs() {
|
|
759
|
+
const override = {};
|
|
760
|
+
const extra = {};
|
|
761
|
+
const loaded = [];
|
|
762
|
+
const ignored = [];
|
|
763
|
+
for (const d of rulesDirs()) {
|
|
764
|
+
if (!existsSync(d)) continue;
|
|
765
|
+
let entries = [];
|
|
766
|
+
try {
|
|
767
|
+
entries = readdirSync(d).sort();
|
|
768
|
+
} catch {
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
for (const name of entries) {
|
|
772
|
+
const f = resolve(d, name);
|
|
773
|
+
try {
|
|
774
|
+
if (statSync(f).isDirectory()) continue;
|
|
775
|
+
} catch {
|
|
584
776
|
continue;
|
|
585
777
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
)
|
|
591
|
-
|
|
778
|
+
if (name === OVERRIDE_FILENAME) {
|
|
779
|
+
const got = scanRulesFile(f, "override");
|
|
780
|
+
if (Object.keys(got).length > 0) loaded.push(f);
|
|
781
|
+
for (const [cat, items] of Object.entries(got)) {
|
|
782
|
+
if (Array.isArray(items)) {
|
|
783
|
+
override[cat] = (override[cat] || []).concat(items);
|
|
784
|
+
} else {
|
|
785
|
+
override[cat] = { ...override[cat] || {}, ...items };
|
|
786
|
+
}
|
|
592
787
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
);
|
|
599
|
-
|
|
788
|
+
} else if (name === EXTRA_FILENAME) {
|
|
789
|
+
const got = scanRulesFile(f, "extra");
|
|
790
|
+
if (Object.values(got).some((v) => v.length > 0)) loaded.push(f);
|
|
791
|
+
for (const [cat, items] of Object.entries(got)) {
|
|
792
|
+
if (Array.isArray(items)) {
|
|
793
|
+
extra[cat] = (extra[cat] || []).concat(items);
|
|
794
|
+
} else {
|
|
795
|
+
extra[cat] = { ...extra[cat] || {}, ...items };
|
|
600
796
|
}
|
|
601
|
-
seenIds.set(rid, p);
|
|
602
797
|
}
|
|
603
|
-
|
|
604
|
-
|
|
798
|
+
} else if (name.toLowerCase().endsWith(".json")) {
|
|
799
|
+
ignored.push(f);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
if (Object.keys(override).length > 0 && identicalToBuiltin(override)) {
|
|
804
|
+
const key = "override-identical::" + Object.entries(override).map(([c, v]) => `${c}=${v.length}`).sort().join("|");
|
|
805
|
+
if (!warnedIgnored.has(key)) {
|
|
806
|
+
warnedIgnored.add(key);
|
|
807
|
+
console.warn(
|
|
808
|
+
`[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
|
|
809
|
+
\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
|
|
810
|
+
\u53EA\u60F3\u8FFD\u52A0\u89C4\u5219\uFF1A\u5220\u6389 ${OVERRIDE_FILENAME}\uFF0C\u6539\u7528 ${EXTRA_FILENAME}\u3002`
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
if (Object.keys(override).length > 0) {
|
|
815
|
+
const key = "override-summary::" + Object.entries(override).map(([c, v]) => `${c}=${v.length}`).sort().join("|");
|
|
816
|
+
if (!warnedIgnored.has(key)) {
|
|
817
|
+
warnedIgnored.add(key);
|
|
818
|
+
const l1 = loadL1();
|
|
819
|
+
const l2 = loadL2();
|
|
820
|
+
const builtinCounts = {
|
|
821
|
+
dangerous_commands: (l1.dangerous_commands || []).length,
|
|
822
|
+
injection_detection: (l1.injection_detection || []).length,
|
|
823
|
+
sensitive_path_access: (l1.sensitive_path_access || []).length,
|
|
824
|
+
intent_consistency: (l2.intent_consistency || []).length,
|
|
825
|
+
password_modify_consent: (l2.password_modify_consent || []).length,
|
|
826
|
+
user_deletion_consent: (l2.user_deletion_consent || []).length
|
|
827
|
+
};
|
|
828
|
+
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`);
|
|
829
|
+
for (const [cat, items] of Object.entries(override).sort()) {
|
|
830
|
+
console.warn(` \u7C7B\u522B ${cat}\uFF1A\u5185\u7F6E ${builtinCounts[cat] ?? 0} \u6761 \u2192 \u4F60\u7684 ${items.length} \u6761`);
|
|
605
831
|
}
|
|
832
|
+
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
833
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
834
|
+
}
|
|
835
|
+
if (ignored.length > 0) {
|
|
836
|
+
const key = "ignored::" + ignored.join("|");
|
|
837
|
+
if (!warnedIgnored.has(key)) {
|
|
838
|
+
warnedIgnored.add(key);
|
|
839
|
+
console.warn(
|
|
840
|
+
`[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(", ")}
|
|
841
|
+
\u8981\u8986\u76D6\u5185\u7F6E\u89C4\u5219 \u2192 \u5185\u5BB9\u653E\u8FDB ${OVERRIDE_FILENAME}\uFF1B\u8981\u8FFD\u52A0 \u2192 \u653E\u8FDB ${EXTRA_FILENAME}\u3002`
|
|
842
|
+
);
|
|
610
843
|
}
|
|
611
844
|
}
|
|
612
|
-
return {
|
|
845
|
+
return { override, extra, loaded, ignored };
|
|
613
846
|
}
|
|
614
|
-
function
|
|
615
|
-
const
|
|
616
|
-
const
|
|
617
|
-
|
|
847
|
+
function rulesCached() {
|
|
848
|
+
const dirs = rulesDirs();
|
|
849
|
+
const sig = RULES_DIR_ENVS.map((e) => `${e}=${process.env[e] || ""}`).join("|") + "|" + dirs.join("|");
|
|
850
|
+
const files = [];
|
|
618
851
|
for (const d of dirs) {
|
|
619
|
-
if (existsSync(d))
|
|
620
|
-
|
|
852
|
+
if (!existsSync(d)) continue;
|
|
853
|
+
try {
|
|
854
|
+
for (const name of readdirSync(d).sort()) {
|
|
855
|
+
if (name.toLowerCase().endsWith(".json")) files.push(resolve(d, name));
|
|
856
|
+
}
|
|
857
|
+
} catch {
|
|
858
|
+
}
|
|
621
859
|
}
|
|
622
|
-
|
|
623
|
-
const fp = files.map((p) => {
|
|
860
|
+
const fp = JSON.stringify(files.map((p) => {
|
|
624
861
|
try {
|
|
625
862
|
const st = statSync(p);
|
|
626
863
|
return [p, st.mtimeMs, st.size];
|
|
627
864
|
} catch {
|
|
628
865
|
return [p, -1, -1];
|
|
629
866
|
}
|
|
630
|
-
});
|
|
631
|
-
if (
|
|
632
|
-
|
|
867
|
+
}));
|
|
868
|
+
if (_RULES_CACHE && _RULES_CACHE.sig === sig && _RULES_CACHE.fp === fp) return _RULES_CACHE;
|
|
869
|
+
_RULES_CACHE = { sig, fp, ...scanRuleDirs() };
|
|
870
|
+
return _RULES_CACHE;
|
|
871
|
+
}
|
|
872
|
+
function identicalToBuiltin(override) {
|
|
873
|
+
const l1 = loadL1();
|
|
874
|
+
const l2 = loadL2();
|
|
875
|
+
for (const [cat, items] of Object.entries(override)) {
|
|
876
|
+
const builtin = l1[cat] ?? l2[cat];
|
|
877
|
+
if (!Array.isArray(builtin) || builtin.length !== items.length) return false;
|
|
878
|
+
if (JSON.stringify(builtin) !== JSON.stringify(items)) return false;
|
|
633
879
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
880
|
+
return Object.keys(override).length > 0;
|
|
881
|
+
}
|
|
882
|
+
function loadOverrideRules() {
|
|
883
|
+
return rulesCached().override;
|
|
884
|
+
}
|
|
885
|
+
function loadExtraRules() {
|
|
886
|
+
return rulesCached().extra;
|
|
637
887
|
}
|
|
638
|
-
function
|
|
639
|
-
|
|
640
|
-
return extraCache ? [...extraCache.loaded] : [];
|
|
888
|
+
function ruleFilesLoaded() {
|
|
889
|
+
return rulesCached().loaded;
|
|
641
890
|
}
|
|
642
|
-
|
|
891
|
+
function ignoredRuleFiles() {
|
|
892
|
+
return rulesCached().ignored;
|
|
893
|
+
}
|
|
894
|
+
var __filename2, __dirname2, OVERRIDE_FILENAME, EXTRA_FILENAME, RULES_DIR_ENVS, RULES_DIR_CANDS, SCENARIO_LIST_KEYS, EXTRA_CATEGORIES, STRUCT_KEYS, EXTRA_FLAT_FIELD, _RULES_CACHE, warnedIgnored;
|
|
643
895
|
var init_rules_loader = __esm({
|
|
644
896
|
"src/infra/rules-loader.ts"() {
|
|
645
897
|
"use strict";
|
|
@@ -648,20 +900,13 @@ var init_rules_loader = __esm({
|
|
|
648
900
|
init_runtime_config_bridge();
|
|
649
901
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
650
902
|
__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();
|
|
903
|
+
OVERRIDE_FILENAME = "override.json";
|
|
904
|
+
EXTRA_FILENAME = "extra.json";
|
|
905
|
+
RULES_DIR_ENVS = ["AGENT_MOSS_EXTRA_RULES_DIR", "AGENT_MOSS_OVERRIDE_RULES_DIR"];
|
|
659
906
|
RULES_DIR_CANDS = (() => {
|
|
660
907
|
const cands = [
|
|
661
|
-
//
|
|
662
|
-
//
|
|
663
|
-
// mc_rules.json 放到 <data_root>/rules/ 即可覆盖内置默认,无需改源码。
|
|
664
|
-
resolve(dataRootDir(), "rules"),
|
|
908
|
+
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
909
|
+
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
665
910
|
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
666
911
|
resolve(__dirname2, "../rules"),
|
|
667
912
|
resolve(__dirname2, "rules"),
|
|
@@ -674,6 +919,7 @@ var init_rules_loader = __esm({
|
|
|
674
919
|
];
|
|
675
920
|
return cands.filter((c) => existsSync(c));
|
|
676
921
|
})();
|
|
922
|
+
SCENARIO_LIST_KEYS = ["extra_command_patterns", "extra_injection_keywords", "extra_sensitive_paths"];
|
|
677
923
|
EXTRA_CATEGORIES = {
|
|
678
924
|
dangerous_commands: ["pattern"],
|
|
679
925
|
injection_detection: ["keyword", "lang"],
|
|
@@ -682,6 +928,7 @@ var init_rules_loader = __esm({
|
|
|
682
928
|
password_modify_consent: ["pattern"],
|
|
683
929
|
user_deletion_consent: ["pattern"]
|
|
684
930
|
};
|
|
931
|
+
STRUCT_KEYS = ["skills", "ltl_rules", "tools"];
|
|
685
932
|
EXTRA_FLAT_FIELD = [
|
|
686
933
|
[["path"], "sensitive_path_access"],
|
|
687
934
|
[["keyword", "lang"], "injection_detection"],
|
|
@@ -689,7 +936,8 @@ var init_rules_loader = __esm({
|
|
|
689
936
|
[["pattern", "risk_level"], "dangerous_commands"],
|
|
690
937
|
[["pattern", "risk_type"], "dangerous_commands"]
|
|
691
938
|
];
|
|
692
|
-
|
|
939
|
+
_RULES_CACHE = null;
|
|
940
|
+
warnedIgnored = /* @__PURE__ */ new Set();
|
|
693
941
|
}
|
|
694
942
|
});
|
|
695
943
|
|
|
@@ -2047,18 +2295,34 @@ function _mergeUserCustom(primary, runtime, layers, key) {
|
|
|
2047
2295
|
const extra = _userCustomRules(runtime, layers, key);
|
|
2048
2296
|
return extra.length > 0 ? [...primary, ...extra] : [...primary];
|
|
2049
2297
|
}
|
|
2050
|
-
function
|
|
2298
|
+
function _dedup(rules, category) {
|
|
2299
|
+
const keys = _DEDUP_KEYS[category];
|
|
2300
|
+
if (!keys) return rules;
|
|
2301
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2302
|
+
const out = [];
|
|
2303
|
+
for (const r of rules) {
|
|
2304
|
+
const k = keys.map((x) => String(r?.[x] ?? "")).join("\0");
|
|
2305
|
+
if (seen.has(k)) continue;
|
|
2306
|
+
seen.add(k);
|
|
2307
|
+
out.push(r);
|
|
2308
|
+
}
|
|
2309
|
+
return out;
|
|
2310
|
+
}
|
|
2311
|
+
function _mergeRulesFromDirs(primary, category) {
|
|
2312
|
+
let base = [...primary];
|
|
2051
2313
|
try {
|
|
2314
|
+
const override = loadOverrideRules()[category];
|
|
2315
|
+
if (override !== void 0 && override !== null) base = [...override];
|
|
2052
2316
|
const extra = loadExtraRules()[category] || [];
|
|
2053
|
-
return
|
|
2317
|
+
return _dedup([...base, ...extra], category);
|
|
2054
2318
|
} catch {
|
|
2055
|
-
return
|
|
2319
|
+
return base;
|
|
2056
2320
|
}
|
|
2057
2321
|
}
|
|
2058
2322
|
function getEnabledL1CommandPatterns(runtime, osType) {
|
|
2059
2323
|
const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
|
|
2060
2324
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2061
|
-
rules =
|
|
2325
|
+
rules = _mergeRulesFromDirs(rules, "dangerous_commands");
|
|
2062
2326
|
rules = _mergeUserCustom(rules, runtime, "L1_rules", "pattern");
|
|
2063
2327
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2064
2328
|
return rules.map((r) => ({
|
|
@@ -2074,7 +2338,7 @@ function getEnabledL2SensitivePaths(runtime, osType) {
|
|
|
2074
2338
|
cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
2075
2339
|
}
|
|
2076
2340
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2077
|
-
rules =
|
|
2341
|
+
rules = _mergeRulesFromDirs(rules, "sensitive_path_access");
|
|
2078
2342
|
rules = _mergeUserCustom(rules, runtime, ["L1_rules", "L2_rules"], "path");
|
|
2079
2343
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2080
2344
|
return rules.map((r) => {
|
|
@@ -2459,25 +2723,11 @@ function generateSourceDefaults() {
|
|
|
2459
2723
|
};
|
|
2460
2724
|
}
|
|
2461
2725
|
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
2726
|
let mc = {};
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
mc = JSON.parse(readFileSync4(p, "utf-8")) || {};
|
|
2476
|
-
} catch {
|
|
2477
|
-
mc = {};
|
|
2478
|
-
}
|
|
2479
|
-
break;
|
|
2480
|
-
}
|
|
2727
|
+
try {
|
|
2728
|
+
mc = loadMCRaw();
|
|
2729
|
+
} catch {
|
|
2730
|
+
mc = {};
|
|
2481
2731
|
}
|
|
2482
2732
|
const rules = mc.rules || [];
|
|
2483
2733
|
return {
|
|
@@ -2706,7 +2956,7 @@ function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
|
|
|
2706
2956
|
function getEnabledL1InjectionKeywords(runtime, osType) {
|
|
2707
2957
|
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
2708
2958
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2709
|
-
rules =
|
|
2959
|
+
rules = _mergeRulesFromDirs(rules, "injection_detection");
|
|
2710
2960
|
rules = _mergeUserCustom(rules, runtime, "L1_rules", "keyword");
|
|
2711
2961
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2712
2962
|
return rules.map((r) => ({
|
|
@@ -2718,7 +2968,7 @@ function getEnabledL1InjectionKeywords(runtime, osType) {
|
|
|
2718
2968
|
function getEnabledL2IntentPatterns(runtime, osType) {
|
|
2719
2969
|
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
2720
2970
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2721
|
-
rules =
|
|
2971
|
+
rules = _mergeRulesFromDirs(rules, "intent_consistency");
|
|
2722
2972
|
rules = _mergeUserCustom(rules, runtime, "L2_rules", "intent_keywords");
|
|
2723
2973
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2724
2974
|
return rules.map((r) => ({
|
|
@@ -2730,7 +2980,7 @@ function getEnabledL2IntentPatterns(runtime, osType) {
|
|
|
2730
2980
|
function getEnabledL2PasswordPatterns(runtime) {
|
|
2731
2981
|
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
2732
2982
|
const rules = _mergeUserCustom(
|
|
2733
|
-
|
|
2983
|
+
_mergeRulesFromDirs(filterEnabledRules(cat.rules || [], cat.category_enabled), "password_modify_consent"),
|
|
2734
2984
|
runtime,
|
|
2735
2985
|
"L2_rules",
|
|
2736
2986
|
"pattern"
|
|
@@ -2740,7 +2990,7 @@ function getEnabledL2PasswordPatterns(runtime) {
|
|
|
2740
2990
|
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
2741
2991
|
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
2742
2992
|
const rules = _mergeUserCustom(
|
|
2743
|
-
|
|
2993
|
+
_mergeRulesFromDirs(filterEnabledRules(cat.rules || [], cat.category_enabled), "user_deletion_consent"),
|
|
2744
2994
|
runtime,
|
|
2745
2995
|
"L2_rules",
|
|
2746
2996
|
"pattern"
|
|
@@ -2777,7 +3027,7 @@ function getEnabledL1CommandPatternsOrDefault(runtime, osType) {
|
|
|
2777
3027
|
return getEnabledL1CommandPatterns(runtime, osType);
|
|
2778
3028
|
}
|
|
2779
3029
|
return _mergeUserCustom(
|
|
2780
|
-
|
|
3030
|
+
_mergeRulesFromDirs(_filterDefaultsByOs(_l1CommandPatternsToRules(), osType), "dangerous_commands"),
|
|
2781
3031
|
runtime,
|
|
2782
3032
|
"L1_rules",
|
|
2783
3033
|
"pattern"
|
|
@@ -2789,7 +3039,7 @@ function getEnabledL1InjectionKeywordsOrDefault(runtime, osType) {
|
|
|
2789
3039
|
return getEnabledL1InjectionKeywords(runtime, osType);
|
|
2790
3040
|
}
|
|
2791
3041
|
return _mergeUserCustom(
|
|
2792
|
-
|
|
3042
|
+
_mergeRulesFromDirs(_l1InjectionKeywordsToRules(), "injection_detection"),
|
|
2793
3043
|
runtime,
|
|
2794
3044
|
"L1_rules",
|
|
2795
3045
|
"keyword"
|
|
@@ -2801,7 +3051,7 @@ function getEnabledL2SensitivePathsOrDefault(runtime, osType) {
|
|
|
2801
3051
|
return getEnabledL2SensitivePaths(runtime, osType);
|
|
2802
3052
|
}
|
|
2803
3053
|
return _mergeUserCustom(
|
|
2804
|
-
|
|
3054
|
+
_mergeRulesFromDirs(_filterDefaultsByOs(_l2SensitivePathsToRules(), osType), "sensitive_path_access"),
|
|
2805
3055
|
runtime,
|
|
2806
3056
|
["L1_rules", "L2_rules"],
|
|
2807
3057
|
"path"
|
|
@@ -2813,7 +3063,7 @@ function getEnabledL2IntentPatternsOrDefault(runtime, osType) {
|
|
|
2813
3063
|
return getEnabledL2IntentPatterns(runtime, osType);
|
|
2814
3064
|
}
|
|
2815
3065
|
return _mergeUserCustom(
|
|
2816
|
-
|
|
3066
|
+
_mergeRulesFromDirs(_filterDefaultsByOs(_l2IntentPatternsToRules(), osType), "intent_consistency"),
|
|
2817
3067
|
runtime,
|
|
2818
3068
|
"L2_rules",
|
|
2819
3069
|
"intent_keywords"
|
|
@@ -3204,7 +3454,7 @@ function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
|
|
|
3204
3454
|
saveRuntimeConfig(runtime);
|
|
3205
3455
|
return runtime;
|
|
3206
3456
|
}
|
|
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;
|
|
3457
|
+
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
3458
|
var init_runtime_config = __esm({
|
|
3209
3459
|
"src/infra/runtime-config.ts"() {
|
|
3210
3460
|
"use strict";
|
|
@@ -3237,6 +3487,14 @@ var init_runtime_config = __esm({
|
|
|
3237
3487
|
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
3238
3488
|
};
|
|
3239
3489
|
runtimeConfigCache = null;
|
|
3490
|
+
_DEDUP_KEYS = {
|
|
3491
|
+
dangerous_commands: ["pattern"],
|
|
3492
|
+
password_modify_consent: ["pattern"],
|
|
3493
|
+
user_deletion_consent: ["pattern"],
|
|
3494
|
+
sensitive_path_access: ["path"],
|
|
3495
|
+
injection_detection: ["keyword", "lang"],
|
|
3496
|
+
intent_consistency: ["intent_keywords", "reason"]
|
|
3497
|
+
};
|
|
3240
3498
|
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
3241
3499
|
_AGENTS_DIR_PROD = join3(__dirname4, "agents");
|
|
3242
3500
|
_AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
@@ -3262,6 +3520,8 @@ var init_runtime_config = __esm({
|
|
|
3262
3520
|
var config_exports = {};
|
|
3263
3521
|
__export(config_exports, {
|
|
3264
3522
|
detectProvider: () => detectProvider,
|
|
3523
|
+
getAgentMossSetting: () => getAgentMossSetting,
|
|
3524
|
+
getCacheOptions: () => getCacheOptions,
|
|
3265
3525
|
getCustomRules: () => getCustomRules,
|
|
3266
3526
|
getLLMConfig: () => getLLMConfig,
|
|
3267
3527
|
getLLMFailMode: () => getLLMFailMode,
|
|
@@ -3273,6 +3533,8 @@ __export(config_exports, {
|
|
|
3273
3533
|
isHeuristicEnabled: () => isHeuristicEnabled,
|
|
3274
3534
|
isLLMAvailable: () => isLLMAvailable,
|
|
3275
3535
|
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
3536
|
+
isPolicyGenEnabled: () => isPolicyGenEnabled,
|
|
3537
|
+
isPolicySaveEnabled: () => isPolicySaveEnabled,
|
|
3276
3538
|
probeLLMConnection: () => probeLLMConnection,
|
|
3277
3539
|
updateLLMConfig: () => updateLLMConfig,
|
|
3278
3540
|
userRuleVariantPattern: () => userRuleVariantPattern,
|
|
@@ -3295,8 +3557,10 @@ function resolveConfigPath() {
|
|
|
3295
3557
|
const userConfig = path2.join(getConfigDir(), "config.json");
|
|
3296
3558
|
if (fs2.existsSync(userConfig)) return userConfig;
|
|
3297
3559
|
const defaultCandidates = [
|
|
3298
|
-
path2.join(
|
|
3299
|
-
path2.join(
|
|
3560
|
+
path2.join(__configModuleDir, "config.json.example"),
|
|
3561
|
+
path2.join(__configModuleDir, "..", "config.json.example"),
|
|
3562
|
+
path2.join(__configModuleDir, "..", "..", "agent_moss", "infra", "config.json.example"),
|
|
3563
|
+
path2.join(__configModuleDir, "..", "..", "..", "agent_moss", "infra", "config.json.example")
|
|
3300
3564
|
];
|
|
3301
3565
|
const defaultConfig = defaultCandidates.find((p) => fs2.existsSync(p));
|
|
3302
3566
|
if (defaultConfig) {
|
|
@@ -3305,8 +3569,8 @@ function resolveConfigPath() {
|
|
|
3305
3569
|
const pkgConfig = loadConfigFile(defaultConfig);
|
|
3306
3570
|
const safeConfig = pkgConfig ? { ...pkgConfig, llm: { ...pkgConfig.llm, api_key: "" } } : {};
|
|
3307
3571
|
fs2.writeFileSync(userConfig, JSON.stringify(safeConfig, null, 4), "utf-8");
|
|
3308
|
-
console.
|
|
3309
|
-
console.
|
|
3572
|
+
console.error(`[AgentMoss] \u5DF2\u521B\u5EFA\u7528\u6237\u914D\u7F6E\u6587\u4EF6: ${userConfig}`);
|
|
3573
|
+
console.error(`[AgentMoss] \u8BF7\u7F16\u8F91\u8BE5\u6587\u4EF6\u914D\u7F6E\u4F60\u7684 LLM API Key`);
|
|
3310
3574
|
}
|
|
3311
3575
|
return userConfig;
|
|
3312
3576
|
}
|
|
@@ -3478,7 +3742,32 @@ function layerEnabledOrDefault(key) {
|
|
|
3478
3742
|
}
|
|
3479
3743
|
}
|
|
3480
3744
|
function isBrainEnabled() {
|
|
3481
|
-
return
|
|
3745
|
+
return getAgentMossSetting("AGENT_MOSS_BRAIN_ENABLED", "") === "1";
|
|
3746
|
+
}
|
|
3747
|
+
function getAgentMossSetting(key, def = "") {
|
|
3748
|
+
const envValue = process.env[key];
|
|
3749
|
+
if (envValue !== void 0) return envValue;
|
|
3750
|
+
const cfg = loadConfigFile(resolveConfigPath());
|
|
3751
|
+
if (cfg && key in cfg) return String(cfg[key]);
|
|
3752
|
+
return def;
|
|
3753
|
+
}
|
|
3754
|
+
function isPolicyGenEnabled() {
|
|
3755
|
+
return getAgentMossSetting("AGENT_MOSS_ENABLE_POLICY_GEN", "") === "1";
|
|
3756
|
+
}
|
|
3757
|
+
function getCacheOptions() {
|
|
3758
|
+
let enabled = true;
|
|
3759
|
+
let maxSize = 1e3;
|
|
3760
|
+
const cfg = loadConfigFile(resolveConfigPath());
|
|
3761
|
+
const cache3 = cfg?.cache;
|
|
3762
|
+
if (cache3 && typeof cache3 === "object") {
|
|
3763
|
+
if (typeof cache3.enabled === "boolean") enabled = cache3.enabled;
|
|
3764
|
+
if (typeof cache3.max_size === "number" && cache3.max_size > 0) maxSize = cache3.max_size;
|
|
3765
|
+
}
|
|
3766
|
+
if (process.env.AGENT_MOSS_DISABLE_CACHE === "1") enabled = false;
|
|
3767
|
+
return { enabled, maxSize };
|
|
3768
|
+
}
|
|
3769
|
+
function isPolicySaveEnabled() {
|
|
3770
|
+
return getAgentMossSetting("AGENT_MOSS_SAVE_POLICY", "1") !== "0";
|
|
3482
3771
|
}
|
|
3483
3772
|
function isLogicRulesEnabled() {
|
|
3484
3773
|
if (process.env.AGENT_MOSS_DISABLE_LOGIC_RULES === "1") return false;
|
|
@@ -3487,13 +3776,20 @@ function isLogicRulesEnabled() {
|
|
|
3487
3776
|
function isFormalObserveMode() {
|
|
3488
3777
|
return process.env.AGENT_MOSS_FORMAL_OBSERVE === "1";
|
|
3489
3778
|
}
|
|
3490
|
-
var timeoutConfig, retryConfig, llmConfig,
|
|
3779
|
+
var __configModuleDir, timeoutConfig, retryConfig, llmConfig, USER_RULES_PATHS, customRules, PROVIDER_HEADERS;
|
|
3491
3780
|
var init_config = __esm({
|
|
3492
3781
|
"src/config.ts"() {
|
|
3493
3782
|
"use strict";
|
|
3494
3783
|
init_esm_shims();
|
|
3495
3784
|
init_paths();
|
|
3496
3785
|
init_runtime_config();
|
|
3786
|
+
__configModuleDir = (() => {
|
|
3787
|
+
try {
|
|
3788
|
+
return path2.dirname(fileURLToPath5(import.meta.url));
|
|
3789
|
+
} catch {
|
|
3790
|
+
return typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
3791
|
+
}
|
|
3792
|
+
})();
|
|
3497
3793
|
llmConfig = (() => {
|
|
3498
3794
|
const configPath = resolveConfigPath();
|
|
3499
3795
|
const fileConfig = fs2.existsSync(configPath) ? loadLLMConfigFromFile(configPath) : {};
|
|
@@ -3511,13 +3807,6 @@ var init_config = __esm({
|
|
|
3511
3807
|
timeout: parseInt(process.env.AGENT_MOSS_LLM_TIMEOUT || String(fileConfig.timeout ?? String(tr.timeout.prompt1Timeout)), 10)
|
|
3512
3808
|
};
|
|
3513
3809
|
})();
|
|
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
3810
|
USER_RULES_PATHS = [
|
|
3522
3811
|
path2.join(__configModuleDir, "rules", "user_rules.json"),
|
|
3523
3812
|
// 源在 ts/src/rules(一般不存在)
|
|
@@ -7223,7 +7512,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
7223
7512
|
}
|
|
7224
7513
|
if (attempt < maxRetries) {
|
|
7225
7514
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
7226
|
-
await new Promise((
|
|
7515
|
+
await new Promise((resolve11) => setTimeout(resolve11, retryCfg.retryInterval * 1e3));
|
|
7227
7516
|
} else {
|
|
7228
7517
|
clearTimeout(totalTimeoutTimer);
|
|
7229
7518
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
@@ -7346,6 +7635,56 @@ var init_rule_store = __esm({
|
|
|
7346
7635
|
getRule(ruleId) {
|
|
7347
7636
|
return this.data.rules.find((r) => r.id === ruleId);
|
|
7348
7637
|
}
|
|
7638
|
+
/** 质疑:active → disputed,并把 disputed_count +1(镜像 Python dispute_rule)。 */
|
|
7639
|
+
disputeRule(ruleId, _reason = "") {
|
|
7640
|
+
const rule = this.getRule(ruleId);
|
|
7641
|
+
if (!rule) throw new Error(`\u89C4\u5219\u4E0D\u5B58\u5728: ${ruleId}`);
|
|
7642
|
+
if (rule.status !== STATUS_ACTIVE) throw new Error(`\u53EA\u6709 active \u72B6\u6001\u53EF\u8D28\u7591\uFF0C\u5F53\u524D: ${rule.status}`);
|
|
7643
|
+
rule.status = STATUS_DISPUTED;
|
|
7644
|
+
rule.disputedCount += 1;
|
|
7645
|
+
this.save();
|
|
7646
|
+
}
|
|
7647
|
+
// ==================== 生命周期(curator 用,镜像 Python rule_store) ====================
|
|
7648
|
+
/** 长期未触发的 active/disputed 规则 → stale。 */
|
|
7649
|
+
markStale(staleDays = 30) {
|
|
7650
|
+
const cutoff = Date.now() - staleDays * 864e5;
|
|
7651
|
+
for (const r of this.data.rules) {
|
|
7652
|
+
if ((r.status === STATUS_ACTIVE || r.status === STATUS_DISPUTED) && r.lastTriggeredAt > 0 && r.lastTriggeredAt < cutoff) {
|
|
7653
|
+
r.status = STATUS_STALE;
|
|
7654
|
+
}
|
|
7655
|
+
}
|
|
7656
|
+
this.save();
|
|
7657
|
+
}
|
|
7658
|
+
/** 长期 stale 的规则 → archived。 */
|
|
7659
|
+
markArchived(archiveDays = 90) {
|
|
7660
|
+
const cutoff = Date.now() - archiveDays * 864e5;
|
|
7661
|
+
for (const r of this.data.rules) {
|
|
7662
|
+
if (r.status === STATUS_STALE && r.lastTriggeredAt > 0 && r.lastTriggeredAt < cutoff) {
|
|
7663
|
+
r.status = STATUS_ARCHIVED;
|
|
7664
|
+
}
|
|
7665
|
+
}
|
|
7666
|
+
this.save();
|
|
7667
|
+
}
|
|
7668
|
+
/** disputed 次数达阈值 → 自动降级 stale(自动纠错闭环)。 */
|
|
7669
|
+
autoDemoteDisputed(disputeThreshold = 3) {
|
|
7670
|
+
for (const r of this.data.rules) {
|
|
7671
|
+
if (r.status === STATUS_DISPUTED && r.disputedCount >= disputeThreshold) {
|
|
7672
|
+
r.status = STATUS_STALE;
|
|
7673
|
+
}
|
|
7674
|
+
}
|
|
7675
|
+
this.save();
|
|
7676
|
+
}
|
|
7677
|
+
// ==================== 中心化预留(镜像 Python) ====================
|
|
7678
|
+
/** 导出本地新增(非 central 来源)的 active 规则。 */
|
|
7679
|
+
getRulesForExport() {
|
|
7680
|
+
return this.data.rules.filter((r) => r.source !== "central" && r.status === STATUS_ACTIVE);
|
|
7681
|
+
}
|
|
7682
|
+
/** 导入中心下发规则(强制标 central 来源,避免被再次回流)。 */
|
|
7683
|
+
importRules(rules) {
|
|
7684
|
+
for (const rule of rules) {
|
|
7685
|
+
this.addRule({ ...rule, source: "central", originNode: "central" });
|
|
7686
|
+
}
|
|
7687
|
+
}
|
|
7349
7688
|
getActiveRules(layer) {
|
|
7350
7689
|
return this.data.rules.filter((r) => r.status === STATUS_ACTIVE && (layer === void 0 || r.layer === layer));
|
|
7351
7690
|
}
|
|
@@ -7453,12 +7792,14 @@ var init_pattern_extractor = __esm({
|
|
|
7453
7792
|
minOccurrences;
|
|
7454
7793
|
builtinPatterns;
|
|
7455
7794
|
reviewFn;
|
|
7795
|
+
skillManager;
|
|
7456
7796
|
constructor(ruleStore, opts) {
|
|
7457
7797
|
this.ruleStore = ruleStore;
|
|
7458
7798
|
this.minConfidence = opts?.minConfidence ?? 0.9;
|
|
7459
7799
|
this.minOccurrences = opts?.minOccurrences ?? 3;
|
|
7460
7800
|
this.builtinPatterns = opts?.builtinPatterns ?? [];
|
|
7461
7801
|
this.reviewFn = opts?.review ?? null;
|
|
7802
|
+
this.skillManager = opts?.skillManager ?? null;
|
|
7462
7803
|
}
|
|
7463
7804
|
async extract(event, llmResult) {
|
|
7464
7805
|
const result = { rulesCreated: 0, patternsRecorded: 0, skippedReason: "", deduped: [] };
|
|
@@ -7547,8 +7888,96 @@ var init_pattern_extractor = __esm({
|
|
|
7547
7888
|
result.rulesCreated += 1;
|
|
7548
7889
|
}
|
|
7549
7890
|
}
|
|
7891
|
+
if (this.skillManager) {
|
|
7892
|
+
const skill = this.buildSkill(actionDetail, reason, riskType, eventId);
|
|
7893
|
+
if (skill) {
|
|
7894
|
+
const id = this.skillManager.addSkill(skill);
|
|
7895
|
+
if (id) {
|
|
7896
|
+
result.skillsCreated = (result.skillsCreated ?? 0) + 1;
|
|
7897
|
+
}
|
|
7898
|
+
}
|
|
7899
|
+
}
|
|
7550
7900
|
return result;
|
|
7551
7901
|
}
|
|
7902
|
+
/**
|
|
7903
|
+
* 生成候选 Skill;不值得沉淀时返回 null(镜像 Python `_extract_skill` 的三道价值评估)。
|
|
7904
|
+
*
|
|
7905
|
+
* 评估 1:reason 太短(<50 字)说明没有可复用的判断逻辑,不沉淀;
|
|
7906
|
+
* 评估 2:同名 skill 已存在 → 跳过;
|
|
7907
|
+
* 评估 3:与已有 active skill 关键词重叠 >= 2 → 跳过(防同类案例重复沉淀,
|
|
7908
|
+
* 如 cat /etc/shadow 与 cat /etc/sudoers 生成两个近乎重复的 skill)。
|
|
7909
|
+
*/
|
|
7910
|
+
buildSkill(actionDetail, reason, riskType, eventId) {
|
|
7911
|
+
if (!reason || reason.length < 50) return null;
|
|
7912
|
+
if (!riskType) return null;
|
|
7913
|
+
const detailWords = (actionDetail.toLowerCase().match(/[a-z]+/g) || []).slice(0, 3);
|
|
7914
|
+
const name = detailWords.length > 0 ? `brain_${riskType}_${detailWords.join("_")}` : `brain_${riskType}`;
|
|
7915
|
+
if (this.skillManager.getSkillMeta(name)) return null;
|
|
7916
|
+
const keywords = this.extractSkillKeywords(actionDetail, reason, riskType);
|
|
7917
|
+
if (this.isCoveredByExistingSkill(keywords)) return null;
|
|
7918
|
+
return {
|
|
7919
|
+
name,
|
|
7920
|
+
category: this.categorizeRisk(riskType),
|
|
7921
|
+
content: this.generateSkillContent(name, actionDetail, reason, riskType),
|
|
7922
|
+
keywords,
|
|
7923
|
+
sourceEventIds: eventId ? [eventId] : [],
|
|
7924
|
+
status: "active"
|
|
7925
|
+
};
|
|
7926
|
+
}
|
|
7927
|
+
/** 关键词提取(镜像 Python `_extract_keywords`)。 */
|
|
7928
|
+
extractSkillKeywords(actionDetail, reason, riskType) {
|
|
7929
|
+
const keywords = /* @__PURE__ */ new Set();
|
|
7930
|
+
if (riskType) keywords.add(riskType);
|
|
7931
|
+
for (const w of (actionDetail.toLowerCase().match(/[a-z_]{3,}/g) || []).slice(0, 10)) keywords.add(w);
|
|
7932
|
+
for (const w of (reason.toLowerCase().match(/[a-z_]{3,}/g) || []).slice(0, 5)) keywords.add(w);
|
|
7933
|
+
return [...keywords];
|
|
7934
|
+
}
|
|
7935
|
+
/** 与已有 active skill 关键词重叠 >= 2 → 视为已覆盖(镜像 `_is_covered_by_existing_skill`)。 */
|
|
7936
|
+
isCoveredByExistingSkill(keywords) {
|
|
7937
|
+
if (keywords.length === 0) return false;
|
|
7938
|
+
let existing = [];
|
|
7939
|
+
try {
|
|
7940
|
+
existing = this.skillManager.getActiveSkills();
|
|
7941
|
+
} catch {
|
|
7942
|
+
return false;
|
|
7943
|
+
}
|
|
7944
|
+
const cand = new Set(keywords.map((k) => k.toLowerCase()));
|
|
7945
|
+
for (const meta of existing) {
|
|
7946
|
+
const metaKeywords = (meta.keywords || []).map((k) => String(k).toLowerCase());
|
|
7947
|
+
if (metaKeywords.length === 0) continue;
|
|
7948
|
+
const overlap = metaKeywords.filter((k) => cand.has(k)).length;
|
|
7949
|
+
if (overlap >= 2) return true;
|
|
7950
|
+
}
|
|
7951
|
+
return false;
|
|
7952
|
+
}
|
|
7953
|
+
/** 风险类型 → skill 分类(镜像 Python `_categorize_risk`)。 */
|
|
7954
|
+
categorizeRisk(riskType) {
|
|
7955
|
+
if (!riskType) return "edge_case";
|
|
7956
|
+
if (["inject", "backdoor", "persist"].some((k) => riskType.includes(k))) return "attack_pattern";
|
|
7957
|
+
if (["exfil", "leak", "export"].some((k) => riskType.includes(k))) return "attack_pattern";
|
|
7958
|
+
return "defense_strategy";
|
|
7959
|
+
}
|
|
7960
|
+
/** skill 正文(与 Python `_generate_skill_content` 同结构,便于双端对拍)。 */
|
|
7961
|
+
generateSkillContent(name, actionDetail, reason, riskType) {
|
|
7962
|
+
return `# ${name}
|
|
7963
|
+
|
|
7964
|
+
## \u6765\u6E90
|
|
7965
|
+
Brain \u81EA\u52A8\u63D0\u53D6\uFF08LLM \u5206\u6790\u6C89\u6DC0\uFF09
|
|
7966
|
+
|
|
7967
|
+
## \u573A\u666F
|
|
7968
|
+
\u5F53 Agent \u6267\u884C\u4EE5\u4E0B\u7C7B\u578B\u64CD\u4F5C\u65F6\u89E6\u53D1\uFF1A
|
|
7969
|
+
- \u98CE\u9669\u7C7B\u578B: ${riskType}
|
|
7970
|
+
- \u64CD\u4F5C\u7279\u5F81: ${actionDetail.slice(0, 200)}
|
|
7971
|
+
|
|
7972
|
+
## \u5224\u65AD\u4F9D\u636E
|
|
7973
|
+
${reason.slice(0, 500)}
|
|
7974
|
+
|
|
7975
|
+
## \u5904\u7406\u5EFA\u8BAE
|
|
7976
|
+
- \u4F18\u5148\u68C0\u67E5\u64CD\u4F5C\u7684\u4E0A\u4E0B\u6587\u548C\u610F\u56FE
|
|
7977
|
+
- \u7ED3\u5408 action_history \u5224\u65AD\u662F\u5426\u5B58\u5728\u6076\u610F\u94FE
|
|
7978
|
+
- \u5FC5\u8981\u65F6 Deny \u5E76\u8BB0\u5F55\u539F\u56E0
|
|
7979
|
+
`;
|
|
7980
|
+
}
|
|
7552
7981
|
extractPatterns(actionDetail, reason, riskType) {
|
|
7553
7982
|
const patterns = [];
|
|
7554
7983
|
const detailLower = actionDetail.toLowerCase();
|
|
@@ -7744,21 +8173,509 @@ var init_reviewer = __esm({
|
|
|
7744
8173
|
}
|
|
7745
8174
|
});
|
|
7746
8175
|
|
|
7747
|
-
// src/brain/
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
8176
|
+
// src/brain/skill-manager.ts
|
|
8177
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8178
|
+
import { homedir as homedir5 } from "os";
|
|
8179
|
+
import { join as join8 } from "path";
|
|
8180
|
+
import { randomUUID } from "crypto";
|
|
8181
|
+
function getMetaPath(override) {
|
|
8182
|
+
return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
|
|
8183
|
+
}
|
|
8184
|
+
var DEFAULT_SKILL_META_PATH, ENV_SKILL_META_PATH, BrainSkillManager;
|
|
8185
|
+
var init_skill_manager = __esm({
|
|
8186
|
+
"src/brain/skill-manager.ts"() {
|
|
8187
|
+
"use strict";
|
|
8188
|
+
init_esm_shims();
|
|
8189
|
+
DEFAULT_SKILL_META_PATH = join8(homedir5(), ".agentmoss", "brain", "skill_meta.json");
|
|
8190
|
+
ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
|
|
8191
|
+
BrainSkillManager = class {
|
|
8192
|
+
constructor(skillsDir, metaPath) {
|
|
8193
|
+
this.skillsDir = skillsDir;
|
|
8194
|
+
this.metaPath = getMetaPath(metaPath);
|
|
8195
|
+
try {
|
|
8196
|
+
mkdirSync4(join8(this.metaPath, ".."), { recursive: true });
|
|
8197
|
+
} catch {
|
|
7760
8198
|
}
|
|
7761
|
-
|
|
8199
|
+
this.load();
|
|
8200
|
+
this.cleanupGhostSkills();
|
|
8201
|
+
}
|
|
8202
|
+
skillsDir;
|
|
8203
|
+
skills = [];
|
|
8204
|
+
metaPath;
|
|
8205
|
+
load() {
|
|
8206
|
+
if (!existsSync13(this.metaPath)) return;
|
|
8207
|
+
try {
|
|
8208
|
+
const data = JSON.parse(readFileSync13(this.metaPath, "utf-8"));
|
|
8209
|
+
this.skills = data.skills || [];
|
|
8210
|
+
} catch {
|
|
8211
|
+
this.skills = [];
|
|
8212
|
+
}
|
|
8213
|
+
}
|
|
8214
|
+
save() {
|
|
8215
|
+
const out = { version: 1, skills: this.skills };
|
|
8216
|
+
const tmp = `${this.metaPath}.tmp`;
|
|
8217
|
+
try {
|
|
8218
|
+
writeFileSync4(tmp, JSON.stringify(out, null, 2), "utf-8");
|
|
8219
|
+
renameSync(tmp, this.metaPath);
|
|
8220
|
+
} catch {
|
|
8221
|
+
}
|
|
8222
|
+
}
|
|
8223
|
+
/**
|
|
8224
|
+
* 清理"幽灵 skill":meta 记 active 但磁盘 .md 已不存在。
|
|
8225
|
+
* (沿用 Python 的处理:置 stale,避免污染关键词表并阻塞同名新 skill 生成。)
|
|
8226
|
+
*/
|
|
8227
|
+
cleanupGhostSkills() {
|
|
8228
|
+
let changed = false;
|
|
8229
|
+
for (const sk of this.skills) {
|
|
8230
|
+
if (sk.status !== "active") continue;
|
|
8231
|
+
if (!existsSync13(join8(this.skillsDir, `${sk.name}.md`))) {
|
|
8232
|
+
sk.status = "stale";
|
|
8233
|
+
changed = true;
|
|
8234
|
+
}
|
|
8235
|
+
}
|
|
8236
|
+
if (changed) this.save();
|
|
8237
|
+
}
|
|
8238
|
+
/** 新增 Skill:写 .md + 记元数据。目录缺失或同名已存在时跳过(返回 "")。 */
|
|
8239
|
+
addSkill(skill) {
|
|
8240
|
+
const id = skill.id || randomUUID();
|
|
8241
|
+
const name = skill.name || `brain_${id.slice(0, 8)}`;
|
|
8242
|
+
if (!existsSync13(this.skillsDir)) return "";
|
|
8243
|
+
const mdPath = join8(this.skillsDir, `${name}.md`);
|
|
8244
|
+
if (existsSync13(mdPath)) return "";
|
|
8245
|
+
try {
|
|
8246
|
+
writeFileSync4(mdPath, skill.content, "utf-8");
|
|
8247
|
+
} catch {
|
|
8248
|
+
return "";
|
|
8249
|
+
}
|
|
8250
|
+
this.skills.push({
|
|
8251
|
+
id,
|
|
8252
|
+
name,
|
|
8253
|
+
category: skill.category || "",
|
|
8254
|
+
keywords: skill.keywords || [],
|
|
8255
|
+
source_event_ids: skill.sourceEventIds || [],
|
|
8256
|
+
status: skill.status || "draft",
|
|
8257
|
+
created_at: skill.createdAt || Date.now() / 1e3,
|
|
8258
|
+
usage_count: skill.usageCount || 0,
|
|
8259
|
+
origin_node: skill.originNode || "local"
|
|
8260
|
+
});
|
|
8261
|
+
this.save();
|
|
8262
|
+
return id;
|
|
8263
|
+
}
|
|
8264
|
+
getSkillMeta(name) {
|
|
8265
|
+
return this.skills.find((s) => s.name === name);
|
|
8266
|
+
}
|
|
8267
|
+
getActiveSkills() {
|
|
8268
|
+
return this.skills.filter((s) => s.status === "active").sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
|
|
8269
|
+
}
|
|
8270
|
+
recordUsage(name) {
|
|
8271
|
+
const sk = this.getSkillMeta(name);
|
|
8272
|
+
if (!sk) return;
|
|
8273
|
+
sk.usage_count = (sk.usage_count || 0) + 1;
|
|
8274
|
+
this.save();
|
|
8275
|
+
}
|
|
8276
|
+
/** 长期未使用(usage_count=0)且创建超过 staleDays 的 active skill → stale。 */
|
|
8277
|
+
markStale(staleDays = 60) {
|
|
8278
|
+
const cutoff = Date.now() / 1e3 - staleDays * 86400;
|
|
8279
|
+
let changed = false;
|
|
8280
|
+
for (const sk of this.skills) {
|
|
8281
|
+
if (sk.status === "active" && (sk.usage_count || 0) === 0 && (sk.created_at || 0) < cutoff) {
|
|
8282
|
+
sk.status = "stale";
|
|
8283
|
+
changed = true;
|
|
8284
|
+
}
|
|
8285
|
+
}
|
|
8286
|
+
if (changed) this.save();
|
|
8287
|
+
}
|
|
8288
|
+
/** 导出本地 active skill(含正文),供回流中心。 */
|
|
8289
|
+
exportSkills() {
|
|
8290
|
+
const out = [];
|
|
8291
|
+
for (const sk of this.skills) {
|
|
8292
|
+
if (sk.origin_node !== "local" || sk.status !== "active") continue;
|
|
8293
|
+
const mdPath = join8(this.skillsDir, `${sk.name}.md`);
|
|
8294
|
+
if (!existsSync13(mdPath)) continue;
|
|
8295
|
+
out.push({
|
|
8296
|
+
name: sk.name,
|
|
8297
|
+
category: sk.category || "",
|
|
8298
|
+
content: readFileSync13(mdPath, "utf-8"),
|
|
8299
|
+
keywords: sk.keywords || []
|
|
8300
|
+
});
|
|
8301
|
+
}
|
|
8302
|
+
return out;
|
|
8303
|
+
}
|
|
8304
|
+
/** 导入中心下发 skill(标 central,避免被再次回流)。 */
|
|
8305
|
+
importSkills(skills) {
|
|
8306
|
+
for (const s of skills) {
|
|
8307
|
+
this.addSkill({
|
|
8308
|
+
name: s.name,
|
|
8309
|
+
category: s.category || "",
|
|
8310
|
+
content: s.content,
|
|
8311
|
+
keywords: s.keywords || [],
|
|
8312
|
+
status: "active",
|
|
8313
|
+
originNode: "central"
|
|
8314
|
+
});
|
|
8315
|
+
}
|
|
8316
|
+
}
|
|
8317
|
+
/** 保留 close 签名(JSON 落盘已逐次持久化)。 */
|
|
8318
|
+
close() {
|
|
8319
|
+
this.save();
|
|
8320
|
+
}
|
|
8321
|
+
};
|
|
8322
|
+
}
|
|
8323
|
+
});
|
|
8324
|
+
|
|
8325
|
+
// src/brain/curator.ts
|
|
8326
|
+
var BrainCurator;
|
|
8327
|
+
var init_curator = __esm({
|
|
8328
|
+
"src/brain/curator.ts"() {
|
|
8329
|
+
"use strict";
|
|
8330
|
+
init_esm_shims();
|
|
8331
|
+
BrainCurator = class {
|
|
8332
|
+
constructor(ruleStore, skillManager) {
|
|
8333
|
+
this.ruleStore = ruleStore;
|
|
8334
|
+
this.skillManager = skillManager;
|
|
8335
|
+
}
|
|
8336
|
+
ruleStore;
|
|
8337
|
+
skillManager;
|
|
8338
|
+
/**
|
|
8339
|
+
* 执行一次维护,返回报告。
|
|
8340
|
+
*
|
|
8341
|
+
* autoApprove 默认 true:brain 提取的规则达阈值后自动生效(学习闭环);
|
|
8342
|
+
* 需人工审核时设 false(由 Console 手动 approve)。
|
|
8343
|
+
*/
|
|
8344
|
+
runMaintenance(opts = {}) {
|
|
8345
|
+
const {
|
|
8346
|
+
staleRuleDays = 30,
|
|
8347
|
+
archiveRuleDays = 90,
|
|
8348
|
+
staleSkillDays = 60,
|
|
8349
|
+
disputeThreshold = 3,
|
|
8350
|
+
autoApprove = true
|
|
8351
|
+
} = opts;
|
|
8352
|
+
const autoApproved = autoApprove ? this.autoApproveDrafts() : [];
|
|
8353
|
+
this.ruleStore.markStale(staleRuleDays);
|
|
8354
|
+
this.ruleStore.markArchived(archiveRuleDays);
|
|
8355
|
+
this.ruleStore.autoDemoteDisputed(disputeThreshold);
|
|
8356
|
+
this.skillManager.markStale(staleSkillDays);
|
|
8357
|
+
return {
|
|
8358
|
+
rule_stats: this.ruleStore.getStats(),
|
|
8359
|
+
auto_approved: autoApproved
|
|
8360
|
+
};
|
|
8361
|
+
}
|
|
8362
|
+
/** draft → active(仅未 disputed 的 draft)。返回被激活规则的 pattern 前缀。 */
|
|
8363
|
+
autoApproveDrafts() {
|
|
8364
|
+
const approved = [];
|
|
8365
|
+
let drafts;
|
|
8366
|
+
try {
|
|
8367
|
+
drafts = this.ruleStore.getDraftRules();
|
|
8368
|
+
} catch {
|
|
8369
|
+
return approved;
|
|
8370
|
+
}
|
|
8371
|
+
for (const rule of drafts) {
|
|
8372
|
+
try {
|
|
8373
|
+
this.ruleStore.approveRule(rule.id);
|
|
8374
|
+
approved.push(rule.pattern.slice(0, 60));
|
|
8375
|
+
} catch {
|
|
8376
|
+
}
|
|
8377
|
+
}
|
|
8378
|
+
return approved;
|
|
8379
|
+
}
|
|
8380
|
+
};
|
|
8381
|
+
}
|
|
8382
|
+
});
|
|
8383
|
+
|
|
8384
|
+
// src/brain/contributor.ts
|
|
8385
|
+
var contributor_exports = {};
|
|
8386
|
+
__export(contributor_exports, {
|
|
8387
|
+
CONTACT_EMAIL: () => CONTACT_EMAIL,
|
|
8388
|
+
ContributionError: () => ContributionError,
|
|
8389
|
+
DEFAULT_TARGET_REPO: () => DEFAULT_TARGET_REPO,
|
|
8390
|
+
EXPORT_RULE_SOURCES: () => EXPORT_RULE_SOURCES,
|
|
8391
|
+
PLATFORM_SSH_HOST: () => PLATFORM_SSH_HOST,
|
|
8392
|
+
TOKEN_ENV: () => TOKEN_ENV,
|
|
8393
|
+
buildCommitMessage: () => buildCommitMessage,
|
|
8394
|
+
buildForkSshUrl: () => buildForkSshUrl,
|
|
8395
|
+
collectRules: () => collectRules,
|
|
8396
|
+
collectSkills: () => collectSkills,
|
|
8397
|
+
contribute: () => contribute,
|
|
8398
|
+
createMrViaApi: () => createMrViaApi,
|
|
8399
|
+
extractMrUrl: () => extractMrUrl,
|
|
8400
|
+
extractRepoPath: () => extractRepoPath,
|
|
8401
|
+
getCurrentUsername: () => getCurrentUsername,
|
|
8402
|
+
gitCommitAndPush: () => gitCommitAndPush,
|
|
8403
|
+
probeAccess: () => probeAccess,
|
|
8404
|
+
resolvePushTarget: () => resolvePushTarget,
|
|
8405
|
+
runGit: () => runGit,
|
|
8406
|
+
writeExportFiles: () => writeExportFiles
|
|
8407
|
+
});
|
|
8408
|
+
import { mkdirSync as mkdirSync5, mkdtempSync, rmSync, writeFileSync as writeFileSync5 } from "fs";
|
|
8409
|
+
import { tmpdir } from "os";
|
|
8410
|
+
import { join as join9 } from "path";
|
|
8411
|
+
import { spawnSync } from "child_process";
|
|
8412
|
+
import { dirname as dirname9 } from "path";
|
|
8413
|
+
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
8414
|
+
function collectRules(store) {
|
|
8415
|
+
return store.getActiveRules().filter((r) => EXPORT_RULE_SOURCES.includes(r.source)).map((r) => ({
|
|
8416
|
+
layer: r.layer,
|
|
8417
|
+
rule_type: r.ruleType,
|
|
8418
|
+
pattern: r.pattern,
|
|
8419
|
+
scope: r.scope,
|
|
8420
|
+
confidence: r.confidence,
|
|
8421
|
+
explanation: r.explanation,
|
|
8422
|
+
source_event_count: (r.sourceEventIds || []).length,
|
|
8423
|
+
trigger_count: r.triggerCount
|
|
8424
|
+
}));
|
|
8425
|
+
}
|
|
8426
|
+
function collectSkills(skillMgr) {
|
|
8427
|
+
return skillMgr.exportSkills();
|
|
8428
|
+
}
|
|
8429
|
+
function writeExportFiles(outDir, rules, skills) {
|
|
8430
|
+
const written = [];
|
|
8431
|
+
const rulesDir = join9(outDir, "rules");
|
|
8432
|
+
mkdirSync5(rulesDir, { recursive: true });
|
|
8433
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
8434
|
+
for (const r of rules) {
|
|
8435
|
+
if (!byLayer.has(r.layer)) byLayer.set(r.layer, []);
|
|
8436
|
+
byLayer.get(r.layer).push(r);
|
|
8437
|
+
}
|
|
8438
|
+
for (const layer of [...byLayer.keys()].sort((a, b) => a - b)) {
|
|
8439
|
+
const path6 = join9(rulesDir, `l${layer}_brain_rules.json`);
|
|
8440
|
+
writeFileSync5(path6, JSON.stringify(byLayer.get(layer), null, 2), "utf-8");
|
|
8441
|
+
written.push(path6);
|
|
8442
|
+
}
|
|
8443
|
+
if (rules.length === 0) {
|
|
8444
|
+
const path6 = join9(rulesDir, "empty.txt");
|
|
8445
|
+
writeFileSync5(path6, "(no brain rules to contribute)", "utf-8");
|
|
8446
|
+
written.push(path6);
|
|
8447
|
+
}
|
|
8448
|
+
const skillsDir = join9(outDir, "skills");
|
|
8449
|
+
mkdirSync5(skillsDir, { recursive: true });
|
|
8450
|
+
for (const sk of skills) {
|
|
8451
|
+
const path6 = join9(skillsDir, `${sk.name}.md`);
|
|
8452
|
+
writeFileSync5(path6, sk.content || "", "utf-8");
|
|
8453
|
+
written.push(path6);
|
|
8454
|
+
}
|
|
8455
|
+
if (skills.length === 0) {
|
|
8456
|
+
const path6 = join9(skillsDir, "empty.txt");
|
|
8457
|
+
writeFileSync5(path6, "(no brain skills to contribute)", "utf-8");
|
|
8458
|
+
written.push(path6);
|
|
8459
|
+
}
|
|
8460
|
+
return written;
|
|
8461
|
+
}
|
|
8462
|
+
function runGit(args, cwd) {
|
|
8463
|
+
const res = spawnSync("git", args, { cwd, encoding: "utf-8", timeout: 6e4 });
|
|
8464
|
+
return {
|
|
8465
|
+
code: res.status ?? 1,
|
|
8466
|
+
stdout: res.stdout ?? "",
|
|
8467
|
+
stderr: res.stderr ?? String(res.error ?? "")
|
|
8468
|
+
};
|
|
8469
|
+
}
|
|
8470
|
+
function probeAccess(targetRepo) {
|
|
8471
|
+
return runGit(["ls-remote", "--heads", targetRepo]).code === 0;
|
|
8472
|
+
}
|
|
8473
|
+
function buildCommitMessage(rules, skills) {
|
|
8474
|
+
const lines = [
|
|
8475
|
+
"feat(brain): \u8D21\u732E\u672C\u5730 LLM \u81EA\u5B66\u4E60\u6C89\u6DC0\u7684\u89C4\u5219\u4E0E Skill",
|
|
8476
|
+
"",
|
|
8477
|
+
`\u672C\u6B21\u8D21\u732E\u7531 AgentMoss Brain \u4ECE ${rules.length} \u6761\u89C4\u5219\u4E0E ${skills.length} \u4E2A Skill \u7EC4\u6210\uFF0C`,
|
|
8478
|
+
"\u5747\u4E3A\u672C\u5730 LLM \u63D0\u53D6 + \u5BA1\u6838\u901A\u8FC7\u7684 active \u9879\u3002"
|
|
8479
|
+
];
|
|
8480
|
+
if (rules.length > 0) {
|
|
8481
|
+
lines.push("", "## \u89C4\u5219");
|
|
8482
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
8483
|
+
for (const r of rules) byLayer.set(r.layer, (byLayer.get(r.layer) ?? 0) + 1);
|
|
8484
|
+
for (const [layer, cnt] of [...byLayer.entries()].sort((a, b) => a[0] - b[0])) {
|
|
8485
|
+
lines.push(`- Layer ${layer}: ${cnt} \u6761`);
|
|
8486
|
+
}
|
|
8487
|
+
for (const r of rules.slice(0, 5)) {
|
|
8488
|
+
lines.push(` - [${r.rule_type}] ${r.pattern.slice(0, 80)} \u2014 ${r.explanation.slice(0, 60)}`);
|
|
8489
|
+
}
|
|
8490
|
+
}
|
|
8491
|
+
if (skills.length > 0) {
|
|
8492
|
+
lines.push("", "## Skill");
|
|
8493
|
+
for (const sk of skills.slice(0, 5)) lines.push(`- ${sk.name}: ${sk.category || ""}`);
|
|
8494
|
+
}
|
|
8495
|
+
lines.push("", "Co-Authored-By: AgentMoss Brain <brain@agentmoss.local>");
|
|
8496
|
+
return lines.join("\n");
|
|
8497
|
+
}
|
|
8498
|
+
function gitCommitAndPush(workdir, targetRepo, branch, rules, skills) {
|
|
8499
|
+
runGit(["init", "-q"], workdir);
|
|
8500
|
+
runGit(["remote", "add", "origin", targetRepo], workdir);
|
|
8501
|
+
runGit(["fetch", "--depth", "1", "origin", "master"], workdir);
|
|
8502
|
+
runGit(["checkout", "-q", "-b", branch, "FETCH_HEAD"], workdir);
|
|
8503
|
+
runGit(["add", "-A"], workdir);
|
|
8504
|
+
runGit(["commit", "-q", "-m", buildCommitMessage(rules, skills)], workdir);
|
|
8505
|
+
const push = runGit(["push", "origin", branch], workdir);
|
|
8506
|
+
if (push.code !== 0) {
|
|
8507
|
+
throw new ContributionError(`push \u5230 ${targetRepo} \u5931\u8D25\uFF1A${push.stderr.trim().slice(0, 300)}`);
|
|
8508
|
+
}
|
|
8509
|
+
return push.stdout + push.stderr;
|
|
8510
|
+
}
|
|
8511
|
+
function extractMrUrl(pushOutput, targetRepo) {
|
|
8512
|
+
for (const rawLine of pushOutput.split("\n")) {
|
|
8513
|
+
const line = rawLine.trim();
|
|
8514
|
+
if (line.includes("merge_requests/new") || line.includes("pull/new") || line.includes("new?source_branch")) {
|
|
8515
|
+
return line;
|
|
8516
|
+
}
|
|
8517
|
+
}
|
|
8518
|
+
if (targetRepo.includes("gitcode.com") || targetRepo.includes("atomgit.com")) {
|
|
8519
|
+
return `https://gitcode.com/${extractRepoPath(targetRepo)}/branches`;
|
|
8520
|
+
}
|
|
8521
|
+
return "";
|
|
8522
|
+
}
|
|
8523
|
+
function extractRepoPath(repoUrl) {
|
|
8524
|
+
let url = repoUrl;
|
|
8525
|
+
if (url.startsWith("ssh://")) {
|
|
8526
|
+
url = url.split("://")[1];
|
|
8527
|
+
const first = url.split("/")[0];
|
|
8528
|
+
if (first.includes("@")) url = url.slice(url.indexOf("/") + 1);
|
|
8529
|
+
} else if (url.startsWith("git@") && url.includes(":")) {
|
|
8530
|
+
url = url.split(":")[1];
|
|
8531
|
+
} else if (url.startsWith("http")) {
|
|
8532
|
+
url = url.split("://")[1];
|
|
8533
|
+
const slash = url.indexOf("/");
|
|
8534
|
+
if (slash >= 0) url = url.slice(slash + 1);
|
|
8535
|
+
}
|
|
8536
|
+
return url.replace(/\.git$/, "").replace(/^\/+|\/+$/g, "");
|
|
8537
|
+
}
|
|
8538
|
+
async function createMrViaApi(targetRepo, branch, title, desc) {
|
|
8539
|
+
const token = process.env[TOKEN_ENV] || "";
|
|
8540
|
+
if (!token) return false;
|
|
8541
|
+
const repoPath = extractRepoPath(targetRepo);
|
|
8542
|
+
const apiUrl = `https://gitcode.com/api/v4/projects/${encodeURIComponent(repoPath)}/merge_requests`;
|
|
8543
|
+
try {
|
|
8544
|
+
const resp = await fetch(apiUrl, {
|
|
8545
|
+
method: "POST",
|
|
8546
|
+
headers: { "Content-Type": "application/json", "PRIVATE-TOKEN": token },
|
|
8547
|
+
body: JSON.stringify({ source_branch: branch, target_branch: "master", title, description: desc }),
|
|
8548
|
+
signal: AbortSignal.timeout(2e4)
|
|
8549
|
+
});
|
|
8550
|
+
return resp.status === 200 || resp.status === 201;
|
|
8551
|
+
} catch {
|
|
8552
|
+
return false;
|
|
8553
|
+
}
|
|
8554
|
+
}
|
|
8555
|
+
function getCurrentUsername() {
|
|
8556
|
+
const res = runGit(["config", "--get", "user.name"]);
|
|
8557
|
+
return res.code === 0 ? res.stdout.trim() : "";
|
|
8558
|
+
}
|
|
8559
|
+
function buildForkSshUrl(username) {
|
|
8560
|
+
return `ssh://git@${PLATFORM_SSH_HOST}/${username}/AgentMoss.git`;
|
|
8561
|
+
}
|
|
8562
|
+
function resolvePushTarget(opts) {
|
|
8563
|
+
if (opts.target) return opts.target;
|
|
8564
|
+
if (opts.forkUser) return buildForkSshUrl(opts.forkUser);
|
|
8565
|
+
const username = getCurrentUsername();
|
|
8566
|
+
if (username) return buildForkSshUrl(username);
|
|
8567
|
+
throw new ContributionError(
|
|
8568
|
+
"\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"
|
|
8569
|
+
);
|
|
8570
|
+
}
|
|
8571
|
+
async function contribute(opts = {}) {
|
|
8572
|
+
if (runGit(["version"]).code !== 0) {
|
|
8573
|
+
console.log("\u9519\u8BEF\uFF1A\u672A\u68C0\u6D4B\u5230 git\uFF0C\u8D21\u732E\u529F\u80FD\u9700\u8981 git CLI\u3002");
|
|
8574
|
+
return 2;
|
|
8575
|
+
}
|
|
8576
|
+
let pushTarget;
|
|
8577
|
+
try {
|
|
8578
|
+
pushTarget = resolvePushTarget(opts);
|
|
8579
|
+
} catch (err) {
|
|
8580
|
+
console.log(`[Brain] ${err.message}`);
|
|
8581
|
+
return 2;
|
|
8582
|
+
}
|
|
8583
|
+
console.log(`[Brain] push \u76EE\u6807: ${pushTarget}`);
|
|
8584
|
+
console.log(`[Brain] MR \u6307\u5411: ${DEFAULT_TARGET_REPO}`);
|
|
8585
|
+
const store = new BrainRuleStore();
|
|
8586
|
+
const skillsDir = resolveAssetDir(__dirname8, "skills") ?? join9(__dirname8, "..", "..", "agent_moss", "skills");
|
|
8587
|
+
const skillMgr = new BrainSkillManager(skillsDir);
|
|
8588
|
+
let rules;
|
|
8589
|
+
let skills;
|
|
8590
|
+
try {
|
|
8591
|
+
rules = collectRules(store);
|
|
8592
|
+
skills = collectSkills(skillMgr);
|
|
8593
|
+
} finally {
|
|
8594
|
+
skillMgr.close();
|
|
8595
|
+
}
|
|
8596
|
+
if (rules.length === 0 && skills.length === 0) {
|
|
8597
|
+
console.log("[Brain] \u672C\u5730\u6CA1\u6709 active \u7684 brain \u89C4\u5219\u6216 skill\uFF0C\u65E0\u53EF\u8D21\u732E\u5185\u5BB9\u3002");
|
|
8598
|
+
return 0;
|
|
8599
|
+
}
|
|
8600
|
+
console.log(`[Brain] \u5BFC\u51FA: ${rules.length} \u6761\u89C4\u5219, ${skills.length} \u4E2A Skill`);
|
|
8601
|
+
const workdir = mkdtempSync(join9(tmpdir(), "agentmoss-contrib-"));
|
|
8602
|
+
try {
|
|
8603
|
+
writeExportFiles(join9(workdir, "agent_moss", "brain_contrib"), rules, skills);
|
|
8604
|
+
console.log(`[Brain] \u63A2\u6D4B ${pushTarget} \u8BBF\u95EE\u6743\u9650 ...`);
|
|
8605
|
+
if (!probeAccess(pushTarget)) {
|
|
8606
|
+
const username = getCurrentUsername();
|
|
8607
|
+
console.log(`[Brain] \u65E0\u6CD5\u8BBF\u95EE ${pushTarget}`, "");
|
|
8608
|
+
if (username) {
|
|
8609
|
+
console.log(` \u5F53\u524D git \u7528\u6237: ${username}`);
|
|
8610
|
+
console.log(" \u53EF\u80FD\u539F\u56E0\uFF1A\u4F60\u8FD8\u6CA1\u6709 fork \u76EE\u6807\u4ED3\u5E93\uFF0C\u6216 fork \u540D\u4E0D\u5339\u914D\u3002");
|
|
8611
|
+
console.log(` \u8BF7\u5148\u5728\u7F51\u9875\u7AEF fork: ${DEFAULT_TARGET_REPO}`);
|
|
8612
|
+
console.log(` \uFF08fork \u540E\u4F60\u7684\u4ED3\u5E94\u8BE5\u662F: ${buildForkSshUrl(username)}\uFF09`);
|
|
8613
|
+
}
|
|
8614
|
+
console.log("");
|
|
8615
|
+
console.log(" \u5982\u679C\u4F60\u786E\u8BA4\u5DF2 fork \u4F46\u5BF9\u79C1\u4ED3\u8BBF\u95EE\u53D7\u9650\uFF1A");
|
|
8616
|
+
console.log(` \u6536\u4EF6\u4EBA: ${CONTACT_EMAIL}`);
|
|
8617
|
+
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");
|
|
8618
|
+
console.log(" fork \u540E\u91CD\u8BD5\u672C\u547D\u4EE4\uFF0C\u6216\u7528 --target <\u4F60\u7684 fork URL> \u663E\u5F0F\u6307\u5B9A\u3002");
|
|
8619
|
+
return 1;
|
|
8620
|
+
}
|
|
8621
|
+
const branch = opts.branch || `brain-contrib/${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 15)}`;
|
|
8622
|
+
console.log(`[Brain] \u63A8\u9001\u5206\u652F ${branch} \u2192 ${pushTarget}`);
|
|
8623
|
+
const pushOutput = gitCommitAndPush(workdir, pushTarget, branch, rules, skills);
|
|
8624
|
+
console.log("[Brain] \u63A8\u9001\u6210\u529F\u3002");
|
|
8625
|
+
const title = `[Brain] \u8D21\u732E ${rules.length} \u6761\u89C4\u5219 + ${skills.length} \u4E2A Skill`;
|
|
8626
|
+
const desc = buildCommitMessage(rules, skills);
|
|
8627
|
+
if (await createMrViaApi(pushTarget, branch, title, desc)) {
|
|
8628
|
+
console.log("[Brain] \u5DF2\u901A\u8FC7 API \u521B\u5EFA Merge Request\uFF1A");
|
|
8629
|
+
console.log(` ${branch} \u2192 master (MR \u6307\u5411 ${DEFAULT_TARGET_REPO})`);
|
|
8630
|
+
} else {
|
|
8631
|
+
const mrUrl = extractMrUrl(pushOutput, pushTarget);
|
|
8632
|
+
console.log("");
|
|
8633
|
+
console.log("[Brain] \u8BF7\u5728\u7F51\u9875\u7AEF\u521B\u5EFA Merge Request\uFF08from \u4F60\u7684 fork \u2192 shared \u4ED3 master\uFF09\uFF1A");
|
|
8634
|
+
if (mrUrl) console.log(` ${mrUrl}`);
|
|
8635
|
+
console.log(` \u5206\u652F: ${branch} \u2192 master (\u76EE\u6807\u4ED3: ${DEFAULT_TARGET_REPO})`);
|
|
8636
|
+
if (!process.env[TOKEN_ENV]) {
|
|
8637
|
+
console.log(` \uFF08\u63D0\u793A\uFF1A\u8BBE\u7F6E env ${TOKEN_ENV} \u53EF\u8BA9\u672C\u547D\u4EE4\u81EA\u52A8\u521B\u5EFA MR\uFF09`);
|
|
8638
|
+
}
|
|
8639
|
+
}
|
|
8640
|
+
return 0;
|
|
8641
|
+
} finally {
|
|
8642
|
+
rmSync(workdir, { recursive: true, force: true });
|
|
8643
|
+
}
|
|
8644
|
+
}
|
|
8645
|
+
var __dirname8, DEFAULT_TARGET_REPO, CONTACT_EMAIL, TOKEN_ENV, PLATFORM_SSH_HOST, EXPORT_RULE_SOURCES, ContributionError;
|
|
8646
|
+
var init_contributor = __esm({
|
|
8647
|
+
"src/brain/contributor.ts"() {
|
|
8648
|
+
"use strict";
|
|
8649
|
+
init_esm_shims();
|
|
8650
|
+
init_rule_store();
|
|
8651
|
+
init_skill_manager();
|
|
8652
|
+
init_asset_dir();
|
|
8653
|
+
__dirname8 = dirname9(fileURLToPath9(import.meta.url));
|
|
8654
|
+
DEFAULT_TARGET_REPO = "ssh://git@atomgit.com/kenhkl/AgentMoss.git";
|
|
8655
|
+
CONTACT_EMAIL = "kenhkl11@hotmail.com";
|
|
8656
|
+
TOKEN_ENV = "AGENT_MOSS_CONTRIBUTE_TOKEN";
|
|
8657
|
+
PLATFORM_SSH_HOST = "atomgit.com";
|
|
8658
|
+
EXPORT_RULE_SOURCES = ["llm_extracted", "user"];
|
|
8659
|
+
ContributionError = class extends Error {
|
|
8660
|
+
};
|
|
8661
|
+
}
|
|
8662
|
+
});
|
|
8663
|
+
|
|
8664
|
+
// src/brain/index.ts
|
|
8665
|
+
function checkBrainRules(store, opts) {
|
|
8666
|
+
if (!store) return null;
|
|
8667
|
+
const rules = store.getActiveRules(opts.layer);
|
|
8668
|
+
if (rules.length === 0) return null;
|
|
8669
|
+
const combined = `${opts.actionType} ${opts.actionDetail}`;
|
|
8670
|
+
for (const rule of rules) {
|
|
8671
|
+
try {
|
|
8672
|
+
if (new RegExp(rule.pattern, "i").test(combined)) {
|
|
8673
|
+
if (opts.layer === 2 && opts.actionHistory) {
|
|
8674
|
+
if (!checkLayer2Context(rule.pattern, opts.actionHistory, opts.actionDetail)) {
|
|
8675
|
+
continue;
|
|
8676
|
+
}
|
|
8677
|
+
}
|
|
8678
|
+
store.recordTrigger(rule.id);
|
|
7762
8679
|
return {
|
|
7763
8680
|
ruleId: rule.id,
|
|
7764
8681
|
pattern: rule.pattern,
|
|
@@ -7796,6 +8713,9 @@ var init_brain = __esm({
|
|
|
7796
8713
|
init_rule_store();
|
|
7797
8714
|
init_pattern_extractor();
|
|
7798
8715
|
init_reviewer();
|
|
8716
|
+
init_skill_manager();
|
|
8717
|
+
init_curator();
|
|
8718
|
+
init_contributor();
|
|
7799
8719
|
}
|
|
7800
8720
|
});
|
|
7801
8721
|
|
|
@@ -9325,19 +10245,64 @@ function extractRawCommand(detail) {
|
|
|
9325
10245
|
function matchesAny(cmd, patterns) {
|
|
9326
10246
|
return patterns.some((p) => p.test(cmd));
|
|
9327
10247
|
}
|
|
9328
|
-
function getBrainStore() {
|
|
10248
|
+
function getBrainStore(osType) {
|
|
9329
10249
|
if (!isBrainEnabled()) return null;
|
|
9330
10250
|
if (!_brainInitialized) {
|
|
9331
10251
|
_brainStore = new BrainRuleStore();
|
|
9332
|
-
|
|
10252
|
+
const skillsDir = resolveAssetDir(__dirname, "skills");
|
|
10253
|
+
_brainSkillManager = skillsDir ? new BrainSkillManager(skillsDir) : null;
|
|
10254
|
+
_brainExtractor = new PatternExtractor(_brainStore, {
|
|
10255
|
+
review: createBrainReviewer(),
|
|
10256
|
+
// 内置规则去重集(对齐 Python coordinator._ensure_brain 收集的 builtin_patterns):
|
|
10257
|
+
// 危险命令正则 + 敏感路径(deny_* 或 credential)。
|
|
10258
|
+
// 历史 bug:TS 这里只传 review,不传 builtinPatterns → 提取器拿不到内置规则,
|
|
10259
|
+
// `isCoveredByBuiltin` 恒 False → 把 L1/L2 本来就能拦的案例重复沉淀成 Brain 规则。
|
|
10260
|
+
builtinPatterns: collectBrainBuiltinPatterns(osType),
|
|
10261
|
+
skillManager: _brainSkillManager
|
|
10262
|
+
});
|
|
10263
|
+
if (_brainSkillManager) {
|
|
10264
|
+
_brainCurator = new BrainCurator(_brainStore, _brainSkillManager);
|
|
10265
|
+
}
|
|
9333
10266
|
_brainInitialized = true;
|
|
9334
10267
|
}
|
|
10268
|
+
const now = Date.now();
|
|
10269
|
+
if (_brainCurator && now - _brainLastMaintenance >= BRAIN_MAINT_INTERVAL_MS) {
|
|
10270
|
+
try {
|
|
10271
|
+
_brainCurator.runMaintenance({ autoApprove: true });
|
|
10272
|
+
} catch {
|
|
10273
|
+
}
|
|
10274
|
+
_brainLastMaintenance = now;
|
|
10275
|
+
}
|
|
9335
10276
|
return _brainStore;
|
|
9336
10277
|
}
|
|
10278
|
+
function collectBrainBuiltinPatterns(osType) {
|
|
10279
|
+
const bridge = runtimeConfigBridge.current;
|
|
10280
|
+
if (!bridge) return [];
|
|
10281
|
+
const out = [];
|
|
10282
|
+
try {
|
|
10283
|
+
for (const cp of bridge.effectiveDangerousPatterns(osType)) {
|
|
10284
|
+
if (cp?.pattern?.source) out.push(cp.pattern.source);
|
|
10285
|
+
}
|
|
10286
|
+
} catch {
|
|
10287
|
+
}
|
|
10288
|
+
try {
|
|
10289
|
+
for (const sp of bridge.effectiveSensitivePaths(osType)) {
|
|
10290
|
+
const denyMode = String(sp?.denyMode || "");
|
|
10291
|
+
if (["deny_both", "deny_write", "deny_read"].includes(denyMode) || sp?.credential) {
|
|
10292
|
+
if (sp?.path) out.push(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
10293
|
+
}
|
|
10294
|
+
}
|
|
10295
|
+
} catch {
|
|
10296
|
+
}
|
|
10297
|
+
return out.filter(Boolean);
|
|
10298
|
+
}
|
|
9337
10299
|
function _resetBrainForTest() {
|
|
9338
10300
|
_brainStore = null;
|
|
9339
10301
|
_brainExtractor = null;
|
|
10302
|
+
_brainSkillManager = null;
|
|
10303
|
+
_brainCurator = null;
|
|
9340
10304
|
_brainInitialized = false;
|
|
10305
|
+
_brainLastMaintenance = 0;
|
|
9341
10306
|
}
|
|
9342
10307
|
async function runAnalysis(input) {
|
|
9343
10308
|
const observedFormalLayers = [];
|
|
@@ -9389,7 +10354,7 @@ async function runAnalysis(input) {
|
|
|
9389
10354
|
}
|
|
9390
10355
|
}
|
|
9391
10356
|
}
|
|
9392
|
-
const brainStore = getBrainStore();
|
|
10357
|
+
const brainStore = getBrainStore(input.osType);
|
|
9393
10358
|
if (brainStore) {
|
|
9394
10359
|
const brainL1 = checkBrainRules(brainStore, {
|
|
9395
10360
|
layer: 1,
|
|
@@ -9686,7 +10651,7 @@ async function runAnalysis(input) {
|
|
|
9686
10651
|
}
|
|
9687
10652
|
return llmResult;
|
|
9688
10653
|
}
|
|
9689
|
-
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS, _brainStore, _brainExtractor, _brainInitialized, formalEngine;
|
|
10654
|
+
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS, _brainStore, _brainExtractor, _brainSkillManager, _brainCurator, _brainInitialized, _brainLastMaintenance, BRAIN_MAINT_INTERVAL_MS, formalEngine;
|
|
9690
10655
|
var init_coordinator = __esm({
|
|
9691
10656
|
"src/engine/coordinator.ts"() {
|
|
9692
10657
|
"use strict";
|
|
@@ -9703,7 +10668,11 @@ var init_coordinator = __esm({
|
|
|
9703
10668
|
init_agents();
|
|
9704
10669
|
init_scenarios();
|
|
9705
10670
|
init_brain();
|
|
10671
|
+
init_skill_manager();
|
|
10672
|
+
init_curator();
|
|
9706
10673
|
init_reviewer();
|
|
10674
|
+
init_asset_dir();
|
|
10675
|
+
init_runtime_config_bridge();
|
|
9707
10676
|
init_formal_engine();
|
|
9708
10677
|
FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
9709
10678
|
"ask_user_question",
|
|
@@ -9726,122 +10695,495 @@ var init_coordinator = __esm({
|
|
|
9726
10695
|
]);
|
|
9727
10696
|
_brainStore = null;
|
|
9728
10697
|
_brainExtractor = null;
|
|
10698
|
+
_brainSkillManager = null;
|
|
10699
|
+
_brainCurator = null;
|
|
9729
10700
|
_brainInitialized = false;
|
|
10701
|
+
_brainLastMaintenance = 0;
|
|
10702
|
+
BRAIN_MAINT_INTERVAL_MS = 3e5;
|
|
9730
10703
|
formalEngine = new FormalEngine();
|
|
9731
10704
|
}
|
|
9732
10705
|
});
|
|
9733
10706
|
|
|
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
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
10707
|
+
// src/infra/policy-cache.ts
|
|
10708
|
+
import { createHash as createHash2 } from "crypto";
|
|
10709
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync5, readFileSync as readFileSync15, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
10710
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
10711
|
+
import { join as join11 } from "path";
|
|
10712
|
+
function cacheDir() {
|
|
10713
|
+
return cacheDirOverride ?? CACHE_DIR;
|
|
10714
|
+
}
|
|
10715
|
+
function getDefaultCache() {
|
|
10716
|
+
if (!defaultCache) defaultCache = new PolicyCache(getCacheOptions());
|
|
10717
|
+
return defaultCache;
|
|
10718
|
+
}
|
|
10719
|
+
var CACHE_DIR, cacheDirOverride, PolicyCache, defaultCache;
|
|
10720
|
+
var init_policy_cache = __esm({
|
|
10721
|
+
"src/infra/policy-cache.ts"() {
|
|
10722
|
+
"use strict";
|
|
10723
|
+
init_esm_shims();
|
|
10724
|
+
init_config();
|
|
10725
|
+
CACHE_DIR = join11(tmpdir2(), "agent_moss_policy_cache");
|
|
10726
|
+
cacheDirOverride = null;
|
|
10727
|
+
PolicyCache = class {
|
|
10728
|
+
// Map 的插入序即 LRU 序(get 时重新 set 到末尾)
|
|
10729
|
+
constructor(options) {
|
|
10730
|
+
this.options = options;
|
|
10731
|
+
}
|
|
10732
|
+
options;
|
|
10733
|
+
memory = /* @__PURE__ */ new Map();
|
|
10734
|
+
cacheKey(sessionId, promptSession) {
|
|
10735
|
+
return `${sessionId}::${promptSession}`;
|
|
10736
|
+
}
|
|
10737
|
+
cacheFilePath(sessionId, promptSession) {
|
|
10738
|
+
const promptHash = createHash2("md5").update(promptSession).digest("hex").slice(0, 12);
|
|
10739
|
+
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10740
|
+
return join11(cacheDir(), safeSessionId, `${promptHash}.toml`);
|
|
10741
|
+
}
|
|
10742
|
+
/** 取缓存:内存 → 文件;未命中返回 null。 */
|
|
10743
|
+
get(sessionId, promptSession) {
|
|
10744
|
+
if (!this.options.enabled) return null;
|
|
10745
|
+
const key = this.cacheKey(sessionId, promptSession);
|
|
10746
|
+
const hit = this.memory.get(key);
|
|
10747
|
+
if (hit !== void 0) {
|
|
10748
|
+
this.memory.delete(key);
|
|
10749
|
+
this.memory.set(key, hit);
|
|
10750
|
+
return hit;
|
|
10751
|
+
}
|
|
10752
|
+
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10753
|
+
if (existsSync15(file)) {
|
|
10754
|
+
try {
|
|
10755
|
+
const policy = readFileSync15(file, "utf-8");
|
|
10756
|
+
this.remember(key, policy);
|
|
10757
|
+
return policy;
|
|
10758
|
+
} catch {
|
|
10759
|
+
}
|
|
10760
|
+
}
|
|
10761
|
+
return null;
|
|
10762
|
+
}
|
|
10763
|
+
/** 写缓存(内存 + 文件)。落盘失败不影响判定(缓存是旁路产物)。 */
|
|
10764
|
+
put(sessionId, promptSession, policy) {
|
|
10765
|
+
if (!this.options.enabled) return;
|
|
10766
|
+
this.remember(this.cacheKey(sessionId, promptSession), policy);
|
|
10767
|
+
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10768
|
+
try {
|
|
10769
|
+
mkdirSync7(join11(cacheDir(), sessionId.replace(/\//g, "_").replace(/\\/g, "_")), { recursive: true });
|
|
10770
|
+
writeFileSync7(file, policy, "utf-8");
|
|
10771
|
+
} catch {
|
|
10772
|
+
}
|
|
10773
|
+
}
|
|
10774
|
+
remember(key, policy) {
|
|
10775
|
+
if (this.memory.has(key)) this.memory.delete(key);
|
|
10776
|
+
this.memory.set(key, policy);
|
|
10777
|
+
while (this.memory.size > this.options.maxSize) {
|
|
10778
|
+
const oldest = this.memory.keys().next().value;
|
|
10779
|
+
if (oldest === void 0) break;
|
|
10780
|
+
this.memory.delete(oldest);
|
|
10781
|
+
}
|
|
10782
|
+
}
|
|
10783
|
+
invalidate(sessionId, promptSession) {
|
|
10784
|
+
this.memory.delete(this.cacheKey(sessionId, promptSession));
|
|
10785
|
+
try {
|
|
10786
|
+
unlinkSync3(this.cacheFilePath(sessionId, promptSession));
|
|
10787
|
+
} catch {
|
|
10788
|
+
}
|
|
10789
|
+
}
|
|
10790
|
+
invalidateSession(sessionId) {
|
|
10791
|
+
for (const k of [...this.memory.keys()]) {
|
|
10792
|
+
if (k.startsWith(`${sessionId}::`)) this.memory.delete(k);
|
|
10793
|
+
}
|
|
10794
|
+
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10795
|
+
const dir = join11(cacheDir(), safeSessionId);
|
|
10796
|
+
try {
|
|
10797
|
+
if (existsSync15(dir)) {
|
|
10798
|
+
for (const f of readdirSync5(dir)) {
|
|
10799
|
+
try {
|
|
10800
|
+
unlinkSync3(join11(dir, f));
|
|
10801
|
+
} catch {
|
|
10802
|
+
}
|
|
10803
|
+
}
|
|
10804
|
+
rmdirSync(dir);
|
|
10805
|
+
}
|
|
10806
|
+
} catch {
|
|
10807
|
+
}
|
|
10808
|
+
}
|
|
10809
|
+
clear() {
|
|
10810
|
+
this.memory.clear();
|
|
10811
|
+
try {
|
|
10812
|
+
if (existsSync15(cacheDir())) {
|
|
10813
|
+
for (const sessionDir of readdirSync5(cacheDir())) {
|
|
10814
|
+
const dir = join11(cacheDir(), sessionDir);
|
|
10815
|
+
try {
|
|
10816
|
+
for (const f of readdirSync5(dir)) unlinkSync3(join11(dir, f));
|
|
10817
|
+
rmdirSync(dir);
|
|
10818
|
+
} catch {
|
|
10819
|
+
}
|
|
10820
|
+
}
|
|
10821
|
+
}
|
|
10822
|
+
} catch {
|
|
10823
|
+
}
|
|
10824
|
+
}
|
|
10825
|
+
/** 当前内存缓存条目数。 */
|
|
10826
|
+
size() {
|
|
10827
|
+
return this.memory.size;
|
|
10828
|
+
}
|
|
10829
|
+
};
|
|
10830
|
+
defaultCache = null;
|
|
10831
|
+
}
|
|
9827
10832
|
});
|
|
9828
10833
|
|
|
9829
|
-
// src/
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
10834
|
+
// src/infra/logging.ts
|
|
10835
|
+
function safe(value, limit = 200) {
|
|
10836
|
+
const s = String(value ?? "");
|
|
10837
|
+
return s.length > limit ? `${s.slice(0, limit)}\u2026` : s;
|
|
10838
|
+
}
|
|
10839
|
+
var MossLogger;
|
|
10840
|
+
var init_logging = __esm({
|
|
10841
|
+
"src/infra/logging.ts"() {
|
|
10842
|
+
"use strict";
|
|
10843
|
+
init_esm_shims();
|
|
10844
|
+
MossLogger = class {
|
|
10845
|
+
constructor(sessionId) {
|
|
10846
|
+
this.sessionId = sessionId;
|
|
10847
|
+
}
|
|
10848
|
+
sessionId;
|
|
10849
|
+
stepTimes = {};
|
|
10850
|
+
stepStart = null;
|
|
10851
|
+
startTime = Date.now();
|
|
10852
|
+
prefix() {
|
|
10853
|
+
return `[${safe(this.sessionId, 64)}]`;
|
|
10854
|
+
}
|
|
10855
|
+
logInput(promptSession, actionHistory, aNext, reason) {
|
|
10856
|
+
console.error(
|
|
10857
|
+
`${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)}`
|
|
10858
|
+
);
|
|
10859
|
+
}
|
|
10860
|
+
startStep(stepName) {
|
|
10861
|
+
this.stepStart = Date.now();
|
|
10862
|
+
}
|
|
10863
|
+
endStep(stepName, detail = "") {
|
|
10864
|
+
if (this.stepStart === null) return;
|
|
10865
|
+
const elapsed = (Date.now() - this.stepStart) / 1e3;
|
|
10866
|
+
this.stepTimes[stepName] = elapsed;
|
|
10867
|
+
const detailStr = detail ? ` | ${safe(detail)}` : "";
|
|
10868
|
+
console.error(
|
|
10869
|
+
`${this.prefix()} \u6B65\u9AA4\u5B8C\u6210: ${safe(stepName, 48)} | \u8017\u65F6=${elapsed.toFixed(3)}s${detailStr}`
|
|
10870
|
+
);
|
|
10871
|
+
this.stepStart = null;
|
|
10872
|
+
}
|
|
10873
|
+
logLlmInteraction(step, promptSummary, responseSummary) {
|
|
10874
|
+
console.error(
|
|
10875
|
+
`${this.prefix()} LLM\u4EA4\u4E92 | \u6B65\u9AA4=${safe(step, 48)} | PROMPT\u6458\u8981=${safe(promptSummary, 200)} | \u54CD\u5E94\u6458\u8981=${safe(responseSummary, 200)}`
|
|
10876
|
+
);
|
|
10877
|
+
}
|
|
10878
|
+
logResult(decision, reason, policy = "") {
|
|
10879
|
+
const total = (Date.now() - this.startTime) / 1e3;
|
|
10880
|
+
const steps = Object.entries(this.stepTimes).map(([k, v]) => `${safe(k, 48)}=${v.toFixed(3)}s`).join(", ");
|
|
10881
|
+
console.error(
|
|
10882
|
+
`${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`
|
|
10883
|
+
);
|
|
10884
|
+
if (policy) {
|
|
10885
|
+
console.error(`${this.prefix()} \u751F\u6210\u7684Policy:
|
|
10886
|
+
${safe(policy, 2e3)}`);
|
|
10887
|
+
}
|
|
10888
|
+
}
|
|
10889
|
+
logError(step, error) {
|
|
10890
|
+
const name = error?.name ?? typeof error;
|
|
10891
|
+
console.error(
|
|
10892
|
+
`${this.prefix()} \u6B65\u9AA4\u5F02\u5E38: ${safe(step, 48)} | \u9519\u8BEF=${name}: ${safe(error?.message ?? error)}`
|
|
10893
|
+
);
|
|
10894
|
+
}
|
|
10895
|
+
};
|
|
10896
|
+
}
|
|
10897
|
+
});
|
|
9835
10898
|
|
|
9836
|
-
// src/
|
|
9837
|
-
|
|
9838
|
-
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
10899
|
+
// src/engine/analyzer.ts
|
|
10900
|
+
var analyzer_exports = {};
|
|
10901
|
+
__export(analyzer_exports, {
|
|
10902
|
+
DEFAULT_READONLY_POLICY: () => DEFAULT_READONLY_POLICY,
|
|
10903
|
+
POLICY_OUTPUT_DIR: () => POLICY_OUTPUT_DIR,
|
|
10904
|
+
_setPolicyOutputDirForTest: () => _setPolicyOutputDirForTest,
|
|
10905
|
+
analyze: () => analyze,
|
|
10906
|
+
buildPolicyPrompt: () => buildPolicyPrompt,
|
|
10907
|
+
parsePolicyFromLlm: () => parsePolicyFromLlm,
|
|
10908
|
+
policyOutputDir: () => policyOutputDir,
|
|
10909
|
+
savePolicyToFile: () => savePolicyToFile,
|
|
10910
|
+
validateToml: () => validateToml
|
|
10911
|
+
});
|
|
10912
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "fs";
|
|
10913
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
10914
|
+
import { join as join12, resolve as resolve10 } from "path";
|
|
10915
|
+
import { parse as parseToml } from "smol-toml";
|
|
10916
|
+
function _setPolicyOutputDirForTest(dir) {
|
|
10917
|
+
policyOutputDirOverride = dir;
|
|
10918
|
+
}
|
|
10919
|
+
function policyOutputDir() {
|
|
10920
|
+
return policyOutputDirOverride ?? POLICY_OUTPUT_DIR;
|
|
10921
|
+
}
|
|
10922
|
+
function savePolicyToFile(sessionId, policyToml) {
|
|
10923
|
+
if (!isPolicySaveEnabled()) return null;
|
|
10924
|
+
try {
|
|
10925
|
+
const outDir = policyOutputDir();
|
|
10926
|
+
mkdirSync8(outDir, { recursive: true });
|
|
10927
|
+
const safeName = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10928
|
+
const filepath = join12(outDir, `${safeName}.toml`);
|
|
10929
|
+
writeFileSync8(filepath, policyToml, "utf-8");
|
|
10930
|
+
return filepath;
|
|
10931
|
+
} catch (err) {
|
|
10932
|
+
console.warn(`[AgentMoss] \u7B56\u7565\u6587\u4EF6\u843D\u76D8\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u5224\u5B9A\uFF09: ${policyOutputDir()}: ${String(err)}`);
|
|
10933
|
+
return null;
|
|
10934
|
+
}
|
|
10935
|
+
}
|
|
10936
|
+
function parsePolicyFromLlm(response) {
|
|
10937
|
+
const tomlBlock = response.match(/```toml\s*\n([\s\S]*?)```/);
|
|
10938
|
+
if (tomlBlock) {
|
|
10939
|
+
const s = tomlBlock[1].trim();
|
|
10940
|
+
validateToml(s);
|
|
10941
|
+
return s;
|
|
10942
|
+
}
|
|
10943
|
+
const anyBlock = response.match(/```\s*\n([\s\S]*?)```/);
|
|
10944
|
+
if (anyBlock) {
|
|
10945
|
+
const s = anyBlock[1].trim();
|
|
10946
|
+
try {
|
|
10947
|
+
validateToml(s);
|
|
10948
|
+
return s;
|
|
10949
|
+
} catch {
|
|
10950
|
+
}
|
|
10951
|
+
}
|
|
10952
|
+
const whole = response.trim();
|
|
10953
|
+
validateToml(whole);
|
|
10954
|
+
return whole;
|
|
10955
|
+
}
|
|
10956
|
+
function validateToml(tomlStr) {
|
|
10957
|
+
try {
|
|
10958
|
+
parseToml(tomlStr);
|
|
10959
|
+
} catch (err) {
|
|
10960
|
+
throw new Error(`TOML \u89E3\u6790\u5931\u8D25: ${err.message}`);
|
|
10961
|
+
}
|
|
10962
|
+
}
|
|
10963
|
+
function loadPolicyMappingDoc() {
|
|
10964
|
+
const dir = resolveAssetDir(__dirname, "templates");
|
|
10965
|
+
if (dir) {
|
|
10966
|
+
const p = resolve10(dir, "policy_mapping.md");
|
|
10967
|
+
if (existsSync16(p)) return readFileSync16(p, "utf-8");
|
|
10968
|
+
}
|
|
10969
|
+
for (const cand of assetDirCandidates(__dirname, "templates")) {
|
|
10970
|
+
const p = resolve10(cand, "policy_mapping.md");
|
|
10971
|
+
if (existsSync16(p)) return readFileSync16(p, "utf-8");
|
|
10972
|
+
}
|
|
10973
|
+
return "";
|
|
10974
|
+
}
|
|
10975
|
+
function buildPolicyPrompt(promptSession) {
|
|
10976
|
+
const template = loadTemplates()["prompt1"] || "";
|
|
10977
|
+
return template.replaceAll("{prompt_policy_mapping_doc}", loadPolicyMappingDoc()).replaceAll("{prompt_session}", promptSession);
|
|
10978
|
+
}
|
|
10979
|
+
async function generatePolicyWithLLM(sessionId, promptSession) {
|
|
10980
|
+
const { default: OpenAI3 } = await import("openai");
|
|
10981
|
+
const cfg = getLLMConfig();
|
|
10982
|
+
const timeoutCfg = getTimeoutConfig();
|
|
10983
|
+
const retryCfg = getRetryConfig();
|
|
10984
|
+
const client = new OpenAI3({
|
|
10985
|
+
baseURL: cfg.baseUrl,
|
|
10986
|
+
apiKey: cfg.apiKey,
|
|
10987
|
+
timeout: (cfg.timeout || timeoutCfg.prompt1Timeout) * 1e3
|
|
10988
|
+
});
|
|
10989
|
+
const prompt1 = buildPolicyPrompt(promptSession);
|
|
10990
|
+
let lastErr = null;
|
|
10991
|
+
for (let attempt = 1; attempt <= Math.max(1, retryCfg.maxRetries); attempt++) {
|
|
10992
|
+
try {
|
|
10993
|
+
const resp = await client.chat.completions.create({
|
|
10994
|
+
model: cfg.model,
|
|
10995
|
+
temperature: 0.1,
|
|
10996
|
+
messages: [{ role: "user", content: prompt1 }]
|
|
10997
|
+
});
|
|
10998
|
+
try {
|
|
10999
|
+
const usage = resp.usage || {};
|
|
11000
|
+
recordTokenUsage({
|
|
11001
|
+
sessionId,
|
|
11002
|
+
step: "step2_policy_gen",
|
|
11003
|
+
model: cfg.model,
|
|
11004
|
+
promptTokens: usage.prompt_tokens || 0,
|
|
11005
|
+
completionTokens: usage.completion_tokens || 0,
|
|
11006
|
+
totalTokens: usage.total_tokens || 0
|
|
11007
|
+
});
|
|
11008
|
+
} catch {
|
|
11009
|
+
}
|
|
11010
|
+
return parsePolicyFromLlm(resp.choices?.[0]?.message?.content || "");
|
|
11011
|
+
} catch (err) {
|
|
11012
|
+
lastErr = err;
|
|
11013
|
+
if (attempt < Math.max(1, retryCfg.maxRetries)) {
|
|
11014
|
+
await new Promise((r) => setTimeout(r, retryCfg.retryInterval * 1e3));
|
|
11015
|
+
}
|
|
11016
|
+
}
|
|
11017
|
+
}
|
|
11018
|
+
throw lastErr ?? new Error("\u7B56\u7565\u751F\u6210\u5931\u8D25");
|
|
9842
11019
|
}
|
|
11020
|
+
function allowResult(policy, judgment) {
|
|
11021
|
+
return {
|
|
11022
|
+
decision: "Allow",
|
|
11023
|
+
reason: judgment.reason,
|
|
11024
|
+
risk_level: judgment.riskLevel,
|
|
11025
|
+
risk_type: judgment.riskType,
|
|
11026
|
+
violated_layers: judgment.violatedLayers || [],
|
|
11027
|
+
policy,
|
|
11028
|
+
violated_policy: "",
|
|
11029
|
+
confidence: judgment.confidence
|
|
11030
|
+
};
|
|
11031
|
+
}
|
|
11032
|
+
async function analyze(input) {
|
|
11033
|
+
const mossLog = new MossLogger(input.sessionId);
|
|
11034
|
+
try {
|
|
11035
|
+
return await analyzeInner(input, mossLog);
|
|
11036
|
+
} catch (err) {
|
|
11037
|
+
mossLog.logError("unknown", err);
|
|
11038
|
+
const result = {
|
|
11039
|
+
decision: "Deny",
|
|
11040
|
+
reason: `\u5206\u6790\u8FC7\u7A0B\u53D1\u751F\u672A\u9884\u671F\u5F02\u5E38\uFF1A${String(err)}`,
|
|
11041
|
+
risk_level: "high",
|
|
11042
|
+
risk_type: "",
|
|
11043
|
+
violated_layers: [],
|
|
11044
|
+
policy: "",
|
|
11045
|
+
violated_policy: "\u672A\u77E5\u5F02\u5E38",
|
|
11046
|
+
confidence: 100
|
|
11047
|
+
};
|
|
11048
|
+
mossLog.logResult("Deny", result.reason);
|
|
11049
|
+
return result;
|
|
11050
|
+
}
|
|
11051
|
+
}
|
|
11052
|
+
async function analyzeInner(input, mossLog) {
|
|
11053
|
+
mossLog.logInput(input.promptSession, input.actionHistory || [], input.aNext, input.reason || "");
|
|
11054
|
+
mossLog.startStep("step1_security_judge");
|
|
11055
|
+
const judgment = await runAnalysis(input);
|
|
11056
|
+
mossLog.endStep(
|
|
11057
|
+
"step1_security_judge",
|
|
11058
|
+
`allowed=${judgment.allowed}, risk_level=${judgment.riskLevel}, source=${judgment.source}`
|
|
11059
|
+
);
|
|
11060
|
+
if (!judgment.allowed) {
|
|
11061
|
+
const denied = {
|
|
11062
|
+
decision: "Deny",
|
|
11063
|
+
reason: judgment.reason,
|
|
11064
|
+
risk_level: judgment.riskLevel,
|
|
11065
|
+
risk_type: judgment.riskType,
|
|
11066
|
+
violated_layers: judgment.violatedLayers || [],
|
|
11067
|
+
policy: "",
|
|
11068
|
+
violated_policy: `[${judgment.riskType}] ${judgment.reason}`,
|
|
11069
|
+
confidence: judgment.confidence
|
|
11070
|
+
};
|
|
11071
|
+
mossLog.logResult("Deny", denied.reason);
|
|
11072
|
+
return denied;
|
|
11073
|
+
}
|
|
11074
|
+
const cache3 = getDefaultCache();
|
|
11075
|
+
mossLog.startStep("step2_cache_lookup");
|
|
11076
|
+
const cached = cache3.get(input.sessionId, input.promptSession);
|
|
11077
|
+
if (cached !== null) {
|
|
11078
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u547D\u4E2D");
|
|
11079
|
+
savePolicyToFile(input.sessionId, cached);
|
|
11080
|
+
const res = allowResult(cached, judgment);
|
|
11081
|
+
mossLog.logResult("Allow", res.reason, cached);
|
|
11082
|
+
return res;
|
|
11083
|
+
}
|
|
11084
|
+
if (!isPolicyGenEnabled()) {
|
|
11085
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u672A\u547D\u4E2D\uFF0CPolicy \u751F\u6210\u5DF2\u7981\u7528");
|
|
11086
|
+
mossLog.startStep("step2_default_policy");
|
|
11087
|
+
cache3.put(input.sessionId, input.promptSession, DEFAULT_READONLY_POLICY);
|
|
11088
|
+
savePolicyToFile(input.sessionId, DEFAULT_READONLY_POLICY);
|
|
11089
|
+
const res = allowResult(DEFAULT_READONLY_POLICY, judgment);
|
|
11090
|
+
mossLog.endStep("step2_default_policy", "\u4F7F\u7528\u9ED8\u8BA4\u6700\u5C0F\u6743\u9650 Policy");
|
|
11091
|
+
mossLog.logResult("Allow", res.reason, DEFAULT_READONLY_POLICY);
|
|
11092
|
+
return res;
|
|
11093
|
+
}
|
|
11094
|
+
if (judgment.source === "whitelist_bypass") {
|
|
11095
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u672A\u547D\u4E2D\uFF0C\u767D\u540D\u5355\u5DE5\u5177");
|
|
11096
|
+
cache3.put(input.sessionId, input.promptSession, DEFAULT_READONLY_POLICY);
|
|
11097
|
+
savePolicyToFile(input.sessionId, DEFAULT_READONLY_POLICY);
|
|
11098
|
+
const res = allowResult(DEFAULT_READONLY_POLICY, judgment);
|
|
11099
|
+
mossLog.logResult("Allow", res.reason, DEFAULT_READONLY_POLICY);
|
|
11100
|
+
return res;
|
|
11101
|
+
}
|
|
11102
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u672A\u547D\u4E2D\uFF0C\u542F\u7528 LLM \u751F\u6210");
|
|
11103
|
+
mossLog.startStep("step2_generate_policy");
|
|
11104
|
+
try {
|
|
11105
|
+
const policy = await generatePolicyWithLLM(input.sessionId, input.promptSession);
|
|
11106
|
+
cache3.put(input.sessionId, input.promptSession, policy);
|
|
11107
|
+
savePolicyToFile(input.sessionId, policy);
|
|
11108
|
+
mossLog.endStep("step2_generate_policy", "Policy \u751F\u6210\u5E76\u7F13\u5B58\u6210\u529F");
|
|
11109
|
+
const res = allowResult(policy, judgment);
|
|
11110
|
+
mossLog.logResult("Allow", res.reason, policy);
|
|
11111
|
+
return res;
|
|
11112
|
+
} catch (err) {
|
|
11113
|
+
mossLog.endStep("step2_generate_policy", `\u5931\u8D25: ${String(err)}`);
|
|
11114
|
+
mossLog.logError("step2_generate_policy", err);
|
|
11115
|
+
const failed = {
|
|
11116
|
+
decision: "Deny",
|
|
11117
|
+
reason: `\u5B89\u5168\u68C0\u6D4B\u901A\u8FC7\uFF0C\u4F46\u7B56\u7565\u751F\u6210\u5931\u8D25\uFF1A${String(err)}`,
|
|
11118
|
+
risk_level: judgment.riskLevel,
|
|
11119
|
+
risk_type: judgment.riskType,
|
|
11120
|
+
violated_layers: judgment.violatedLayers || [],
|
|
11121
|
+
policy: "",
|
|
11122
|
+
violated_policy: "\u65E0\u6CD5\u751F\u6210\u7B56\u7565\uFF1ALLM \u670D\u52A1\u5F02\u5E38\u6216\u8FD4\u56DE\u683C\u5F0F\u65E0\u6548",
|
|
11123
|
+
confidence: judgment.confidence
|
|
11124
|
+
};
|
|
11125
|
+
mossLog.logResult("Deny", failed.reason);
|
|
11126
|
+
return failed;
|
|
11127
|
+
}
|
|
11128
|
+
}
|
|
11129
|
+
var POLICY_OUTPUT_DIR, policyOutputDirOverride, DEFAULT_READONLY_POLICY;
|
|
11130
|
+
var init_analyzer = __esm({
|
|
11131
|
+
"src/engine/analyzer.ts"() {
|
|
11132
|
+
"use strict";
|
|
11133
|
+
init_esm_shims();
|
|
11134
|
+
init_coordinator();
|
|
11135
|
+
init_policy_cache();
|
|
11136
|
+
init_config();
|
|
11137
|
+
init_template_loader();
|
|
11138
|
+
init_asset_dir();
|
|
11139
|
+
init_token_stats();
|
|
11140
|
+
init_logging();
|
|
11141
|
+
POLICY_OUTPUT_DIR = join12(tmpdir3(), "agent_moss");
|
|
11142
|
+
policyOutputDirOverride = null;
|
|
11143
|
+
DEFAULT_READONLY_POLICY = `landlock_optional = false
|
|
11144
|
+
mount_isolation_fallback = false
|
|
11145
|
+
|
|
11146
|
+
[path_groups]
|
|
11147
|
+
system_binaries = true
|
|
11148
|
+
system_libraries = true
|
|
11149
|
+
temp_directories = true
|
|
11150
|
+
device_files = false
|
|
11151
|
+
proc_filesystem = false
|
|
11152
|
+
network_config = false
|
|
11153
|
+
wsl_paths = false
|
|
11154
|
+
|
|
11155
|
+
[namespaces]
|
|
11156
|
+
mount = true
|
|
11157
|
+
pid = false
|
|
11158
|
+
network = true
|
|
11159
|
+
user = false
|
|
11160
|
+
|
|
11161
|
+
[resources]
|
|
11162
|
+
timeout_secs = 30
|
|
11163
|
+
max_memory_bytes = 268435456
|
|
11164
|
+
|
|
11165
|
+
[environment]
|
|
11166
|
+
whitelist = ["PATH", "LANG", "HOME", "USER", "HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy", "ALL_PROXY", "all_proxy", "NO_PROXY", "no_proxy"]
|
|
11167
|
+
`;
|
|
11168
|
+
}
|
|
11169
|
+
});
|
|
11170
|
+
|
|
11171
|
+
// src/infra/esm-require.ts
|
|
11172
|
+
import { createRequire as createRequire2 } from "module";
|
|
11173
|
+
function esmRequire(id) {
|
|
11174
|
+
return requireFromHere(id);
|
|
11175
|
+
}
|
|
11176
|
+
var requireFromHere;
|
|
11177
|
+
var init_esm_require = __esm({
|
|
11178
|
+
"src/infra/esm-require.ts"() {
|
|
11179
|
+
"use strict";
|
|
11180
|
+
init_esm_shims();
|
|
11181
|
+
requireFromHere = createRequire2(import.meta.url);
|
|
11182
|
+
}
|
|
11183
|
+
});
|
|
9843
11184
|
|
|
9844
11185
|
// src/actrail.ts
|
|
11186
|
+
import * as fs5 from "fs";
|
|
9845
11187
|
function escape(value) {
|
|
9846
11188
|
return value.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/=/g, "\\e");
|
|
9847
11189
|
}
|
|
@@ -9939,15 +11281,15 @@ function reportEnforcementEvent(opts) {
|
|
|
9939
11281
|
return false;
|
|
9940
11282
|
}
|
|
9941
11283
|
}
|
|
9942
|
-
|
|
9943
|
-
|
|
9944
|
-
|
|
11284
|
+
var init_actrail = __esm({
|
|
11285
|
+
"src/actrail.ts"() {
|
|
11286
|
+
"use strict";
|
|
11287
|
+
init_esm_shims();
|
|
11288
|
+
init_esm_require();
|
|
11289
|
+
}
|
|
11290
|
+
});
|
|
9945
11291
|
|
|
9946
11292
|
// 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
11293
|
function projectStages(actionHistory) {
|
|
9952
11294
|
const stages = [];
|
|
9953
11295
|
let prev = null;
|
|
@@ -10019,19 +11361,21 @@ function mineDangerousPatterns(sessions, minSup = 2, maxLen = 4) {
|
|
|
10019
11361
|
out.sort((a, b) => b.support - a.support || b.pattern.length - a.pattern.length);
|
|
10020
11362
|
return out;
|
|
10021
11363
|
}
|
|
11364
|
+
var DANGER_STAGES, SENSITIVE_PAIRS;
|
|
11365
|
+
var init_pattern_miner = __esm({
|
|
11366
|
+
"src/engine/formal-engine/pattern_miner.ts"() {
|
|
11367
|
+
"use strict";
|
|
11368
|
+
init_esm_shims();
|
|
11369
|
+
init_builder();
|
|
11370
|
+
DANGER_STAGES = /* @__PURE__ */ new Set(["PRIVESC", "EXFIL", "LISTEN"]);
|
|
11371
|
+
SENSITIVE_PAIRS = /* @__PURE__ */ new Set(["CRED|LISTEN", "LISTEN|CRED"]);
|
|
11372
|
+
}
|
|
11373
|
+
});
|
|
10022
11374
|
|
|
10023
11375
|
// src/agent-gate-reader.ts
|
|
10024
|
-
init_esm_shims();
|
|
10025
11376
|
import * as fs6 from "fs";
|
|
10026
11377
|
import * as os3 from "os";
|
|
10027
11378
|
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
11379
|
function normalizeAction(row) {
|
|
10036
11380
|
return {
|
|
10037
11381
|
actionType: String(row.action_type || row.tool_name || row.name || ""),
|
|
@@ -10115,8 +11459,97 @@ function loadAgentGateSessions(dbPath = "") {
|
|
|
10115
11459
|
const jsonDefault = path5.join(os3.homedir(), ".local", "share", "agent_gate", "tool_calls");
|
|
10116
11460
|
return readJsonDir(jsonDefault);
|
|
10117
11461
|
}
|
|
11462
|
+
var DEFAULT_AGENTGATE_SQLITE;
|
|
11463
|
+
var init_agent_gate_reader = __esm({
|
|
11464
|
+
"src/agent-gate-reader.ts"() {
|
|
11465
|
+
"use strict";
|
|
11466
|
+
init_esm_shims();
|
|
11467
|
+
init_esm_require();
|
|
11468
|
+
DEFAULT_AGENTGATE_SQLITE = path5.join(
|
|
11469
|
+
os3.homedir(),
|
|
11470
|
+
".local",
|
|
11471
|
+
"share",
|
|
11472
|
+
"agent_gate",
|
|
11473
|
+
"agent_gate_ts.db"
|
|
11474
|
+
);
|
|
11475
|
+
}
|
|
11476
|
+
});
|
|
11477
|
+
|
|
11478
|
+
// src/adapters/observable.ts
|
|
11479
|
+
function isRawFormat(data) {
|
|
11480
|
+
if (!data || typeof data !== "object") return false;
|
|
11481
|
+
const d = data;
|
|
11482
|
+
if ("current_action" in d) return true;
|
|
11483
|
+
if (!("a_next" in d)) return true;
|
|
11484
|
+
if (!("session_id" in d) && "agent_id" in d) return true;
|
|
11485
|
+
return false;
|
|
11486
|
+
}
|
|
11487
|
+
function adaptObservable(rawData) {
|
|
11488
|
+
const sessionId = rawData.session_id || rawData.agent_id || "unknown";
|
|
11489
|
+
const actionRaw = rawData.a_next || rawData.current_action || {};
|
|
11490
|
+
const aNext = actionRaw && typeof actionRaw === "object" ? {
|
|
11491
|
+
action_type: actionRaw.action_type || actionRaw.tool || "unknown",
|
|
11492
|
+
action_detail: actionRaw.action_detail || actionRaw.command || ""
|
|
11493
|
+
} : { action_type: "unknown", action_detail: "" };
|
|
11494
|
+
const historyRaw = Array.isArray(rawData.action_history) ? rawData.action_history : [];
|
|
11495
|
+
const actionHistory = historyRaw.filter((h) => h && typeof h === "object").map((h) => {
|
|
11496
|
+
const item = {
|
|
11497
|
+
action_type: String(h.action_type || h.name || h.tool || ""),
|
|
11498
|
+
action_detail: String(h.action_detail || h.command || "")
|
|
11499
|
+
};
|
|
11500
|
+
if ("output" in h) item.output = h.output ?? "";
|
|
11501
|
+
return item;
|
|
11502
|
+
});
|
|
11503
|
+
const result = {
|
|
11504
|
+
session_id: sessionId,
|
|
11505
|
+
prompt_session: rawData.prompt_session || rawData.prompt || "",
|
|
11506
|
+
action_history: actionHistory,
|
|
11507
|
+
a_next: aNext,
|
|
11508
|
+
reason: rawData.reason || (actionRaw && typeof actionRaw === "object" ? actionRaw.description : "") || "",
|
|
11509
|
+
os_type: rawData.os_type || "",
|
|
11510
|
+
cwd: rawData.cwd || "",
|
|
11511
|
+
agent_id: rawData.agent_id || ""
|
|
11512
|
+
};
|
|
11513
|
+
if (!result.reason && actionRaw && typeof actionRaw === "object") {
|
|
11514
|
+
result.reason = actionRaw.description || "";
|
|
11515
|
+
}
|
|
11516
|
+
let metadata = rawData.metadata;
|
|
11517
|
+
if (!metadata || typeof metadata !== "object" || Object.keys(metadata).length === 0) {
|
|
11518
|
+
metadata = {};
|
|
11519
|
+
for (const k of ["agent_id", "sandbox_id", "trace_id"]) {
|
|
11520
|
+
if (k in rawData && !(k in result)) metadata[k] = rawData[k];
|
|
11521
|
+
}
|
|
11522
|
+
}
|
|
11523
|
+
if (metadata && Object.keys(metadata).length > 0) result.metadata = metadata;
|
|
11524
|
+
return result;
|
|
11525
|
+
}
|
|
11526
|
+
function normalizeObservablePayload(body) {
|
|
11527
|
+
if (!isRawFormat(body)) return body;
|
|
11528
|
+
const adapted = adaptObservable(body);
|
|
11529
|
+
const actionType = String(adapted.a_next?.action_type || "").trim().toLowerCase();
|
|
11530
|
+
const actionDetail = String(adapted.a_next?.action_detail || "").trim();
|
|
11531
|
+
if (!actionDetail && (actionType === "" || actionType === "unknown")) {
|
|
11532
|
+
const err = new Error(
|
|
11533
|
+
"\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"
|
|
11534
|
+
);
|
|
11535
|
+
err.issues = [{
|
|
11536
|
+
path: ["a_next"],
|
|
11537
|
+
message: err.message
|
|
11538
|
+
}];
|
|
11539
|
+
throw err;
|
|
11540
|
+
}
|
|
11541
|
+
return adapted;
|
|
11542
|
+
}
|
|
11543
|
+
var init_observable = __esm({
|
|
11544
|
+
"src/adapters/observable.ts"() {
|
|
11545
|
+
"use strict";
|
|
11546
|
+
init_esm_shims();
|
|
11547
|
+
}
|
|
11548
|
+
});
|
|
10118
11549
|
|
|
10119
11550
|
// src/routes.ts
|
|
11551
|
+
import { join as join14, dirname as dirname11 } from "path";
|
|
11552
|
+
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
10120
11553
|
async function healthRoute(c) {
|
|
10121
11554
|
return c.json(HealthResponseSchema.parse({
|
|
10122
11555
|
instance: process.env.AGENT_MOSS_INSTANCE || ""
|
|
@@ -10126,13 +11559,13 @@ async function analyzeRoute(c) {
|
|
|
10126
11559
|
const t0 = performance.now();
|
|
10127
11560
|
try {
|
|
10128
11561
|
const body = await c.req.json();
|
|
10129
|
-
const req = AnalyzeRequestSchema.parse(body);
|
|
11562
|
+
const req = AnalyzeRequestSchema.parse(normalizeObservablePayload(body));
|
|
10130
11563
|
let osType = req.os_type;
|
|
10131
11564
|
if (!osType) {
|
|
10132
11565
|
osType = process.platform === "win32" ? "windows" : "linux";
|
|
10133
11566
|
}
|
|
10134
11567
|
const agentId = req.agent_id || req.metadata?.agent_type || "";
|
|
10135
|
-
const result = await
|
|
11568
|
+
const result = await analyze({
|
|
10136
11569
|
sessionId: req.session_id,
|
|
10137
11570
|
promptSession: req.prompt_session,
|
|
10138
11571
|
// output 必须透传:L2 的「授权(consent)」判定看的是**用户对提问的回答**,
|
|
@@ -10152,6 +11585,20 @@ async function analyzeRoute(c) {
|
|
|
10152
11585
|
output: h.output
|
|
10153
11586
|
}))
|
|
10154
11587
|
})),
|
|
11588
|
+
// prompt_history 必须透传:L2 意图一致性(logic-rules.ts 的
|
|
11589
|
+
// checkIntentConsistency)与 L3 语义分析(llm-analyzer.ts 的
|
|
11590
|
+
// formatPromptHistory)都靠它看**完整的交错历史**;只传 prompt_session
|
|
11591
|
+
// (最后一条用户消息)会让这两处在压缩/续跑场景下失明。
|
|
11592
|
+
// 历史 bug:请求模型里有这个字段,但路由的 runAnalysis 调用没映射它 →
|
|
11593
|
+
// 经 TS HTTP 入口的判定与 Python HTTP(routes.py 有透传)、与库内直调
|
|
11594
|
+
// runAnalysis 的结果都不一致(2026-10 修正,映射口径对齐 Python)。
|
|
11595
|
+
promptHistory: (req.prompt_history || []).map((t) => ({
|
|
11596
|
+
text: t.text,
|
|
11597
|
+
actions: t.actions.map((a) => ({
|
|
11598
|
+
actionType: a.action_type,
|
|
11599
|
+
actionDetail: a.action_detail
|
|
11600
|
+
}))
|
|
11601
|
+
})),
|
|
10155
11602
|
aNext: {
|
|
10156
11603
|
actionType: req.a_next.action_type,
|
|
10157
11604
|
actionDetail: req.a_next.action_detail
|
|
@@ -10169,15 +11616,15 @@ async function analyzeRoute(c) {
|
|
|
10169
11616
|
try {
|
|
10170
11617
|
reportEnforcementEvent({
|
|
10171
11618
|
traceId: req.session_id,
|
|
10172
|
-
decision: result.
|
|
11619
|
+
decision: result.decision === "Allow" ? "allow" : "deny",
|
|
10173
11620
|
toolName: req.a_next.action_type,
|
|
10174
11621
|
toolCommand: req.a_next.action_detail,
|
|
10175
11622
|
sessionId: req.session_id,
|
|
10176
11623
|
prompt: req.prompt_session,
|
|
10177
|
-
riskLevel: result.
|
|
10178
|
-
riskType: result.
|
|
11624
|
+
riskLevel: result.risk_level,
|
|
11625
|
+
riskType: result.risk_type,
|
|
10179
11626
|
reason: result.reason,
|
|
10180
|
-
violatedLayers: result.
|
|
11627
|
+
violatedLayers: result.violated_layers,
|
|
10181
11628
|
confidence: result.confidence,
|
|
10182
11629
|
durationMs: Math.round(durationMs * 10) / 10
|
|
10183
11630
|
});
|
|
@@ -10194,7 +11641,7 @@ async function analyzeRoute(c) {
|
|
|
10194
11641
|
}, req.session_id, Boolean(meta.is_error));
|
|
10195
11642
|
const judgment = incMatchedToDeny(inc);
|
|
10196
11643
|
if (judgment) {
|
|
10197
|
-
const layers = [...result.
|
|
11644
|
+
const layers = [...result.violated_layers];
|
|
10198
11645
|
if (!layers.includes("L2.2-stateful")) layers.push("L2.2-stateful");
|
|
10199
11646
|
console.error(
|
|
10200
11647
|
`[AgentMoss] stateful \u589E\u91CF\u62E6\u622A: agent=${agentId || "-"} property=${judgment.property_id} risk=${judgment.risk_level}`
|
|
@@ -10220,13 +11667,15 @@ async function analyzeRoute(c) {
|
|
|
10220
11667
|
}
|
|
10221
11668
|
return c.json(
|
|
10222
11669
|
AnalyzeResponseSchema.parse({
|
|
10223
|
-
decision: result.
|
|
11670
|
+
decision: result.decision,
|
|
10224
11671
|
reason: result.reason,
|
|
10225
|
-
risk_level: result.
|
|
10226
|
-
risk_type: result.
|
|
10227
|
-
violated_layers: result.
|
|
10228
|
-
policy
|
|
10229
|
-
violated_policy
|
|
11672
|
+
risk_level: result.risk_level,
|
|
11673
|
+
risk_type: result.risk_type,
|
|
11674
|
+
violated_layers: result.violated_layers,
|
|
11675
|
+
// policy / violated_policy 现在真的有值(镜像 Python analyze()):
|
|
11676
|
+
// Allow → 最小权限 TOML;Deny → 空 policy + violated_policy 描述
|
|
11677
|
+
policy: result.policy,
|
|
11678
|
+
violated_policy: result.violated_policy,
|
|
10230
11679
|
confidence: result.confidence,
|
|
10231
11680
|
analysis_duration_ms: Math.round(durationMs * 10) / 10
|
|
10232
11681
|
})
|
|
@@ -10234,15 +11683,14 @@ async function analyzeRoute(c) {
|
|
|
10234
11683
|
} catch (err) {
|
|
10235
11684
|
console.error("[AgentMoss] Analysis error:", err);
|
|
10236
11685
|
if (err instanceof SyntaxError) {
|
|
10237
|
-
return c.json({ error: "Invalid JSON"
|
|
11686
|
+
return c.json({ detail: err.message, error: "Invalid JSON" }, 422);
|
|
10238
11687
|
}
|
|
10239
11688
|
if (err?.issues) {
|
|
10240
|
-
return c.json({ error: "Validation error"
|
|
11689
|
+
return c.json({ detail: err.issues, error: "Validation error" }, 422);
|
|
10241
11690
|
}
|
|
10242
|
-
return c.json({ error: "Internal error"
|
|
11691
|
+
return c.json({ detail: String(err), error: "Internal error" }, 500);
|
|
10243
11692
|
}
|
|
10244
11693
|
}
|
|
10245
|
-
var reviewStore = null;
|
|
10246
11694
|
function getChainStateStoreFor(_agentId) {
|
|
10247
11695
|
if (!reviewStore) reviewStore = new ChainStateStore();
|
|
10248
11696
|
return reviewStore;
|
|
@@ -10297,13 +11745,25 @@ async function reviewRoute(c) {
|
|
|
10297
11745
|
const violations = [];
|
|
10298
11746
|
if (sessions.length > 0 || tracker.nodes.length > 0) {
|
|
10299
11747
|
const eng = new FormalEngine();
|
|
11748
|
+
const verifySessions = sessions.length > 0 ? sessions.map((s) => ({ sessionId: s.sessionId, actionHistory: s.actionHistory })) : tracker.nodes.length > 0 ? [{
|
|
11749
|
+
sessionId: tracker.agentId,
|
|
11750
|
+
actionHistory: tracker.nodes.map((n) => ({
|
|
11751
|
+
actionType: n.actionType,
|
|
11752
|
+
actionDetail: n.actionDetail
|
|
11753
|
+
}))
|
|
11754
|
+
}] : [];
|
|
10300
11755
|
const full = eng.verify(
|
|
10301
|
-
|
|
11756
|
+
verifySessions,
|
|
10302
11757
|
req.a_next ? { actionType: req.a_next.action_type, actionDetail: req.a_next.action_detail } : void 0
|
|
10303
11758
|
);
|
|
10304
11759
|
verified = full.verified;
|
|
10305
11760
|
for (const v of full.violations) {
|
|
10306
|
-
violations.push({
|
|
11761
|
+
violations.push({
|
|
11762
|
+
property_id: v.propertyId,
|
|
11763
|
+
reason: v.reason,
|
|
11764
|
+
risk_level: v.riskLevel,
|
|
11765
|
+
path: v.path
|
|
11766
|
+
});
|
|
10307
11767
|
}
|
|
10308
11768
|
}
|
|
10309
11769
|
const pending = tracker.pendingPrefixes.map((p) => ({
|
|
@@ -10333,22 +11793,126 @@ async function reviewRoute(c) {
|
|
|
10333
11793
|
return c.json({ error: "Internal error", detail: String(err) }, 500);
|
|
10334
11794
|
}
|
|
10335
11795
|
}
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
11796
|
+
function getBrainRuleStore() {
|
|
11797
|
+
return new BrainRuleStore();
|
|
11798
|
+
}
|
|
11799
|
+
function getBrainSkillManager() {
|
|
11800
|
+
const dir = resolveAssetDir(__dirname9, "skills") ?? join14(__dirname9, "..", "..", "agent_moss", "skills");
|
|
11801
|
+
return new BrainSkillManager(dir);
|
|
11802
|
+
}
|
|
11803
|
+
async function brainRulesRoute(c) {
|
|
11804
|
+
const store = getBrainRuleStore();
|
|
11805
|
+
const layerRaw = c.req.query("layer");
|
|
11806
|
+
const layer = layerRaw ? parseInt(layerRaw, 10) : void 0;
|
|
11807
|
+
const rules = store.getActiveRules(layer);
|
|
11808
|
+
return c.json({
|
|
11809
|
+
rules: rules.map((r) => ({
|
|
11810
|
+
id: r.id,
|
|
11811
|
+
layer: r.layer,
|
|
11812
|
+
rule_type: r.ruleType,
|
|
11813
|
+
pattern: r.pattern,
|
|
11814
|
+
scope: r.scope,
|
|
11815
|
+
confidence: r.confidence,
|
|
11816
|
+
explanation: r.explanation
|
|
11817
|
+
}))
|
|
11818
|
+
});
|
|
11819
|
+
}
|
|
11820
|
+
async function brainDraftsRoute(c) {
|
|
11821
|
+
const store = getBrainRuleStore();
|
|
11822
|
+
return c.json({
|
|
11823
|
+
drafts: store.getDraftRules().map((r) => ({
|
|
11824
|
+
id: r.id,
|
|
11825
|
+
layer: r.layer,
|
|
11826
|
+
rule_type: r.ruleType,
|
|
11827
|
+
pattern: r.pattern,
|
|
11828
|
+
scope: r.scope,
|
|
11829
|
+
confidence: r.confidence,
|
|
11830
|
+
explanation: r.explanation,
|
|
11831
|
+
source_event_ids: r.sourceEventIds
|
|
11832
|
+
}))
|
|
11833
|
+
});
|
|
11834
|
+
}
|
|
11835
|
+
async function brainApproveRoute(c) {
|
|
11836
|
+
const ruleId = c.req.param("rule_id") ?? "";
|
|
11837
|
+
try {
|
|
11838
|
+
getBrainRuleStore().approveRule(ruleId);
|
|
11839
|
+
return c.json({ status: "approved" });
|
|
11840
|
+
} catch (err) {
|
|
11841
|
+
return c.json({ detail: String(err.message) }, 400);
|
|
11842
|
+
}
|
|
11843
|
+
}
|
|
11844
|
+
async function brainRejectRoute(c) {
|
|
11845
|
+
const ruleId = c.req.param("rule_id") ?? "";
|
|
11846
|
+
const reason = c.req.query("reason") || "";
|
|
11847
|
+
try {
|
|
11848
|
+
getBrainRuleStore().rejectRule(ruleId);
|
|
11849
|
+
return c.json({ status: "rejected" });
|
|
11850
|
+
} catch (err) {
|
|
11851
|
+
return c.json({ detail: String(err.message) }, 400);
|
|
11852
|
+
}
|
|
11853
|
+
}
|
|
11854
|
+
async function brainStatsRoute(c) {
|
|
11855
|
+
return c.json(getBrainRuleStore().getStats());
|
|
11856
|
+
}
|
|
11857
|
+
async function brainSkillsRoute(c) {
|
|
11858
|
+
const mgr = getBrainSkillManager();
|
|
11859
|
+
const skills = mgr.getActiveSkills();
|
|
11860
|
+
mgr.close();
|
|
11861
|
+
return c.json({ skills });
|
|
11862
|
+
}
|
|
11863
|
+
async function brainExportRoute(c) {
|
|
11864
|
+
const store = getBrainRuleStore();
|
|
11865
|
+
return c.json({
|
|
11866
|
+
rules: store.getRulesForExport().map((r) => ({
|
|
11867
|
+
id: r.id,
|
|
11868
|
+
layer: r.layer,
|
|
11869
|
+
rule_type: r.ruleType,
|
|
11870
|
+
pattern: r.pattern,
|
|
11871
|
+
scope: r.scope,
|
|
11872
|
+
confidence: r.confidence,
|
|
11873
|
+
explanation: r.explanation,
|
|
11874
|
+
origin_node: r.originNode
|
|
11875
|
+
}))
|
|
11876
|
+
});
|
|
11877
|
+
}
|
|
11878
|
+
async function brainImportRoute(c) {
|
|
11879
|
+
const body = await c.req.json();
|
|
11880
|
+
const rules = Array.isArray(body) ? body : [];
|
|
11881
|
+
getBrainRuleStore().importRules(rules.map((r) => ({
|
|
11882
|
+
layer: r.layer ?? 1,
|
|
11883
|
+
ruleType: r.rule_type ?? "",
|
|
11884
|
+
pattern: r.pattern ?? "",
|
|
11885
|
+
scope: r.scope ?? "all",
|
|
11886
|
+
confidence: r.confidence ?? 0.9,
|
|
11887
|
+
explanation: r.explanation ?? ""
|
|
11888
|
+
})));
|
|
11889
|
+
return c.json({ imported: rules.length });
|
|
11890
|
+
}
|
|
11891
|
+
var __dirname9, reviewStore;
|
|
11892
|
+
var init_routes = __esm({
|
|
11893
|
+
"src/routes.ts"() {
|
|
11894
|
+
"use strict";
|
|
11895
|
+
init_esm_shims();
|
|
11896
|
+
init_models();
|
|
11897
|
+
init_analyzer();
|
|
11898
|
+
init_actrail();
|
|
11899
|
+
init_formal_engine();
|
|
11900
|
+
init_pattern_miner();
|
|
11901
|
+
init_agent_gate_reader();
|
|
11902
|
+
init_observable();
|
|
11903
|
+
init_rule_store();
|
|
11904
|
+
init_skill_manager();
|
|
11905
|
+
init_asset_dir();
|
|
11906
|
+
__dirname9 = dirname11(fileURLToPath10(import.meta.url));
|
|
11907
|
+
reviewStore = null;
|
|
11908
|
+
}
|
|
11909
|
+
});
|
|
10339
11910
|
|
|
10340
11911
|
// src/console/router.ts
|
|
10341
|
-
init_esm_shims();
|
|
10342
|
-
init_runtime_config();
|
|
10343
|
-
init_token_stats();
|
|
10344
|
-
init_rules_loader();
|
|
10345
11912
|
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";
|
|
11913
|
+
import { readFileSync as readFileSync18, existsSync as existsSync18, unlinkSync as unlinkSync4 } from "fs";
|
|
11914
|
+
import { join as join15, dirname as dirname12 } from "path";
|
|
11915
|
+
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
10352
11916
|
function getConsoleToken() {
|
|
10353
11917
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
10354
11918
|
}
|
|
@@ -10362,7 +11926,7 @@ function verifyAuth(req) {
|
|
|
10362
11926
|
function authMiddleware() {
|
|
10363
11927
|
return async (c, next) => {
|
|
10364
11928
|
if (!verifyAuth(c.req.raw)) {
|
|
10365
|
-
return c.json({
|
|
11929
|
+
return c.json({ detail: "Unauthorized: invalid or missing Bearer token" }, 401);
|
|
10366
11930
|
}
|
|
10367
11931
|
await next();
|
|
10368
11932
|
};
|
|
@@ -10371,9 +11935,9 @@ function createConsoleRouter() {
|
|
|
10371
11935
|
const console2 = new Hono();
|
|
10372
11936
|
console2.use("/api/*", authMiddleware());
|
|
10373
11937
|
const serveIndex = (c) => {
|
|
10374
|
-
const indexFile =
|
|
10375
|
-
if (
|
|
10376
|
-
return c.html(
|
|
11938
|
+
const indexFile = join15(STATIC_DIR, "index.html");
|
|
11939
|
+
if (existsSync18(indexFile)) {
|
|
11940
|
+
return c.html(readFileSync18(indexFile, "utf-8"));
|
|
10377
11941
|
}
|
|
10378
11942
|
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
10379
11943
|
};
|
|
@@ -10385,8 +11949,18 @@ function createConsoleRouter() {
|
|
|
10385
11949
|
});
|
|
10386
11950
|
console2.put("/api/layers", async (c) => {
|
|
10387
11951
|
const body = await c.req.json();
|
|
11952
|
+
const validKeys = /* @__PURE__ */ new Set(["L1_heuristic", "L2_logic_rules", "L3_llm_analysis"]);
|
|
11953
|
+
if (!validKeys.has(body.layer_key)) {
|
|
11954
|
+
return c.json({ detail: `Invalid layer_key: ${body.layer_key}` }, 400);
|
|
11955
|
+
}
|
|
11956
|
+
const envOverrides = getEnvOverrides();
|
|
11957
|
+
if (envOverrides[body.layer_key]?.overridden) {
|
|
11958
|
+
return c.json({
|
|
11959
|
+
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`
|
|
11960
|
+
}, 409);
|
|
11961
|
+
}
|
|
10388
11962
|
const rc = updateLayerEnabled(body.layer_key, body.enabled);
|
|
10389
|
-
return c.json({ layers: rc.layers, env_overrides:
|
|
11963
|
+
return c.json({ layers: rc.layers, env_overrides: envOverrides });
|
|
10390
11964
|
});
|
|
10391
11965
|
function getL22View() {
|
|
10392
11966
|
const ltlHuman = {
|
|
@@ -10463,7 +12037,21 @@ function createConsoleRouter() {
|
|
|
10463
12037
|
}
|
|
10464
12038
|
console2.get("/api/rules", (c) => {
|
|
10465
12039
|
const rc = loadRuntimeConfig();
|
|
10466
|
-
|
|
12040
|
+
const layer = c.req.query("layer");
|
|
12041
|
+
const category = c.req.query("category");
|
|
12042
|
+
const out = {};
|
|
12043
|
+
for (const layerKey of ["L1_rules", "L2_rules"]) {
|
|
12044
|
+
if (layer && layer !== layerKey) continue;
|
|
12045
|
+
const layerData = rc[layerKey] || {};
|
|
12046
|
+
const picked = {};
|
|
12047
|
+
for (const [catName, catData] of Object.entries(layerData)) {
|
|
12048
|
+
if (category && category !== catName) continue;
|
|
12049
|
+
picked[catName] = catData;
|
|
12050
|
+
}
|
|
12051
|
+
out[layerKey] = picked;
|
|
12052
|
+
}
|
|
12053
|
+
if (!category) out.L2_2 = getL22View();
|
|
12054
|
+
return c.json(out);
|
|
10467
12055
|
});
|
|
10468
12056
|
console2.put("/api/rules/enabled", async (c) => {
|
|
10469
12057
|
const b = await c.req.json();
|
|
@@ -10472,6 +12060,9 @@ function createConsoleRouter() {
|
|
|
10472
12060
|
});
|
|
10473
12061
|
console2.put("/api/rules/deny_mode", async (c) => {
|
|
10474
12062
|
const b = await c.req.json();
|
|
12063
|
+
if (!["deny_write", "deny_read", "deny_both"].includes(b.deny_mode)) {
|
|
12064
|
+
return c.json({ detail: `\u65E0\u6548\u7684 deny_mode: ${b.deny_mode}` }, 400);
|
|
12065
|
+
}
|
|
10475
12066
|
const rc = updateRuleDenyMode(b.layer, b.category, b.rule_id, b.deny_mode);
|
|
10476
12067
|
return c.json(withL22View(rc));
|
|
10477
12068
|
});
|
|
@@ -10503,8 +12094,10 @@ function createConsoleRouter() {
|
|
|
10503
12094
|
});
|
|
10504
12095
|
console2.delete("/api/rules", async (c) => {
|
|
10505
12096
|
const b = await c.req.json();
|
|
10506
|
-
deleteCustomRule(b.layer, b.category, b.rule_id);
|
|
10507
|
-
|
|
12097
|
+
const rc = deleteCustomRule(b.layer, b.category, b.rule_id);
|
|
12098
|
+
if (rc === null) {
|
|
12099
|
+
return c.json({ detail: "\u5185\u7F6E\u89C4\u5219\u4E0D\u53EF\u5220\u9664\uFF0C\u53EA\u80FD\u7981\u7528" }, 403);
|
|
12100
|
+
}
|
|
10508
12101
|
return c.json(withL22View(rc));
|
|
10509
12102
|
});
|
|
10510
12103
|
console2.get("/api/skills", (c) => {
|
|
@@ -10528,22 +12121,26 @@ function createConsoleRouter() {
|
|
|
10528
12121
|
});
|
|
10529
12122
|
console2.delete("/api/skills", async (c) => {
|
|
10530
12123
|
const b = await c.req.json();
|
|
10531
|
-
deleteCustomSkill(b.skill_id, b.category);
|
|
10532
|
-
|
|
12124
|
+
const rc = deleteCustomSkill(b.skill_id, b.category);
|
|
12125
|
+
if (rc === null) {
|
|
12126
|
+
return c.json({ detail: "\u5185\u7F6E skill \u4E0D\u53EF\u5220\u9664\uFF0C\u53EA\u80FD\u7981\u7528" }, 403);
|
|
12127
|
+
}
|
|
10533
12128
|
return c.json(withL22View(rc));
|
|
10534
12129
|
});
|
|
10535
12130
|
console2.get("/api/skills/:skill_id/content", async (c) => {
|
|
10536
12131
|
const skillId = c.req.param("skill_id");
|
|
10537
12132
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
10538
|
-
return c.json({
|
|
12133
|
+
return c.json({ detail: "\u975E\u6CD5 skill id" }, 400);
|
|
10539
12134
|
}
|
|
10540
|
-
const
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
|
|
12135
|
+
const userPath = join15(dirname12(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
12136
|
+
const builtinDir = resolveAssetDir(__dirname10, "skills");
|
|
12137
|
+
const candidates = [userPath, builtinDir ? join15(builtinDir, `${skillId}.md`) : ""].filter(Boolean);
|
|
12138
|
+
for (const p of candidates) {
|
|
12139
|
+
if (existsSync18(p)) {
|
|
12140
|
+
return c.json({ skill_id: skillId, path: p, content: readFileSync18(p, "utf-8") });
|
|
12141
|
+
}
|
|
10545
12142
|
}
|
|
10546
|
-
return c.
|
|
12143
|
+
return c.json({ detail: `skill ${skillId} \u4E0D\u5B58\u5728` }, 404);
|
|
10547
12144
|
});
|
|
10548
12145
|
console2.get("/api/agents", (c) => {
|
|
10549
12146
|
const rc = loadRuntimeConfig();
|
|
@@ -10635,12 +12232,19 @@ function createConsoleRouter() {
|
|
|
10635
12232
|
});
|
|
10636
12233
|
console2.post("/api/reset", (c) => {
|
|
10637
12234
|
const defaults = generateSourceDefaults();
|
|
10638
|
-
|
|
12235
|
+
try {
|
|
12236
|
+
const p = getRuntimeConfigPath();
|
|
12237
|
+
if (existsSync18(p)) unlinkSync4(p);
|
|
12238
|
+
} catch {
|
|
12239
|
+
}
|
|
10639
12240
|
return c.json(withL22View(defaults));
|
|
10640
12241
|
});
|
|
12242
|
+
const parseDays = (v) => {
|
|
12243
|
+
const n = parseInt(v || "0", 10);
|
|
12244
|
+
return Number.isFinite(n) ? n : 0;
|
|
12245
|
+
};
|
|
10641
12246
|
console2.get("/api/token-stats", (c) => {
|
|
10642
|
-
|
|
10643
|
-
return c.json(getTokenStats(days));
|
|
12247
|
+
return c.json(getTokenStats(parseDays(c.req.query("days"))));
|
|
10644
12248
|
});
|
|
10645
12249
|
console2.get("/api/token-stats/recent", (c) => {
|
|
10646
12250
|
const limit = parseInt(c.req.query("limit") || "20", 10);
|
|
@@ -10648,19 +12252,52 @@ function createConsoleRouter() {
|
|
|
10648
12252
|
return c.json({ records: getRecentRecords(limit, offset), total: getRecentRecordsCount() });
|
|
10649
12253
|
});
|
|
10650
12254
|
console2.get("/api/token-stats/trend", (c) => {
|
|
10651
|
-
|
|
10652
|
-
return c.json(getTokenTrend(days));
|
|
12255
|
+
return c.json(getTokenTrend(parseDays(c.req.query("days"))));
|
|
10653
12256
|
});
|
|
10654
12257
|
console2.post("/api/token-stats/reset", (c) => {
|
|
10655
12258
|
resetTokenStats();
|
|
10656
|
-
return c.json({
|
|
12259
|
+
return c.json({ detail: "Token stats reset successfully" });
|
|
10657
12260
|
});
|
|
10658
12261
|
return console2;
|
|
10659
12262
|
}
|
|
12263
|
+
var __dirname10, STATIC_DIR, CONSOLE_TOKEN_ENV;
|
|
12264
|
+
var init_router = __esm({
|
|
12265
|
+
"src/console/router.ts"() {
|
|
12266
|
+
"use strict";
|
|
12267
|
+
init_esm_shims();
|
|
12268
|
+
init_runtime_config();
|
|
12269
|
+
init_token_stats();
|
|
12270
|
+
init_rules_loader();
|
|
12271
|
+
init_asset_dir();
|
|
12272
|
+
__dirname10 = dirname12(fileURLToPath11(import.meta.url));
|
|
12273
|
+
STATIC_DIR = join15(__dirname10, "static");
|
|
12274
|
+
CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
12275
|
+
}
|
|
12276
|
+
});
|
|
12277
|
+
|
|
12278
|
+
// src/console/index.ts
|
|
12279
|
+
var init_console = __esm({
|
|
12280
|
+
"src/console/index.ts"() {
|
|
12281
|
+
"use strict";
|
|
12282
|
+
init_esm_shims();
|
|
12283
|
+
init_router();
|
|
12284
|
+
}
|
|
12285
|
+
});
|
|
10660
12286
|
|
|
10661
12287
|
// src/server.ts
|
|
10662
|
-
|
|
10663
|
-
|
|
12288
|
+
var server_exports = {};
|
|
12289
|
+
__export(server_exports, {
|
|
12290
|
+
createApp: () => createApp,
|
|
12291
|
+
findFreePort: () => findFreePort,
|
|
12292
|
+
logDeploymentSelfCheck: () => logDeploymentSelfCheck,
|
|
12293
|
+
startServer: () => startServer,
|
|
12294
|
+
startUnixSocketServer: () => startUnixSocketServer
|
|
12295
|
+
});
|
|
12296
|
+
import { Hono as Hono2 } from "hono";
|
|
12297
|
+
import { cors } from "hono/cors";
|
|
12298
|
+
import { logger } from "hono/logger";
|
|
12299
|
+
import net from "net";
|
|
12300
|
+
import { existsSync as existsSync19 } from "fs";
|
|
10664
12301
|
function logDeploymentSelfCheck() {
|
|
10665
12302
|
try {
|
|
10666
12303
|
console.log("================ AgentMoss \u90E8\u7F72\u81EA\u68C0 ================");
|
|
@@ -10668,25 +12305,27 @@ function logDeploymentSelfCheck() {
|
|
|
10668
12305
|
const layerKeys = ["L1_heuristic", "L2_logic_rules", "L3_llm_analysis", "agent_rules_enabled", "scenario_rules_enabled"];
|
|
10669
12306
|
console.log("\u5C42\u7EA7\u5F00\u5173: " + layerKeys.map((k) => `${k}=${isLayerEnabled(k) ? "on" : "OFF"}`).join(" "));
|
|
10670
12307
|
try {
|
|
10671
|
-
const
|
|
10672
|
-
if (
|
|
10673
|
-
console.
|
|
10674
|
-
|
|
12308
|
+
const dirs = rulesDirs().filter((d) => existsSync19(d));
|
|
12309
|
+
if (dirs.length > 0) {
|
|
12310
|
+
console.log(`\u89C4\u5219\u76EE\u5F55: ${dirs.join(", ")}\uFF08\u53EA\u8BFB\u5176\u4E2D ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09`);
|
|
12311
|
+
}
|
|
12312
|
+
const loaded = ruleFilesLoaded();
|
|
12313
|
+
if (loaded.length > 0) {
|
|
12314
|
+
const ov = loadOverrideRules();
|
|
12315
|
+
const ex = loadExtraRules();
|
|
12316
|
+
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:`);
|
|
12317
|
+
for (const f of loaded) console.error(` ${f}`);
|
|
12318
|
+
const ovs = Object.entries(ov).sort().map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
12319
|
+
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`);
|
|
12320
|
+
const exs = Object.entries(ex).sort().map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
12321
|
+
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`);
|
|
12322
|
+
}
|
|
12323
|
+
const ignored = ignoredRuleFiles();
|
|
12324
|
+
if (ignored.length > 0) {
|
|
10675
12325
|
console.error(
|
|
10676
|
-
|
|
10677
|
-
);
|
|
10678
|
-
}
|
|
10679
|
-
} catch {
|
|
10680
|
-
}
|
|
10681
|
-
try {
|
|
10682
|
-
const extras = extraRuleFiles();
|
|
10683
|
-
if (extras.length > 0) {
|
|
10684
|
-
const cats = loadExtraRules();
|
|
10685
|
-
const total = Object.values(cats).reduce((n, v) => n + v.length, 0);
|
|
10686
|
-
const catSummary = Object.entries(cats).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
10687
|
-
console.log(
|
|
10688
|
-
`\u5DF2\u4ECE\u8FFD\u52A0\u76EE\u5F55 ${extraRulesDirs().join(", ")} \u52A0\u8F7D ${extras.length} \u4E2A\u89C4\u5219\u6587\u4EF6\uFF08\u5171 ${total} \u6761${catSummary ? `\uFF0C\u7C7B\u522B\uFF1A${catSummary}` : ""}\uFF09\u3002\u8FFD\u52A0\u8BED\u4E49\uFF1A\u5185\u7F6E\u89C4\u5219\u5168\u90E8\u4FDD\u7559\uFF0C\u8FD9\u4E9B\u89C4\u5219\u6302\u5728\u5176\u540E\u4E00\u8D77\u5224\u5B9A\u3002`
|
|
12326
|
+
`\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:`
|
|
10689
12327
|
);
|
|
12328
|
+
for (const f of ignored) console.error(` ${f}`);
|
|
10690
12329
|
}
|
|
10691
12330
|
} catch {
|
|
10692
12331
|
}
|
|
@@ -10710,18 +12349,26 @@ function createApp() {
|
|
|
10710
12349
|
app.get("/api/v1/health", healthRoute);
|
|
10711
12350
|
app.post("/api/v1/analyze", analyzeRoute);
|
|
10712
12351
|
app.post("/api/v1/review", reviewRoute);
|
|
12352
|
+
app.get("/api/v1/brain/rules", brainRulesRoute);
|
|
12353
|
+
app.get("/api/v1/brain/rules/drafts", brainDraftsRoute);
|
|
12354
|
+
app.post("/api/v1/brain/rules/:rule_id/approve", brainApproveRoute);
|
|
12355
|
+
app.post("/api/v1/brain/rules/:rule_id/reject", brainRejectRoute);
|
|
12356
|
+
app.get("/api/v1/brain/stats", brainStatsRoute);
|
|
12357
|
+
app.get("/api/v1/brain/skills", brainSkillsRoute);
|
|
12358
|
+
app.get("/api/v1/brain/rules/export", brainExportRoute);
|
|
12359
|
+
app.post("/api/v1/brain/rules/import", brainImportRoute);
|
|
10713
12360
|
app.route("/console", createConsoleRouter());
|
|
10714
12361
|
app.get("/console/", (c) => c.redirect("/console"));
|
|
10715
12362
|
return app;
|
|
10716
12363
|
}
|
|
10717
12364
|
async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
10718
12365
|
for (let port = startPort; port < startPort + scan; port++) {
|
|
10719
|
-
const free = await new Promise((
|
|
12366
|
+
const free = await new Promise((resolve11) => {
|
|
10720
12367
|
const server = net.createServer();
|
|
10721
12368
|
server.listen(port, host, () => {
|
|
10722
|
-
server.close(() =>
|
|
12369
|
+
server.close(() => resolve11(true));
|
|
10723
12370
|
});
|
|
10724
|
-
server.on("error", () =>
|
|
12371
|
+
server.on("error", () => resolve11(false));
|
|
10725
12372
|
});
|
|
10726
12373
|
if (free) {
|
|
10727
12374
|
if (port !== startPort) {
|
|
@@ -10732,12 +12379,6 @@ async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
|
10732
12379
|
}
|
|
10733
12380
|
return startPort;
|
|
10734
12381
|
}
|
|
10735
|
-
var WARMUP_COMMANDS = {
|
|
10736
|
-
linux: "whoami",
|
|
10737
|
-
macos: "whoami",
|
|
10738
|
-
harmony: "whoami",
|
|
10739
|
-
windows: "dir"
|
|
10740
|
-
};
|
|
10741
12382
|
async function warmUpEngine() {
|
|
10742
12383
|
const prevSavePolicy = process.env.AGENT_MOSS_SAVE_POLICY;
|
|
10743
12384
|
try {
|
|
@@ -10777,9 +12418,240 @@ async function startServer(host = "127.0.0.1", port = 9090) {
|
|
|
10777
12418
|
}
|
|
10778
12419
|
};
|
|
10779
12420
|
}
|
|
12421
|
+
async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_moss.sock") {
|
|
12422
|
+
const app = createApp();
|
|
12423
|
+
let server = null;
|
|
12424
|
+
return {
|
|
12425
|
+
socketPath,
|
|
12426
|
+
close: async () => {
|
|
12427
|
+
if (!server) return;
|
|
12428
|
+
await new Promise((resolve11) => {
|
|
12429
|
+
server.close(() => resolve11());
|
|
12430
|
+
});
|
|
12431
|
+
server = null;
|
|
12432
|
+
},
|
|
12433
|
+
serve: async () => {
|
|
12434
|
+
const { mkdirSync: mkdirSync10, existsSync: existsSync21, unlinkSync: unlinkSync5 } = await import("fs");
|
|
12435
|
+
const { dirname: dirname14 } = await import("path");
|
|
12436
|
+
const dir = dirname14(socketPath);
|
|
12437
|
+
if (dir) {
|
|
12438
|
+
try {
|
|
12439
|
+
mkdirSync10(dir, { recursive: true });
|
|
12440
|
+
} catch {
|
|
12441
|
+
}
|
|
12442
|
+
}
|
|
12443
|
+
if (existsSync21(socketPath)) {
|
|
12444
|
+
const { connect } = await import("net");
|
|
12445
|
+
const inUse = await new Promise((resolve11) => {
|
|
12446
|
+
const probe = connect(socketPath);
|
|
12447
|
+
const done = (v) => {
|
|
12448
|
+
probe.destroy();
|
|
12449
|
+
resolve11(v);
|
|
12450
|
+
};
|
|
12451
|
+
probe.once("connect", () => done(true));
|
|
12452
|
+
probe.once("error", () => done(false));
|
|
12453
|
+
probe.setTimeout(500, () => done(false));
|
|
12454
|
+
});
|
|
12455
|
+
if (inUse) {
|
|
12456
|
+
console.warn(
|
|
12457
|
+
`[AgentMoss] socket ${socketPath} \u5DF2\u6709\u670D\u52A1\u5728\u76D1\u542C\uFF0C\u4E0D\u6E05\u7406\uFF08bind \u4F1A\u56E0\u5360\u7528\u800C\u5931\u8D25\uFF09`
|
|
12458
|
+
);
|
|
12459
|
+
} else {
|
|
12460
|
+
try {
|
|
12461
|
+
unlinkSync5(socketPath);
|
|
12462
|
+
console.log(`[AgentMoss] \u6E05\u7406\u6B8B\u7559 socket \u6587\u4EF6: ${socketPath}`);
|
|
12463
|
+
} catch {
|
|
12464
|
+
}
|
|
12465
|
+
}
|
|
12466
|
+
}
|
|
12467
|
+
logDeploymentSelfCheck();
|
|
12468
|
+
await warmUpEngine();
|
|
12469
|
+
const { getRequestListener } = await import("@hono/node-server");
|
|
12470
|
+
const { createServer } = await import("http");
|
|
12471
|
+
server = createServer(getRequestListener(app.fetch));
|
|
12472
|
+
await new Promise((resolve11, reject) => {
|
|
12473
|
+
server.once("error", reject);
|
|
12474
|
+
server.listen(socketPath, () => resolve11());
|
|
12475
|
+
});
|
|
12476
|
+
console.log(`AgentMoss HTTP server on unix socket: ${socketPath}`);
|
|
12477
|
+
}
|
|
12478
|
+
};
|
|
12479
|
+
}
|
|
12480
|
+
var WARMUP_COMMANDS;
|
|
12481
|
+
var init_server = __esm({
|
|
12482
|
+
"src/server.ts"() {
|
|
12483
|
+
"use strict";
|
|
12484
|
+
init_esm_shims();
|
|
12485
|
+
init_routes();
|
|
12486
|
+
init_console();
|
|
12487
|
+
init_runtime_config();
|
|
12488
|
+
init_rules_loader();
|
|
12489
|
+
WARMUP_COMMANDS = {
|
|
12490
|
+
linux: "whoami",
|
|
12491
|
+
macos: "whoami",
|
|
12492
|
+
harmony: "whoami",
|
|
12493
|
+
windows: "dir"
|
|
12494
|
+
};
|
|
12495
|
+
}
|
|
12496
|
+
});
|
|
12497
|
+
|
|
12498
|
+
// src/rules-tools.ts
|
|
12499
|
+
var rules_tools_exports = {};
|
|
12500
|
+
__export(rules_tools_exports, {
|
|
12501
|
+
EXTRA_TEMPLATE: () => EXTRA_TEMPLATE,
|
|
12502
|
+
OVERRIDE_TEMPLATE: () => OVERRIDE_TEMPLATE,
|
|
12503
|
+
initRulesDir: () => initRulesDir,
|
|
12504
|
+
rulesStatusText: () => rulesStatusText,
|
|
12505
|
+
templateSourceDir: () => templateSourceDir
|
|
12506
|
+
});
|
|
12507
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "fs";
|
|
12508
|
+
import { dirname as dirname13, join as join16 } from "path";
|
|
12509
|
+
import { fileURLToPath as fileURLToPath12 } from "url";
|
|
12510
|
+
function initRulesDir(targetDir, opts = {}) {
|
|
12511
|
+
mkdirSync9(targetDir, { recursive: true });
|
|
12512
|
+
const written = [];
|
|
12513
|
+
const extraPath = join16(targetDir, EXTRA_FILENAME);
|
|
12514
|
+
if (opts.force || !existsSync20(extraPath)) {
|
|
12515
|
+
writeFileSync9(extraPath, JSON.stringify(EXTRA_TEMPLATE, null, 2), "utf-8");
|
|
12516
|
+
written.push(extraPath);
|
|
12517
|
+
}
|
|
12518
|
+
const samplePath = join16(targetDir, OVERRIDE_FILENAME);
|
|
12519
|
+
if (opts.force || !existsSync20(samplePath)) {
|
|
12520
|
+
let payload = OVERRIDE_TEMPLATE;
|
|
12521
|
+
if (opts.withBuiltin) {
|
|
12522
|
+
let data = {};
|
|
12523
|
+
const builtin = resolveRulesFile("l1_rules.json");
|
|
12524
|
+
if (builtin && existsSync20(builtin)) {
|
|
12525
|
+
try {
|
|
12526
|
+
data = JSON.parse(readFileSync19(builtin, "utf-8")) || {};
|
|
12527
|
+
} catch {
|
|
12528
|
+
data = {};
|
|
12529
|
+
}
|
|
12530
|
+
}
|
|
12531
|
+
payload = { _\u8BF4\u660E: OVERRIDE_BUILTIN_NOTE };
|
|
12532
|
+
for (const k of L1_KEYS) payload[k] = data[k] || [];
|
|
12533
|
+
}
|
|
12534
|
+
writeFileSync9(samplePath, JSON.stringify(payload, null, 2), "utf-8");
|
|
12535
|
+
written.push(samplePath);
|
|
12536
|
+
}
|
|
12537
|
+
const readmeSrc = join16(templateSourceDir(), "README.md");
|
|
12538
|
+
const readmeDst = join16(targetDir, "README.md");
|
|
12539
|
+
if (existsSync20(readmeSrc) && (opts.force || !existsSync20(readmeDst))) {
|
|
12540
|
+
writeFileSync9(readmeDst, readFileSync19(readmeSrc, "utf-8"), "utf-8");
|
|
12541
|
+
written.push(readmeDst);
|
|
12542
|
+
}
|
|
12543
|
+
return written;
|
|
12544
|
+
}
|
|
12545
|
+
function templateSourceDir() {
|
|
12546
|
+
const cands = [join16(__dirname11, "data", "rules_template"), join16(__dirname11, "..", "..", "agent_moss", "data", "rules_template")];
|
|
12547
|
+
for (const c of cands) if (existsSync20(c)) return c;
|
|
12548
|
+
return cands[0];
|
|
12549
|
+
}
|
|
12550
|
+
function templateFiles(dirs) {
|
|
12551
|
+
const out = [];
|
|
12552
|
+
for (const d of dirs) {
|
|
12553
|
+
try {
|
|
12554
|
+
for (const name of readdirSync7(d)) {
|
|
12555
|
+
if (name.endsWith(".json.\u793A\u4F8B")) out.push(join16(d, name));
|
|
12556
|
+
}
|
|
12557
|
+
} catch {
|
|
12558
|
+
}
|
|
12559
|
+
}
|
|
12560
|
+
return out.sort();
|
|
12561
|
+
}
|
|
12562
|
+
function rulesStatusText() {
|
|
12563
|
+
const lines = [];
|
|
12564
|
+
const existing = rulesDirs().filter((d) => existsSync20(d));
|
|
12565
|
+
lines.push("\u89C4\u5219\u76EE\u5F55: " + (existing.length > 0 ? existing.join(", ") : "\uFF08\u672A\u8BBE\u7F6E / \u4E0D\u5B58\u5728\uFF09"));
|
|
12566
|
+
if (existing.length > 0) {
|
|
12567
|
+
lines.push(` \uFF08\u53EA\u8BFB\u5176\u4E2D\u7684 ${OVERRIDE_FILENAME} \u4E0E ${EXTRA_FILENAME}\uFF1B\u5176\u4ED6\u6587\u4EF6\u540D\u4E00\u5F8B\u4E0D\u8BFB\uFF09`);
|
|
12568
|
+
}
|
|
12569
|
+
const override = loadOverrideRules();
|
|
12570
|
+
const extraAll = loadExtraRules();
|
|
12571
|
+
const extra = {};
|
|
12572
|
+
for (const [k, v] of Object.entries(extraAll)) if (v.length > 0) extra[k] = v;
|
|
12573
|
+
const loaded = ruleFilesLoaded();
|
|
12574
|
+
if (Object.keys(override).length > 0) {
|
|
12575
|
+
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:`);
|
|
12576
|
+
for (const [cat, items] of Object.entries(override).sort()) lines.push(` ${cat}: \u4F60\u7684 ${items.length} \u6761`);
|
|
12577
|
+
}
|
|
12578
|
+
if (Object.keys(extra).length > 0) {
|
|
12579
|
+
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:`);
|
|
12580
|
+
for (const [cat, items] of Object.entries(extra).sort()) lines.push(` ${cat}: ${items.length} \u6761`);
|
|
12581
|
+
}
|
|
12582
|
+
if (Object.keys(override).length === 0 && Object.keys(extra).length === 0) {
|
|
12583
|
+
if (loaded.length > 0 || templateFiles(existing).length > 0) {
|
|
12584
|
+
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");
|
|
12585
|
+
} else {
|
|
12586
|
+
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`);
|
|
12587
|
+
lines.push(` \u6A21\u677F\u968F\u5305\u5206\u53D1\uFF08\u4E0D\u7528\u8DD1\u547D\u4EE4\uFF09: ${templateSourceDir()}`);
|
|
12588
|
+
lines.push(" \u6216\u4E00\u884C\u547D\u4EE4\u751F\u6210\u5230\u76EE\u6807\u76EE\u5F55: agent-moss rules init --dir <\u76EE\u5F55>");
|
|
12589
|
+
}
|
|
12590
|
+
}
|
|
12591
|
+
const templates = templateFiles(existing);
|
|
12592
|
+
if (templates.length > 0) {
|
|
12593
|
+
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:");
|
|
12594
|
+
for (const p of templates) lines.push(` ${p}`);
|
|
12595
|
+
}
|
|
12596
|
+
const ignored = ignoredRuleFiles();
|
|
12597
|
+
if (ignored.length > 0) {
|
|
12598
|
+
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:`);
|
|
12599
|
+
for (const p of ignored) lines.push(` ${p}`);
|
|
12600
|
+
}
|
|
12601
|
+
lines.push("");
|
|
12602
|
+
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");
|
|
12603
|
+
return lines.join("\n");
|
|
12604
|
+
}
|
|
12605
|
+
var __dirname11, EXTRA_TEMPLATE, OVERRIDE_TEMPLATE, OVERRIDE_BUILTIN_NOTE, L1_KEYS;
|
|
12606
|
+
var init_rules_tools = __esm({
|
|
12607
|
+
"src/rules-tools.ts"() {
|
|
12608
|
+
"use strict";
|
|
12609
|
+
init_esm_shims();
|
|
12610
|
+
init_rules_loader();
|
|
12611
|
+
__dirname11 = dirname13(fileURLToPath12(import.meta.url));
|
|
12612
|
+
EXTRA_TEMPLATE = {
|
|
12613
|
+
_\u8BF4\u660E: [
|
|
12614
|
+
"\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",
|
|
12615
|
+
"\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",
|
|
12616
|
+
"\u600E\u4E48\u586B\u3001\u53EF\u76F4\u63A5\u7167\u6284\u7684\u6837\u4F8B\uFF1A\u89C1\u540C\u76EE\u5F55 README.md\u3002",
|
|
12617
|
+
"\u6539\u5B8C\u81EA\u67E5\uFF1Aagent-moss rules status"
|
|
12618
|
+
],
|
|
12619
|
+
dangerous_commands: [],
|
|
12620
|
+
injection_detection: [],
|
|
12621
|
+
sensitive_path_access: [],
|
|
12622
|
+
intent_consistency: [],
|
|
12623
|
+
password_modify_consent: [],
|
|
12624
|
+
user_deletion_consent: []
|
|
12625
|
+
};
|
|
12626
|
+
OVERRIDE_TEMPLATE = {
|
|
12627
|
+
_\u8BF4\u660E: [
|
|
12628
|
+
"\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",
|
|
12629
|
+
"\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",
|
|
12630
|
+
"\u600E\u4E48\u586B\u3001\u53EF\u76F4\u63A5\u7167\u6284\u7684\u6837\u4F8B\uFF1A\u89C1\u540C\u76EE\u5F55 README.md\u3002",
|
|
12631
|
+
"\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",
|
|
12632
|
+
"\u6539\u5B8C\u81EA\u67E5\uFF1Aagent-moss rules status"
|
|
12633
|
+
],
|
|
12634
|
+
dangerous_commands: [],
|
|
12635
|
+
injection_detection: [],
|
|
12636
|
+
sensitive_path_access: [],
|
|
12637
|
+
intent_consistency: [],
|
|
12638
|
+
password_modify_consent: [],
|
|
12639
|
+
user_deletion_consent: []
|
|
12640
|
+
};
|
|
12641
|
+
OVERRIDE_BUILTIN_NOTE = [
|
|
12642
|
+
"\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",
|
|
12643
|
+
"\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",
|
|
12644
|
+
"\u5220\u6389/\u4FEE\u6539\u4F60\u4E0D\u8981\u7684\u90A3\u6761\u5373\u53EF\uFF1B\u6539\u5B8C\u81EA\u67E5\uFF1Aagent-moss rules status"
|
|
12645
|
+
];
|
|
12646
|
+
L1_KEYS = ["dangerous_commands", "injection_detection", "sensitive_path_access"];
|
|
12647
|
+
}
|
|
12648
|
+
});
|
|
10780
12649
|
|
|
10781
12650
|
// src/cli.ts
|
|
10782
|
-
|
|
12651
|
+
init_esm_shims();
|
|
12652
|
+
init_server();
|
|
12653
|
+
init_package_json();
|
|
12654
|
+
import { fileURLToPath as fileURLToPath13 } from "url";
|
|
10783
12655
|
import { realpathSync } from "fs";
|
|
10784
12656
|
function printHelp() {
|
|
10785
12657
|
console.log(`AgentMoss security analysis engine (TypeScript v${version})
|
|
@@ -10802,9 +12674,29 @@ Install options:
|
|
|
10802
12674
|
--enable Enable service on boot (systemctl enable)
|
|
10803
12675
|
`);
|
|
10804
12676
|
}
|
|
12677
|
+
function buildAnalysisInputFromFile(data) {
|
|
12678
|
+
return {
|
|
12679
|
+
sessionId: data.session_id || "unknown",
|
|
12680
|
+
promptSession: data.prompt_session || "",
|
|
12681
|
+
actionHistory: (data.action_history || []).map((h) => ({
|
|
12682
|
+
actionType: h.action_type || h.name || "",
|
|
12683
|
+
actionDetail: h.action_detail || "",
|
|
12684
|
+
output: h.output
|
|
12685
|
+
})),
|
|
12686
|
+
aNext: {
|
|
12687
|
+
actionType: data.a_next?.action_type || "",
|
|
12688
|
+
actionDetail: data.a_next?.action_detail || ""
|
|
12689
|
+
},
|
|
12690
|
+
reason: data.reason || "",
|
|
12691
|
+
osType: data.os_type || (process.platform === "win32" ? "windows" : "linux"),
|
|
12692
|
+
cwd: data.cwd || process.cwd(),
|
|
12693
|
+
agentId: data.agent_id || "",
|
|
12694
|
+
scenarioId: data.scenario_id || ""
|
|
12695
|
+
};
|
|
12696
|
+
}
|
|
10805
12697
|
async function analyzeRawCommand(command, useJson = false) {
|
|
10806
|
-
const {
|
|
10807
|
-
const result = await
|
|
12698
|
+
const { analyze: analyze2 } = await Promise.resolve().then(() => (init_analyzer(), analyzer_exports));
|
|
12699
|
+
const result = await analyze2({
|
|
10808
12700
|
sessionId: "cli-command",
|
|
10809
12701
|
promptSession: "",
|
|
10810
12702
|
actionHistory: [],
|
|
@@ -10815,19 +12707,24 @@ async function analyzeRawCommand(command, useJson = false) {
|
|
|
10815
12707
|
agentId: ""
|
|
10816
12708
|
});
|
|
10817
12709
|
if (useJson) console.log(JSON.stringify(result, null, 2));
|
|
10818
|
-
else console.log(result.
|
|
10819
|
-
return result.
|
|
12710
|
+
else console.log(result.decision);
|
|
12711
|
+
return result.decision === "Allow" ? 0 : 1;
|
|
10820
12712
|
}
|
|
10821
12713
|
async function main() {
|
|
10822
12714
|
const args = process.argv.slice(2);
|
|
10823
12715
|
const command = args[0];
|
|
12716
|
+
if (command === "--version" || command === "-V") {
|
|
12717
|
+
console.log(`agent-moss ${version}`);
|
|
12718
|
+
process.exit(0);
|
|
12719
|
+
}
|
|
10824
12720
|
if (!command || command === "--help" || command === "-h") {
|
|
10825
12721
|
printHelp();
|
|
10826
12722
|
process.exit(command ? 0 : 1);
|
|
10827
12723
|
}
|
|
10828
12724
|
switch (command) {
|
|
10829
12725
|
case "init": {
|
|
10830
|
-
const
|
|
12726
|
+
const flagIdx = args.findIndex((a) => a === "--output" || a === "-o");
|
|
12727
|
+
const output = (flagIdx >= 0 ? args[flagIdx + 1] : void 0) || args.slice(1).find((a) => !a.startsWith("-")) || "input.json";
|
|
10831
12728
|
const fs7 = await import("fs");
|
|
10832
12729
|
const template = {
|
|
10833
12730
|
session_id: "my-session-001",
|
|
@@ -10850,7 +12747,6 @@ async function main() {
|
|
|
10850
12747
|
}
|
|
10851
12748
|
const fs7 = await import("fs");
|
|
10852
12749
|
const path6 = await import("path");
|
|
10853
|
-
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_coordinator(), coordinator_exports));
|
|
10854
12750
|
const useJson = args.includes("--json");
|
|
10855
12751
|
const osType = process.platform === "win32" ? "windows" : "linux";
|
|
10856
12752
|
const isFile = fs7.existsSync(target) && fs7.statSync(target).isFile();
|
|
@@ -10871,34 +12767,102 @@ async function main() {
|
|
|
10871
12767
|
}
|
|
10872
12768
|
const filePath = path6.resolve(target);
|
|
10873
12769
|
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
|
-
});
|
|
12770
|
+
const { analyze: analyze2 } = await Promise.resolve().then(() => (init_analyzer(), analyzer_exports));
|
|
12771
|
+
const sessionId = String(data.session_id || "unknown");
|
|
12772
|
+
const result = await analyze2(buildAnalysisInputFromFile(data));
|
|
10890
12773
|
console.log(JSON.stringify(result, null, 2));
|
|
10891
|
-
|
|
12774
|
+
if (!args.includes("--no-file")) {
|
|
12775
|
+
const outDirIdx = args.findIndex((a) => a === "--output-dir" || a === "-o");
|
|
12776
|
+
const outDir = outDirIdx >= 0 && args[outDirIdx + 1] ? args[outDirIdx + 1] : process.cwd();
|
|
12777
|
+
fs7.mkdirSync(outDir, { recursive: true });
|
|
12778
|
+
const safeSession = sessionId.replace(/\//g, "_");
|
|
12779
|
+
if (result.decision === "Allow") {
|
|
12780
|
+
const policyPath = path6.join(outDir, `${safeSession}_policy.toml`);
|
|
12781
|
+
fs7.writeFileSync(policyPath, result.policy || "", "utf-8");
|
|
12782
|
+
console.log(`Policy written to ${policyPath}`);
|
|
12783
|
+
} else {
|
|
12784
|
+
const denyPath = path6.join(outDir, `${safeSession}_deny_reason.txt`);
|
|
12785
|
+
fs7.writeFileSync(denyPath, result.violated_policy || result.reason || "", "utf-8");
|
|
12786
|
+
console.log(`Deny reason written to ${denyPath}`);
|
|
12787
|
+
}
|
|
12788
|
+
}
|
|
12789
|
+
process.exit(result.decision === "Allow" ? 0 : 1);
|
|
12790
|
+
break;
|
|
12791
|
+
}
|
|
12792
|
+
case "rules": {
|
|
12793
|
+
const sub = args[1];
|
|
12794
|
+
const dirIdx = args.findIndex((a) => a === "--dir" || a === "-d");
|
|
12795
|
+
const dir = dirIdx >= 0 ? args[dirIdx + 1] : void 0;
|
|
12796
|
+
const { initRulesDir: initRulesDir2, rulesStatusText: rulesStatusText2 } = await Promise.resolve().then(() => (init_rules_tools(), rules_tools_exports));
|
|
12797
|
+
if (sub === "init") {
|
|
12798
|
+
if (!dir) {
|
|
12799
|
+
console.error("\u7528\u6CD5: agent-moss rules init --dir <\u76EE\u5F55>");
|
|
12800
|
+
process.exit(2);
|
|
12801
|
+
}
|
|
12802
|
+
const written = initRulesDir2(dir, { force: args.includes("--force"), withBuiltin: args.includes("--with-builtin") });
|
|
12803
|
+
for (const p of written) console.log(`\u5DF2\u751F\u6210: ${p}`);
|
|
12804
|
+
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");
|
|
12805
|
+
console.log("");
|
|
12806
|
+
console.log("\u4E0B\u4E00\u6B65\uFF1A");
|
|
12807
|
+
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`);
|
|
12808
|
+
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`);
|
|
12809
|
+
console.log(` 3) \u8BA9 AgentMoss \u8BFB\u8FD9\u4E2A\u76EE\u5F55\uFF1Aexport AGENT_MOSS_EXTRA_RULES_DIR=${dir}\uFF08\u91CD\u542F\u8FDB\u7A0B\uFF09`);
|
|
12810
|
+
console.log(" 4) \u81EA\u67E5\uFF1Aagent-moss rules status");
|
|
12811
|
+
process.exit(0);
|
|
12812
|
+
}
|
|
12813
|
+
if (sub === "status") {
|
|
12814
|
+
if (dir) process.env.AGENT_MOSS_EXTRA_RULES_DIR = dir;
|
|
12815
|
+
console.log(rulesStatusText2());
|
|
12816
|
+
process.exit(0);
|
|
12817
|
+
}
|
|
12818
|
+
console.log("rules \u5B50\u547D\u4EE4\uFF1Astatus\uFF08\u81EA\u67E5\u89C4\u5219\u76EE\u5F55\uFF09/ init\uFF08\u751F\u6210\u6A21\u677F\uFF09");
|
|
12819
|
+
process.exit(1);
|
|
12820
|
+
break;
|
|
12821
|
+
}
|
|
12822
|
+
case "brain": {
|
|
12823
|
+
const sub = args[1];
|
|
12824
|
+
if (sub !== "contribute") {
|
|
12825
|
+
console.log("Brain \u5B50\u547D\u4EE4\uFF1Acontribute\uFF08\u8D21\u732E\u672C\u5730\u89C4\u5219/Skill \u4E3A MR\uFF09");
|
|
12826
|
+
process.exit(1);
|
|
12827
|
+
}
|
|
12828
|
+
let target;
|
|
12829
|
+
let forkUser;
|
|
12830
|
+
let branch;
|
|
12831
|
+
for (let i = 2; i < args.length; i++) {
|
|
12832
|
+
if ((args[i] === "--target" || args[i] === "-t") && args[i + 1]) target = args[++i];
|
|
12833
|
+
else if (args[i] === "--fork-user" && args[i + 1]) forkUser = args[++i];
|
|
12834
|
+
else if (args[i] === "--branch" && args[i + 1]) branch = args[++i];
|
|
12835
|
+
}
|
|
12836
|
+
const { contribute: contribute2 } = await Promise.resolve().then(() => (init_contributor(), contributor_exports));
|
|
12837
|
+
process.exit(await contribute2({ target, forkUser, branch }));
|
|
10892
12838
|
break;
|
|
10893
12839
|
}
|
|
10894
12840
|
case "server": {
|
|
10895
12841
|
let host = "127.0.0.1";
|
|
10896
12842
|
let port = 9090;
|
|
12843
|
+
let mode = "http";
|
|
12844
|
+
let socketPath = "/var/run/agent_moss/agent_moss.sock";
|
|
12845
|
+
let configPath = "";
|
|
10897
12846
|
for (let i = 1; i < args.length; i++) {
|
|
10898
12847
|
if (args[i] === "--host" && args[i + 1]) host = args[++i];
|
|
10899
12848
|
else if (args[i] === "--port" && args[i + 1]) port = parseInt(args[++i], 10);
|
|
12849
|
+
else if (args[i] === "--mode" && args[i + 1]) mode = args[++i];
|
|
12850
|
+
else if ((args[i] === "--socket" || args[i] === "-s") && args[i + 1]) socketPath = args[++i];
|
|
12851
|
+
else if ((args[i] === "--config" || args[i] === "-c") && args[i + 1]) configPath = args[++i];
|
|
10900
12852
|
}
|
|
12853
|
+
if (configPath) process.env.AGENT_MOSS_CONFIG_PATH = configPath;
|
|
10901
12854
|
if (port === 0) port = 9090;
|
|
12855
|
+
if (mode === "socket") {
|
|
12856
|
+
const { startUnixSocketServer: startUnixSocketServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
12857
|
+
const srv = await startUnixSocketServer2(socketPath);
|
|
12858
|
+
console.log(`Listening on unix socket ${srv.socketPath}`);
|
|
12859
|
+
await srv.serve();
|
|
12860
|
+
break;
|
|
12861
|
+
}
|
|
12862
|
+
if (mode !== "http") {
|
|
12863
|
+
console.error(`\u9519\u8BEF: \u672A\u77E5\u7684 --mode ${mode}\uFF08\u53EF\u9009 http / socket\uFF09`);
|
|
12864
|
+
process.exit(2);
|
|
12865
|
+
}
|
|
10902
12866
|
const server = await startServer(host, port);
|
|
10903
12867
|
console.log(`Listening on http://${host}:${server.port}`);
|
|
10904
12868
|
await server.serve();
|
|
@@ -10909,7 +12873,7 @@ async function main() {
|
|
|
10909
12873
|
const { execSync } = await import("child_process");
|
|
10910
12874
|
const fs7 = await import("fs");
|
|
10911
12875
|
const path6 = await import("path");
|
|
10912
|
-
const { fileURLToPath:
|
|
12876
|
+
const { fileURLToPath: fileURLToPath14 } = await import("url");
|
|
10913
12877
|
if (process.getuid?.() !== 0) {
|
|
10914
12878
|
try {
|
|
10915
12879
|
execSync("sudo -n true", { stdio: "ignore", timeout: 5e3 });
|
|
@@ -10924,18 +12888,18 @@ async function main() {
|
|
|
10924
12888
|
execSync(sudoArgs.join(" "), { stdio: "inherit" });
|
|
10925
12889
|
process.exit(0);
|
|
10926
12890
|
}
|
|
10927
|
-
const __filename7 =
|
|
10928
|
-
const
|
|
10929
|
-
let srcPath = path6.join(
|
|
12891
|
+
const __filename7 = fileURLToPath14(import.meta.url);
|
|
12892
|
+
const __dirname12 = path6.dirname(__filename7);
|
|
12893
|
+
let srcPath = path6.join(__dirname12, "data", "agent_moss.service");
|
|
10930
12894
|
if (!fs7.existsSync(srcPath)) {
|
|
10931
|
-
srcPath = path6.join(
|
|
12895
|
+
srcPath = path6.join(__dirname12, "..", "src", "data", "agent_moss.service");
|
|
10932
12896
|
}
|
|
10933
12897
|
if (!fs7.existsSync(srcPath)) {
|
|
10934
12898
|
console.error("\u9519\u8BEF\uFF1A\u627E\u4E0D\u5230 agent_moss.service \u6A21\u677F\u6587\u4EF6");
|
|
10935
12899
|
process.exit(1);
|
|
10936
12900
|
}
|
|
10937
12901
|
const which = execSync("which agent-moss 2>/dev/null || true", { encoding: "utf-8" }).trim();
|
|
10938
|
-
const binPath = which || `${process.execPath} ${path6.join(
|
|
12902
|
+
const binPath = which || `${process.execPath} ${path6.join(__dirname12, "cli.js")}`;
|
|
10939
12903
|
let content = fs7.readFileSync(srcPath, "utf-8");
|
|
10940
12904
|
content = content.replace("__AGENT_MOSS_BIN__", binPath);
|
|
10941
12905
|
const dst = "/etc/systemd/system/agent-moss.service";
|
|
@@ -10975,17 +12939,19 @@ async function main() {
|
|
|
10975
12939
|
}
|
|
10976
12940
|
} catch {
|
|
10977
12941
|
}
|
|
12942
|
+
const expectInstance = process.env.AGENT_MOSS_INSTANCE || "";
|
|
10978
12943
|
let foundPort = 0;
|
|
10979
12944
|
let version2 = "";
|
|
12945
|
+
const otherInstances = [];
|
|
10980
12946
|
for (let port = 9090; port <= 9095; port++) {
|
|
10981
12947
|
try {
|
|
10982
|
-
const data = await new Promise((
|
|
12948
|
+
const data = await new Promise((resolve11, reject) => {
|
|
10983
12949
|
const req = http.get(`http://127.0.0.1:${port}/api/v1/health`, { timeout: 2e3 }, (res) => {
|
|
10984
12950
|
let body = "";
|
|
10985
12951
|
res.on("data", (chunk) => body += chunk);
|
|
10986
12952
|
res.on("end", () => {
|
|
10987
12953
|
try {
|
|
10988
|
-
|
|
12954
|
+
resolve11(JSON.parse(body));
|
|
10989
12955
|
} catch {
|
|
10990
12956
|
reject();
|
|
10991
12957
|
}
|
|
@@ -10994,9 +12960,13 @@ async function main() {
|
|
|
10994
12960
|
req.on("error", reject);
|
|
10995
12961
|
});
|
|
10996
12962
|
if (data.status === "healthy") {
|
|
10997
|
-
|
|
10998
|
-
|
|
10999
|
-
|
|
12963
|
+
const inst = data.instance || "";
|
|
12964
|
+
if (inst === expectInstance) {
|
|
12965
|
+
foundPort = port;
|
|
12966
|
+
version2 = data.version || "";
|
|
12967
|
+
break;
|
|
12968
|
+
}
|
|
12969
|
+
otherInstances.push({ port, instance: inst });
|
|
11000
12970
|
}
|
|
11001
12971
|
} catch {
|
|
11002
12972
|
}
|
|
@@ -11013,13 +12983,13 @@ async function main() {
|
|
|
11013
12983
|
console.log(` Systemd: \u672A\u6CE8\u518C\uFF08\u8FD0\u884C agent-moss install --enable \u5B89\u88C5\uFF09`);
|
|
11014
12984
|
}
|
|
11015
12985
|
try {
|
|
11016
|
-
const data = await new Promise((
|
|
12986
|
+
const data = await new Promise((resolve11, reject) => {
|
|
11017
12987
|
const req = http.get(`http://127.0.0.1:${foundPort}/console/api/config`, { timeout: 3e3 }, (res) => {
|
|
11018
12988
|
let body = "";
|
|
11019
12989
|
res.on("data", (chunk) => body += chunk);
|
|
11020
12990
|
res.on("end", () => {
|
|
11021
12991
|
try {
|
|
11022
|
-
|
|
12992
|
+
resolve11(JSON.parse(body));
|
|
11023
12993
|
} catch {
|
|
11024
12994
|
reject();
|
|
11025
12995
|
}
|
|
@@ -11043,6 +13013,14 @@ async function main() {
|
|
|
11043
13013
|
console.log(" systemctl restart agent-moss \u91CD\u542F");
|
|
11044
13014
|
console.log(" systemctl status agent-moss \u72B6\u6001");
|
|
11045
13015
|
console.log(" journalctl -u agent_moss -f \u65E5\u5FD7");
|
|
13016
|
+
} else if (otherInstances.length > 0) {
|
|
13017
|
+
console.log(`\u274C AgentMoss \u670D\u52A1\u672A\u8FD0\u884C\uFF08instance=${expectInstance || "(\u7A7A)"}\uFF09`);
|
|
13018
|
+
console.log(` Systemd: ${systemdInfo ? "active" : "inactive"}`);
|
|
13019
|
+
const detail = otherInstances.map((o) => `\u7AEF\u53E3 ${o.port} (instance=${o.instance || "(\u7A7A)"})`).join("; ");
|
|
13020
|
+
console.log(` \u26A0\uFE0F \u68C0\u6D4B\u5230\u5176\u4ED6 agent \u7684 agentmoss \u5B9E\u4F8B: ${detail}`);
|
|
13021
|
+
console.log(` \u8FD9\u4E9B\u4E0D\u5C5E\u4E8E\u672C\u5B9E\u4F8B\uFF08instance=${expectInstance || "(\u7A7A)"}\uFF09\uFF0C\u672A\u8BA1\u5165\u8FD0\u884C\u72B6\u6001\u3002`);
|
|
13022
|
+
console.log(` \u542F\u52A8\u672C\u5B9E\u4F8B: AGENT_MOSS_INSTANCE=${expectInstance || "xiaoo"} agent-moss server`);
|
|
13023
|
+
process.exit(1);
|
|
11046
13024
|
} else {
|
|
11047
13025
|
console.log(`\u274C AgentMoss \u670D\u52A1\u672A\u8FD0\u884C`);
|
|
11048
13026
|
if (systemdInfo) {
|
|
@@ -11066,9 +13044,9 @@ async function main() {
|
|
|
11066
13044
|
var isMain = (() => {
|
|
11067
13045
|
try {
|
|
11068
13046
|
if (!process.argv[1]) return false;
|
|
11069
|
-
const thisFile = realpathSync(
|
|
13047
|
+
const thisFile = realpathSync(fileURLToPath13(import.meta.url));
|
|
11070
13048
|
const argv1 = realpathSync(
|
|
11071
|
-
process.argv[1].startsWith("file://") ?
|
|
13049
|
+
process.argv[1].startsWith("file://") ? fileURLToPath13(process.argv[1]) : process.argv[1]
|
|
11072
13050
|
);
|
|
11073
13051
|
return thisFile === argv1;
|
|
11074
13052
|
} catch {
|
|
@@ -11082,7 +13060,8 @@ if (isMain) {
|
|
|
11082
13060
|
});
|
|
11083
13061
|
}
|
|
11084
13062
|
export {
|
|
11085
|
-
analyzeRawCommand
|
|
13063
|
+
analyzeRawCommand,
|
|
13064
|
+
buildAnalysisInputFromFile
|
|
11086
13065
|
};
|
|
11087
13066
|
//! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
|
|
11088
13067
|
//! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
|