@kenhkl/agent-moss 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +814 -19
- package/dist/cli.js.map +1 -1
- package/dist/data/agent_moss.service +16 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2307,6 +2307,7 @@ var init_token_stats = __esm({
|
|
|
2307
2307
|
});
|
|
2308
2308
|
|
|
2309
2309
|
// src/engine/llm-analyzer.ts
|
|
2310
|
+
import { appendFileSync } from "fs";
|
|
2310
2311
|
import OpenAI from "openai";
|
|
2311
2312
|
async function runLLMAnalysis(input, previous) {
|
|
2312
2313
|
if (!isLLMAvailable()) {
|
|
@@ -2340,6 +2341,7 @@ async function runLLMAnalysis(input, previous) {
|
|
|
2340
2341
|
scriptAnalysis = analyzeScriptContent(input.aNext.actionType, input.aNext.actionDetail);
|
|
2341
2342
|
}
|
|
2342
2343
|
const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText);
|
|
2344
|
+
logPrompt(prompt, input.aNext, previous.llmLogPath);
|
|
2343
2345
|
const result = await analyzeWithLLM(prompt, config);
|
|
2344
2346
|
if (llmCache.size >= LLM_CACHE_MAX) {
|
|
2345
2347
|
const firstKey = llmCache.keys().next().value;
|
|
@@ -2348,6 +2350,20 @@ async function runLLMAnalysis(input, previous) {
|
|
|
2348
2350
|
llmCache.set(cacheKey, result);
|
|
2349
2351
|
return result;
|
|
2350
2352
|
}
|
|
2353
|
+
function logPrompt(prompt, aNext, logPath) {
|
|
2354
|
+
if (!logPath) return;
|
|
2355
|
+
try {
|
|
2356
|
+
const actionType = aNext.actionType || "unknown";
|
|
2357
|
+
const actionDetail = (aNext.actionDetail || "").slice(0, 100);
|
|
2358
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [LLM_PROMPT] action_type=${actionType}, action_detail=${actionDetail}... prompt_length=${prompt.length}
|
|
2359
|
+
${prompt}
|
|
2360
|
+
--- END PROMPT ---
|
|
2361
|
+
`;
|
|
2362
|
+
appendFileSync(logPath, line, "utf-8");
|
|
2363
|
+
} catch (e) {
|
|
2364
|
+
console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2351
2367
|
function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
|
|
2352
2368
|
const skillsText = formatSkillsForPrompt(matchedSkills);
|
|
2353
2369
|
const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
|
|
@@ -2508,18 +2524,44 @@ var init_llm_analyzer = __esm({
|
|
|
2508
2524
|
var runtime_config_exports = {};
|
|
2509
2525
|
__export(runtime_config_exports, {
|
|
2510
2526
|
DENY_MODE_LABELS: () => DENY_MODE_LABELS,
|
|
2527
|
+
addCustomRule: () => addCustomRule,
|
|
2528
|
+
addCustomSkill: () => addCustomSkill,
|
|
2529
|
+
deleteCustomRule: () => deleteCustomRule,
|
|
2530
|
+
deleteCustomSkill: () => deleteCustomSkill,
|
|
2511
2531
|
filterEnabledRules: () => filterEnabledRules,
|
|
2532
|
+
generateSourceDefaults: () => generateSourceDefaults,
|
|
2512
2533
|
getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
|
|
2534
|
+
getDisabledSkillIds: () => getDisabledSkillIds,
|
|
2513
2535
|
getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
|
|
2536
|
+
getEnabledL1CommandPatternsOrDefault: () => getEnabledL1CommandPatternsOrDefault,
|
|
2537
|
+
getEnabledL1InjectionKeywords: () => getEnabledL1InjectionKeywords,
|
|
2538
|
+
getEnabledL1InjectionKeywordsOrDefault: () => getEnabledL1InjectionKeywordsOrDefault,
|
|
2539
|
+
getEnabledL2IntentPatterns: () => getEnabledL2IntentPatterns,
|
|
2540
|
+
getEnabledL2IntentPatternsOrDefault: () => getEnabledL2IntentPatternsOrDefault,
|
|
2541
|
+
getEnabledL2PasswordPatterns: () => getEnabledL2PasswordPatterns,
|
|
2542
|
+
getEnabledL2PasswordPatternsOrDefault: () => getEnabledL2PasswordPatternsOrDefault,
|
|
2514
2543
|
getEnabledL2SensitivePaths: () => getEnabledL2SensitivePaths,
|
|
2544
|
+
getEnabledL2SensitivePathsOrDefault: () => getEnabledL2SensitivePathsOrDefault,
|
|
2545
|
+
getEnabledL2UserDeletionPatterns: () => getEnabledL2UserDeletionPatterns,
|
|
2546
|
+
getEnabledL2UserDeletionPatternsOrDefault: () => getEnabledL2UserDeletionPatternsOrDefault,
|
|
2515
2547
|
getEnvOverrides: () => getEnvOverrides,
|
|
2548
|
+
getProfileOrDetect: () => getProfileOrDetect,
|
|
2516
2549
|
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
2517
2550
|
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
2551
|
+
isL2CategoryEnabled: () => isL2CategoryEnabled,
|
|
2518
2552
|
isLayerEnabled: () => isLayerEnabled,
|
|
2519
2553
|
loadRuntimeConfig: () => loadRuntimeConfig,
|
|
2520
|
-
|
|
2554
|
+
mergeConfig: () => mergeConfig,
|
|
2555
|
+
saveRuntimeConfig: () => saveRuntimeConfig,
|
|
2556
|
+
updateCategoryEnabled: () => updateCategoryEnabled,
|
|
2557
|
+
updateLayerEnabled: () => updateLayerEnabled,
|
|
2558
|
+
updateRuleDenyMode: () => updateRuleDenyMode,
|
|
2559
|
+
updateRuleEnabled: () => updateRuleEnabled,
|
|
2560
|
+
updateRuleSkipL3: () => updateRuleSkipL3,
|
|
2561
|
+
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
2562
|
+
updateSkillEnabled: () => updateSkillEnabled
|
|
2521
2563
|
});
|
|
2522
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
2564
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
2523
2565
|
import { join as join4 } from "path";
|
|
2524
2566
|
import { homedir as homedir3 } from "os";
|
|
2525
2567
|
function getRuntimeConfigPath() {
|
|
@@ -2544,9 +2586,17 @@ function emptyRuntimeConfig() {
|
|
|
2544
2586
|
}
|
|
2545
2587
|
function loadRuntimeConfig() {
|
|
2546
2588
|
const p = getRuntimeConfigPath();
|
|
2547
|
-
if (!existsSync7(p))
|
|
2589
|
+
if (!existsSync7(p)) {
|
|
2590
|
+
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
2591
|
+
try {
|
|
2592
|
+
saveRuntimeConfig(seeded);
|
|
2593
|
+
} catch {
|
|
2594
|
+
}
|
|
2595
|
+
return seeded;
|
|
2596
|
+
}
|
|
2548
2597
|
try {
|
|
2549
2598
|
const data = JSON.parse(readFileSync7(p, "utf-8"));
|
|
2599
|
+
const merged = mergeConfig(data, generateSourceDefaults());
|
|
2550
2600
|
return {
|
|
2551
2601
|
version: data.version || 1,
|
|
2552
2602
|
layers: {
|
|
@@ -2554,9 +2604,9 @@ function loadRuntimeConfig() {
|
|
|
2554
2604
|
L2_logic_rules: data.layers?.L2_logic_rules ?? true,
|
|
2555
2605
|
L3_llm_analysis: data.layers?.L3_llm_analysis ?? true
|
|
2556
2606
|
},
|
|
2557
|
-
L1_rules:
|
|
2558
|
-
L2_rules:
|
|
2559
|
-
L3_skills:
|
|
2607
|
+
L1_rules: merged.L1_rules || {},
|
|
2608
|
+
L2_rules: merged.L2_rules || {},
|
|
2609
|
+
L3_skills: merged.L3_skills || {}
|
|
2560
2610
|
};
|
|
2561
2611
|
} catch {
|
|
2562
2612
|
return emptyRuntimeConfig();
|
|
@@ -2644,11 +2694,508 @@ function getDisabledL2RulesWithSkipL3() {
|
|
|
2644
2694
|
}
|
|
2645
2695
|
return result;
|
|
2646
2696
|
}
|
|
2647
|
-
|
|
2697
|
+
function _slugify(text) {
|
|
2698
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "x";
|
|
2699
|
+
}
|
|
2700
|
+
function _generatePatternId(pattern) {
|
|
2701
|
+
return `pat_${_slugify(pattern)}`;
|
|
2702
|
+
}
|
|
2703
|
+
function _deriveDenyMode(rule) {
|
|
2704
|
+
if (rule.deny_mode) return rule.deny_mode;
|
|
2705
|
+
if (rule.credential) return "deny_both";
|
|
2706
|
+
if (rule.read_only) return "deny_read";
|
|
2707
|
+
return "deny_write";
|
|
2708
|
+
}
|
|
2709
|
+
function _syncDenyModeFields(rule) {
|
|
2710
|
+
const dm = rule.deny_mode || "deny_write";
|
|
2711
|
+
if (dm === "deny_both") {
|
|
2712
|
+
rule.credential = true;
|
|
2713
|
+
delete rule.read_only;
|
|
2714
|
+
} else if (dm === "deny_read") {
|
|
2715
|
+
delete rule.credential;
|
|
2716
|
+
rule.read_only = true;
|
|
2717
|
+
} else {
|
|
2718
|
+
delete rule.credential;
|
|
2719
|
+
delete rule.read_only;
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
function _l1CommandPatternsToRules() {
|
|
2723
|
+
return DANGEROUS_PATTERNS.map((p) => ({
|
|
2724
|
+
id: _generatePatternId(p.pattern instanceof RegExp ? p.pattern.source : p.pattern),
|
|
2725
|
+
pattern: p.pattern instanceof RegExp ? p.pattern.source : p.pattern,
|
|
2726
|
+
risk_level: p.severity,
|
|
2727
|
+
risk_type: p.riskType,
|
|
2728
|
+
reason: p.description,
|
|
2729
|
+
enabled: true,
|
|
2730
|
+
builtin: true
|
|
2731
|
+
}));
|
|
2732
|
+
}
|
|
2733
|
+
function _l1InjectionKeywordsToRules() {
|
|
2734
|
+
return INJECTION_KEYWORDS.map((kw) => ({
|
|
2735
|
+
id: `inj_${_slugify(kw.keyword)}`,
|
|
2736
|
+
keyword: kw.keyword,
|
|
2737
|
+
risk_level: kw.riskLevel,
|
|
2738
|
+
lang: kw.lang || "en",
|
|
2739
|
+
enabled: true,
|
|
2740
|
+
builtin: true
|
|
2741
|
+
}));
|
|
2742
|
+
}
|
|
2743
|
+
function _l2SensitivePathsToRules() {
|
|
2744
|
+
const all = [...ROOT_SENSITIVE_PATHS, ...HOME_SENSITIVE_PATHS, ...CREDENTIAL_PATHS];
|
|
2745
|
+
return all.map((sp) => {
|
|
2746
|
+
const denyMode = _deriveDenyMode({
|
|
2747
|
+
deny_mode: sp.deny_mode,
|
|
2748
|
+
credential: sp.credential,
|
|
2749
|
+
read_only: sp.read_only
|
|
2750
|
+
});
|
|
2751
|
+
const rule = {
|
|
2752
|
+
id: `path_${_slugify(sp.path)}`,
|
|
2753
|
+
path: sp.path,
|
|
2754
|
+
risk_level: sp.riskLevel,
|
|
2755
|
+
desc: sp.desc,
|
|
2756
|
+
deny_mode: denyMode,
|
|
2757
|
+
source_deny_mode: denyMode,
|
|
2758
|
+
skip_l3_on_disabled: true,
|
|
2759
|
+
enabled: true,
|
|
2760
|
+
builtin: true
|
|
2761
|
+
};
|
|
2762
|
+
_syncDenyModeFields(rule);
|
|
2763
|
+
return rule;
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2766
|
+
function _l2IntentPatternsToRules() {
|
|
2767
|
+
return INTENT_DEVIATION_PATTERNS.map((pat, i) => ({
|
|
2768
|
+
id: `intent_${i + 1}_${_slugify(pat.reason.slice(0, 30))}`,
|
|
2769
|
+
intent_keywords: pat.intentKeywords,
|
|
2770
|
+
dangerous_actions: pat.dangerousActions,
|
|
2771
|
+
reason: pat.reason,
|
|
2772
|
+
skip_l3_on_disabled: true,
|
|
2773
|
+
enabled: true,
|
|
2774
|
+
builtin: true
|
|
2775
|
+
}));
|
|
2776
|
+
}
|
|
2777
|
+
function _l2PasswordPatternsToRules() {
|
|
2778
|
+
return PASSWORD_MODIFY_PATTERNS.map((p) => ({
|
|
2779
|
+
id: `pw_${_slugify(p.source)}`,
|
|
2780
|
+
pattern: p.source,
|
|
2781
|
+
skip_l3_on_disabled: true,
|
|
2782
|
+
enabled: true,
|
|
2783
|
+
builtin: true
|
|
2784
|
+
}));
|
|
2785
|
+
}
|
|
2786
|
+
function _l2UserDeletionPatternsToRules() {
|
|
2787
|
+
return USER_DELETION_PATTERNS.map((p) => ({
|
|
2788
|
+
id: `ud_${_slugify(p.source)}`,
|
|
2789
|
+
pattern: p.source,
|
|
2790
|
+
skip_l3_on_disabled: true,
|
|
2791
|
+
enabled: true,
|
|
2792
|
+
builtin: true
|
|
2793
|
+
}));
|
|
2794
|
+
}
|
|
2795
|
+
function _l3SkillsToRules() {
|
|
2796
|
+
const skillCategories = {
|
|
2797
|
+
file_operations: ["file_access_guard", "script_execution_guard", "indirect_access_guard"],
|
|
2798
|
+
network_security: [
|
|
2799
|
+
"data_exfiltration_guard",
|
|
2800
|
+
"browser_web_access_guard",
|
|
2801
|
+
"lateral_movement_guard",
|
|
2802
|
+
"email_operation_guard"
|
|
2803
|
+
],
|
|
2804
|
+
persistence_supply_chain: [
|
|
2805
|
+
"persistence_backdoor_guard",
|
|
2806
|
+
"supply_chain_guard",
|
|
2807
|
+
"skill_installation_guard"
|
|
2808
|
+
],
|
|
2809
|
+
intent_and_general: ["intent_deviation_guard", "general_tool_risk_guard"],
|
|
2810
|
+
resource: ["resource_exhaustion_guard"]
|
|
2811
|
+
};
|
|
2812
|
+
const out = {};
|
|
2813
|
+
for (const [cat, ids] of Object.entries(skillCategories)) {
|
|
2814
|
+
out[cat] = {
|
|
2815
|
+
category_enabled: true,
|
|
2816
|
+
category_desc: "",
|
|
2817
|
+
skills: ids.map((s) => ({
|
|
2818
|
+
id: s,
|
|
2819
|
+
keywords: SKILL_KEYWORD_MAP[s] || [],
|
|
2820
|
+
enabled: true,
|
|
2821
|
+
builtin: true
|
|
2822
|
+
}))
|
|
2823
|
+
};
|
|
2824
|
+
}
|
|
2825
|
+
const categorized = new Set(Object.values(skillCategories).flat());
|
|
2826
|
+
for (const s of Object.keys(SKILL_KEYWORD_MAP)) {
|
|
2827
|
+
if (!categorized.has(s)) {
|
|
2828
|
+
(out.intent_and_general ??= { category_enabled: true, category_desc: "", skills: [] }).skills.push({
|
|
2829
|
+
id: s,
|
|
2830
|
+
keywords: SKILL_KEYWORD_MAP[s],
|
|
2831
|
+
enabled: true,
|
|
2832
|
+
builtin: true
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
out.user_custom = { category_enabled: true, category_desc: "", skills: [] };
|
|
2837
|
+
return out;
|
|
2838
|
+
}
|
|
2839
|
+
function generateSourceDefaults() {
|
|
2840
|
+
return {
|
|
2841
|
+
version: 1,
|
|
2842
|
+
layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
|
|
2843
|
+
L1_rules: {
|
|
2844
|
+
dangerous_commands: {
|
|
2845
|
+
category_enabled: true,
|
|
2846
|
+
category_desc: "\u5371\u9669\u547D\u4EE4\u68C0\u6D4B\uFF08\u9012\u5F52\u5220\u9664\u3001\u5168\u6743\u9650\u8BBE\u7F6E\u3001\u63D0\u6743\u6267\u884C\u7B49\uFF09",
|
|
2847
|
+
rules: _l1CommandPatternsToRules()
|
|
2848
|
+
},
|
|
2849
|
+
injection_detection: {
|
|
2850
|
+
category_enabled: true,
|
|
2851
|
+
category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
|
|
2852
|
+
rules: _l1InjectionKeywordsToRules()
|
|
2853
|
+
},
|
|
2854
|
+
user_sensitive_actions: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u654F\u611F\u52A8\u4F5C/\u5DE5\u5177\u89C4\u5219", rules: [] },
|
|
2855
|
+
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
|
|
2856
|
+
},
|
|
2857
|
+
L2_rules: {
|
|
2858
|
+
sensitive_path_access: {
|
|
2859
|
+
category_enabled: true,
|
|
2860
|
+
category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B",
|
|
2861
|
+
rules: _l2SensitivePathsToRules()
|
|
2862
|
+
},
|
|
2863
|
+
intent_consistency: {
|
|
2864
|
+
category_enabled: true,
|
|
2865
|
+
category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
|
|
2866
|
+
rules: _l2IntentPatternsToRules()
|
|
2867
|
+
},
|
|
2868
|
+
password_modify_consent: {
|
|
2869
|
+
category_enabled: true,
|
|
2870
|
+
category_desc: "\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u6388\u6743\u68C0\u6D4B",
|
|
2871
|
+
rules: _l2PasswordPatternsToRules()
|
|
2872
|
+
},
|
|
2873
|
+
user_deletion_consent: {
|
|
2874
|
+
category_enabled: true,
|
|
2875
|
+
category_desc: "\u7528\u6237/\u7EC4\u5220\u9664\u64CD\u4F5C\u6388\u6743\u68C0\u6D4B",
|
|
2876
|
+
rules: _l2UserDeletionPatternsToRules()
|
|
2877
|
+
},
|
|
2878
|
+
read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
|
|
2879
|
+
dangerous_patterns: { category_enabled: true, category_desc: "\u5371\u9669\u64CD\u4F5C\u6A21\u5F0F\u68C0\u6D4B", rules: [] }
|
|
2880
|
+
},
|
|
2881
|
+
L3_skills: _l3SkillsToRules()
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
function _ruleFingerprint(rule) {
|
|
2885
|
+
if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
|
|
2886
|
+
return JSON.stringify([
|
|
2887
|
+
[...rule.intent_keywords || []].sort(),
|
|
2888
|
+
[...rule.dangerous_actions || []].sort()
|
|
2889
|
+
]);
|
|
2890
|
+
}
|
|
2891
|
+
if (rule.path) return JSON.stringify([rule.path]);
|
|
2892
|
+
if (rule.pattern) return JSON.stringify([rule.pattern]);
|
|
2893
|
+
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
2894
|
+
return JSON.stringify([rule.id || ""]);
|
|
2895
|
+
}
|
|
2896
|
+
function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
2897
|
+
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
2898
|
+
const srcLayer = srcDefaults[layerKey] || {};
|
|
2899
|
+
for (const [catName, srcCat] of Object.entries(srcLayer)) {
|
|
2900
|
+
if (!(catName in userLayer)) {
|
|
2901
|
+
userLayer[catName] = srcCat;
|
|
2902
|
+
continue;
|
|
2903
|
+
}
|
|
2904
|
+
const userCat = userLayer[catName];
|
|
2905
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
2906
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
2907
|
+
const srcRules = srcCat.rules || [];
|
|
2908
|
+
const userRules = userCat.rules || (userCat.rules = []);
|
|
2909
|
+
const userByFp = /* @__PURE__ */ new Map();
|
|
2910
|
+
for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
|
|
2911
|
+
const srcFps = /* @__PURE__ */ new Set();
|
|
2912
|
+
for (const srcR of srcRules) {
|
|
2913
|
+
const fp = _ruleFingerprint(srcR);
|
|
2914
|
+
srcFps.add(fp);
|
|
2915
|
+
if (userByFp.has(fp)) {
|
|
2916
|
+
const existing = userByFp.get(fp);
|
|
2917
|
+
const userEnabled = existing.enabled;
|
|
2918
|
+
const userSkipL3 = existing.skip_l3_on_disabled;
|
|
2919
|
+
const userDenyMode = existing.deny_mode;
|
|
2920
|
+
const userSourceDeny = existing.source_deny_mode;
|
|
2921
|
+
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
2922
|
+
Object.assign(existing, srcR);
|
|
2923
|
+
existing.enabled = userEnabled;
|
|
2924
|
+
if (userSkipL3 !== void 0) existing.skip_l3_on_disabled = userSkipL3;
|
|
2925
|
+
if (userDenyMode && userSourceDeny && userDenyMode !== userSourceDeny) {
|
|
2926
|
+
existing.deny_mode = userDenyMode;
|
|
2927
|
+
}
|
|
2928
|
+
if (existing.deny_mode) _syncDenyModeFields(existing);
|
|
2929
|
+
} else {
|
|
2930
|
+
const newR = { ...srcR, enabled: true };
|
|
2931
|
+
userRules.push(newR);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
for (const r of userRules) {
|
|
2935
|
+
if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
|
|
2936
|
+
r.source_removed = true;
|
|
2937
|
+
r.enabled = false;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
if (catName === "sensitive_path_access") {
|
|
2941
|
+
for (const r of userRules) {
|
|
2942
|
+
if (!r.deny_mode) r.deny_mode = _deriveDenyMode(r);
|
|
2943
|
+
_syncDenyModeFields(r);
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
|
|
2949
|
+
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
2950
|
+
const srcLayer = srcDefaults[layerKey] || {};
|
|
2951
|
+
for (const [catName, srcCat] of Object.entries(srcLayer)) {
|
|
2952
|
+
if (!(catName in userLayer)) {
|
|
2953
|
+
userLayer[catName] = srcCat;
|
|
2954
|
+
continue;
|
|
2955
|
+
}
|
|
2956
|
+
const userCat = userLayer[catName];
|
|
2957
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
2958
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
2959
|
+
const userSkills = userCat.skills || (userCat.skills = []);
|
|
2960
|
+
const existingIds = new Set(userSkills.map((s) => s.id));
|
|
2961
|
+
for (const srcS of srcCat.skills || []) {
|
|
2962
|
+
if (!existingIds.has(srcS.id)) userSkills.push({ ...srcS });
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
function mergeConfig(userCfg, srcDefaults) {
|
|
2967
|
+
const cfg = userCfg;
|
|
2968
|
+
if (cfg.version === void 0) cfg.version = srcDefaults.version || 1;
|
|
2969
|
+
if (!cfg.layers) cfg.layers = {};
|
|
2970
|
+
for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
|
|
2971
|
+
if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
|
|
2972
|
+
}
|
|
2973
|
+
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
2974
|
+
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
2975
|
+
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
2976
|
+
return cfg;
|
|
2977
|
+
}
|
|
2978
|
+
function getEnabledL1InjectionKeywords(runtime) {
|
|
2979
|
+
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
2980
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
2981
|
+
keyword: r.keyword || "",
|
|
2982
|
+
risk_level: r.risk_level || "",
|
|
2983
|
+
lang: r.lang || "en"
|
|
2984
|
+
}));
|
|
2985
|
+
}
|
|
2986
|
+
function getEnabledL2IntentPatterns(runtime) {
|
|
2987
|
+
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
2988
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
2989
|
+
intent_keywords: r.intent_keywords || [],
|
|
2990
|
+
dangerous_actions: r.dangerous_actions || [],
|
|
2991
|
+
reason: r.reason || ""
|
|
2992
|
+
}));
|
|
2993
|
+
}
|
|
2994
|
+
function getEnabledL2PasswordPatterns(runtime) {
|
|
2995
|
+
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
2996
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
|
|
2997
|
+
}
|
|
2998
|
+
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
2999
|
+
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
3000
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
|
|
3001
|
+
}
|
|
3002
|
+
function isL2CategoryEnabled(runtime, categoryKey) {
|
|
3003
|
+
const cat = runtime.L2_rules?.[categoryKey];
|
|
3004
|
+
return cat?.category_enabled ?? true;
|
|
3005
|
+
}
|
|
3006
|
+
function getDisabledSkillIds(runtime) {
|
|
3007
|
+
const disabled = /* @__PURE__ */ new Set();
|
|
3008
|
+
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
3009
|
+
if (!catData.category_enabled) {
|
|
3010
|
+
for (const s of catData.skills || []) disabled.add(s.id);
|
|
3011
|
+
} else {
|
|
3012
|
+
for (const s of catData.skills || []) {
|
|
3013
|
+
if (s.enabled === false) disabled.add(s.id);
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
return disabled;
|
|
3018
|
+
}
|
|
3019
|
+
function getEnabledL1CommandPatternsOrDefault(runtime) {
|
|
3020
|
+
const enabled = getEnabledL1CommandPatterns(runtime);
|
|
3021
|
+
if (enabled.length) return enabled;
|
|
3022
|
+
if (!hasRuntimeConfig()) return _l1CommandPatternsToRules();
|
|
3023
|
+
return [];
|
|
3024
|
+
}
|
|
3025
|
+
function getEnabledL1InjectionKeywordsOrDefault(runtime) {
|
|
3026
|
+
const enabled = getEnabledL1InjectionKeywords(runtime);
|
|
3027
|
+
if (enabled.length) return enabled;
|
|
3028
|
+
if (!hasRuntimeConfig()) return _l1InjectionKeywordsToRules();
|
|
3029
|
+
return [];
|
|
3030
|
+
}
|
|
3031
|
+
function getEnabledL2SensitivePathsOrDefault(runtime) {
|
|
3032
|
+
const enabled = getEnabledL2SensitivePaths(runtime);
|
|
3033
|
+
if (enabled.length) return enabled;
|
|
3034
|
+
if (!hasRuntimeConfig()) return _l2SensitivePathsToRules();
|
|
3035
|
+
return [];
|
|
3036
|
+
}
|
|
3037
|
+
function getEnabledL2IntentPatternsOrDefault(runtime) {
|
|
3038
|
+
const enabled = getEnabledL2IntentPatterns(runtime);
|
|
3039
|
+
if (enabled.length) return enabled;
|
|
3040
|
+
if (!hasRuntimeConfig()) return _l2IntentPatternsToRules();
|
|
3041
|
+
return [];
|
|
3042
|
+
}
|
|
3043
|
+
function getEnabledL2PasswordPatternsOrDefault(runtime) {
|
|
3044
|
+
const enabled = getEnabledL2PasswordPatterns(runtime);
|
|
3045
|
+
if (enabled.length) return enabled;
|
|
3046
|
+
if (!hasRuntimeConfig()) {
|
|
3047
|
+
return PASSWORD_MODIFY_PATTERNS.map((p) => p.source);
|
|
3048
|
+
}
|
|
3049
|
+
return [];
|
|
3050
|
+
}
|
|
3051
|
+
function getEnabledL2UserDeletionPatternsOrDefault(runtime) {
|
|
3052
|
+
const enabled = getEnabledL2UserDeletionPatterns(runtime);
|
|
3053
|
+
if (enabled.length) return enabled;
|
|
3054
|
+
if (!hasRuntimeConfig()) {
|
|
3055
|
+
return USER_DELETION_PATTERNS.map((p) => p.source);
|
|
3056
|
+
}
|
|
3057
|
+
return [];
|
|
3058
|
+
}
|
|
3059
|
+
function _validateSkillId(skillId) {
|
|
3060
|
+
if (!skillId || !SKILL_ID_RE.test(skillId)) {
|
|
3061
|
+
throw new Error(`skill_id '${skillId}' \u4E0D\u5408\u6CD5\uFF1A\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26`);
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
function updateLayerEnabled(layerKey, enabled) {
|
|
3065
|
+
const runtime = loadRuntimeConfig();
|
|
3066
|
+
runtime.layers[layerKey] = enabled;
|
|
3067
|
+
saveRuntimeConfig(runtime);
|
|
3068
|
+
return runtime;
|
|
3069
|
+
}
|
|
3070
|
+
function updateRuleEnabled(layer, category, ruleId, enabled) {
|
|
3071
|
+
const runtime = loadRuntimeConfig();
|
|
3072
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
3073
|
+
if (r.id === ruleId) {
|
|
3074
|
+
r.enabled = enabled;
|
|
3075
|
+
break;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
saveRuntimeConfig(runtime);
|
|
3079
|
+
return runtime;
|
|
3080
|
+
}
|
|
3081
|
+
function updateRuleDenyMode(layer, category, ruleId, denyMode) {
|
|
3082
|
+
const runtime = loadRuntimeConfig();
|
|
3083
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
3084
|
+
if (r.id === ruleId) {
|
|
3085
|
+
r.deny_mode = denyMode;
|
|
3086
|
+
_syncDenyModeFields(r);
|
|
3087
|
+
break;
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
saveRuntimeConfig(runtime);
|
|
3091
|
+
return runtime;
|
|
3092
|
+
}
|
|
3093
|
+
function updateRuleSkipL3(layer, category, ruleId, skipL3) {
|
|
3094
|
+
const runtime = loadRuntimeConfig();
|
|
3095
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
3096
|
+
if (r.id === ruleId) {
|
|
3097
|
+
r.skip_l3_on_disabled = skipL3;
|
|
3098
|
+
break;
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
saveRuntimeConfig(runtime);
|
|
3102
|
+
return runtime;
|
|
3103
|
+
}
|
|
3104
|
+
function updateCategoryEnabled(layer, category, enabled) {
|
|
3105
|
+
const runtime = loadRuntimeConfig();
|
|
3106
|
+
const cat = runtime[layer]?.[category];
|
|
3107
|
+
if (cat) cat.category_enabled = enabled;
|
|
3108
|
+
saveRuntimeConfig(runtime);
|
|
3109
|
+
return runtime;
|
|
3110
|
+
}
|
|
3111
|
+
function addCustomRule(layer, category, rule) {
|
|
3112
|
+
const runtime = loadRuntimeConfig();
|
|
3113
|
+
const layerObj = runtime[layer] || (runtime[layer] = {});
|
|
3114
|
+
const cat = layerObj[category] || (layerObj[category] = { category_enabled: true, category_desc: "", rules: [] });
|
|
3115
|
+
const rules = cat.rules || (cat.rules = []);
|
|
3116
|
+
const newRule = { ...rule, builtin: false, enabled: true };
|
|
3117
|
+
if (!newRule.id) {
|
|
3118
|
+
newRule.id = `custom_${Date.now()}_${_slugify(JSON.stringify(rule).slice(0, 30))}`;
|
|
3119
|
+
}
|
|
3120
|
+
if (newRule.deny_mode) _syncDenyModeFields(newRule);
|
|
3121
|
+
rules.push(newRule);
|
|
3122
|
+
saveRuntimeConfig(runtime);
|
|
3123
|
+
return runtime;
|
|
3124
|
+
}
|
|
3125
|
+
function deleteCustomRule(layer, category, ruleId) {
|
|
3126
|
+
const runtime = loadRuntimeConfig();
|
|
3127
|
+
const rules = runtime[layer]?.[category]?.rules || [];
|
|
3128
|
+
const idx = rules.findIndex((r) => r.id === ruleId);
|
|
3129
|
+
if (idx >= 0) {
|
|
3130
|
+
if (rules[idx].builtin) return null;
|
|
3131
|
+
rules.splice(idx, 1);
|
|
3132
|
+
saveRuntimeConfig(runtime);
|
|
3133
|
+
}
|
|
3134
|
+
return runtime;
|
|
3135
|
+
}
|
|
3136
|
+
function addCustomSkill(skillId, category, keywords, content) {
|
|
3137
|
+
_validateSkillId(skillId);
|
|
3138
|
+
const runtime = loadRuntimeConfig();
|
|
3139
|
+
const skillsDir = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
3140
|
+
try {
|
|
3141
|
+
mkdirSync2(skillsDir, { recursive: true });
|
|
3142
|
+
} catch {
|
|
3143
|
+
}
|
|
3144
|
+
writeFileSync2(join4(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
3145
|
+
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
3146
|
+
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
3147
|
+
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
3148
|
+
saveRuntimeConfig(runtime);
|
|
3149
|
+
return runtime;
|
|
3150
|
+
}
|
|
3151
|
+
function deleteCustomSkill(skillId, category) {
|
|
3152
|
+
_validateSkillId(skillId);
|
|
3153
|
+
const runtime = loadRuntimeConfig();
|
|
3154
|
+
const skills = runtime.L3_skills?.[category]?.skills || [];
|
|
3155
|
+
const idx = skills.findIndex((s) => s.id === skillId);
|
|
3156
|
+
if (idx >= 0) {
|
|
3157
|
+
if (skills[idx].builtin) return null;
|
|
3158
|
+
skills.splice(idx, 1);
|
|
3159
|
+
const skillPath = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
3160
|
+
try {
|
|
3161
|
+
if (existsSync7(skillPath)) unlinkSync2(skillPath);
|
|
3162
|
+
} catch {
|
|
3163
|
+
}
|
|
3164
|
+
saveRuntimeConfig(runtime);
|
|
3165
|
+
}
|
|
3166
|
+
return runtime;
|
|
3167
|
+
}
|
|
3168
|
+
function updateSkillEnabled(skillId, enabled) {
|
|
3169
|
+
const runtime = loadRuntimeConfig();
|
|
3170
|
+
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
3171
|
+
for (const s of catData.skills || []) {
|
|
3172
|
+
if (s.id === skillId) {
|
|
3173
|
+
s.enabled = enabled;
|
|
3174
|
+
break;
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
saveRuntimeConfig(runtime);
|
|
3179
|
+
return runtime;
|
|
3180
|
+
}
|
|
3181
|
+
function updateSkillCategoryEnabled(category, enabled) {
|
|
3182
|
+
const runtime = loadRuntimeConfig();
|
|
3183
|
+
const cat = runtime.L3_skills?.[category];
|
|
3184
|
+
if (cat) cat.category_enabled = enabled;
|
|
3185
|
+
saveRuntimeConfig(runtime);
|
|
3186
|
+
return runtime;
|
|
3187
|
+
}
|
|
3188
|
+
function getProfileOrDetect() {
|
|
3189
|
+
return null;
|
|
3190
|
+
}
|
|
3191
|
+
var RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, SKILL_ID_RE;
|
|
2648
3192
|
var init_runtime_config = __esm({
|
|
2649
3193
|
"src/infra/runtime-config.ts"() {
|
|
2650
3194
|
"use strict";
|
|
2651
3195
|
init_esm_shims();
|
|
3196
|
+
init_heuristic();
|
|
3197
|
+
init_logic_rules();
|
|
3198
|
+
init_skill_loader();
|
|
2652
3199
|
RUNTIME_CONFIG_DIR = join4(homedir3(), ".config", "agentmoss");
|
|
2653
3200
|
RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
2654
3201
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
@@ -2662,6 +3209,7 @@ var init_runtime_config = __esm({
|
|
|
2662
3209
|
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2663
3210
|
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2664
3211
|
};
|
|
3212
|
+
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2665
3213
|
}
|
|
2666
3214
|
});
|
|
2667
3215
|
|
|
@@ -2862,7 +3410,8 @@ async function runAnalysis(input) {
|
|
|
2862
3410
|
heuristic: heuristicResult,
|
|
2863
3411
|
logic: logicResult,
|
|
2864
3412
|
skipL3Hints,
|
|
2865
|
-
scriptChain: scriptChain ?? void 0
|
|
3413
|
+
scriptChain: scriptChain ?? void 0,
|
|
3414
|
+
llmLogPath: input.llmLogPath
|
|
2866
3415
|
});
|
|
2867
3416
|
} catch (e) {
|
|
2868
3417
|
const { getLLMFailMode: getLLMFailMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
@@ -2976,9 +3525,10 @@ init_esm_shims();
|
|
|
2976
3525
|
|
|
2977
3526
|
// src/server.ts
|
|
2978
3527
|
init_esm_shims();
|
|
2979
|
-
import { Hono } from "hono";
|
|
3528
|
+
import { Hono as Hono2 } from "hono";
|
|
2980
3529
|
import { cors } from "hono/cors";
|
|
2981
3530
|
import { logger } from "hono/logger";
|
|
3531
|
+
import net from "net";
|
|
2982
3532
|
|
|
2983
3533
|
// src/routes.ts
|
|
2984
3534
|
init_esm_shims();
|
|
@@ -3158,7 +3708,8 @@ async function analyzeRoute(c) {
|
|
|
3158
3708
|
osType,
|
|
3159
3709
|
cwd: req.cwd,
|
|
3160
3710
|
agentId: req.agent_id,
|
|
3161
|
-
llmConfig: req.metadata?.llm_config
|
|
3711
|
+
llmConfig: req.metadata?.llm_config,
|
|
3712
|
+
llmLogPath: req.metadata?.llm_log_path
|
|
3162
3713
|
});
|
|
3163
3714
|
const durationMs = performance.now() - t0;
|
|
3164
3715
|
try {
|
|
@@ -3203,15 +3754,216 @@ async function analyzeRoute(c) {
|
|
|
3203
3754
|
}
|
|
3204
3755
|
}
|
|
3205
3756
|
|
|
3757
|
+
// src/console/index.ts
|
|
3758
|
+
init_esm_shims();
|
|
3759
|
+
|
|
3760
|
+
// src/console/router.ts
|
|
3761
|
+
init_esm_shims();
|
|
3762
|
+
init_runtime_config();
|
|
3763
|
+
init_token_stats();
|
|
3764
|
+
import { Hono } from "hono";
|
|
3765
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
3766
|
+
import { join as join5, dirname as dirname5 } from "path";
|
|
3767
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
3768
|
+
var __dirname5 = dirname5(fileURLToPath5(import.meta.url));
|
|
3769
|
+
var STATIC_DIR = join5(__dirname5, "static");
|
|
3770
|
+
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
3771
|
+
function getConsoleToken() {
|
|
3772
|
+
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
3773
|
+
}
|
|
3774
|
+
function verifyAuth(req) {
|
|
3775
|
+
const token = getConsoleToken();
|
|
3776
|
+
if (!token) return true;
|
|
3777
|
+
const auth = req.headers.get("Authorization") || "";
|
|
3778
|
+
if (auth.startsWith("Bearer ")) return auth.slice(7) === token;
|
|
3779
|
+
return false;
|
|
3780
|
+
}
|
|
3781
|
+
function authMiddleware() {
|
|
3782
|
+
return async (c, next) => {
|
|
3783
|
+
if (!verifyAuth(c.req.raw)) {
|
|
3784
|
+
return c.json({ error: "Unauthorized: invalid or missing Bearer token" }, 401);
|
|
3785
|
+
}
|
|
3786
|
+
await next();
|
|
3787
|
+
};
|
|
3788
|
+
}
|
|
3789
|
+
function createConsoleRouter() {
|
|
3790
|
+
const console2 = new Hono();
|
|
3791
|
+
console2.use("/api/*", authMiddleware());
|
|
3792
|
+
const serveIndex = (c) => {
|
|
3793
|
+
const indexFile = join5(STATIC_DIR, "index.html");
|
|
3794
|
+
if (existsSync9(indexFile)) {
|
|
3795
|
+
return c.html(readFileSync8(indexFile, "utf-8"));
|
|
3796
|
+
}
|
|
3797
|
+
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
3798
|
+
};
|
|
3799
|
+
console2.get("/", serveIndex);
|
|
3800
|
+
console2.get("", serveIndex);
|
|
3801
|
+
console2.get("/api/layers", (c) => {
|
|
3802
|
+
const rc = loadRuntimeConfig();
|
|
3803
|
+
return c.json({ layers: rc.layers });
|
|
3804
|
+
});
|
|
3805
|
+
console2.put("/api/layers", async (c) => {
|
|
3806
|
+
const body = await c.req.json();
|
|
3807
|
+
const rc = updateLayerEnabled(body.layer_key, body.enabled);
|
|
3808
|
+
return c.json({ layers: rc.layers });
|
|
3809
|
+
});
|
|
3810
|
+
console2.get("/api/rules", (c) => {
|
|
3811
|
+
const rc = loadRuntimeConfig();
|
|
3812
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3813
|
+
});
|
|
3814
|
+
console2.put("/api/rules/enabled", async (c) => {
|
|
3815
|
+
const b = await c.req.json();
|
|
3816
|
+
const rc = updateRuleEnabled(b.layer, b.category, b.rule_id, b.enabled);
|
|
3817
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3818
|
+
});
|
|
3819
|
+
console2.put("/api/rules/deny_mode", async (c) => {
|
|
3820
|
+
const b = await c.req.json();
|
|
3821
|
+
const rc = updateRuleDenyMode(b.layer, b.category, b.rule_id, b.deny_mode);
|
|
3822
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3823
|
+
});
|
|
3824
|
+
console2.put("/api/rules/skip_l3", async (c) => {
|
|
3825
|
+
const b = await c.req.json();
|
|
3826
|
+
const rc = updateRuleSkipL3(b.layer, b.category, b.rule_id, b.skip_l3_on_disabled);
|
|
3827
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3828
|
+
});
|
|
3829
|
+
console2.put("/api/categories/enabled", async (c) => {
|
|
3830
|
+
const b = await c.req.json();
|
|
3831
|
+
const rc = updateCategoryEnabled(b.layer, b.category, b.enabled);
|
|
3832
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3833
|
+
});
|
|
3834
|
+
console2.post("/api/rules", async (c) => {
|
|
3835
|
+
const b = await c.req.json();
|
|
3836
|
+
const rc = addCustomRule(b.layer, b.category, {
|
|
3837
|
+
pattern: b.pattern,
|
|
3838
|
+
risk_level: b.risk_level,
|
|
3839
|
+
risk_type: b.risk_type,
|
|
3840
|
+
reason: b.reason,
|
|
3841
|
+
keyword: b.keyword,
|
|
3842
|
+
lang: b.lang,
|
|
3843
|
+
path: b.path,
|
|
3844
|
+
desc: b.desc,
|
|
3845
|
+
credential: b.credential,
|
|
3846
|
+
deny_mode: b.deny_mode
|
|
3847
|
+
});
|
|
3848
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3849
|
+
});
|
|
3850
|
+
console2.delete("/api/rules", async (c) => {
|
|
3851
|
+
const b = await c.req.json();
|
|
3852
|
+
deleteCustomRule(b.layer, b.category, b.rule_id);
|
|
3853
|
+
const rc = loadRuntimeConfig();
|
|
3854
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3855
|
+
});
|
|
3856
|
+
console2.get("/api/skills", (c) => {
|
|
3857
|
+
const rc = loadRuntimeConfig();
|
|
3858
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
3859
|
+
});
|
|
3860
|
+
console2.put("/api/skills/enabled", async (c) => {
|
|
3861
|
+
const b = await c.req.json();
|
|
3862
|
+
const rc = updateSkillEnabled(b.skill_id, b.enabled);
|
|
3863
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
3864
|
+
});
|
|
3865
|
+
console2.put("/api/skill-categories/enabled", async (c) => {
|
|
3866
|
+
const b = await c.req.json();
|
|
3867
|
+
const rc = updateSkillCategoryEnabled(b.category, b.enabled);
|
|
3868
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
3869
|
+
});
|
|
3870
|
+
console2.post("/api/skills", async (c) => {
|
|
3871
|
+
const b = await c.req.json();
|
|
3872
|
+
const rc = addCustomSkill(b.skill_id, b.category || "user_custom", b.keywords || [], b.content);
|
|
3873
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
3874
|
+
});
|
|
3875
|
+
console2.delete("/api/skills", async (c) => {
|
|
3876
|
+
const b = await c.req.json();
|
|
3877
|
+
deleteCustomSkill(b.skill_id, b.category);
|
|
3878
|
+
const rc = loadRuntimeConfig();
|
|
3879
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
3880
|
+
});
|
|
3881
|
+
console2.get("/api/skills/:skill_id/content", async (c) => {
|
|
3882
|
+
const skillId = c.req.param("skill_id");
|
|
3883
|
+
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
3884
|
+
return c.json({ error: "invalid skill_id" }, 400);
|
|
3885
|
+
}
|
|
3886
|
+
const skillPath = join5(dirname5(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
3887
|
+
if (!existsSync9(skillPath)) {
|
|
3888
|
+
const builtinPath = join5(__dirname5, "..", "..", "agent_moss", "skills", `${skillId}.md`);
|
|
3889
|
+
if (existsSync9(builtinPath)) return c.text(readFileSync8(builtinPath, "utf-8"));
|
|
3890
|
+
return c.json({ error: "skill not found" }, 404);
|
|
3891
|
+
}
|
|
3892
|
+
return c.text(readFileSync8(skillPath, "utf-8"));
|
|
3893
|
+
});
|
|
3894
|
+
console2.get("/api/config", (c) => {
|
|
3895
|
+
const rc = loadRuntimeConfig();
|
|
3896
|
+
return c.json({ config: rc, config_path: getRuntimeConfigPath() });
|
|
3897
|
+
});
|
|
3898
|
+
console2.get("/api/env-overrides", (c) => {
|
|
3899
|
+
return c.json({ overrides: getEnvOverrides() });
|
|
3900
|
+
});
|
|
3901
|
+
console2.post("/api/reset", (c) => {
|
|
3902
|
+
const defaults = generateSourceDefaults();
|
|
3903
|
+
saveRuntimeConfig(defaults);
|
|
3904
|
+
return c.json({ config: defaults });
|
|
3905
|
+
});
|
|
3906
|
+
console2.get("/api/token-stats", (c) => {
|
|
3907
|
+
const days = parseInt(c.req.query("days") || "7", 10);
|
|
3908
|
+
return c.json(getTokenStats(days));
|
|
3909
|
+
});
|
|
3910
|
+
console2.get("/api/token-stats/recent", (c) => {
|
|
3911
|
+
const limit = parseInt(c.req.query("limit") || "20", 10);
|
|
3912
|
+
return c.json({ records: getRecentRecords(limit) });
|
|
3913
|
+
});
|
|
3914
|
+
console2.get("/api/token-stats/trend", (c) => {
|
|
3915
|
+
const days = parseInt(c.req.query("days") || "7", 10);
|
|
3916
|
+
return c.json(getTokenTrend(days));
|
|
3917
|
+
});
|
|
3918
|
+
console2.post("/api/token-stats/reset", (c) => {
|
|
3919
|
+
resetTokenStats();
|
|
3920
|
+
return c.json({ reset: true });
|
|
3921
|
+
});
|
|
3922
|
+
return console2;
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3206
3925
|
// src/server.ts
|
|
3207
3926
|
function createApp() {
|
|
3208
|
-
const app = new
|
|
3209
|
-
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "OPTIONS"], allowHeaders: ["*"] }));
|
|
3927
|
+
const app = new Hono2();
|
|
3928
|
+
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["*"] }));
|
|
3210
3929
|
app.use("*", logger());
|
|
3211
3930
|
app.get("/api/v1/health", healthRoute);
|
|
3212
3931
|
app.post("/api/v1/analyze", analyzeRoute);
|
|
3932
|
+
app.route("/console", createConsoleRouter());
|
|
3933
|
+
app.get("/console/", (c) => c.redirect("/console"));
|
|
3213
3934
|
return app;
|
|
3214
3935
|
}
|
|
3936
|
+
async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
3937
|
+
for (let port = startPort; port < startPort + scan; port++) {
|
|
3938
|
+
const free = await new Promise((resolve6) => {
|
|
3939
|
+
const server = net.createServer();
|
|
3940
|
+
server.listen(port, host, () => {
|
|
3941
|
+
server.close(() => resolve6(true));
|
|
3942
|
+
});
|
|
3943
|
+
server.on("error", () => resolve6(false));
|
|
3944
|
+
});
|
|
3945
|
+
if (free) {
|
|
3946
|
+
if (port !== startPort) {
|
|
3947
|
+
console.warn(`[AgentMoss] Port ${startPort} in use, using ${port} instead`);
|
|
3948
|
+
}
|
|
3949
|
+
return port;
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
return startPort;
|
|
3953
|
+
}
|
|
3954
|
+
async function startServer(host = "127.0.0.1", port = 9090) {
|
|
3955
|
+
const actualPort = await findFreePort(port, host);
|
|
3956
|
+
const app = createApp();
|
|
3957
|
+
return {
|
|
3958
|
+
port: actualPort,
|
|
3959
|
+
serve: () => {
|
|
3960
|
+
console.log(`AgentMoss HTTP server on ${host}:${actualPort}`);
|
|
3961
|
+
import("@hono/node-server").then(({ serve: honoServe }) => {
|
|
3962
|
+
honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
|
|
3963
|
+
});
|
|
3964
|
+
}
|
|
3965
|
+
};
|
|
3966
|
+
}
|
|
3215
3967
|
|
|
3216
3968
|
// src/cli.ts
|
|
3217
3969
|
function printHelp() {
|
|
@@ -3221,10 +3973,14 @@ Commands:
|
|
|
3221
3973
|
init Generate input.json template
|
|
3222
3974
|
analyze <input> Run security analysis on input JSON
|
|
3223
3975
|
server Start HTTP API server
|
|
3976
|
+
install Install systemd service for AgentMoss
|
|
3224
3977
|
|
|
3225
3978
|
Server options:
|
|
3226
3979
|
--host <host> Listen host (default: 127.0.0.1)
|
|
3227
|
-
--port <port> Listen port (default: 9090)
|
|
3980
|
+
--port <port> Listen port (default: 9090, 0=auto assign)
|
|
3981
|
+
|
|
3982
|
+
Install options:
|
|
3983
|
+
--enable Enable service on boot (systemctl enable)
|
|
3228
3984
|
`);
|
|
3229
3985
|
}
|
|
3230
3986
|
async function main() {
|
|
@@ -3288,12 +4044,51 @@ async function main() {
|
|
|
3288
4044
|
if (args[i] === "--host" && args[i + 1]) host = args[++i];
|
|
3289
4045
|
else if (args[i] === "--port" && args[i + 1]) port = parseInt(args[++i], 10);
|
|
3290
4046
|
}
|
|
3291
|
-
const
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
4047
|
+
const server = await startServer(host, port);
|
|
4048
|
+
console.log(`Listening on http://${host}:${server.port}`);
|
|
4049
|
+
server.serve();
|
|
4050
|
+
break;
|
|
4051
|
+
}
|
|
4052
|
+
case "install": {
|
|
4053
|
+
const enable = args.includes("--enable");
|
|
4054
|
+
const { execSync } = await import("child_process");
|
|
4055
|
+
const fs4 = await import("fs");
|
|
4056
|
+
const path3 = await import("path");
|
|
4057
|
+
const { fileURLToPath: fileURLToPath6 } = await import("url");
|
|
4058
|
+
if (process.getuid?.() !== 0) {
|
|
4059
|
+
const sudoArgs = ["sudo", process.execPath, ...process.argv.slice(1)];
|
|
4060
|
+
if (enable) sudoArgs.push("--enable");
|
|
4061
|
+
console.log("\u9700\u8981 root \u6743\u9650\u5B89\u88C5 systemd service\uFF0C\u6B63\u5728\u901A\u8FC7 sudo \u91CD\u65B0\u6267\u884C...");
|
|
4062
|
+
execSync(sudoArgs.join(" "), { stdio: "inherit" });
|
|
4063
|
+
process.exit(0);
|
|
4064
|
+
}
|
|
4065
|
+
const __filename5 = fileURLToPath6(import.meta.url);
|
|
4066
|
+
const __dirname6 = path3.dirname(__filename5);
|
|
4067
|
+
let srcPath = path3.join(__dirname6, "data", "agent_moss.service");
|
|
4068
|
+
if (!fs4.existsSync(srcPath)) {
|
|
4069
|
+
srcPath = path3.join(__dirname6, "..", "src", "data", "agent_moss.service");
|
|
4070
|
+
}
|
|
4071
|
+
if (!fs4.existsSync(srcPath)) {
|
|
4072
|
+
console.error("\u9519\u8BEF\uFF1A\u627E\u4E0D\u5230 agent_moss.service \u6A21\u677F\u6587\u4EF6");
|
|
4073
|
+
process.exit(1);
|
|
4074
|
+
}
|
|
4075
|
+
const which = execSync("which agent-moss 2>/dev/null || true", { encoding: "utf-8" }).trim();
|
|
4076
|
+
const binPath = which || `${process.execPath} ${path3.join(__dirname6, "cli.js")}`;
|
|
4077
|
+
let content = fs4.readFileSync(srcPath, "utf-8");
|
|
4078
|
+
content = content.replace("__AGENT_MOSS_BIN__", binPath);
|
|
4079
|
+
const dst = "/etc/systemd/system/agent_moss.service";
|
|
4080
|
+
fs4.writeFileSync(dst, content, "utf-8");
|
|
4081
|
+
fs4.chmodSync(dst, 420);
|
|
4082
|
+
console.log(`\u2705 \u5DF2\u5B89\u88C5 systemd service: ${dst}`);
|
|
4083
|
+
execSync("systemctl daemon-reload", { stdio: "inherit" });
|
|
4084
|
+
console.log("\u2705 systemctl daemon-reload \u5B8C\u6210");
|
|
4085
|
+
if (enable) {
|
|
4086
|
+
execSync("systemctl enable agent_moss", { stdio: "inherit" });
|
|
4087
|
+
console.log("\u2705 systemctl enable agent_moss \u5B8C\u6210");
|
|
4088
|
+
}
|
|
4089
|
+
console.log("\n\u542F\u52A8\u670D\u52A1\uFF1A sudo systemctl start agent_moss");
|
|
4090
|
+
console.log("\u67E5\u770B\u72B6\u6001\uFF1A sudo systemctl status agent_moss");
|
|
4091
|
+
console.log("\u67E5\u770B\u65E5\u5FD7\uFF1A journalctl -u agent_moss -f");
|
|
3297
4092
|
break;
|
|
3298
4093
|
}
|
|
3299
4094
|
default:
|