@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 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
- saveRuntimeConfig: () => saveRuntimeConfig
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)) return emptyRuntimeConfig();
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: data.L1_rules || {},
2558
- L2_rules: data.L2_rules || {},
2559
- L3_skills: data.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
- var RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS;
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 Hono();
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 { serve } = await import("@hono/node-server");
3292
- const app = createApp();
3293
- console.log(`AgentMoss HTTP server on ${host}:${port}`);
3294
- serve({ fetch: app.fetch, hostname: host, port }, (info) => {
3295
- console.log(`Listening on http://${host}:${port}`);
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: