@kenhkl/agent-moss 0.16.14 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1124 -343
- package/dist/engine/coordinator.js +333 -158
- package/dist/rules/l1_rules.json +51 -2
- package/dist/rules/l2_rules.json +2 -1
- package/dist/server.js +1083 -302
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
var __esm = (fn, res) => function __init() {
|
|
4
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
11
|
};
|
|
@@ -25,6 +31,10 @@ var init_esm_shims = __esm({
|
|
|
25
31
|
import { join } from "path";
|
|
26
32
|
import { homedir } from "os";
|
|
27
33
|
import { readFileSync } from "fs";
|
|
34
|
+
function isSafeId(value) {
|
|
35
|
+
if (!value || value.length > ID_MAX_LEN) return false;
|
|
36
|
+
return ID_PATTERN.test(value);
|
|
37
|
+
}
|
|
28
38
|
function getRulesRoot() {
|
|
29
39
|
const raw = (process.env[ENV_RULES_ROOT] ?? "").trim();
|
|
30
40
|
return raw ? raw : null;
|
|
@@ -98,7 +108,7 @@ function getTokenStatsPath() {
|
|
|
98
108
|
}
|
|
99
109
|
return join(getDataRoot(), "agent_moss_token_stats.json");
|
|
100
110
|
}
|
|
101
|
-
var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT, ENV_RULES_ROOT;
|
|
111
|
+
var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT, ENV_RULES_ROOT, ID_MAX_LEN, ID_PATTERN;
|
|
102
112
|
var init_paths = __esm({
|
|
103
113
|
"src/infra/paths.ts"() {
|
|
104
114
|
"use strict";
|
|
@@ -107,6 +117,8 @@ var init_paths = __esm({
|
|
|
107
117
|
ENV_INSTANCE = "AGENT_MOSS_INSTANCE";
|
|
108
118
|
DEFAULT_DATA_ROOT = join(homedir(), ".config", "agentmoss");
|
|
109
119
|
ENV_RULES_ROOT = "AGENT_MOSS_RULES_ROOT";
|
|
120
|
+
ID_MAX_LEN = 64;
|
|
121
|
+
ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
110
122
|
}
|
|
111
123
|
});
|
|
112
124
|
|
|
@@ -184,7 +196,7 @@ function parseDirEnv(envName) {
|
|
|
184
196
|
function rulesDirs() {
|
|
185
197
|
if (isBuiltinOnly()) return [];
|
|
186
198
|
const dirs = [];
|
|
187
|
-
for (const
|
|
199
|
+
for (const env2 of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env2));
|
|
188
200
|
dirs.push(resolve(dataRootDir(), "rules"));
|
|
189
201
|
return [...new Set(dirs)];
|
|
190
202
|
}
|
|
@@ -232,6 +244,7 @@ function loadJson(name) {
|
|
|
232
244
|
}
|
|
233
245
|
}
|
|
234
246
|
function resolveScenarioRulesFile(scenarioId) {
|
|
247
|
+
if (!isSafeId(scenarioId)) return null;
|
|
235
248
|
const rel = join2("scenarios", scenarioId, "rules.json");
|
|
236
249
|
for (const dir of rulesDirs()) {
|
|
237
250
|
const p = resolve(dir, rel);
|
|
@@ -716,6 +729,491 @@ var init_rules_loader = __esm({
|
|
|
716
729
|
}
|
|
717
730
|
});
|
|
718
731
|
|
|
732
|
+
// src/infra/security-policy.ts
|
|
733
|
+
import { createHash as createHash2, randomBytes, timingSafeEqual } from "crypto";
|
|
734
|
+
import { chmodSync, closeSync, existsSync as existsSync3, lstatSync, mkdirSync, openSync, readFileSync as readFileSync4, readSync, realpathSync, writeSync } from "fs";
|
|
735
|
+
import { homedir as homedir2, tmpdir } from "os";
|
|
736
|
+
import { basename, isAbsolute, join as join4, relative, resolve as resolve2, dirname as dirname2 } from "path";
|
|
737
|
+
function env(name, dflt = "") {
|
|
738
|
+
return (process.env[name] ?? dflt).trim();
|
|
739
|
+
}
|
|
740
|
+
function envInt(name, dflt) {
|
|
741
|
+
const raw = env(name);
|
|
742
|
+
if (!raw) return dflt;
|
|
743
|
+
const n = Number.parseInt(raw, 10);
|
|
744
|
+
return Number.isFinite(n) ? n : dflt;
|
|
745
|
+
}
|
|
746
|
+
function splitList(raw) {
|
|
747
|
+
return raw.replace(/;/g, ",").split(",").map((x) => x.trim()).filter(Boolean);
|
|
748
|
+
}
|
|
749
|
+
function originCheckEnabled() {
|
|
750
|
+
return TRUTHY2.includes(env("AGENT_MOSS_ORIGIN_CHECK", "1").toLowerCase());
|
|
751
|
+
}
|
|
752
|
+
function allowedOrigins() {
|
|
753
|
+
return splitList(env("AGENT_MOSS_CORS_ORIGINS"));
|
|
754
|
+
}
|
|
755
|
+
function normOrigin(o) {
|
|
756
|
+
return (o || "").trim().replace(/\/+$/, "");
|
|
757
|
+
}
|
|
758
|
+
function originAllowed(origin, hostHeader, extra) {
|
|
759
|
+
const o = normOrigin(origin);
|
|
760
|
+
if (!o) return true;
|
|
761
|
+
const entries = (extra ?? allowedOrigins()).map(normOrigin);
|
|
762
|
+
if (entries.includes(o)) return true;
|
|
763
|
+
const host = (hostHeader || "").trim();
|
|
764
|
+
if (!host) return false;
|
|
765
|
+
try {
|
|
766
|
+
const u = new URL(o);
|
|
767
|
+
return u.host === host;
|
|
768
|
+
} catch {
|
|
769
|
+
return false;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
function authMode() {
|
|
773
|
+
return (env("AGENT_MOSS_AUTH_MODE", AUTH_MODE_AUTO) || AUTH_MODE_AUTO).toLowerCase();
|
|
774
|
+
}
|
|
775
|
+
function autoTokenPath() {
|
|
776
|
+
let root;
|
|
777
|
+
try {
|
|
778
|
+
root = getDataRoot();
|
|
779
|
+
} catch {
|
|
780
|
+
root = join4(homedir2(), ".config", "agentmoss");
|
|
781
|
+
}
|
|
782
|
+
return join4(root, "auth_token");
|
|
783
|
+
}
|
|
784
|
+
function readFdToken() {
|
|
785
|
+
const raw = env("AGENT_MOSS_AUTH_TOKEN_FD");
|
|
786
|
+
if (!raw) return "";
|
|
787
|
+
const fd = Number.parseInt(raw, 10);
|
|
788
|
+
if (!Number.isFinite(fd)) return "";
|
|
789
|
+
if (fdTokenCache.has(fd)) return fdTokenCache.get(fd);
|
|
790
|
+
const buf = Buffer.alloc(8192);
|
|
791
|
+
let n = 0;
|
|
792
|
+
try {
|
|
793
|
+
n = readSync(fd, buf, 0, buf.length, null);
|
|
794
|
+
} catch (e) {
|
|
795
|
+
console.error(`[AgentMoss] \u4ECE fd ${fd} \u8BFB\u53D6\u8BBF\u95EE token \u5931\u8D25:`, e);
|
|
796
|
+
}
|
|
797
|
+
try {
|
|
798
|
+
closeSync(fd);
|
|
799
|
+
} catch {
|
|
800
|
+
}
|
|
801
|
+
const tok = n > 0 ? buf.subarray(0, n).toString("utf-8").trim() : "";
|
|
802
|
+
fdTokenCache.set(fd, tok);
|
|
803
|
+
return tok;
|
|
804
|
+
}
|
|
805
|
+
function readExplicitToken() {
|
|
806
|
+
const tok = env("AGENT_MOSS_AUTH_TOKEN");
|
|
807
|
+
if (tok) return tok;
|
|
808
|
+
const fdTok = readFdToken();
|
|
809
|
+
if (fdTok) return fdTok;
|
|
810
|
+
const path6 = env("AGENT_MOSS_AUTH_TOKEN_FILE");
|
|
811
|
+
if (path6) {
|
|
812
|
+
try {
|
|
813
|
+
return readFileSync4(path6, "utf-8").trim();
|
|
814
|
+
} catch {
|
|
815
|
+
return "";
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return "";
|
|
819
|
+
}
|
|
820
|
+
function ensureAutoToken() {
|
|
821
|
+
const path6 = autoTokenPath();
|
|
822
|
+
try {
|
|
823
|
+
if (existsSync3(path6) && !lstatSync(path6).isSymbolicLink()) {
|
|
824
|
+
const tok2 = readFileSync4(path6, "utf-8").trim();
|
|
825
|
+
if (tok2) {
|
|
826
|
+
try {
|
|
827
|
+
chmodSync(path6, 384);
|
|
828
|
+
} catch {
|
|
829
|
+
}
|
|
830
|
+
return tok2;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
const tok = randomBytes(32).toString("base64url");
|
|
834
|
+
ensurePrivateDir(dirname2(path6));
|
|
835
|
+
if (!writePrivateText(path6, tok + "\n")) return "";
|
|
836
|
+
console.warn(
|
|
837
|
+
`[AgentMoss] \u5DF2\u751F\u6210\u8BBF\u95EE token \u5E76\u5199\u5165 ${path6}\uFF080600\uFF09\u3002\u5BA2\u6237\u7AEF\u9700\u5E26 \`Authorization: Bearer <token>\`\uFF1B\u67E5\u770B\uFF1Acat ${path6}\uFF1B\u5982\u9700\u5173\u95ED\u9274\u6743\uFF1AAGENT_MOSS_AUTH_MODE=off`
|
|
838
|
+
);
|
|
839
|
+
return tok;
|
|
840
|
+
} catch (e) {
|
|
841
|
+
console.error("[AgentMoss] \u751F\u6210\u8BBF\u95EE token \u5931\u8D25\uFF08\u672C\u6B21\u8FD0\u884C\u5C06\u4E0D\u9274\u6743\uFF09:", e);
|
|
842
|
+
return "";
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
function parseAgentTokens(raw) {
|
|
846
|
+
const out = {};
|
|
847
|
+
for (const pair of splitList(raw)) {
|
|
848
|
+
const i = pair.indexOf("=");
|
|
849
|
+
if (i > 0) {
|
|
850
|
+
const a = pair.slice(0, i).trim();
|
|
851
|
+
const t = pair.slice(i + 1).trim();
|
|
852
|
+
if (a && t) out[a] = t;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return out;
|
|
856
|
+
}
|
|
857
|
+
function resolveAuth() {
|
|
858
|
+
const rawAgents = env("AGENT_MOSS_AGENT_TOKENS");
|
|
859
|
+
const key = [
|
|
860
|
+
authMode(),
|
|
861
|
+
env("AGENT_MOSS_AUTH_TOKEN"),
|
|
862
|
+
env("AGENT_MOSS_AUTH_TOKEN_FILE"),
|
|
863
|
+
env("AGENT_MOSS_AUTH_TOKEN_FD"),
|
|
864
|
+
rawAgents,
|
|
865
|
+
env("AGENT_MOSS_DATA_DIR")
|
|
866
|
+
].join("\0");
|
|
867
|
+
if (tokenCache && tokenCache.key === key) {
|
|
868
|
+
return [tokenCache.token, tokenCache.agents, tokenCache.error];
|
|
869
|
+
}
|
|
870
|
+
let error = "";
|
|
871
|
+
let token = readExplicitToken();
|
|
872
|
+
if (!token && env("AGENT_MOSS_AUTH_TOKEN_FD") && authMode() !== AUTH_MODE_OFF) {
|
|
873
|
+
error = "AGENT_MOSS_AUTH_TOKEN_FD \u5DF2\u8BBE\u7F6E\uFF0C\u4F46\u6CA1\u80FD\u4ECE\u8BE5\u6587\u4EF6\u63CF\u8FF0\u7B26\u8BFB\u5230 token\u3002\u62D2\u7EDD\u964D\u7EA7\u5230\u78C1\u76D8 token\uFF08\u90A3\u4F1A\u5728\u672C\u673A\u7559\u4E0B\u4E00\u4EFD\u540C\u7528\u6237\u53EF\u8BFB\u7684\u51ED\u8BC1\uFF09\u3002";
|
|
874
|
+
console.error(`[agent-moss] ${error}`);
|
|
875
|
+
}
|
|
876
|
+
if (!token && !error && authMode() !== AUTH_MODE_OFF) token = ensureAutoToken();
|
|
877
|
+
const agents = parseAgentTokens(rawAgents);
|
|
878
|
+
tokenCache = { key, token, agents, error };
|
|
879
|
+
return [token, agents, error];
|
|
880
|
+
}
|
|
881
|
+
function agentTokens() {
|
|
882
|
+
return resolveAuth()[1];
|
|
883
|
+
}
|
|
884
|
+
function authConfigError() {
|
|
885
|
+
return resolveAuth()[2];
|
|
886
|
+
}
|
|
887
|
+
function failFastOnAuthError() {
|
|
888
|
+
const err = authConfigError();
|
|
889
|
+
if (err) {
|
|
890
|
+
console.error(`[agent-moss] \u9274\u6743\u914D\u7F6E\u9519\u8BEF\uFF0C\u62D2\u7EDD\u542F\u52A8: ${err}`);
|
|
891
|
+
process.exit(1);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
function authEnabled() {
|
|
895
|
+
if (authMode() === AUTH_MODE_OFF) return false;
|
|
896
|
+
const [tok, agents] = resolveAuth();
|
|
897
|
+
return Boolean(tok) || Object.keys(agents).length > 0;
|
|
898
|
+
}
|
|
899
|
+
function bearer(headers) {
|
|
900
|
+
const get = (k) => headers instanceof Headers ? headers.get(k) ?? "" : headers[k] ?? "";
|
|
901
|
+
const v = get("authorization") || get("Authorization");
|
|
902
|
+
return v.toLowerCase().startsWith("bearer ") ? v.slice(7).trim() : "";
|
|
903
|
+
}
|
|
904
|
+
function constEq(a, b) {
|
|
905
|
+
const ba = Buffer.from(a, "utf-8");
|
|
906
|
+
const bb = Buffer.from(b, "utf-8");
|
|
907
|
+
if (ba.length !== bb.length) return false;
|
|
908
|
+
return timingSafeEqual(ba, bb);
|
|
909
|
+
}
|
|
910
|
+
function checkRequestAuth(headers, path6) {
|
|
911
|
+
if (authMode() === AUTH_MODE_OFF) return [true, ""];
|
|
912
|
+
const isHealth = HEALTH_PATHS.includes(path6.replace(/\/+$/, ""));
|
|
913
|
+
const [gt0, agents, err] = resolveAuth();
|
|
914
|
+
if (err) return isHealth ? [true, ""] : [false, ""];
|
|
915
|
+
if (!gt0 && Object.keys(agents).length === 0) return [true, ""];
|
|
916
|
+
if (isHealth) return [true, ""];
|
|
917
|
+
const tok = bearer(headers);
|
|
918
|
+
if (!tok) return [false, ""];
|
|
919
|
+
if (path6.startsWith("/console/api")) {
|
|
920
|
+
const consoleTok = env("AGENT_MOSS_CONSOLE_TOKEN");
|
|
921
|
+
if (consoleTok && constEq(tok, consoleTok)) return [true, "console"];
|
|
922
|
+
}
|
|
923
|
+
const gt = gt0;
|
|
924
|
+
if (gt && constEq(tok, gt)) return [true, "global"];
|
|
925
|
+
for (const [agent, expected] of Object.entries(agents)) {
|
|
926
|
+
if (constEq(tok, expected)) return [true, `agent:${agent}`];
|
|
927
|
+
}
|
|
928
|
+
return [false, ""];
|
|
929
|
+
}
|
|
930
|
+
function bindAgent(principal, agentId) {
|
|
931
|
+
const tokens = agentTokens();
|
|
932
|
+
if (Object.keys(tokens).length === 0) return [true, agentId];
|
|
933
|
+
if (!principal.startsWith("agent:")) return [true, agentId];
|
|
934
|
+
const bound = principal.slice(6);
|
|
935
|
+
if (!agentId) return [true, bound];
|
|
936
|
+
return [bound === agentId, bound];
|
|
937
|
+
}
|
|
938
|
+
function principalOf(headers, path6 = "") {
|
|
939
|
+
const [ok, principal] = checkRequestAuth(headers, path6);
|
|
940
|
+
return ok ? principal : "";
|
|
941
|
+
}
|
|
942
|
+
function principalHash(principal) {
|
|
943
|
+
if (!principal) return "";
|
|
944
|
+
return createHash2("sha256").update(principal, "utf-8").digest("hex").slice(0, 12);
|
|
945
|
+
}
|
|
946
|
+
function homeDir() {
|
|
947
|
+
try {
|
|
948
|
+
return homedir2();
|
|
949
|
+
} catch {
|
|
950
|
+
return tmpdir();
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
function logRoots() {
|
|
954
|
+
const raw = env("AGENT_MOSS_LOG_ROOTS");
|
|
955
|
+
if (raw) return splitList(raw);
|
|
956
|
+
return [.../* @__PURE__ */ new Set([homeDir(), "/tmp", tmpdir()])];
|
|
957
|
+
}
|
|
958
|
+
function reviewRoots() {
|
|
959
|
+
const roots = [join4(homeDir(), ".local", "share", "agent_gate")];
|
|
960
|
+
for (const name of ["AGENT_GATE_DB", "AGENT_GATE_JSON_PATH"]) {
|
|
961
|
+
const v = env(name);
|
|
962
|
+
if (v) roots.push(existsSync3(v) && lstatSync(v).isDirectory() ? v : dirname2(v));
|
|
963
|
+
}
|
|
964
|
+
roots.push(...splitList(env("AGENT_MOSS_REVIEW_PATHS")));
|
|
965
|
+
return [...new Set(roots)];
|
|
966
|
+
}
|
|
967
|
+
function realpathLoose(p) {
|
|
968
|
+
const abs = resolve2(p.replace(/^~(?=$|\/)/, homeDir()));
|
|
969
|
+
try {
|
|
970
|
+
return realpathSync(abs);
|
|
971
|
+
} catch {
|
|
972
|
+
}
|
|
973
|
+
const parts = [];
|
|
974
|
+
let cur = abs;
|
|
975
|
+
for (; ; ) {
|
|
976
|
+
const parent = dirname2(cur);
|
|
977
|
+
if (parent === cur) return null;
|
|
978
|
+
parts.unshift(basename(cur));
|
|
979
|
+
cur = parent;
|
|
980
|
+
try {
|
|
981
|
+
return join4(realpathSync(cur), ...parts);
|
|
982
|
+
} catch {
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
function pathWithin(p, roots) {
|
|
987
|
+
if (!p) return true;
|
|
988
|
+
const target = realpathLoose(p);
|
|
989
|
+
if (target === null) return false;
|
|
990
|
+
for (const root of roots) {
|
|
991
|
+
const rootReal = realpathLoose(root);
|
|
992
|
+
if (rootReal === null) continue;
|
|
993
|
+
const rel = relative(rootReal, target);
|
|
994
|
+
if (rel === "" || !rel.startsWith("..") && !isAbsolute(rel)) return true;
|
|
995
|
+
}
|
|
996
|
+
return false;
|
|
997
|
+
}
|
|
998
|
+
function ensurePrivateDir(dir) {
|
|
999
|
+
try {
|
|
1000
|
+
mkdirSync(dir, { recursive: true });
|
|
1001
|
+
if (lstatSync(dir).isSymbolicLink()) return false;
|
|
1002
|
+
if (typeof process.getuid === "function" && lstatSync(dir).uid !== process.getuid()) return false;
|
|
1003
|
+
chmodSync(dir, 448);
|
|
1004
|
+
return true;
|
|
1005
|
+
} catch {
|
|
1006
|
+
return false;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
function writePrivateText(file, text) {
|
|
1010
|
+
try {
|
|
1011
|
+
const flags = __require("fs").constants;
|
|
1012
|
+
let openFlags = flags.O_WRONLY | flags.O_CREAT | flags.O_TRUNC;
|
|
1013
|
+
if (flags.O_NOFOLLOW) openFlags |= flags.O_NOFOLLOW;
|
|
1014
|
+
const fd = openSync(file, openFlags, 384);
|
|
1015
|
+
try {
|
|
1016
|
+
writeSync(fd, text);
|
|
1017
|
+
} finally {
|
|
1018
|
+
closeSync(fd);
|
|
1019
|
+
}
|
|
1020
|
+
return true;
|
|
1021
|
+
} catch {
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function perRequestLlmEnabled() {
|
|
1026
|
+
return TRUTHY2.includes(env("AGENT_MOSS_ALLOW_PER_REQUEST_LLM", "1").toLowerCase());
|
|
1027
|
+
}
|
|
1028
|
+
function validateLlmBaseUrl(url, allowPrivate) {
|
|
1029
|
+
if (!url) return [true, ""];
|
|
1030
|
+
let u;
|
|
1031
|
+
try {
|
|
1032
|
+
u = new URL(url);
|
|
1033
|
+
} catch {
|
|
1034
|
+
return [false, "baseUrl \u4E0D\u662F\u5408\u6CD5 URL"];
|
|
1035
|
+
}
|
|
1036
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") {
|
|
1037
|
+
return [false, `baseUrl \u534F\u8BAE\u4E0D\u53D7\u652F\u6301: ${u.protocol}`];
|
|
1038
|
+
}
|
|
1039
|
+
const host = (u.hostname || "").toLowerCase();
|
|
1040
|
+
if (!host) return [false, "baseUrl \u7F3A\u5C11\u4E3B\u673A\u540D"];
|
|
1041
|
+
if (BLOCKED_HOSTS.includes(host)) return [false, `baseUrl \u6307\u5411\u4E91\u5143\u6570\u636E\u5730\u5740\uFF0C\u5DF2\u62D2\u7EDD: ${host}`];
|
|
1042
|
+
if (/^169\.254\.\d{1,3}\.\d{1,3}$/.test(host)) {
|
|
1043
|
+
return [false, `baseUrl \u6307\u5411\u94FE\u8DEF\u672C\u5730\u5730\u5740\uFF0C\u5DF2\u62D2\u7EDD: ${host}`];
|
|
1044
|
+
}
|
|
1045
|
+
const allowlist = splitList(env("AGENT_MOSS_LLM_HOST_ALLOWLIST")).map((h) => h.toLowerCase());
|
|
1046
|
+
if (allowlist.length && !allowlist.includes(host)) {
|
|
1047
|
+
return [false, `baseUrl \u4E3B\u673A\u4E0D\u5728\u5141\u8BB8\u5217\u8868: ${host}`];
|
|
1048
|
+
}
|
|
1049
|
+
if (allowPrivate === void 0) {
|
|
1050
|
+
allowPrivate = TRUTHY2.includes(env("AGENT_MOSS_TRUST_PRIVATE_LLM", "1").toLowerCase());
|
|
1051
|
+
}
|
|
1052
|
+
if (!allowPrivate) {
|
|
1053
|
+
if (host === "localhost" || host === "::1") return [false, "baseUrl \u6307\u5411\u672C\u673A\uFF0C\u5DF2\u62D2\u7EDD"];
|
|
1054
|
+
const m = host.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
|
|
1055
|
+
if (m) {
|
|
1056
|
+
const [a, b] = [Number(m[1]), Number(m[2])];
|
|
1057
|
+
if (a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 127 || a === 169 && b === 254) {
|
|
1058
|
+
return [false, `baseUrl \u6307\u5411\u5185\u7F51\u5730\u5740\uFF0C\u5DF2\u62D2\u7EDD: ${host}`];
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
return [true, ""];
|
|
1063
|
+
}
|
|
1064
|
+
function hardenExistingPaths() {
|
|
1065
|
+
try {
|
|
1066
|
+
const roots = [join4(homeDir(), ".config", "agentmoss"), join4(homeDir(), ".agentmoss")];
|
|
1067
|
+
const names = [
|
|
1068
|
+
"agent_moss_runtime.json",
|
|
1069
|
+
"config.json",
|
|
1070
|
+
"agent_moss_settings.json",
|
|
1071
|
+
"agent_moss_token_stats.json",
|
|
1072
|
+
"stateful_chains.db",
|
|
1073
|
+
"stateful_chains.db-wal",
|
|
1074
|
+
"stateful_chains.db-shm"
|
|
1075
|
+
];
|
|
1076
|
+
for (const root of roots) {
|
|
1077
|
+
try {
|
|
1078
|
+
if (existsSync3(root) && !lstatSync(root).isSymbolicLink()) chmodSync(root, 448);
|
|
1079
|
+
} catch {
|
|
1080
|
+
}
|
|
1081
|
+
for (const n of names) {
|
|
1082
|
+
const p = join4(root, n);
|
|
1083
|
+
try {
|
|
1084
|
+
if (existsSync3(p) && !lstatSync(p).isSymbolicLink()) chmodSync(p, 384);
|
|
1085
|
+
} catch {
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
} catch {
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
function chainDetailMode() {
|
|
1093
|
+
return (env("AGENT_MOSS_CHAIN_DETAIL", "redacted") || "redacted").toLowerCase();
|
|
1094
|
+
}
|
|
1095
|
+
function redactChain(chain, currentSessionId) {
|
|
1096
|
+
if (!chain || chain.length === 0) return [];
|
|
1097
|
+
if (chainDetailMode() === "full") return [...chain];
|
|
1098
|
+
return chain.map((step) => {
|
|
1099
|
+
if (Array.isArray(step) && step.length >= 3) {
|
|
1100
|
+
const [sid, seq, , ts] = step;
|
|
1101
|
+
if (String(sid) === String(currentSessionId)) return [...step];
|
|
1102
|
+
return ["<other-session>", seq, "<\u5DF2\u9690\u85CF>", ts ?? ""];
|
|
1103
|
+
}
|
|
1104
|
+
return step;
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
function sessionSweepThreshold() {
|
|
1108
|
+
return envInt("AGENT_MOSS_SESSION_SWEEP_THRESHOLD", 50);
|
|
1109
|
+
}
|
|
1110
|
+
function sessionSweepWindow() {
|
|
1111
|
+
return envInt("AGENT_MOSS_SESSION_SWEEP_WINDOW_SEC", 300);
|
|
1112
|
+
}
|
|
1113
|
+
function sessionSweepAction() {
|
|
1114
|
+
return (env("AGENT_MOSS_SESSION_SWEEP_ACTION", "log") || "log").toLowerCase();
|
|
1115
|
+
}
|
|
1116
|
+
function noteSessionAccess(key, sessionId) {
|
|
1117
|
+
const limit = sessionSweepThreshold();
|
|
1118
|
+
if (limit <= 0 || !sessionId) return [0, false];
|
|
1119
|
+
const now = Date.now() / 1e3;
|
|
1120
|
+
const cutoff = now - sessionSweepWindow();
|
|
1121
|
+
let entries = (sessionWindow.get(key) ?? []).filter(([t]) => t >= cutoff);
|
|
1122
|
+
entries.push([now, sessionId]);
|
|
1123
|
+
if (entries.length > limit * 4) entries = entries.slice(-limit * 4);
|
|
1124
|
+
sessionWindow.set(key, entries);
|
|
1125
|
+
if (sessionWindow.size > 1e4) sessionWindow.clear();
|
|
1126
|
+
const distinct = new Set(entries.map(([, s]) => s)).size;
|
|
1127
|
+
return [distinct, distinct > limit];
|
|
1128
|
+
}
|
|
1129
|
+
function queueAbuseThreshold() {
|
|
1130
|
+
return envInt("AGENT_MOSS_QUEUE_ABUSE_THRESHOLD", 10);
|
|
1131
|
+
}
|
|
1132
|
+
function queueAbuseWindow() {
|
|
1133
|
+
return envInt("AGENT_MOSS_QUEUE_ABUSE_WINDOW_SEC", 60);
|
|
1134
|
+
}
|
|
1135
|
+
function queueAbuseAction() {
|
|
1136
|
+
return (env("AGENT_MOSS_QUEUE_ABUSE_ACTION", "log") || "log").toLowerCase();
|
|
1137
|
+
}
|
|
1138
|
+
function noteQueueReject(key) {
|
|
1139
|
+
const limit = queueAbuseThreshold();
|
|
1140
|
+
if (limit <= 0 || !key) return [0, false];
|
|
1141
|
+
const now = Date.now() / 1e3;
|
|
1142
|
+
const cutoff = now - queueAbuseWindow();
|
|
1143
|
+
let entries = (queueRejects.get(key) ?? []).filter((t) => t >= cutoff);
|
|
1144
|
+
entries.push(now);
|
|
1145
|
+
if (entries.length > limit * 4) entries = entries.slice(-limit * 4);
|
|
1146
|
+
queueRejects.set(key, entries);
|
|
1147
|
+
if (queueRejects.size > 1e4) queueRejects.clear();
|
|
1148
|
+
return [entries.length, entries.length > limit];
|
|
1149
|
+
}
|
|
1150
|
+
function socketMode() {
|
|
1151
|
+
const raw = env("AGENT_MOSS_SOCKET_MODE", "600");
|
|
1152
|
+
const mode = parseInt(raw, 8);
|
|
1153
|
+
if (!Number.isFinite(mode) || mode < 0 || mode > 511) {
|
|
1154
|
+
console.warn(`[agent-moss] AGENT_MOSS_SOCKET_MODE=${raw} \u975E\u6CD5\uFF0C\u56DE\u9000 600`);
|
|
1155
|
+
return 384;
|
|
1156
|
+
}
|
|
1157
|
+
return mode;
|
|
1158
|
+
}
|
|
1159
|
+
function errorDetailMode() {
|
|
1160
|
+
return (env("AGENT_MOSS_ERROR_DETAIL", "generic") || "generic").toLowerCase();
|
|
1161
|
+
}
|
|
1162
|
+
function maxBodyBytes() {
|
|
1163
|
+
return envInt("AGENT_MOSS_MAX_BODY_BYTES", 16 * 1024 * 1024);
|
|
1164
|
+
}
|
|
1165
|
+
function poolOf(path6) {
|
|
1166
|
+
if (path6 === "/api/v1/analyze" || path6.startsWith("/api/v1/analyze/")) return "biz";
|
|
1167
|
+
if (path6 === "/api/v1/review" || path6.startsWith("/api/v1/review/")) return "review";
|
|
1168
|
+
if (path6 === "/console" || path6.startsWith("/console/") || path6.startsWith("/api/v1/brain")) return "ops";
|
|
1169
|
+
if (path6 === "/api/v1/health") return "health";
|
|
1170
|
+
return "other";
|
|
1171
|
+
}
|
|
1172
|
+
function poolLimit(name) {
|
|
1173
|
+
let dflt = POOL_DEFAULTS[name] ?? 4;
|
|
1174
|
+
if (name === "biz") dflt = envInt("AGENT_MOSS_MAX_INFLIGHT", dflt);
|
|
1175
|
+
return envInt(`AGENT_MOSS_POOL_${name.toUpperCase()}`, dflt);
|
|
1176
|
+
}
|
|
1177
|
+
function unauthInflight() {
|
|
1178
|
+
return envInt("AGENT_MOSS_MAX_UNAUTH_INFLIGHT", 4);
|
|
1179
|
+
}
|
|
1180
|
+
function unauthPerIp() {
|
|
1181
|
+
return envInt("AGENT_MOSS_MAX_UNAUTH_PER_IP", 2);
|
|
1182
|
+
}
|
|
1183
|
+
function agentInflight() {
|
|
1184
|
+
return envInt("AGENT_MOSS_MAX_AGENT_INFLIGHT", 8);
|
|
1185
|
+
}
|
|
1186
|
+
function boundAgentOf(principal) {
|
|
1187
|
+
return principal.startsWith("agent:") ? principal.slice("agent:".length) : "";
|
|
1188
|
+
}
|
|
1189
|
+
function rateLimitQps() {
|
|
1190
|
+
return envInt("AGENT_MOSS_RATE_LIMIT_QPS", 100);
|
|
1191
|
+
}
|
|
1192
|
+
function maxConnections() {
|
|
1193
|
+
return envInt("AGENT_MOSS_MAX_CONNECTIONS", 128);
|
|
1194
|
+
}
|
|
1195
|
+
function keepAliveSec() {
|
|
1196
|
+
return envInt("AGENT_MOSS_KEEP_ALIVE_SEC", 5);
|
|
1197
|
+
}
|
|
1198
|
+
var TRUTHY2, HEALTH_PATHS, AUTH_MODE_AUTO, AUTH_MODE_OFF, tokenCache, fdTokenCache, BLOCKED_HOSTS, sessionWindow, queueRejects, POOL_DEFAULTS;
|
|
1199
|
+
var init_security_policy = __esm({
|
|
1200
|
+
"src/infra/security-policy.ts"() {
|
|
1201
|
+
"use strict";
|
|
1202
|
+
init_esm_shims();
|
|
1203
|
+
init_paths();
|
|
1204
|
+
TRUTHY2 = ["1", "true", "yes", "on"];
|
|
1205
|
+
HEALTH_PATHS = ["/api/v1/health"];
|
|
1206
|
+
AUTH_MODE_AUTO = "auto";
|
|
1207
|
+
AUTH_MODE_OFF = "off";
|
|
1208
|
+
tokenCache = null;
|
|
1209
|
+
fdTokenCache = /* @__PURE__ */ new Map();
|
|
1210
|
+
BLOCKED_HOSTS = ["169.254.169.254", "metadata.google.internal", "metadata.tencentyun.com"];
|
|
1211
|
+
sessionWindow = /* @__PURE__ */ new Map();
|
|
1212
|
+
queueRejects = /* @__PURE__ */ new Map();
|
|
1213
|
+
POOL_DEFAULTS = { biz: 16, review: 1, ops: 4, health: 4, other: 4 };
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
|
|
719
1217
|
// src/engine/shell-split.ts
|
|
720
1218
|
function hasCommandSubstitution(text) {
|
|
721
1219
|
return CMD_SUBSTITUTION.test(text);
|
|
@@ -1291,7 +1789,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1291
1789
|
targetPaths.push(...segPathsFiltered);
|
|
1292
1790
|
}
|
|
1293
1791
|
}
|
|
1294
|
-
const uniqueTargetPaths = [...new Set(targetPaths)];
|
|
1792
|
+
const uniqueTargetPaths = [...new Set(targetPaths)].filter((p) => !PSEUDO_FS_PREFIXES.some((pfx) => p.startsWith(pfx)));
|
|
1295
1793
|
if (uniqueTargetPaths.length === 0) return null;
|
|
1296
1794
|
const historyReadPaths = /* @__PURE__ */ new Set();
|
|
1297
1795
|
for (const action of actionHistory) {
|
|
@@ -1470,7 +1968,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1470
1968
|
violatedLayers: ["L2.1"]
|
|
1471
1969
|
};
|
|
1472
1970
|
}
|
|
1473
|
-
var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
|
|
1971
|
+
var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, PSEUDO_FS_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
|
|
1474
1972
|
var init_logic_rules = __esm({
|
|
1475
1973
|
"src/engine/logic-rules.ts"() {
|
|
1476
1974
|
"use strict";
|
|
@@ -1517,6 +2015,7 @@ var init_logic_rules = __esm({
|
|
|
1517
2015
|
"cat"
|
|
1518
2016
|
]);
|
|
1519
2017
|
NON_WRITE_TOOL_PREFIXES = _LOGIC.non_write_tool_prefixes ?? [];
|
|
2018
|
+
PSEUDO_FS_PREFIXES = ["/sys/", "/proc/", "/dev/", "/run/"];
|
|
1520
2019
|
WRITE_KEYWORDS = ["write", "\u5199\u5165", "save", "\u4FDD\u5B58", "modify", "\u4FEE\u6539", "update", "\u66F4\u65B0", "delete", "\u5220\u9664", "remove", "\u79FB\u9664", "create", "\u521B\u5EFA", "append", "\u8FFD\u52A0", "truncate", "\u622A\u65AD", "overwrite", "\u8986\u76D6", "replace", "\u66FF\u6362", "edit"];
|
|
1521
2020
|
SED_INPLACE_RE = /\bsed\s+(?:-\S+\s+)*(?:-[a-zA-Z]*i[a-zA-Z]*|--in-place)\b/i;
|
|
1522
2021
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
@@ -1567,22 +2066,22 @@ var init_logic_rules = __esm({
|
|
|
1567
2066
|
});
|
|
1568
2067
|
|
|
1569
2068
|
// src/engine/asset-dir.ts
|
|
1570
|
-
import { existsSync as
|
|
1571
|
-
import { join as
|
|
2069
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2070
|
+
import { join as join5, resolve as resolve3 } from "path";
|
|
1572
2071
|
function assetDirCandidates(moduleDir, name) {
|
|
1573
2072
|
const root = getRulesRoot();
|
|
1574
2073
|
if (root) {
|
|
1575
|
-
const p =
|
|
2074
|
+
const p = join5(root, name);
|
|
1576
2075
|
return [p, p];
|
|
1577
2076
|
}
|
|
1578
2077
|
return [
|
|
1579
|
-
|
|
1580
|
-
|
|
2078
|
+
resolve3(moduleDir, name),
|
|
2079
|
+
resolve3(moduleDir, "../../..", "agent_moss", name)
|
|
1581
2080
|
];
|
|
1582
2081
|
}
|
|
1583
2082
|
function resolveAssetDir(moduleDir, name) {
|
|
1584
2083
|
for (const dir of assetDirCandidates(moduleDir, name)) {
|
|
1585
|
-
if (
|
|
2084
|
+
if (existsSync5(dir)) return dir;
|
|
1586
2085
|
}
|
|
1587
2086
|
return null;
|
|
1588
2087
|
}
|
|
@@ -1595,8 +2094,8 @@ var init_asset_dir = __esm({
|
|
|
1595
2094
|
});
|
|
1596
2095
|
|
|
1597
2096
|
// src/engine/skill-loader.ts
|
|
1598
|
-
import { readFileSync as
|
|
1599
|
-
import { resolve as
|
|
2097
|
+
import { readFileSync as readFileSync5, readdirSync as readdirSync3, existsSync as existsSync6 } from "fs";
|
|
2098
|
+
import { resolve as resolve4, dirname as dirname3 } from "path";
|
|
1600
2099
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1601
2100
|
function loadAllSkills() {
|
|
1602
2101
|
if (cachedSkills) return cachedSkills;
|
|
@@ -1610,13 +2109,13 @@ function loadAllSkills() {
|
|
|
1610
2109
|
const files = readdirSync3(dir).filter((f) => f.endsWith(".md"));
|
|
1611
2110
|
for (const f of files) {
|
|
1612
2111
|
const name = f.replace(".md", "");
|
|
1613
|
-
cachedSkills[name] =
|
|
2112
|
+
cachedSkills[name] = readFileSync5(resolve4(dir, f), "utf-8");
|
|
1614
2113
|
}
|
|
1615
2114
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1616
2115
|
return cachedSkills;
|
|
1617
2116
|
}
|
|
1618
2117
|
function userSkillsDir() {
|
|
1619
|
-
return
|
|
2118
|
+
return resolve4(getDataRoot(), "agent_moss_skills");
|
|
1620
2119
|
}
|
|
1621
2120
|
function runtimeCustomSkillKeywords() {
|
|
1622
2121
|
if (isRuntimeConfigDisabled()) return {};
|
|
@@ -1640,8 +2139,8 @@ function loadUserSkill(name) {
|
|
|
1640
2139
|
if (isRuntimeConfigDisabled()) return "";
|
|
1641
2140
|
if (userSkillCache[name] !== void 0) return userSkillCache[name];
|
|
1642
2141
|
try {
|
|
1643
|
-
const p =
|
|
1644
|
-
userSkillCache[name] =
|
|
2142
|
+
const p = resolve4(userSkillsDir(), `${name}.md`);
|
|
2143
|
+
userSkillCache[name] = existsSync6(p) ? readFileSync5(p, "utf-8") : "";
|
|
1645
2144
|
} catch {
|
|
1646
2145
|
userSkillCache[name] = "";
|
|
1647
2146
|
}
|
|
@@ -1710,7 +2209,7 @@ var init_skill_loader = __esm({
|
|
|
1710
2209
|
init_asset_dir();
|
|
1711
2210
|
init_runtime_config_bridge();
|
|
1712
2211
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
1713
|
-
__dirname3 =
|
|
2212
|
+
__dirname3 = dirname3(__filename3);
|
|
1714
2213
|
SKILL_KEYWORD_MAP = (() => {
|
|
1715
2214
|
const map = {};
|
|
1716
2215
|
const cats = loadL3();
|
|
@@ -1790,10 +2289,10 @@ __export(runtime_config_exports, {
|
|
|
1790
2289
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1791
2290
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1792
2291
|
});
|
|
1793
|
-
import { readFileSync as
|
|
1794
|
-
import { join as
|
|
2292
|
+
import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
2293
|
+
import { join as join6 } from "path";
|
|
1795
2294
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1796
|
-
import { dirname as
|
|
2295
|
+
import { dirname as dirname4 } from "path";
|
|
1797
2296
|
function runtimeDir() {
|
|
1798
2297
|
return getRuntimeConfigDir();
|
|
1799
2298
|
}
|
|
@@ -1820,7 +2319,7 @@ function saveRuntimeConfig(config) {
|
|
|
1820
2319
|
}
|
|
1821
2320
|
const p = getRuntimeConfigPath();
|
|
1822
2321
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1823
|
-
if (dir && !
|
|
2322
|
+
if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
|
|
1824
2323
|
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
1825
2324
|
invalidateRuntimeConfigCache();
|
|
1826
2325
|
}
|
|
@@ -1854,7 +2353,7 @@ function loadRuntimeConfig() {
|
|
|
1854
2353
|
};
|
|
1855
2354
|
}
|
|
1856
2355
|
const p = getRuntimeConfigPath();
|
|
1857
|
-
if (!
|
|
2356
|
+
if (!existsSync7(p)) {
|
|
1858
2357
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
1859
2358
|
try {
|
|
1860
2359
|
saveRuntimeConfig(seeded);
|
|
@@ -1868,7 +2367,7 @@ function loadRuntimeConfig() {
|
|
|
1868
2367
|
if (runtimeConfigCache && runtimeConfigCache.key === cacheKey) {
|
|
1869
2368
|
return runtimeConfigCache.value;
|
|
1870
2369
|
}
|
|
1871
|
-
const data = JSON.parse(
|
|
2370
|
+
const data = JSON.parse(readFileSync6(p, "utf-8"));
|
|
1872
2371
|
const merged = mergeConfig(data, generateSourceDefaults());
|
|
1873
2372
|
const result = {
|
|
1874
2373
|
version: data.version || 1,
|
|
@@ -2016,7 +2515,7 @@ function hasRuntimeConfig() {
|
|
|
2016
2515
|
if (cached2 !== void 0) return cached2;
|
|
2017
2516
|
let ok = false;
|
|
2018
2517
|
try {
|
|
2019
|
-
JSON.parse(
|
|
2518
|
+
JSON.parse(readFileSync6(p, "utf-8"));
|
|
2020
2519
|
ok = true;
|
|
2021
2520
|
} catch {
|
|
2022
2521
|
ok = false;
|
|
@@ -2131,7 +2630,7 @@ function _l2SensitivePathsToRules() {
|
|
|
2131
2630
|
const all = [...root, ...home, ...credential];
|
|
2132
2631
|
for (const sp of all) {
|
|
2133
2632
|
const os4 = String(sp.os || "linux").toLowerCase();
|
|
2134
|
-
const denyMode = _deriveDenyMode({
|
|
2633
|
+
const denyMode = sp.denyMode || _deriveDenyMode({
|
|
2135
2634
|
deny_mode: sp.deny_mode,
|
|
2136
2635
|
credential: sp.credential,
|
|
2137
2636
|
read_only: sp.read_only
|
|
@@ -2237,8 +2736,8 @@ function _l3SkillsToRules() {
|
|
|
2237
2736
|
function _scenarioRulesToDefaults() {
|
|
2238
2737
|
const out = {};
|
|
2239
2738
|
const _root = getRulesRoot();
|
|
2240
|
-
const scenDir = _root ?
|
|
2241
|
-
if (!
|
|
2739
|
+
const scenDir = _root ? join6(_root, "scenarios") : existsSync7(_SCENARIOS_DIR_PROD) ? _SCENARIOS_DIR_PROD : existsSync7(_SCENARIOS_DIR_DEV) ? _SCENARIOS_DIR_DEV : _SCENARIOS_DIR_PROD;
|
|
2740
|
+
if (!existsSync7(scenDir)) return out;
|
|
2242
2741
|
let entries;
|
|
2243
2742
|
try {
|
|
2244
2743
|
entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -2246,11 +2745,11 @@ function _scenarioRulesToDefaults() {
|
|
|
2246
2745
|
return out;
|
|
2247
2746
|
}
|
|
2248
2747
|
for (const scenarioId of entries) {
|
|
2249
|
-
const rulesFile = resolveScenarioRulesFile(scenarioId) ??
|
|
2250
|
-
if (!
|
|
2748
|
+
const rulesFile = resolveScenarioRulesFile(scenarioId) ?? join6(scenDir, scenarioId, "rules.json");
|
|
2749
|
+
if (!existsSync7(rulesFile)) continue;
|
|
2251
2750
|
let data;
|
|
2252
2751
|
try {
|
|
2253
|
-
data = JSON.parse(
|
|
2752
|
+
data = JSON.parse(readFileSync6(rulesFile, "utf-8"));
|
|
2254
2753
|
} catch {
|
|
2255
2754
|
continue;
|
|
2256
2755
|
}
|
|
@@ -2287,8 +2786,8 @@ function _scenarioRulesToDefaults() {
|
|
|
2287
2786
|
function _agentRulesToDefaults() {
|
|
2288
2787
|
const out = {};
|
|
2289
2788
|
const _root = getRulesRoot();
|
|
2290
|
-
const agentsDir = _root ?
|
|
2291
|
-
if (!
|
|
2789
|
+
const agentsDir = _root ? join6(_root, "agents") : existsSync7(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync7(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
2790
|
+
if (!existsSync7(agentsDir)) return out;
|
|
2292
2791
|
let entries;
|
|
2293
2792
|
try {
|
|
2294
2793
|
entries = readdirSync4(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -2296,11 +2795,11 @@ function _agentRulesToDefaults() {
|
|
|
2296
2795
|
return out;
|
|
2297
2796
|
}
|
|
2298
2797
|
for (const agentId of entries) {
|
|
2299
|
-
const rulesFile =
|
|
2300
|
-
if (!
|
|
2798
|
+
const rulesFile = join6(agentsDir, agentId, "rules.json");
|
|
2799
|
+
if (!existsSync7(rulesFile)) continue;
|
|
2301
2800
|
let data;
|
|
2302
2801
|
try {
|
|
2303
|
-
data = JSON.parse(
|
|
2802
|
+
data = JSON.parse(readFileSync6(rulesFile, "utf-8"));
|
|
2304
2803
|
} catch {
|
|
2305
2804
|
continue;
|
|
2306
2805
|
}
|
|
@@ -2908,12 +3407,12 @@ function addCustomSkill(skillId, category, keywords, content) {
|
|
|
2908
3407
|
);
|
|
2909
3408
|
}
|
|
2910
3409
|
const runtime = loadRuntimeConfig();
|
|
2911
|
-
const skillsDir =
|
|
3410
|
+
const skillsDir = join6(getRuntimeConfigDir(), "agent_moss_skills");
|
|
2912
3411
|
try {
|
|
2913
|
-
|
|
3412
|
+
mkdirSync2(skillsDir, { recursive: true });
|
|
2914
3413
|
} catch {
|
|
2915
3414
|
}
|
|
2916
|
-
writeFileSync(
|
|
3415
|
+
writeFileSync(join6(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
2917
3416
|
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
2918
3417
|
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
2919
3418
|
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
@@ -2928,9 +3427,9 @@ function deleteCustomSkill(skillId, category) {
|
|
|
2928
3427
|
if (idx >= 0) {
|
|
2929
3428
|
if (skills[idx].builtin) return null;
|
|
2930
3429
|
skills.splice(idx, 1);
|
|
2931
|
-
const skillPath =
|
|
3430
|
+
const skillPath = join6(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
|
|
2932
3431
|
try {
|
|
2933
|
-
if (
|
|
3432
|
+
if (existsSync7(skillPath)) unlinkSync(skillPath);
|
|
2934
3433
|
} catch {
|
|
2935
3434
|
}
|
|
2936
3435
|
saveRuntimeConfig(runtime);
|
|
@@ -3134,10 +3633,10 @@ var init_runtime_config = __esm({
|
|
|
3134
3633
|
init_rules_loader();
|
|
3135
3634
|
init_runtime_config_bridge();
|
|
3136
3635
|
init_rules_mode();
|
|
3137
|
-
__dirname4 =
|
|
3636
|
+
__dirname4 = dirname4(fileURLToPath4(import.meta.url));
|
|
3138
3637
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
3139
3638
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
3140
|
-
RUNTIME_CONFIG_PATH =
|
|
3639
|
+
RUNTIME_CONFIG_PATH = join6(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
3141
3640
|
LAYER_DISABLE_ENV = {
|
|
3142
3641
|
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
3143
3642
|
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
@@ -3165,10 +3664,10 @@ var init_runtime_config = __esm({
|
|
|
3165
3664
|
intent_consistency: ["intent_keywords", "reason"]
|
|
3166
3665
|
};
|
|
3167
3666
|
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
3168
|
-
_AGENTS_DIR_PROD =
|
|
3169
|
-
_AGENTS_DIR_DEV =
|
|
3170
|
-
_SCENARIOS_DIR_PROD =
|
|
3171
|
-
_SCENARIOS_DIR_DEV =
|
|
3667
|
+
_AGENTS_DIR_PROD = join6(__dirname4, "agents");
|
|
3668
|
+
_AGENTS_DIR_DEV = join6(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
3669
|
+
_SCENARIOS_DIR_PROD = join6(__dirname4, "scenarios");
|
|
3670
|
+
_SCENARIOS_DIR_DEV = join6(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
3172
3671
|
_effectiveCache = /* @__PURE__ */ new WeakMap();
|
|
3173
3672
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
3174
3673
|
registerRuntimeConfigBridge({
|
|
@@ -3506,18 +4005,23 @@ var init_config = __esm({
|
|
|
3506
4005
|
});
|
|
3507
4006
|
|
|
3508
4007
|
// src/agents/index.ts
|
|
3509
|
-
import { readFileSync as
|
|
3510
|
-
import { resolve as
|
|
4008
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
4009
|
+
import { resolve as resolve5, dirname as dirname5, join as join7 } from "path";
|
|
3511
4010
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
3512
4011
|
function resolveAgentsDir() {
|
|
3513
4012
|
const root = getRulesRoot();
|
|
3514
|
-
if (root) return
|
|
3515
|
-
if (
|
|
3516
|
-
if (
|
|
4013
|
+
if (root) return join7(root, "agents");
|
|
4014
|
+
if (existsSync8(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
4015
|
+
if (existsSync8(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
3517
4016
|
return AGENTS_DIR_PROD;
|
|
3518
4017
|
}
|
|
3519
4018
|
function getAgentExtensions(agentId) {
|
|
3520
4019
|
if (!agentId) return null;
|
|
4020
|
+
if (!isSafeId(agentId)) {
|
|
4021
|
+
console.warn(`[AgentMoss agents] \u975E\u6CD5 agent_id \u5DF2\u5FFD\u7565\uFF08\u542B\u8DEF\u5F84\u5B57\u7B26\u6216\u8D85\u957F\uFF09: ${agentId.slice(0, 120)}`);
|
|
4022
|
+
return null;
|
|
4023
|
+
}
|
|
4024
|
+
if (cache.size >= CACHE_MAX) cache.clear();
|
|
3521
4025
|
const fp = getRuleSourceFingerprint();
|
|
3522
4026
|
const cached2 = cache.get(agentId);
|
|
3523
4027
|
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
@@ -3530,13 +4034,13 @@ function getAgentExtensions(agentId) {
|
|
|
3530
4034
|
}
|
|
3531
4035
|
if (ext === null) {
|
|
3532
4036
|
const agentsDir = resolveAgentsDir();
|
|
3533
|
-
const rulesFile =
|
|
3534
|
-
if (!
|
|
4037
|
+
const rulesFile = resolve5(agentsDir, agentId, "rules.json");
|
|
4038
|
+
if (!existsSync8(rulesFile)) {
|
|
3535
4039
|
cache.set(agentId, { fp, ext: null });
|
|
3536
4040
|
return null;
|
|
3537
4041
|
}
|
|
3538
4042
|
try {
|
|
3539
|
-
const raw =
|
|
4043
|
+
const raw = readFileSync7(rulesFile, "utf-8");
|
|
3540
4044
|
ext = JSON.parse(raw);
|
|
3541
4045
|
if (!ext.agent_id) {
|
|
3542
4046
|
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
@@ -3570,7 +4074,7 @@ function mergeAgentExtensions(...exts) {
|
|
|
3570
4074
|
extra_skills: nonNull.flatMap((e) => e.extra_skills || [])
|
|
3571
4075
|
};
|
|
3572
4076
|
}
|
|
3573
|
-
var __filename4, __dirname5, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
|
|
4077
|
+
var __filename4, __dirname5, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache, CACHE_MAX;
|
|
3574
4078
|
var init_agents = __esm({
|
|
3575
4079
|
"src/agents/index.ts"() {
|
|
3576
4080
|
"use strict";
|
|
@@ -3578,26 +4082,32 @@ var init_agents = __esm({
|
|
|
3578
4082
|
init_paths();
|
|
3579
4083
|
init_runtime_config();
|
|
3580
4084
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
3581
|
-
__dirname5 =
|
|
3582
|
-
AGENTS_DIR_PROD =
|
|
3583
|
-
AGENTS_DIR_DEV_FALLBACK =
|
|
4085
|
+
__dirname5 = dirname5(__filename4);
|
|
4086
|
+
AGENTS_DIR_PROD = resolve5(__dirname5, "agents");
|
|
4087
|
+
AGENTS_DIR_DEV_FALLBACK = resolve5(__dirname5, "../../../agent_moss/agents");
|
|
3584
4088
|
cache = /* @__PURE__ */ new Map();
|
|
4089
|
+
CACHE_MAX = 512;
|
|
3585
4090
|
}
|
|
3586
4091
|
});
|
|
3587
4092
|
|
|
3588
4093
|
// src/scenarios/index.ts
|
|
3589
|
-
import { readFileSync as
|
|
3590
|
-
import { resolve as
|
|
4094
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4095
|
+
import { resolve as resolve6, dirname as dirname6, join as join8 } from "path";
|
|
3591
4096
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
3592
4097
|
function resolveScenariosDir() {
|
|
3593
4098
|
const root = getRulesRoot();
|
|
3594
|
-
if (root) return
|
|
3595
|
-
if (
|
|
3596
|
-
if (
|
|
4099
|
+
if (root) return join8(root, "scenarios");
|
|
4100
|
+
if (existsSync9(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
|
|
4101
|
+
if (existsSync9(SCENARIOS_DIR_DEV_FALLBACK)) return SCENARIOS_DIR_DEV_FALLBACK;
|
|
3597
4102
|
return SCENARIOS_DIR_PROD;
|
|
3598
4103
|
}
|
|
3599
4104
|
function getScenarioExtensions(scenarioId) {
|
|
3600
4105
|
if (!scenarioId) return null;
|
|
4106
|
+
if (!isSafeId(scenarioId)) {
|
|
4107
|
+
console.warn(`[AgentMoss scenarios] \u975E\u6CD5 scenario_id \u5DF2\u5FFD\u7565\uFF08\u542B\u8DEF\u5F84\u5B57\u7B26\u6216\u8D85\u957F\uFF09: ${scenarioId.slice(0, 120)}`);
|
|
4108
|
+
return null;
|
|
4109
|
+
}
|
|
4110
|
+
if (cache2.size >= CACHE_MAX2) cache2.clear();
|
|
3601
4111
|
const fp = getRuleSourceFingerprint();
|
|
3602
4112
|
const cached2 = cache2.get(scenarioId);
|
|
3603
4113
|
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
@@ -3616,19 +4126,19 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3616
4126
|
}
|
|
3617
4127
|
}
|
|
3618
4128
|
if (ext === null) {
|
|
3619
|
-
const builtinFile =
|
|
4129
|
+
const builtinFile = resolve6(resolveScenariosDir(), scenarioId, "rules.json");
|
|
3620
4130
|
const rulesFile = overrideHit ?? builtinFile;
|
|
3621
4131
|
if (overrideHit) {
|
|
3622
4132
|
console.warn(
|
|
3623
4133
|
`[AgentMoss] \u26A0\uFE0F \u573A\u666F ${scenarioId} \u89C4\u5219\u88AB\u8986\u76D6\u76EE\u5F55\u8986\u76D6: ${overrideHit}\uFF08\u5185\u7F6E\u9ED8\u8BA4 ${builtinFile} \u88AB\u5FFD\u7565\uFF09\u3002\u8BE5\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4\uFF0C\u5185\u7F6E\u573A\u666F\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762\u3002`
|
|
3624
4134
|
);
|
|
3625
4135
|
}
|
|
3626
|
-
if (!
|
|
4136
|
+
if (!existsSync9(rulesFile)) {
|
|
3627
4137
|
cache2.set(scenarioId, { fp, ext: null });
|
|
3628
4138
|
return null;
|
|
3629
4139
|
}
|
|
3630
4140
|
try {
|
|
3631
|
-
const raw =
|
|
4141
|
+
const raw = readFileSync8(rulesFile, "utf-8");
|
|
3632
4142
|
const data = JSON.parse(raw);
|
|
3633
4143
|
const id = data.scenario_id || data.agent_id || "";
|
|
3634
4144
|
if (!id) {
|
|
@@ -3667,7 +4177,7 @@ function getScenarioExtensionsMulti(scenarioSpec) {
|
|
|
3667
4177
|
if (nonNull.length === 0) return null;
|
|
3668
4178
|
return mergeAgentExtensions(...nonNull);
|
|
3669
4179
|
}
|
|
3670
|
-
var __filename5, __dirname6, SCENARIOS_DIR_PROD, SCENARIOS_DIR_DEV_FALLBACK, cache2;
|
|
4180
|
+
var __filename5, __dirname6, SCENARIOS_DIR_PROD, SCENARIOS_DIR_DEV_FALLBACK, cache2, CACHE_MAX2;
|
|
3671
4181
|
var init_scenarios = __esm({
|
|
3672
4182
|
"src/scenarios/index.ts"() {
|
|
3673
4183
|
"use strict";
|
|
@@ -3677,10 +4187,11 @@ var init_scenarios = __esm({
|
|
|
3677
4187
|
init_runtime_config();
|
|
3678
4188
|
init_rules_loader();
|
|
3679
4189
|
__filename5 = fileURLToPath7(import.meta.url);
|
|
3680
|
-
__dirname6 =
|
|
3681
|
-
SCENARIOS_DIR_PROD =
|
|
3682
|
-
SCENARIOS_DIR_DEV_FALLBACK =
|
|
4190
|
+
__dirname6 = dirname6(__filename5);
|
|
4191
|
+
SCENARIOS_DIR_PROD = resolve6(__dirname6, "scenarios");
|
|
4192
|
+
SCENARIOS_DIR_DEV_FALLBACK = resolve6(__dirname6, "../../../agent_moss/scenarios");
|
|
3683
4193
|
cache2 = /* @__PURE__ */ new Map();
|
|
4194
|
+
CACHE_MAX2 = 512;
|
|
3684
4195
|
}
|
|
3685
4196
|
});
|
|
3686
4197
|
|
|
@@ -4560,8 +5071,8 @@ var init_heuristic = __esm({
|
|
|
4560
5071
|
});
|
|
4561
5072
|
|
|
4562
5073
|
// src/engine/template-loader.ts
|
|
4563
|
-
import { readFileSync as
|
|
4564
|
-
import { resolve as
|
|
5074
|
+
import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
|
|
5075
|
+
import { resolve as resolve7, dirname as dirname7 } from "path";
|
|
4565
5076
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
4566
5077
|
function loadTemplates() {
|
|
4567
5078
|
if (cachedTemplates) return cachedTemplates;
|
|
@@ -4573,10 +5084,10 @@ function loadTemplates() {
|
|
|
4573
5084
|
return cachedTemplates;
|
|
4574
5085
|
}
|
|
4575
5086
|
for (const file of TEMPLATE_FILES) {
|
|
4576
|
-
const filePath =
|
|
4577
|
-
if (
|
|
5087
|
+
const filePath = resolve7(dir, file);
|
|
5088
|
+
if (existsSync10(filePath)) {
|
|
4578
5089
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
4579
|
-
cachedTemplates[key] =
|
|
5090
|
+
cachedTemplates[key] = readFileSync9(filePath, "utf-8");
|
|
4580
5091
|
}
|
|
4581
5092
|
}
|
|
4582
5093
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
@@ -4589,15 +5100,15 @@ var init_template_loader = __esm({
|
|
|
4589
5100
|
init_esm_shims();
|
|
4590
5101
|
init_asset_dir();
|
|
4591
5102
|
__filename6 = fileURLToPath8(import.meta.url);
|
|
4592
|
-
__dirname7 =
|
|
5103
|
+
__dirname7 = dirname7(__filename6);
|
|
4593
5104
|
TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
4594
5105
|
cachedTemplates = null;
|
|
4595
5106
|
}
|
|
4596
5107
|
});
|
|
4597
5108
|
|
|
4598
5109
|
// src/engine/script-analyzer.ts
|
|
4599
|
-
import { readFileSync as
|
|
4600
|
-
import { resolve as
|
|
5110
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
5111
|
+
import { resolve as resolve8, join as join9 } from "path";
|
|
4601
5112
|
function emptyResult() {
|
|
4602
5113
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
4603
5114
|
}
|
|
@@ -4650,8 +5161,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
4650
5161
|
}
|
|
4651
5162
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
4652
5163
|
try {
|
|
4653
|
-
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ?
|
|
4654
|
-
const raw =
|
|
5164
|
+
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ? join9(process.env.HOME || "/home/user", scriptPath.slice(1)) : resolve8(scriptPath);
|
|
5165
|
+
const raw = readFileSync10(expandedPath, "utf-8");
|
|
4655
5166
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
4656
5167
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
4657
5168
|
} catch (e) {
|
|
@@ -4779,14 +5290,14 @@ function isSysArgv(node) {
|
|
|
4779
5290
|
const prop = node.getChild("PropertyName");
|
|
4780
5291
|
return prop !== null && srcSlice(prop) === "argv";
|
|
4781
5292
|
}
|
|
4782
|
-
function evalExpr(node,
|
|
5293
|
+
function evalExpr(node, env2) {
|
|
4783
5294
|
if (!node) return null;
|
|
4784
5295
|
const name = node.name;
|
|
4785
5296
|
if (name === "String") return stringValue(srcSlice(node));
|
|
4786
5297
|
if (name === "Number") return srcSlice(node);
|
|
4787
5298
|
if (name === "VariableName") {
|
|
4788
5299
|
const id = srcSlice(node);
|
|
4789
|
-
return id in
|
|
5300
|
+
return id in env2.vars ? env2.vars[id] : null;
|
|
4790
5301
|
}
|
|
4791
5302
|
if (name === "MemberExpression") {
|
|
4792
5303
|
const bracket = node.getChild("[");
|
|
@@ -4796,13 +5307,13 @@ function evalExpr(node, env) {
|
|
|
4796
5307
|
const idxNode = bracket.nextSibling;
|
|
4797
5308
|
if (idxNode && idxNode.name === "Number") {
|
|
4798
5309
|
const idx = parseInt(srcSlice(idxNode), 10);
|
|
4799
|
-
if (idx >= 1 && idx <=
|
|
5310
|
+
if (idx >= 1 && idx <= env2.argv.length) return env2.argv[idx - 1];
|
|
4800
5311
|
return null;
|
|
4801
5312
|
}
|
|
4802
5313
|
}
|
|
4803
5314
|
return null;
|
|
4804
5315
|
}
|
|
4805
|
-
if (isSysArgv(node)) return
|
|
5316
|
+
if (isSysArgv(node)) return env2.argv[0] ?? null;
|
|
4806
5317
|
return null;
|
|
4807
5318
|
}
|
|
4808
5319
|
if (name === "CallExpression") {
|
|
@@ -4810,7 +5321,7 @@ function evalExpr(node, env) {
|
|
|
4810
5321
|
if (func && func.name === "VariableName" && srcSlice(func) === "len") {
|
|
4811
5322
|
const argList = node.getChild("ArgList");
|
|
4812
5323
|
const inner = argList ? firstNamedChild(argList) : null;
|
|
4813
|
-
if (inner && isSysArgv(inner)) return String(
|
|
5324
|
+
if (inner && isSysArgv(inner)) return String(env2.argv.length + 1);
|
|
4814
5325
|
}
|
|
4815
5326
|
return null;
|
|
4816
5327
|
}
|
|
@@ -4822,16 +5333,16 @@ function evalExpr(node, env) {
|
|
|
4822
5333
|
const condNode = ifKw.nextSibling;
|
|
4823
5334
|
const orelseNode = elseKw.nextSibling;
|
|
4824
5335
|
if (!thenNode || !condNode || !orelseNode) return null;
|
|
4825
|
-
const cond = evalCondition(condNode,
|
|
4826
|
-
if (cond === true) return evalExpr(thenNode,
|
|
4827
|
-
if (cond === false) return evalExpr(orelseNode,
|
|
5336
|
+
const cond = evalCondition(condNode, env2);
|
|
5337
|
+
if (cond === true) return evalExpr(thenNode, env2);
|
|
5338
|
+
if (cond === false) return evalExpr(orelseNode, env2);
|
|
4828
5339
|
return null;
|
|
4829
5340
|
}
|
|
4830
5341
|
if (name === "BinaryExpression") {
|
|
4831
5342
|
const op = findOp(node);
|
|
4832
5343
|
if (op === "+") {
|
|
4833
|
-
const left = evalExpr(node.firstChild,
|
|
4834
|
-
const right = evalExpr(lastNamedChild(node),
|
|
5344
|
+
const left = evalExpr(node.firstChild, env2);
|
|
5345
|
+
const right = evalExpr(lastNamedChild(node), env2);
|
|
4835
5346
|
if (left !== null && right !== null) return left + right;
|
|
4836
5347
|
}
|
|
4837
5348
|
return null;
|
|
@@ -4864,7 +5375,7 @@ function findOp(node) {
|
|
|
4864
5375
|
}
|
|
4865
5376
|
return null;
|
|
4866
5377
|
}
|
|
4867
|
-
function evalCondition(node,
|
|
5378
|
+
function evalCondition(node, env2) {
|
|
4868
5379
|
if (!node) return null;
|
|
4869
5380
|
const name = node.name;
|
|
4870
5381
|
if (name === "Boolean") {
|
|
@@ -4875,7 +5386,7 @@ function evalCondition(node, env) {
|
|
|
4875
5386
|
const op = findOp(node);
|
|
4876
5387
|
if (op === "and" || op === "or") {
|
|
4877
5388
|
const parts2 = namedChildren(node);
|
|
4878
|
-
const vals = parts2.map((p) => evalCondition(p,
|
|
5389
|
+
const vals = parts2.map((p) => evalCondition(p, env2));
|
|
4879
5390
|
if (op === "and") {
|
|
4880
5391
|
if (vals.some((v) => v === false)) return false;
|
|
4881
5392
|
if (vals.every((v) => v === true)) return true;
|
|
@@ -4889,13 +5400,13 @@ function evalCondition(node, env) {
|
|
|
4889
5400
|
if (parts.length !== 3) return null;
|
|
4890
5401
|
const [left, opNode, right] = parts;
|
|
4891
5402
|
const opText = srcSlice(opNode).trim();
|
|
4892
|
-
return evalCompare(left, opText, right,
|
|
5403
|
+
return evalCompare(left, opText, right, env2);
|
|
4893
5404
|
}
|
|
4894
5405
|
if (name === "UnaryExpression") {
|
|
4895
5406
|
const kw = node.getChild("not");
|
|
4896
5407
|
if (kw) {
|
|
4897
5408
|
const operand = kw.nextSibling;
|
|
4898
|
-
const v = evalCondition(operand,
|
|
5409
|
+
const v = evalCondition(operand, env2);
|
|
4899
5410
|
return v === null ? null : !v;
|
|
4900
5411
|
}
|
|
4901
5412
|
return null;
|
|
@@ -4916,39 +5427,39 @@ function namedChildren(node) {
|
|
|
4916
5427
|
}
|
|
4917
5428
|
return out;
|
|
4918
5429
|
}
|
|
4919
|
-
function evalCompare(left, opText, right,
|
|
5430
|
+
function evalCompare(left, opText, right, env2) {
|
|
4920
5431
|
if (opText === "==" && left.name === "VariableName" && srcSlice(left) === "__name__") {
|
|
4921
|
-
const rv = evalExpr(right,
|
|
5432
|
+
const rv = evalExpr(right, env2);
|
|
4922
5433
|
if (rv !== null) return rv === "__main__";
|
|
4923
5434
|
return null;
|
|
4924
5435
|
}
|
|
4925
5436
|
if (opText === "==" || opText === "!=") {
|
|
4926
|
-
const lv = evalExpr(left,
|
|
4927
|
-
const rv = evalExpr(right,
|
|
5437
|
+
const lv = evalExpr(left, env2);
|
|
5438
|
+
const rv = evalExpr(right, env2);
|
|
4928
5439
|
if (lv !== null && rv !== null) {
|
|
4929
5440
|
return opText === "==" ? lv === rv : lv !== rv;
|
|
4930
5441
|
}
|
|
4931
5442
|
return null;
|
|
4932
5443
|
}
|
|
4933
5444
|
if (opText === "in") {
|
|
4934
|
-
const lv = evalExpr(left,
|
|
5445
|
+
const lv = evalExpr(left, env2);
|
|
4935
5446
|
if (lv === null) return null;
|
|
4936
5447
|
const rightName = right.name;
|
|
4937
5448
|
if (rightName === "TupleExpression" || rightName === "ArrayExpression" || rightName === "SetExpression" || rightName === "ParenthesizedExpression") {
|
|
4938
5449
|
const container = rightName === "ParenthesizedExpression" && right.firstChild && right.firstChild.name === "TupleExpression" ? right.firstChild : right;
|
|
4939
5450
|
for (const elt of namedChildren(container)) {
|
|
4940
|
-
const ev = evalExpr(elt,
|
|
5451
|
+
const ev = evalExpr(elt, env2);
|
|
4941
5452
|
if (ev !== null && ev === lv) return true;
|
|
4942
5453
|
}
|
|
4943
5454
|
return false;
|
|
4944
5455
|
}
|
|
4945
|
-
const rv = evalExpr(right,
|
|
5456
|
+
const rv = evalExpr(right, env2);
|
|
4946
5457
|
if (rv !== null) return rv.includes(lv);
|
|
4947
5458
|
return null;
|
|
4948
5459
|
}
|
|
4949
5460
|
if (opText === ">" || opText === ">=" || opText === "<" || opText === "<=") {
|
|
4950
|
-
const lv = evalExpr(left,
|
|
4951
|
-
const rv = evalExpr(right,
|
|
5461
|
+
const lv = evalExpr(left, env2);
|
|
5462
|
+
const rv = evalExpr(right, env2);
|
|
4952
5463
|
if (lv === null || rv === null) return null;
|
|
4953
5464
|
const ln = parseInt(lv, 10);
|
|
4954
5465
|
const rn = parseInt(rv, 10);
|
|
@@ -4960,71 +5471,71 @@ function evalCompare(left, opText, right, env) {
|
|
|
4960
5471
|
}
|
|
4961
5472
|
return null;
|
|
4962
5473
|
}
|
|
4963
|
-
function processStmts(stmts,
|
|
4964
|
-
for (const s of stmts) processStmt(s,
|
|
5474
|
+
function processStmts(stmts, env2, lineStarts, lines, branches) {
|
|
5475
|
+
for (const s of stmts) processStmt(s, env2, lineStarts, lines, branches);
|
|
4965
5476
|
}
|
|
4966
|
-
function invalidateEnvOnCalls(node,
|
|
5477
|
+
function invalidateEnvOnCalls(node, env2) {
|
|
4967
5478
|
const cursor = node.cursor();
|
|
4968
5479
|
cursor.iterate((n) => {
|
|
4969
5480
|
const nd = n.node;
|
|
4970
5481
|
if (nd.name !== "CallExpression") return true;
|
|
4971
5482
|
const func = nd.firstChild;
|
|
4972
5483
|
if (func && func.name === "VariableName" && !PURE_BUILTINS.has(srcSlice(func))) {
|
|
4973
|
-
for (const k of Object.keys(
|
|
5484
|
+
for (const k of Object.keys(env2.vars)) env2.vars[k] = null;
|
|
4974
5485
|
return false;
|
|
4975
5486
|
}
|
|
4976
5487
|
return true;
|
|
4977
5488
|
});
|
|
4978
5489
|
}
|
|
4979
|
-
function processStmt(stmt,
|
|
5490
|
+
function processStmt(stmt, env2, lineStarts, lines, branches) {
|
|
4980
5491
|
const name = stmt.name;
|
|
4981
5492
|
if (name === "AssignStatement") {
|
|
4982
5493
|
const target = stmt.firstChild;
|
|
4983
5494
|
if (target && target.name === "VariableName") {
|
|
4984
5495
|
const valNode = target.nextSibling ? target.nextSibling.nextSibling : null;
|
|
4985
|
-
invalidateEnvOnCalls(stmt,
|
|
4986
|
-
const val = evalExpr(valNode,
|
|
4987
|
-
|
|
5496
|
+
invalidateEnvOnCalls(stmt, env2);
|
|
5497
|
+
const val = evalExpr(valNode, env2);
|
|
5498
|
+
env2.vars[srcSlice(target)] = val;
|
|
4988
5499
|
}
|
|
4989
5500
|
markNodeLines(stmt, lines, lineStarts);
|
|
4990
5501
|
return;
|
|
4991
5502
|
}
|
|
4992
5503
|
if (name === "UpdateStatement") {
|
|
4993
5504
|
const target = stmt.firstChild;
|
|
4994
|
-
if (target && target.name === "VariableName")
|
|
4995
|
-
invalidateEnvOnCalls(stmt,
|
|
5505
|
+
if (target && target.name === "VariableName") env2.vars[srcSlice(target)] = null;
|
|
5506
|
+
invalidateEnvOnCalls(stmt, env2);
|
|
4996
5507
|
markNodeLines(stmt, lines, lineStarts);
|
|
4997
5508
|
return;
|
|
4998
5509
|
}
|
|
4999
5510
|
if (name === "IfStatement") {
|
|
5000
|
-
processIf(stmt,
|
|
5511
|
+
processIf(stmt, env2, lineStarts, lines, branches);
|
|
5001
5512
|
return;
|
|
5002
5513
|
}
|
|
5003
5514
|
if (name === "ForStatement" || name === "WhileStatement") {
|
|
5004
5515
|
markNodeLines(stmt, lines, lineStarts);
|
|
5005
5516
|
for (const body of stmt.getChildren("Body")) {
|
|
5006
|
-
processStmts(bodyStmts(body), { argv:
|
|
5517
|
+
processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
|
|
5007
5518
|
}
|
|
5008
5519
|
return;
|
|
5009
5520
|
}
|
|
5010
5521
|
if (name === "TryStatement") {
|
|
5011
5522
|
markNodeLines(stmt, lines, lineStarts);
|
|
5012
5523
|
for (const body of stmt.getChildren("Body")) {
|
|
5013
|
-
processStmts(bodyStmts(body), { argv:
|
|
5524
|
+
processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
|
|
5014
5525
|
}
|
|
5015
5526
|
return;
|
|
5016
5527
|
}
|
|
5017
5528
|
if (name === "WithStatement") {
|
|
5018
5529
|
markNodeLines(stmt, lines, lineStarts);
|
|
5019
5530
|
const body = stmt.getChild("Body");
|
|
5020
|
-
if (body) processStmts(bodyStmts(body), { argv:
|
|
5531
|
+
if (body) processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
|
|
5021
5532
|
return;
|
|
5022
5533
|
}
|
|
5023
5534
|
if (name === "MatchStatement") {
|
|
5024
5535
|
markNodeLines(stmt, lines, lineStarts);
|
|
5025
5536
|
for (const clause of stmt.getChildren("MatchClause")) {
|
|
5026
5537
|
const body = clause.getChild("Body");
|
|
5027
|
-
if (body) processStmts(bodyStmts(body), { argv:
|
|
5538
|
+
if (body) processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
|
|
5028
5539
|
}
|
|
5029
5540
|
return;
|
|
5030
5541
|
}
|
|
@@ -5033,7 +5544,7 @@ function processStmt(stmt, env, lineStarts, lines, branches) {
|
|
|
5033
5544
|
return;
|
|
5034
5545
|
}
|
|
5035
5546
|
if (name === "ExpressionStatement") {
|
|
5036
|
-
invalidateEnvOnCalls(stmt,
|
|
5547
|
+
invalidateEnvOnCalls(stmt, env2);
|
|
5037
5548
|
}
|
|
5038
5549
|
markNodeLines(stmt, lines, lineStarts);
|
|
5039
5550
|
}
|
|
@@ -5053,7 +5564,7 @@ function bodyStmts(body) {
|
|
|
5053
5564
|
}
|
|
5054
5565
|
return out;
|
|
5055
5566
|
}
|
|
5056
|
-
function processIf(stmt,
|
|
5567
|
+
function processIf(stmt, env2, lineStarts, lines, branches) {
|
|
5057
5568
|
const sections = [];
|
|
5058
5569
|
let c = stmt.firstChild;
|
|
5059
5570
|
while (c) {
|
|
@@ -5068,17 +5579,17 @@ function processIf(stmt, env, lineStarts, lines, branches) {
|
|
|
5068
5579
|
}
|
|
5069
5580
|
c = c.nextSibling;
|
|
5070
5581
|
}
|
|
5071
|
-
processIfSections(stmt, sections, 0,
|
|
5582
|
+
processIfSections(stmt, sections, 0, env2, lineStarts, lines, branches);
|
|
5072
5583
|
}
|
|
5073
|
-
function processIfSections(_stmt, sections, idx,
|
|
5584
|
+
function processIfSections(_stmt, sections, idx, env2, lineStarts, lines, branches) {
|
|
5074
5585
|
if (idx >= sections.length) return;
|
|
5075
5586
|
const s = sections[idx];
|
|
5076
5587
|
if (s.cond) markNodeLines(s.cond, lines, lineStarts);
|
|
5077
5588
|
if (s.isElse) {
|
|
5078
|
-
if (s.body) processStmts(bodyStmts(s.body),
|
|
5589
|
+
if (s.body) processStmts(bodyStmts(s.body), env2, lineStarts, lines, branches);
|
|
5079
5590
|
return;
|
|
5080
5591
|
}
|
|
5081
|
-
const cond = s.cond ? evalCondition(s.cond,
|
|
5592
|
+
const cond = s.cond ? evalCondition(s.cond, env2) : null;
|
|
5082
5593
|
branches.push({
|
|
5083
5594
|
kind: "if",
|
|
5084
5595
|
label: s.cond ? srcSlice(s.cond) : "",
|
|
@@ -5086,18 +5597,18 @@ function processIfSections(_stmt, sections, idx, env, lineStarts, lines, branche
|
|
|
5086
5597
|
reachable: cond !== false
|
|
5087
5598
|
});
|
|
5088
5599
|
if (cond === true) {
|
|
5089
|
-
if (s.body) processStmts(bodyStmts(s.body),
|
|
5600
|
+
if (s.body) processStmts(bodyStmts(s.body), env2, lineStarts, lines, branches);
|
|
5090
5601
|
return;
|
|
5091
5602
|
}
|
|
5092
5603
|
if (cond === false) {
|
|
5093
|
-
processIfSections(_stmt, sections, idx + 1,
|
|
5604
|
+
processIfSections(_stmt, sections, idx + 1, env2, lineStarts, lines, branches);
|
|
5094
5605
|
return;
|
|
5095
5606
|
}
|
|
5096
|
-
if (s.body) processStmts(bodyStmts(s.body),
|
|
5607
|
+
if (s.body) processStmts(bodyStmts(s.body), env2, lineStarts, lines, branches);
|
|
5097
5608
|
for (let j = idx + 1; j < sections.length; j++) {
|
|
5098
5609
|
const sj = sections[j];
|
|
5099
5610
|
if (sj.cond) markNodeLines(sj.cond, lines, lineStarts);
|
|
5100
|
-
if (sj.body) processStmts(bodyStmts(sj.body),
|
|
5611
|
+
if (sj.body) processStmts(bodyStmts(sj.body), env2, lineStarts, lines, branches);
|
|
5101
5612
|
}
|
|
5102
5613
|
}
|
|
5103
5614
|
function parseShellString(s) {
|
|
@@ -5273,7 +5784,7 @@ var init_python_backend = __esm({
|
|
|
5273
5784
|
SOURCE = c;
|
|
5274
5785
|
const sourceLines = c.split("\n");
|
|
5275
5786
|
const lineStarts = buildLineStarts(c);
|
|
5276
|
-
const
|
|
5787
|
+
const env2 = makeEnv(entryArgs || []);
|
|
5277
5788
|
const lines = /* @__PURE__ */ new Set();
|
|
5278
5789
|
const branches = [];
|
|
5279
5790
|
if (sourceLines.length > MAX_LINES) {
|
|
@@ -5300,7 +5811,7 @@ var init_python_backend = __esm({
|
|
|
5300
5811
|
};
|
|
5301
5812
|
}
|
|
5302
5813
|
const stmts = bodyStmts(tree);
|
|
5303
|
-
processStmts(stmts,
|
|
5814
|
+
processStmts(stmts, env2, lineStarts, lines, branches);
|
|
5304
5815
|
const reachable = reconstruct(sourceLines, lines);
|
|
5305
5816
|
let calls = extractCallsFromTree(tree, lines);
|
|
5306
5817
|
if (calls.length === 0) calls = extractCallsRegex(c);
|
|
@@ -5528,7 +6039,7 @@ function tokenize(content) {
|
|
|
5528
6039
|
}
|
|
5529
6040
|
return stmts;
|
|
5530
6041
|
}
|
|
5531
|
-
function evalExpr2(text,
|
|
6042
|
+
function evalExpr2(text, env2) {
|
|
5532
6043
|
const isSingleQuoted = text.length >= 2 && text[0] === "'" && text[text.length - 1] === "'";
|
|
5533
6044
|
if (text.length >= 2 && (text[0] === '"' || text[0] === "'") && text[text.length - 1] === text[0]) {
|
|
5534
6045
|
text = text.slice(1, -1);
|
|
@@ -5544,19 +6055,19 @@ function evalExpr2(text, env) {
|
|
|
5544
6055
|
const dm = inner.match(/^([A-Za-z_][A-Za-z0-9_]*|\d+):?-(.*)$/s);
|
|
5545
6056
|
if (dm) {
|
|
5546
6057
|
const key = /^\d+$/.test(dm[1]) ? `$${dm[1]}` : dm[1];
|
|
5547
|
-
const bound =
|
|
6058
|
+
const bound = env2.get(key);
|
|
5548
6059
|
if (bound && !(bound.kind === "concrete" && !bound.value)) {
|
|
5549
6060
|
result.push(bound);
|
|
5550
6061
|
} else {
|
|
5551
|
-
result.push(evalExpr2(dm[2],
|
|
6062
|
+
result.push(evalExpr2(dm[2], env2));
|
|
5552
6063
|
}
|
|
5553
6064
|
continue;
|
|
5554
6065
|
}
|
|
5555
6066
|
if (/^\d+$/.test(inner)) {
|
|
5556
|
-
const v =
|
|
6067
|
+
const v = env2.get(`$${inner}`);
|
|
5557
6068
|
result.push(v || svalSym(`$${inner}`));
|
|
5558
6069
|
} else {
|
|
5559
|
-
const v =
|
|
6070
|
+
const v = env2.get(inner);
|
|
5560
6071
|
result.push(v || svalSym(inner));
|
|
5561
6072
|
}
|
|
5562
6073
|
continue;
|
|
@@ -5565,7 +6076,7 @@ function evalExpr2(text, env) {
|
|
|
5565
6076
|
if (bare) {
|
|
5566
6077
|
const varName = bare[1];
|
|
5567
6078
|
const key = /^\d+$/.test(varName) ? `$${varName}` : varName;
|
|
5568
|
-
const v =
|
|
6079
|
+
const v = env2.get(key);
|
|
5569
6080
|
result.push(v || svalSym(p));
|
|
5570
6081
|
continue;
|
|
5571
6082
|
}
|
|
@@ -5575,13 +6086,13 @@ function evalExpr2(text, env) {
|
|
|
5575
6086
|
return fold(svalExpr(result));
|
|
5576
6087
|
}
|
|
5577
6088
|
function initEnv(entryArgs) {
|
|
5578
|
-
const
|
|
6089
|
+
const env2 = /* @__PURE__ */ new Map();
|
|
5579
6090
|
if (entryArgs && entryArgs.length) {
|
|
5580
|
-
entryArgs.forEach((a, idx) =>
|
|
5581
|
-
|
|
5582
|
-
|
|
6091
|
+
entryArgs.forEach((a, idx) => env2.set(`$${idx + 1}`, svalConc(a)));
|
|
6092
|
+
env2.set("$@", svalConc(entryArgs.join(" ")));
|
|
6093
|
+
env2.set("$*", svalConc(entryArgs.join(" ")));
|
|
5583
6094
|
}
|
|
5584
|
-
return
|
|
6095
|
+
return env2;
|
|
5585
6096
|
}
|
|
5586
6097
|
function globToRegex(pattern) {
|
|
5587
6098
|
let out = "^";
|
|
@@ -5639,12 +6150,12 @@ function evalNumericCompare(op, left, right) {
|
|
|
5639
6150
|
return null;
|
|
5640
6151
|
}
|
|
5641
6152
|
}
|
|
5642
|
-
function resolveVarSide(tok,
|
|
6153
|
+
function resolveVarSide(tok, env2) {
|
|
5643
6154
|
if (!tok.startsWith("$")) return tok;
|
|
5644
|
-
const ev = evalExpr2(tok,
|
|
6155
|
+
const ev = evalExpr2(tok, env2);
|
|
5645
6156
|
return ev.kind === "concrete" ? ev.value : null;
|
|
5646
6157
|
}
|
|
5647
|
-
function evalCondition2(cond,
|
|
6158
|
+
function evalCondition2(cond, env2) {
|
|
5648
6159
|
let inner = cond.trim().replace(/^\[\[?|\]\]?$/g, "").trim();
|
|
5649
6160
|
if (/^-[A-Za-z]\s/.test(inner) || inner.startsWith("-d") || inner.startsWith("-f")) return null;
|
|
5650
6161
|
const innerQ = inner.replace(/"/g, "").replace(/'/g, "");
|
|
@@ -5654,8 +6165,8 @@ function evalCondition2(cond, env) {
|
|
|
5654
6165
|
if (!opm) continue;
|
|
5655
6166
|
const left2 = innerQ.slice(0, opm.index).trim();
|
|
5656
6167
|
const right2 = innerQ.slice(opm.index + opm[0].length).trim();
|
|
5657
|
-
const lv2 = resolveVarSide(left2,
|
|
5658
|
-
const rv2 = resolveVarSide(right2,
|
|
6168
|
+
const lv2 = resolveVarSide(left2, env2);
|
|
6169
|
+
const rv2 = resolveVarSide(right2, env2);
|
|
5659
6170
|
if (lv2 === null || rv2 === null) return null;
|
|
5660
6171
|
return evalNumericCompare(op2, lv2, rv2);
|
|
5661
6172
|
}
|
|
@@ -5666,7 +6177,7 @@ function evalCondition2(cond, env) {
|
|
|
5666
6177
|
let right = sm[3].trim();
|
|
5667
6178
|
if (op === "=~") {
|
|
5668
6179
|
if (right.startsWith("/") && right.endsWith("/")) right = right.slice(1, -1);
|
|
5669
|
-
const lv2 = resolveVarSide(left,
|
|
6180
|
+
const lv2 = resolveVarSide(left, env2);
|
|
5670
6181
|
if (lv2 === null) return null;
|
|
5671
6182
|
try {
|
|
5672
6183
|
return new RegExp(right).test(lv2);
|
|
@@ -5674,8 +6185,8 @@ function evalCondition2(cond, env) {
|
|
|
5674
6185
|
return null;
|
|
5675
6186
|
}
|
|
5676
6187
|
}
|
|
5677
|
-
const lv = resolveVarSide(left,
|
|
5678
|
-
const rv = resolveVarSide(right,
|
|
6188
|
+
const lv = resolveVarSide(left, env2);
|
|
6189
|
+
const rv = resolveVarSide(right, env2);
|
|
5679
6190
|
if (lv === null || rv === null) return null;
|
|
5680
6191
|
if (op === "!=") return lv !== rv;
|
|
5681
6192
|
return lv === rv;
|
|
@@ -5685,7 +6196,7 @@ function symbolicProcess(content, entryArgs) {
|
|
|
5685
6196
|
const stmts = tokenize(content);
|
|
5686
6197
|
if (!stmts.some((s) => s.kind === "case" || s.kind === "if" || s.kind === "assign")) return null;
|
|
5687
6198
|
if (stmts.some((s) => !s.modelable)) return null;
|
|
5688
|
-
const
|
|
6199
|
+
const env2 = initEnv(entryArgs);
|
|
5689
6200
|
let linesBudget = 0;
|
|
5690
6201
|
let branchesBudget = 0;
|
|
5691
6202
|
let modelable = false;
|
|
@@ -5695,9 +6206,9 @@ function symbolicProcess(content, entryArgs) {
|
|
|
5695
6206
|
if (linesBudget > MAX_LINES2) return null;
|
|
5696
6207
|
if (s.kind === "assign") {
|
|
5697
6208
|
modelable = true;
|
|
5698
|
-
if (
|
|
5699
|
-
const v = evalExpr2(s.rhs,
|
|
5700
|
-
|
|
6209
|
+
if (env2.size >= MAX_VARS) return null;
|
|
6210
|
+
const v = evalExpr2(s.rhs, env2);
|
|
6211
|
+
env2.set(s.name, fold(v));
|
|
5701
6212
|
out.push(s.line);
|
|
5702
6213
|
continue;
|
|
5703
6214
|
}
|
|
@@ -5705,7 +6216,7 @@ function symbolicProcess(content, entryArgs) {
|
|
|
5705
6216
|
modelable = true;
|
|
5706
6217
|
branchesBudget += s.branches.length;
|
|
5707
6218
|
if (branchesBudget > MAX_BRANCHES) return null;
|
|
5708
|
-
const selVal = fold(evalExpr2(s.selector,
|
|
6219
|
+
const selVal = fold(evalExpr2(s.selector, env2));
|
|
5709
6220
|
if (selVal.kind !== "concrete") return null;
|
|
5710
6221
|
const concrete = selVal.value || "";
|
|
5711
6222
|
let selected = null;
|
|
@@ -5732,7 +6243,7 @@ esac`);
|
|
|
5732
6243
|
modelable = true;
|
|
5733
6244
|
branchesBudget++;
|
|
5734
6245
|
if (branchesBudget > MAX_BRANCHES) return null;
|
|
5735
|
-
const truth = evalCondition2(s.cond,
|
|
6246
|
+
const truth = evalCondition2(s.cond, env2);
|
|
5736
6247
|
if (truth === null) return null;
|
|
5737
6248
|
const chosen = truth ? s.thenBody : s.elseBody;
|
|
5738
6249
|
if (chosen === null) return null;
|
|
@@ -6265,9 +6776,9 @@ var init_tracker = __esm({
|
|
|
6265
6776
|
});
|
|
6266
6777
|
|
|
6267
6778
|
// src/engine/script-content-analyzer.ts
|
|
6268
|
-
import { statSync as statSync4, existsSync as
|
|
6269
|
-
import { resolve as
|
|
6270
|
-
import { homedir as
|
|
6779
|
+
import { statSync as statSync4, existsSync as existsSync12 } from "fs";
|
|
6780
|
+
import { resolve as resolve10, join as join11, isAbsolute as isAbsolute3 } from "path";
|
|
6781
|
+
import { homedir as homedir4 } from "os";
|
|
6271
6782
|
function emptyChain() {
|
|
6272
6783
|
return {
|
|
6273
6784
|
nodes: [],
|
|
@@ -6378,7 +6889,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6378
6889
|
if (looksLikeScriptPath(tok)) {
|
|
6379
6890
|
try {
|
|
6380
6891
|
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
6381
|
-
if (
|
|
6892
|
+
if (existsSync12(resolved)) {
|
|
6382
6893
|
const stat = statSync4(resolved);
|
|
6383
6894
|
if (stat.isFile()) return resolved;
|
|
6384
6895
|
}
|
|
@@ -6392,7 +6903,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6392
6903
|
const p = m[1];
|
|
6393
6904
|
try {
|
|
6394
6905
|
const resolved = resolvePath2(p);
|
|
6395
|
-
if (
|
|
6906
|
+
if (existsSync12(resolved)) {
|
|
6396
6907
|
const stat = statSync4(resolved);
|
|
6397
6908
|
if (stat.isFile()) return resolved;
|
|
6398
6909
|
}
|
|
@@ -6408,7 +6919,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6408
6919
|
let resolved;
|
|
6409
6920
|
try {
|
|
6410
6921
|
resolved = resolvePath2(p);
|
|
6411
|
-
if (
|
|
6922
|
+
if (existsSync12(resolved)) {
|
|
6412
6923
|
const stat = statSync4(resolved);
|
|
6413
6924
|
if (stat.isFile()) return resolved;
|
|
6414
6925
|
}
|
|
@@ -6449,9 +6960,9 @@ function shlexSplit(s) {
|
|
|
6449
6960
|
function resolvePath2(p) {
|
|
6450
6961
|
let expanded = p;
|
|
6451
6962
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
6452
|
-
expanded =
|
|
6963
|
+
expanded = join11(homedir4(), expanded.slice(1));
|
|
6453
6964
|
}
|
|
6454
|
-
return
|
|
6965
|
+
return resolve10(expanded);
|
|
6455
6966
|
}
|
|
6456
6967
|
function scanExtensionPatterns(ext, content) {
|
|
6457
6968
|
const pats = ext?.extra_command_patterns;
|
|
@@ -6572,8 +7083,8 @@ function analyzeScriptContent2(input, ext) {
|
|
|
6572
7083
|
resolvedPath = inline;
|
|
6573
7084
|
isInline = true;
|
|
6574
7085
|
}
|
|
6575
|
-
if (resolvedPath && !
|
|
6576
|
-
resolvedPath =
|
|
7086
|
+
if (resolvedPath && !isAbsolute3(resolvedPath) && !resolvedPath.startsWith("~/")) {
|
|
7087
|
+
resolvedPath = join11(input.cwd || process.cwd(), resolvedPath);
|
|
6577
7088
|
}
|
|
6578
7089
|
const l1Adapter = (content) => {
|
|
6579
7090
|
const j = scanDangerousCommandPatterns("bash", content, input.osType);
|
|
@@ -6699,16 +7210,16 @@ __export(token_stats_exports, {
|
|
|
6699
7210
|
recordTokenUsage: () => recordTokenUsage,
|
|
6700
7211
|
resetTokenStats: () => resetTokenStats
|
|
6701
7212
|
});
|
|
6702
|
-
import { readFileSync as
|
|
7213
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync2, existsSync as existsSync13, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
6703
7214
|
function getStatsPath() {
|
|
6704
7215
|
const envPath = process.env[ENV_STATS_PATH];
|
|
6705
7216
|
if (envPath) return envPath;
|
|
6706
7217
|
return getTokenStatsPath();
|
|
6707
7218
|
}
|
|
6708
7219
|
function loadAllRecords(statsPath) {
|
|
6709
|
-
if (!
|
|
7220
|
+
if (!existsSync13(statsPath)) return [];
|
|
6710
7221
|
try {
|
|
6711
|
-
const data = JSON.parse(
|
|
7222
|
+
const data = JSON.parse(readFileSync13(statsPath, "utf-8"));
|
|
6712
7223
|
if (Array.isArray(data)) return data;
|
|
6713
7224
|
if (data && Array.isArray(data.records)) return data.records;
|
|
6714
7225
|
return [];
|
|
@@ -6719,7 +7230,7 @@ function loadAllRecords(statsPath) {
|
|
|
6719
7230
|
function saveRecords(statsPath, records) {
|
|
6720
7231
|
try {
|
|
6721
7232
|
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
6722
|
-
if (dir && !
|
|
7233
|
+
if (dir && !existsSync13(dir)) mkdirSync3(dir, { recursive: true });
|
|
6723
7234
|
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
6724
7235
|
} catch (e) {
|
|
6725
7236
|
}
|
|
@@ -6846,7 +7357,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
|
|
|
6846
7357
|
}
|
|
6847
7358
|
function resetTokenStats() {
|
|
6848
7359
|
const statsPath = getStatsPath();
|
|
6849
|
-
if (
|
|
7360
|
+
if (existsSync13(statsPath)) {
|
|
6850
7361
|
try {
|
|
6851
7362
|
unlinkSync2(statsPath);
|
|
6852
7363
|
} catch {
|
|
@@ -6865,7 +7376,7 @@ var init_token_stats = __esm({
|
|
|
6865
7376
|
});
|
|
6866
7377
|
|
|
6867
7378
|
// src/engine/llm-analyzer.ts
|
|
6868
|
-
import {
|
|
7379
|
+
import { openSync as openSync2, writeSync as writeSync2, closeSync as closeSync2, constants as fsConstants } from "fs";
|
|
6869
7380
|
import OpenAI from "openai";
|
|
6870
7381
|
async function runLLMAnalysis(input, previous) {
|
|
6871
7382
|
if (!isLLMAvailable()) {
|
|
@@ -6881,7 +7392,15 @@ async function runLLMAnalysis(input, previous) {
|
|
|
6881
7392
|
};
|
|
6882
7393
|
}
|
|
6883
7394
|
const globalConfig = getLLMConfig();
|
|
6884
|
-
|
|
7395
|
+
let perRequest = input.llmConfig;
|
|
7396
|
+
if (perRequest?.apiKey) {
|
|
7397
|
+
const [okUrl, why] = validateLlmBaseUrl(String(perRequest.baseUrl || ""));
|
|
7398
|
+
if (!okUrl) {
|
|
7399
|
+
console.warn("[AgentMoss] \u5FFD\u7565\u4E0D\u88AB\u5141\u8BB8\u7684 per-request llmConfig.baseUrl:", why);
|
|
7400
|
+
perRequest = { ...perRequest, baseUrl: void 0 };
|
|
7401
|
+
}
|
|
7402
|
+
}
|
|
7403
|
+
const config = perRequest?.apiKey ? { ...globalConfig, ...perRequest } : globalConfig;
|
|
6885
7404
|
const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
|
|
6886
7405
|
const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
|
|
6887
7406
|
const cached2 = llmCache.get(cacheKey);
|
|
@@ -6910,6 +7429,10 @@ async function runLLMAnalysis(input, previous) {
|
|
|
6910
7429
|
}
|
|
6911
7430
|
function logPrompt(prompt, aNext, logPath) {
|
|
6912
7431
|
if (!logPath) return;
|
|
7432
|
+
if (!pathWithin(logPath, logRoots())) {
|
|
7433
|
+
console.warn("[AgentMoss] \u5FFD\u7565\u8D8A\u754C\u7684 llm_log_path\uFF08\u4E0D\u5728\u5141\u8BB8\u6839\u76EE\u5F55\u5185\uFF09:", logPath);
|
|
7434
|
+
return;
|
|
7435
|
+
}
|
|
6913
7436
|
try {
|
|
6914
7437
|
const actionType = aNext.actionType || "unknown";
|
|
6915
7438
|
const actionDetail = (aNext.actionDetail || "").slice(0, 100);
|
|
@@ -6917,7 +7440,14 @@ function logPrompt(prompt, aNext, logPath) {
|
|
|
6917
7440
|
${prompt}
|
|
6918
7441
|
--- END PROMPT ---
|
|
6919
7442
|
`;
|
|
6920
|
-
|
|
7443
|
+
let flags = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_APPEND;
|
|
7444
|
+
if (fsConstants.O_NOFOLLOW) flags |= fsConstants.O_NOFOLLOW;
|
|
7445
|
+
const fd = openSync2(logPath, flags, 384);
|
|
7446
|
+
try {
|
|
7447
|
+
writeSync2(fd, line);
|
|
7448
|
+
} finally {
|
|
7449
|
+
closeSync2(fd);
|
|
7450
|
+
}
|
|
6921
7451
|
} catch (e) {
|
|
6922
7452
|
console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
|
|
6923
7453
|
}
|
|
@@ -7076,7 +7606,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
7076
7606
|
}
|
|
7077
7607
|
if (attempt < maxRetries) {
|
|
7078
7608
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
7079
|
-
await new Promise((
|
|
7609
|
+
await new Promise((resolve12) => setTimeout(resolve12, retryCfg.retryInterval * 1e3));
|
|
7080
7610
|
} else {
|
|
7081
7611
|
clearTimeout(totalTimeoutTimer);
|
|
7082
7612
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
@@ -7092,6 +7622,7 @@ var init_llm_analyzer = __esm({
|
|
|
7092
7622
|
"src/engine/llm-analyzer.ts"() {
|
|
7093
7623
|
"use strict";
|
|
7094
7624
|
init_esm_shims();
|
|
7625
|
+
init_security_policy();
|
|
7095
7626
|
init_config();
|
|
7096
7627
|
init_skill_loader();
|
|
7097
7628
|
init_template_loader();
|
|
@@ -7103,9 +7634,9 @@ var init_llm_analyzer = __esm({
|
|
|
7103
7634
|
});
|
|
7104
7635
|
|
|
7105
7636
|
// src/brain/rule-store.ts
|
|
7106
|
-
import { existsSync as
|
|
7107
|
-
import { join as
|
|
7108
|
-
import { homedir as
|
|
7637
|
+
import { existsSync as existsSync14, readFileSync as readFileSync14, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
|
|
7638
|
+
import { join as join12 } from "path";
|
|
7639
|
+
import { homedir as homedir5 } from "os";
|
|
7109
7640
|
function getRulesPath() {
|
|
7110
7641
|
const envPath = process.env[ENV_RULES_PATH];
|
|
7111
7642
|
if (envPath) return envPath;
|
|
@@ -7123,7 +7654,7 @@ var init_rule_store = __esm({
|
|
|
7123
7654
|
"src/brain/rule-store.ts"() {
|
|
7124
7655
|
"use strict";
|
|
7125
7656
|
init_esm_shims();
|
|
7126
|
-
DEFAULT_RULES_PATH =
|
|
7657
|
+
DEFAULT_RULES_PATH = join12(homedir5(), ".agentmoss", "brain", "rules.json");
|
|
7127
7658
|
ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
7128
7659
|
STATUS_DRAFT = "draft";
|
|
7129
7660
|
STATUS_APPROVED = "approved";
|
|
@@ -7140,11 +7671,11 @@ var init_rule_store = __esm({
|
|
|
7140
7671
|
this.data = this.load();
|
|
7141
7672
|
}
|
|
7142
7673
|
load() {
|
|
7143
|
-
if (!
|
|
7674
|
+
if (!existsSync14(this.path)) {
|
|
7144
7675
|
return { version: 1, rules: [], pattern_candidates: [] };
|
|
7145
7676
|
}
|
|
7146
7677
|
try {
|
|
7147
|
-
const raw = JSON.parse(
|
|
7678
|
+
const raw = JSON.parse(readFileSync14(this.path, "utf-8"));
|
|
7148
7679
|
return {
|
|
7149
7680
|
version: raw.version || 1,
|
|
7150
7681
|
rules: raw.rules || [],
|
|
@@ -7156,7 +7687,7 @@ var init_rule_store = __esm({
|
|
|
7156
7687
|
}
|
|
7157
7688
|
save() {
|
|
7158
7689
|
const dir = this.path.substring(0, this.path.lastIndexOf("/"));
|
|
7159
|
-
if (dir)
|
|
7690
|
+
if (dir) mkdirSync4(dir, { recursive: true });
|
|
7160
7691
|
writeFileSync3(this.path, JSON.stringify(this.data, null, 2), "utf-8");
|
|
7161
7692
|
}
|
|
7162
7693
|
addRule(rule) {
|
|
@@ -7738,9 +8269,9 @@ var init_reviewer = __esm({
|
|
|
7738
8269
|
});
|
|
7739
8270
|
|
|
7740
8271
|
// src/brain/skill-manager.ts
|
|
7741
|
-
import { existsSync as
|
|
7742
|
-
import { homedir as
|
|
7743
|
-
import { join as
|
|
8272
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8273
|
+
import { homedir as homedir6 } from "os";
|
|
8274
|
+
import { join as join13 } from "path";
|
|
7744
8275
|
import { randomUUID } from "crypto";
|
|
7745
8276
|
function getMetaPath(override) {
|
|
7746
8277
|
return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
|
|
@@ -7750,14 +8281,14 @@ var init_skill_manager = __esm({
|
|
|
7750
8281
|
"src/brain/skill-manager.ts"() {
|
|
7751
8282
|
"use strict";
|
|
7752
8283
|
init_esm_shims();
|
|
7753
|
-
DEFAULT_SKILL_META_PATH =
|
|
8284
|
+
DEFAULT_SKILL_META_PATH = join13(homedir6(), ".agentmoss", "brain", "skill_meta.json");
|
|
7754
8285
|
ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
|
|
7755
8286
|
BrainSkillManager = class {
|
|
7756
8287
|
constructor(skillsDir, metaPath) {
|
|
7757
8288
|
this.skillsDir = skillsDir;
|
|
7758
8289
|
this.metaPath = getMetaPath(metaPath);
|
|
7759
8290
|
try {
|
|
7760
|
-
|
|
8291
|
+
mkdirSync5(join13(this.metaPath, ".."), { recursive: true });
|
|
7761
8292
|
} catch {
|
|
7762
8293
|
}
|
|
7763
8294
|
this.load();
|
|
@@ -7767,9 +8298,9 @@ var init_skill_manager = __esm({
|
|
|
7767
8298
|
skills = [];
|
|
7768
8299
|
metaPath;
|
|
7769
8300
|
load() {
|
|
7770
|
-
if (!
|
|
8301
|
+
if (!existsSync15(this.metaPath)) return;
|
|
7771
8302
|
try {
|
|
7772
|
-
const data = JSON.parse(
|
|
8303
|
+
const data = JSON.parse(readFileSync15(this.metaPath, "utf-8"));
|
|
7773
8304
|
this.skills = data.skills || [];
|
|
7774
8305
|
} catch {
|
|
7775
8306
|
this.skills = [];
|
|
@@ -7792,7 +8323,7 @@ var init_skill_manager = __esm({
|
|
|
7792
8323
|
let changed = false;
|
|
7793
8324
|
for (const sk of this.skills) {
|
|
7794
8325
|
if (sk.status !== "active") continue;
|
|
7795
|
-
if (!
|
|
8326
|
+
if (!existsSync15(join13(this.skillsDir, `${sk.name}.md`))) {
|
|
7796
8327
|
sk.status = "stale";
|
|
7797
8328
|
changed = true;
|
|
7798
8329
|
}
|
|
@@ -7803,9 +8334,9 @@ var init_skill_manager = __esm({
|
|
|
7803
8334
|
addSkill(skill) {
|
|
7804
8335
|
const id = skill.id || randomUUID();
|
|
7805
8336
|
const name = skill.name || `brain_${id.slice(0, 8)}`;
|
|
7806
|
-
if (!
|
|
7807
|
-
const mdPath =
|
|
7808
|
-
if (
|
|
8337
|
+
if (!existsSync15(this.skillsDir)) return "";
|
|
8338
|
+
const mdPath = join13(this.skillsDir, `${name}.md`);
|
|
8339
|
+
if (existsSync15(mdPath)) return "";
|
|
7809
8340
|
try {
|
|
7810
8341
|
writeFileSync4(mdPath, skill.content, "utf-8");
|
|
7811
8342
|
} catch {
|
|
@@ -7854,12 +8385,12 @@ var init_skill_manager = __esm({
|
|
|
7854
8385
|
const out = [];
|
|
7855
8386
|
for (const sk of this.skills) {
|
|
7856
8387
|
if (sk.origin_node !== "local" || sk.status !== "active") continue;
|
|
7857
|
-
const mdPath =
|
|
7858
|
-
if (!
|
|
8388
|
+
const mdPath = join13(this.skillsDir, `${sk.name}.md`);
|
|
8389
|
+
if (!existsSync15(mdPath)) continue;
|
|
7859
8390
|
out.push({
|
|
7860
8391
|
name: sk.name,
|
|
7861
8392
|
category: sk.category || "",
|
|
7862
|
-
content:
|
|
8393
|
+
content: readFileSync15(mdPath, "utf-8"),
|
|
7863
8394
|
keywords: sk.keywords || []
|
|
7864
8395
|
});
|
|
7865
8396
|
}
|
|
@@ -7946,7 +8477,7 @@ var init_curator = __esm({
|
|
|
7946
8477
|
});
|
|
7947
8478
|
|
|
7948
8479
|
// src/brain/contributor.ts
|
|
7949
|
-
import { dirname as
|
|
8480
|
+
import { dirname as dirname10 } from "path";
|
|
7950
8481
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
7951
8482
|
var __dirname8;
|
|
7952
8483
|
var init_contributor = __esm({
|
|
@@ -7956,7 +8487,7 @@ var init_contributor = __esm({
|
|
|
7956
8487
|
init_rule_store();
|
|
7957
8488
|
init_skill_manager();
|
|
7958
8489
|
init_asset_dir();
|
|
7959
|
-
__dirname8 =
|
|
8490
|
+
__dirname8 = dirname10(fileURLToPath9(import.meta.url));
|
|
7960
8491
|
}
|
|
7961
8492
|
});
|
|
7962
8493
|
|
|
@@ -8324,7 +8855,7 @@ function markIntegrity(actionDetail) {
|
|
|
8324
8855
|
}
|
|
8325
8856
|
return { integrity: "medium", isLowSource: false };
|
|
8326
8857
|
}
|
|
8327
|
-
function initNode(sessionId, seq, actionType, actionDetail) {
|
|
8858
|
+
function initNode(sessionId, seq, actionType, actionDetail, timestamp) {
|
|
8328
8859
|
const { integrity, isLowSource } = markIntegrity(actionDetail);
|
|
8329
8860
|
return {
|
|
8330
8861
|
sessionId,
|
|
@@ -8334,7 +8865,8 @@ function initNode(sessionId, seq, actionType, actionDetail) {
|
|
|
8334
8865
|
stage: markStage(actionDetail),
|
|
8335
8866
|
resources: extractResources(actionDetail),
|
|
8336
8867
|
integrity,
|
|
8337
|
-
isLowSource
|
|
8868
|
+
isLowSource,
|
|
8869
|
+
timestamp: timestamp || void 0
|
|
8338
8870
|
};
|
|
8339
8871
|
}
|
|
8340
8872
|
function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
|
|
@@ -8344,13 +8876,21 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
|
|
|
8344
8876
|
const details = [];
|
|
8345
8877
|
for (const a of actionHistory) {
|
|
8346
8878
|
const ad = String(a.actionDetail || "");
|
|
8347
|
-
if (ad) details.push({
|
|
8879
|
+
if (ad) details.push({
|
|
8880
|
+
actionType: String(a.actionType || ""),
|
|
8881
|
+
actionDetail: ad,
|
|
8882
|
+
calledAt: a.calledAt || void 0
|
|
8883
|
+
});
|
|
8348
8884
|
}
|
|
8349
8885
|
if (aNext) {
|
|
8350
|
-
details.push({
|
|
8886
|
+
details.push({
|
|
8887
|
+
actionType: String(aNext.actionType || ""),
|
|
8888
|
+
actionDetail: String(aNext.actionDetail || ""),
|
|
8889
|
+
calledAt: aNext.calledAt || void 0
|
|
8890
|
+
});
|
|
8351
8891
|
}
|
|
8352
8892
|
details.forEach((d, seq) => {
|
|
8353
|
-
const node = initNode(sessionId, seq, d.actionType, d.actionDetail);
|
|
8893
|
+
const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt);
|
|
8354
8894
|
nodes.push(node);
|
|
8355
8895
|
if (node.stage) stageFlags.add(node.stage);
|
|
8356
8896
|
});
|
|
@@ -8376,7 +8916,8 @@ function buildMultiSessionGraph(sessions, aNext) {
|
|
|
8376
8916
|
last.sessionId,
|
|
8377
8917
|
last.nodes.length,
|
|
8378
8918
|
String(aNext.actionType || ""),
|
|
8379
|
-
String(aNext.actionDetail || "")
|
|
8919
|
+
String(aNext.actionDetail || ""),
|
|
8920
|
+
aNext.calledAt || void 0
|
|
8380
8921
|
));
|
|
8381
8922
|
if (last.nodes.length > 1) {
|
|
8382
8923
|
last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
|
|
@@ -8434,6 +8975,27 @@ var init_builder = __esm({
|
|
|
8434
8975
|
});
|
|
8435
8976
|
|
|
8436
8977
|
// src/engine/formal-engine/properties.ts
|
|
8978
|
+
function parseTs(ts) {
|
|
8979
|
+
if (!ts) return null;
|
|
8980
|
+
const t = Date.parse(String(ts).trim());
|
|
8981
|
+
return Number.isFinite(t) ? t : null;
|
|
8982
|
+
}
|
|
8983
|
+
function privescExfilWindowSec() {
|
|
8984
|
+
const raw = (process.env.AGENT_MOSS_PRIVESC_EXFIL_WINDOW_SEC || "").trim();
|
|
8985
|
+
if (!raw) return 1800;
|
|
8986
|
+
const n = parseInt(raw, 10);
|
|
8987
|
+
return Number.isFinite(n) ? Math.max(0, n) : 1800;
|
|
8988
|
+
}
|
|
8989
|
+
function privescInWindow(nodes, indexes, exfilIdx, windowSec) {
|
|
8990
|
+
const before = indexes.filter((i) => i < exfilIdx);
|
|
8991
|
+
if (windowSec <= 0) return before;
|
|
8992
|
+
const exfilTs = parseTs(nodes[exfilIdx].timestamp);
|
|
8993
|
+
if (exfilTs === null) return before;
|
|
8994
|
+
return before.filter((i) => {
|
|
8995
|
+
const ts = parseTs(nodes[i].timestamp);
|
|
8996
|
+
return ts === null || exfilTs - ts <= windowSec * 1e3;
|
|
8997
|
+
});
|
|
8998
|
+
}
|
|
8437
8999
|
function loadCrossSessionChains() {
|
|
8438
9000
|
if (CROSS_SESSION_CHAINS) return CROSS_SESSION_CHAINS;
|
|
8439
9001
|
const combo = loadL2().combo_chains || {};
|
|
@@ -8625,6 +9187,7 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
8625
9187
|
const nodes = allNodes(graph);
|
|
8626
9188
|
if (nodes.length < 2) return violations;
|
|
8627
9189
|
const witness = graph.witnessIdx;
|
|
9190
|
+
const windowSec = privescExfilWindowSec();
|
|
8628
9191
|
if (witness !== void 0) {
|
|
8629
9192
|
const evIdx = [];
|
|
8630
9193
|
nodes.forEach((n, i) => {
|
|
@@ -8632,15 +9195,17 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
8632
9195
|
});
|
|
8633
9196
|
if (evIdx.length === 0) return violations;
|
|
8634
9197
|
const firstEv = Math.min(...evIdx);
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
if (isPrivescNode(
|
|
8638
|
-
}
|
|
8639
|
-
|
|
9198
|
+
const privescIdx = [];
|
|
9199
|
+
nodes.forEach((n, i) => {
|
|
9200
|
+
if (isPrivescNode(n)) privescIdx.push(i);
|
|
9201
|
+
});
|
|
9202
|
+
const candidates = privescInWindow(nodes, privescIdx, firstEv, windowSec);
|
|
9203
|
+
if (candidates.length > 0) {
|
|
9204
|
+
const pre = nodes[candidates[candidates.length - 1]];
|
|
8640
9205
|
const post = nodes[firstEv];
|
|
8641
9206
|
return [{
|
|
8642
9207
|
propertyId: "no-exfil-after-privesc",
|
|
8643
|
-
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\
|
|
9208
|
+
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
|
|
8644
9209
|
riskLevel: "critical",
|
|
8645
9210
|
path: [
|
|
8646
9211
|
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
@@ -8653,18 +9218,18 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
8653
9218
|
for (let i = 0; i < nodes.length; i++) {
|
|
8654
9219
|
if (!isPrivescNode(nodes[i])) continue;
|
|
8655
9220
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
8656
|
-
if (isExfilNode(nodes[j]))
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
]
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
9221
|
+
if (!isExfilNode(nodes[j])) continue;
|
|
9222
|
+
if (privescInWindow(nodes, [i], j, windowSec).length === 0) continue;
|
|
9223
|
+
violations.push({
|
|
9224
|
+
propertyId: "no-exfil-after-privesc",
|
|
9225
|
+
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
|
|
9226
|
+
riskLevel: "critical",
|
|
9227
|
+
path: [
|
|
9228
|
+
{ sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
|
|
9229
|
+
{ sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
|
|
9230
|
+
]
|
|
9231
|
+
});
|
|
9232
|
+
return violations;
|
|
8668
9233
|
}
|
|
8669
9234
|
}
|
|
8670
9235
|
return violations;
|
|
@@ -8827,6 +9392,10 @@ function checkCrossSessionReachability(graph) {
|
|
|
8827
9392
|
}
|
|
8828
9393
|
return violations;
|
|
8829
9394
|
}
|
|
9395
|
+
function isDataSourceNode(n) {
|
|
9396
|
+
if (n.stage === "CRED" || n.isLowSource) return true;
|
|
9397
|
+
return DATA_SOURCE_READ_RE.test(n.actionDetail);
|
|
9398
|
+
}
|
|
8830
9399
|
function checkSensitiveDataAggregation(graph) {
|
|
8831
9400
|
const violations = [];
|
|
8832
9401
|
const nodes = allNodes(graph);
|
|
@@ -8846,6 +9415,7 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
8846
9415
|
for (const r of n.resources) {
|
|
8847
9416
|
for (const j of resIndex.get(r) || []) {
|
|
8848
9417
|
if (j === exfilIdx) continue;
|
|
9418
|
+
if (!isDataSourceNode(nodes[j])) continue;
|
|
8849
9419
|
srcs.add(`${nodes[j].sessionId}#${nodes[j].seq}`);
|
|
8850
9420
|
}
|
|
8851
9421
|
}
|
|
@@ -8868,7 +9438,7 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
8868
9438
|
}
|
|
8869
9439
|
return violations;
|
|
8870
9440
|
}
|
|
8871
|
-
var CROSS_SESSION_CHAINS, CRED_FILE_RE, EXFIL_RE, EBPF_NET_RE, REMOTE_RE, EXFIL_ACTION_RE, CRED_STRICT_RE, INLINE_SHELL_RE, FORMAL_RULES, PKG_MGR, SHELLS, TOOL_REGISTRY, COMPUTE_RE, STRONG_COMPUTE_TYPES, DL_OR_LOW_RE, ALL_PROPERTIES;
|
|
9441
|
+
var CROSS_SESSION_CHAINS, CRED_FILE_RE, EXFIL_RE, EBPF_NET_RE, REMOTE_RE, EXFIL_ACTION_RE, CRED_STRICT_RE, INLINE_SHELL_RE, FORMAL_RULES, PKG_MGR, SHELLS, TOOL_REGISTRY, COMPUTE_RE, STRONG_COMPUTE_TYPES, DL_OR_LOW_RE, DATA_SOURCE_READ_RE, ALL_PROPERTIES;
|
|
8872
9442
|
var init_properties = __esm({
|
|
8873
9443
|
"src/engine/formal-engine/properties.ts"() {
|
|
8874
9444
|
"use strict";
|
|
@@ -8892,6 +9462,7 @@ var init_properties = __esm({
|
|
|
8892
9462
|
COMPUTE_RE = /subprocess|os\.system|shell\s*=\s*True|\beval\(|\bexec\(|bash\s+[^\s]|sh\s+-c|python3?\s+|node\s+|\btimeout\s+|nohup\s+|\b\.\/[\w./-]+/i;
|
|
8893
9463
|
STRONG_COMPUTE_TYPES = new Set(FORMAL_RULES.compute_action_types ?? []);
|
|
8894
9464
|
DL_OR_LOW_RE = /curl\s+.*-o\b|wget\s+.*\s+-O\b|curl\s+.*\|\s*(bash|sh|python)|wget\s+.*\|\s*(bash|sh|python)|git\s+clone|docker\s+pull|pip\s+install|npm\s+install|base64\s+-d/i;
|
|
9465
|
+
DATA_SOURCE_READ_RE = /\b(?:cat|head|tail|less|more|sed|awk|grep|strings|base64|xxd|od|dd)\b|\b(?:open|read_text|read_bytes)\s*\(/i;
|
|
8895
9466
|
ALL_PROPERTIES = [
|
|
8896
9467
|
{ id: "cross-session-chain", check: checkCrossSessionChain, shortName: "P1" },
|
|
8897
9468
|
{ id: "sensitive-data-flow", check: checkSensitiveDataFlow, shortName: "P2" },
|
|
@@ -9414,10 +9985,10 @@ var init_stateful = __esm({
|
|
|
9414
9985
|
import * as fs4 from "fs";
|
|
9415
9986
|
import * as os2 from "os";
|
|
9416
9987
|
import * as path4 from "path";
|
|
9417
|
-
import { createHash as
|
|
9988
|
+
import { createHash as createHash3 } from "crypto";
|
|
9418
9989
|
function defaultStateDbPath() {
|
|
9419
9990
|
const dir = process.env.AGENT_MOSS_STATE_DIR || path4.join(os2.homedir(), ".agentmoss");
|
|
9420
|
-
const tag =
|
|
9991
|
+
const tag = createHash3("sha1").update("agentmoss-stateful").digest("hex").slice(0, 8);
|
|
9421
9992
|
return path4.join(dir, `stateful_chains_${tag}.json`);
|
|
9422
9993
|
}
|
|
9423
9994
|
var ChainStateStore;
|
|
@@ -9425,6 +9996,7 @@ var init_chain_state = __esm({
|
|
|
9425
9996
|
"src/engine/formal-engine/chain_state.ts"() {
|
|
9426
9997
|
"use strict";
|
|
9427
9998
|
init_esm_shims();
|
|
9999
|
+
init_security_policy();
|
|
9428
10000
|
init_stateful();
|
|
9429
10001
|
ChainStateStore = class {
|
|
9430
10002
|
filePath;
|
|
@@ -9451,8 +10023,8 @@ var init_chain_state = __esm({
|
|
|
9451
10023
|
this._timer = setTimeout(() => {
|
|
9452
10024
|
this._timer = null;
|
|
9453
10025
|
try {
|
|
9454
|
-
|
|
9455
|
-
|
|
10026
|
+
ensurePrivateDir(path4.dirname(this.filePath));
|
|
10027
|
+
writePrivateText(this.filePath, JSON.stringify(this.data, null, 2));
|
|
9456
10028
|
} catch {
|
|
9457
10029
|
}
|
|
9458
10030
|
}, this._flushDelayMs);
|
|
@@ -9464,8 +10036,8 @@ var init_chain_state = __esm({
|
|
|
9464
10036
|
this._timer = null;
|
|
9465
10037
|
}
|
|
9466
10038
|
try {
|
|
9467
|
-
|
|
9468
|
-
|
|
10039
|
+
ensurePrivateDir(path4.dirname(this.filePath));
|
|
10040
|
+
writePrivateText(this.filePath, JSON.stringify(this.data, null, 2));
|
|
9469
10041
|
} catch {
|
|
9470
10042
|
}
|
|
9471
10043
|
}
|
|
@@ -10130,13 +10702,16 @@ function enforceRulesContract() {
|
|
|
10130
10702
|
}
|
|
10131
10703
|
|
|
10132
10704
|
// src/server.ts
|
|
10705
|
+
init_security_policy();
|
|
10133
10706
|
import { cors } from "hono/cors";
|
|
10134
10707
|
import { logger } from "hono/logger";
|
|
10135
10708
|
import net from "net";
|
|
10136
|
-
import { existsSync as
|
|
10709
|
+
import { existsSync as existsSync21 } from "fs";
|
|
10137
10710
|
|
|
10138
10711
|
// src/routes.ts
|
|
10139
10712
|
init_esm_shims();
|
|
10713
|
+
init_security_policy();
|
|
10714
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
10140
10715
|
|
|
10141
10716
|
// src/models.ts
|
|
10142
10717
|
init_esm_shims();
|
|
@@ -10156,7 +10731,10 @@ var ActionItemSchema = z.object({
|
|
|
10156
10731
|
// 工具返回给 agent 的内容。仅 L2 的授权(consent)判定用:判断「用户是否已授权」
|
|
10157
10732
|
// 看的是 ask_user_question 的**回答**,不是提问正文。历史 bug:模型里没有这个字段、
|
|
10158
10733
|
// 路由也没透传,TS 的授权判定退化成「提问正文非空」,等于问过任何问题即已授权。
|
|
10159
|
-
output: z.string().optional().describe("Tool result / user answer (needed by L2 consent checks)")
|
|
10734
|
+
output: z.string().optional().describe("Tool result / user answer (needed by L2 consent checks)"),
|
|
10735
|
+
// 动作真实发生时间(可选)。供 P4「提权→外传」时间窗口判定;缺省时该性质
|
|
10736
|
+
// 退回不设窗口的旧行为。与 Python ActionItem.called_at 对齐。
|
|
10737
|
+
called_at: z.string().optional().describe("Action timestamp (optional)")
|
|
10160
10738
|
});
|
|
10161
10739
|
var PromptTurnSchema = z.object({
|
|
10162
10740
|
text: z.string().default("").describe("User prompt text at this turn"),
|
|
@@ -10222,23 +10800,39 @@ var ReviewResponseSchema = z.object({
|
|
|
10222
10800
|
|
|
10223
10801
|
// src/engine/analyzer.ts
|
|
10224
10802
|
init_esm_shims();
|
|
10803
|
+
init_security_policy();
|
|
10225
10804
|
init_coordinator();
|
|
10226
|
-
import { existsSync as
|
|
10227
|
-
import { tmpdir as
|
|
10228
|
-
import { join as
|
|
10805
|
+
import { existsSync as existsSync18, readFileSync as readFileSync18 } from "fs";
|
|
10806
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
10807
|
+
import { join as join16, resolve as resolve11 } from "path";
|
|
10229
10808
|
|
|
10230
10809
|
// src/infra/policy-cache.ts
|
|
10231
10810
|
init_esm_shims();
|
|
10811
|
+
init_security_policy();
|
|
10812
|
+
init_paths();
|
|
10232
10813
|
init_config();
|
|
10233
|
-
import { createHash as
|
|
10234
|
-
import { existsSync as
|
|
10235
|
-
import { tmpdir } from "os";
|
|
10236
|
-
import { join as
|
|
10237
|
-
var CACHE_DIR =
|
|
10814
|
+
import { createHash as createHash4 } from "crypto";
|
|
10815
|
+
import { existsSync as existsSync17, lstatSync as lstatSync2, readdirSync as readdirSync5, readFileSync as readFileSync17, rmdirSync, unlinkSync as unlinkSync3 } from "fs";
|
|
10816
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
10817
|
+
import { join as join15 } from "path";
|
|
10818
|
+
var CACHE_DIR = join15(tmpdir2(), "agent_moss_policy_cache");
|
|
10238
10819
|
var cacheDirOverride = null;
|
|
10239
10820
|
function cacheDir() {
|
|
10240
10821
|
return cacheDirOverride ?? CACHE_DIR;
|
|
10241
10822
|
}
|
|
10823
|
+
function safeCacheDir() {
|
|
10824
|
+
const dir = cacheDir();
|
|
10825
|
+
if (ensurePrivateDir(dir)) return dir;
|
|
10826
|
+
try {
|
|
10827
|
+
const fallback = join15(getDataRoot(), "policy_cache");
|
|
10828
|
+
if (ensurePrivateDir(fallback)) {
|
|
10829
|
+
console.warn(`[AgentMoss] \u7F13\u5B58\u76EE\u5F55 ${dir} \u4E0D\u5B89\u5168\uFF08\u88AB\u5176\u4ED6\u7528\u6237\u62E5\u6709\u6216\u4E3A\u7B26\u53F7\u94FE\u63A5\uFF09\uFF0C\u5DF2\u56DE\u9000\u5230 ${fallback}`);
|
|
10830
|
+
return fallback;
|
|
10831
|
+
}
|
|
10832
|
+
} catch {
|
|
10833
|
+
}
|
|
10834
|
+
return dir;
|
|
10835
|
+
}
|
|
10242
10836
|
var PolicyCache = class {
|
|
10243
10837
|
// Map 的插入序即 LRU 序(get 时重新 set 到末尾)
|
|
10244
10838
|
constructor(options) {
|
|
@@ -10250,9 +10844,9 @@ var PolicyCache = class {
|
|
|
10250
10844
|
return `${sessionId}::${promptSession}`;
|
|
10251
10845
|
}
|
|
10252
10846
|
cacheFilePath(sessionId, promptSession) {
|
|
10253
|
-
const promptHash =
|
|
10254
|
-
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10255
|
-
return
|
|
10847
|
+
const promptHash = createHash4("md5").update(promptSession).digest("hex").slice(0, 12);
|
|
10848
|
+
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_").replace(/\.\./g, "__");
|
|
10849
|
+
return join15(safeCacheDir(), safeSessionId, `${promptHash}.toml`);
|
|
10256
10850
|
}
|
|
10257
10851
|
/** 取缓存:内存 → 文件;未命中返回 null。 */
|
|
10258
10852
|
get(sessionId, promptSession) {
|
|
@@ -10265,9 +10859,9 @@ var PolicyCache = class {
|
|
|
10265
10859
|
return hit;
|
|
10266
10860
|
}
|
|
10267
10861
|
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10268
|
-
if (
|
|
10862
|
+
if (existsSync17(file) && !lstatSync2(file).isSymbolicLink()) {
|
|
10269
10863
|
try {
|
|
10270
|
-
const policy =
|
|
10864
|
+
const policy = readFileSync17(file, "utf-8");
|
|
10271
10865
|
this.remember(key, policy);
|
|
10272
10866
|
return policy;
|
|
10273
10867
|
} catch {
|
|
@@ -10281,8 +10875,10 @@ var PolicyCache = class {
|
|
|
10281
10875
|
this.remember(this.cacheKey(sessionId, promptSession), policy);
|
|
10282
10876
|
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10283
10877
|
try {
|
|
10284
|
-
|
|
10285
|
-
|
|
10878
|
+
const dir = safeCacheDir();
|
|
10879
|
+
const sub = sessionId.replace(/\//g, "_").replace(/\\/g, "_").replace(/\.\./g, "__");
|
|
10880
|
+
if (!ensurePrivateDir(join15(dir, sub))) return;
|
|
10881
|
+
writePrivateText(file, policy);
|
|
10286
10882
|
} catch {
|
|
10287
10883
|
}
|
|
10288
10884
|
}
|
|
@@ -10306,13 +10902,13 @@ var PolicyCache = class {
|
|
|
10306
10902
|
for (const k of [...this.memory.keys()]) {
|
|
10307
10903
|
if (k.startsWith(`${sessionId}::`)) this.memory.delete(k);
|
|
10308
10904
|
}
|
|
10309
|
-
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10310
|
-
const dir =
|
|
10905
|
+
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_").replace(/\.\./g, "__");
|
|
10906
|
+
const dir = join15(cacheDir(), safeSessionId);
|
|
10311
10907
|
try {
|
|
10312
|
-
if (
|
|
10908
|
+
if (existsSync17(dir)) {
|
|
10313
10909
|
for (const f of readdirSync5(dir)) {
|
|
10314
10910
|
try {
|
|
10315
|
-
unlinkSync3(
|
|
10911
|
+
unlinkSync3(join15(dir, f));
|
|
10316
10912
|
} catch {
|
|
10317
10913
|
}
|
|
10318
10914
|
}
|
|
@@ -10324,11 +10920,11 @@ var PolicyCache = class {
|
|
|
10324
10920
|
clear() {
|
|
10325
10921
|
this.memory.clear();
|
|
10326
10922
|
try {
|
|
10327
|
-
if (
|
|
10923
|
+
if (existsSync17(cacheDir())) {
|
|
10328
10924
|
for (const sessionDir of readdirSync5(cacheDir())) {
|
|
10329
|
-
const dir =
|
|
10925
|
+
const dir = join15(cacheDir(), sessionDir);
|
|
10330
10926
|
try {
|
|
10331
|
-
for (const f of readdirSync5(dir)) unlinkSync3(
|
|
10927
|
+
for (const f of readdirSync5(dir)) unlinkSync3(join15(dir, f));
|
|
10332
10928
|
rmdirSync(dir);
|
|
10333
10929
|
} catch {
|
|
10334
10930
|
}
|
|
@@ -10415,7 +11011,7 @@ ${safe(policy, 2e3)}`);
|
|
|
10415
11011
|
|
|
10416
11012
|
// src/engine/analyzer.ts
|
|
10417
11013
|
import { parse as parseToml } from "smol-toml";
|
|
10418
|
-
var POLICY_OUTPUT_DIR =
|
|
11014
|
+
var POLICY_OUTPUT_DIR = join16(tmpdir3(), "agent_moss");
|
|
10419
11015
|
var policyOutputDirOverride = null;
|
|
10420
11016
|
function policyOutputDir() {
|
|
10421
11017
|
return policyOutputDirOverride ?? POLICY_OUTPUT_DIR;
|
|
@@ -10449,10 +11045,13 @@ function savePolicyToFile(sessionId, policyToml) {
|
|
|
10449
11045
|
if (!isPolicySaveEnabled()) return null;
|
|
10450
11046
|
try {
|
|
10451
11047
|
const outDir = policyOutputDir();
|
|
10452
|
-
|
|
10453
|
-
|
|
10454
|
-
|
|
10455
|
-
|
|
11048
|
+
if (!ensurePrivateDir(outDir)) {
|
|
11049
|
+
console.warn(`[AgentMoss] Policy \u8F93\u51FA\u76EE\u5F55 ${outDir} \u4E0D\u5B89\u5168\uFF0C\u8DF3\u8FC7\u843D\u76D8\uFF08\u4E0D\u5F71\u54CD\u5224\u5B9A\uFF09`);
|
|
11050
|
+
return null;
|
|
11051
|
+
}
|
|
11052
|
+
const safeName = sessionId.replace(/\//g, "_").replace(/\\/g, "_").replace(/\.\./g, "__");
|
|
11053
|
+
const filepath = join16(outDir, `${safeName}.toml`);
|
|
11054
|
+
writePrivateText(filepath, policyToml);
|
|
10456
11055
|
return filepath;
|
|
10457
11056
|
} catch (err) {
|
|
10458
11057
|
console.warn(`[AgentMoss] \u7B56\u7565\u6587\u4EF6\u843D\u76D8\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u5224\u5B9A\uFF09: ${policyOutputDir()}: ${String(err)}`);
|
|
@@ -10489,12 +11088,12 @@ function validateToml(tomlStr) {
|
|
|
10489
11088
|
function loadPolicyMappingDoc() {
|
|
10490
11089
|
const dir = resolveAssetDir(__dirname, "templates");
|
|
10491
11090
|
if (dir) {
|
|
10492
|
-
const p =
|
|
10493
|
-
if (
|
|
11091
|
+
const p = resolve11(dir, "policy_mapping.md");
|
|
11092
|
+
if (existsSync18(p)) return readFileSync18(p, "utf-8");
|
|
10494
11093
|
}
|
|
10495
11094
|
for (const cand of assetDirCandidates(__dirname, "templates")) {
|
|
10496
|
-
const p =
|
|
10497
|
-
if (
|
|
11095
|
+
const p = resolve11(cand, "policy_mapping.md");
|
|
11096
|
+
if (existsSync18(p)) return readFileSync18(p, "utf-8");
|
|
10498
11097
|
}
|
|
10499
11098
|
return "";
|
|
10500
11099
|
}
|
|
@@ -11013,9 +11612,9 @@ function normalizeObservablePayload(body) {
|
|
|
11013
11612
|
init_rule_store();
|
|
11014
11613
|
init_skill_manager();
|
|
11015
11614
|
init_asset_dir();
|
|
11016
|
-
import { join as
|
|
11615
|
+
import { join as join18, dirname as dirname12 } from "path";
|
|
11017
11616
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
11018
|
-
var __dirname9 =
|
|
11617
|
+
var __dirname9 = dirname12(fileURLToPath10(import.meta.url));
|
|
11019
11618
|
async function healthRoute(c) {
|
|
11020
11619
|
return c.json(HealthResponseSchema.parse({
|
|
11021
11620
|
instance: process.env.AGENT_MOSS_INSTANCE || ""
|
|
@@ -11030,7 +11629,23 @@ async function analyzeRoute(c) {
|
|
|
11030
11629
|
if (!osType) {
|
|
11031
11630
|
osType = process.platform === "win32" ? "windows" : "linux";
|
|
11032
11631
|
}
|
|
11033
|
-
const
|
|
11632
|
+
const _principal = principalOf(c.req.raw.headers, new URL(c.req.url).pathname);
|
|
11633
|
+
const [_boundOk, agentId] = bindAgent(_principal, req.agent_id || req.metadata?.agent_type || "");
|
|
11634
|
+
if (!_boundOk) {
|
|
11635
|
+
return c.json({ detail: `agent_id \u4E0E\u6240\u6301\u51ED\u8BC1\u4E0D\u5339\u914D: ${agentId}` }, 403);
|
|
11636
|
+
}
|
|
11637
|
+
const _sweepKey = _principal || `ip:${c.req.header("x-forwarded-for") || "local"}`;
|
|
11638
|
+
const [_sweepN, _sweepHit] = noteSessionAccess(_sweepKey, req.session_id);
|
|
11639
|
+
if (_sweepHit) {
|
|
11640
|
+
if (sessionSweepAction() === "deny") {
|
|
11641
|
+
return c.json({
|
|
11642
|
+
detail: `\u77ED\u65F6\u95F4\u5185\u8BBF\u95EE\u4E86 ${_sweepN} \u4E2A\u4E0D\u540C\u4F1A\u8BDD\uFF0C\u8D85\u51FA\u9608\u503C ${sessionSweepThreshold()}\uFF08\u7591\u4F3C\u5192\u5145/\u626B\u63CF\uFF09`
|
|
11643
|
+
}, 429);
|
|
11644
|
+
}
|
|
11645
|
+
console.warn(
|
|
11646
|
+
`[AgentMoss] \u26A0\uFE0F \u8DE8\u4F1A\u8BDD\u6279\u91CF\u8BBF\u95EE: key=${_sweepKey} \u7A97\u53E3\u5185 ${_sweepN} \u4E2A\u4E0D\u540C session\uFF08\u9608\u503C ${sessionSweepThreshold()}\uFF09`
|
|
11647
|
+
);
|
|
11648
|
+
}
|
|
11034
11649
|
const result = await analyze({
|
|
11035
11650
|
sessionId: req.session_id,
|
|
11036
11651
|
promptSession: req.prompt_session,
|
|
@@ -11041,14 +11656,16 @@ async function analyzeRoute(c) {
|
|
|
11041
11656
|
actionHistory: req.action_history.map((h) => ({
|
|
11042
11657
|
actionType: h.action_type,
|
|
11043
11658
|
actionDetail: h.action_detail,
|
|
11044
|
-
output: h.output
|
|
11659
|
+
output: h.output,
|
|
11660
|
+
calledAt: h.called_at
|
|
11045
11661
|
})),
|
|
11046
11662
|
sessionHistory: (req.session_history || []).map((s) => ({
|
|
11047
11663
|
sessionId: s.session_id,
|
|
11048
11664
|
actionHistory: (s.action_history || []).map((h) => ({
|
|
11049
11665
|
actionType: h.action_type,
|
|
11050
11666
|
actionDetail: h.action_detail,
|
|
11051
|
-
output: h.output
|
|
11667
|
+
output: h.output,
|
|
11668
|
+
calledAt: h.called_at
|
|
11052
11669
|
}))
|
|
11053
11670
|
})),
|
|
11054
11671
|
// prompt_history 必须透传:L2 意图一致性(logic-rules.ts 的
|
|
@@ -11075,7 +11692,8 @@ async function analyzeRoute(c) {
|
|
|
11075
11692
|
// agent_id:AgentGate V1 normalizer 历史上把 agent_type 塞 metadata,
|
|
11076
11693
|
// 顶层为空时兜底(OpenDesk 的 agent-moss 需 agent_id 加载专属规则)。
|
|
11077
11694
|
agentId,
|
|
11078
|
-
|
|
11695
|
+
// 严格模式:完全不接受 per-request LLM 配置(默认接受,保持原行为)
|
|
11696
|
+
llmConfig: perRequestLlmEnabled() ? req.metadata?.llm_config : void 0,
|
|
11079
11697
|
llmLogPath: req.metadata?.llm_log_path
|
|
11080
11698
|
});
|
|
11081
11699
|
const durationMs = performance.now() - t0;
|
|
@@ -11123,7 +11741,8 @@ async function analyzeRoute(c) {
|
|
|
11123
11741
|
violated_policy: "",
|
|
11124
11742
|
confidence: 90,
|
|
11125
11743
|
analysis_duration_ms: Math.round((performance.now() - t0) * 10) / 10,
|
|
11126
|
-
|
|
11744
|
+
// 跨会话节点脱敏(时间线按 agent_id 共享,原样返回等于泄露别人的命令原文)
|
|
11745
|
+
violation_chain: redactChain(judgment.violation_chain, req.session_id)
|
|
11127
11746
|
})
|
|
11128
11747
|
);
|
|
11129
11748
|
}
|
|
@@ -11147,14 +11766,19 @@ async function analyzeRoute(c) {
|
|
|
11147
11766
|
})
|
|
11148
11767
|
);
|
|
11149
11768
|
} catch (err) {
|
|
11150
|
-
console.error("[AgentMoss] Analysis error:", err);
|
|
11151
11769
|
if (err instanceof SyntaxError) {
|
|
11152
11770
|
return c.json({ detail: err.message, error: "Invalid JSON" }, 422);
|
|
11153
11771
|
}
|
|
11154
11772
|
if (err?.issues) {
|
|
11155
11773
|
return c.json({ detail: err.issues, error: "Validation error" }, 422);
|
|
11156
11774
|
}
|
|
11157
|
-
|
|
11775
|
+
if (errorDetailMode() === "full") {
|
|
11776
|
+
console.error("[AgentMoss] Analysis error:", err);
|
|
11777
|
+
return c.json({ detail: String(err), error: "Internal error" }, 500);
|
|
11778
|
+
}
|
|
11779
|
+
const ref = randomBytes2(4).toString("hex");
|
|
11780
|
+
console.error(`[AgentMoss] Analysis internal error (ref=${ref}):`, err);
|
|
11781
|
+
return c.json({ detail: `Internal server error (ref: ${ref})`, error: "Internal error" }, 500);
|
|
11158
11782
|
}
|
|
11159
11783
|
}
|
|
11160
11784
|
var reviewStore = null;
|
|
@@ -11193,6 +11817,11 @@ async function reviewRoute(c) {
|
|
|
11193
11817
|
if (!tracker) {
|
|
11194
11818
|
tracker = new StatefulChainTracker(agentId);
|
|
11195
11819
|
}
|
|
11820
|
+
if (req.db_path && !pathWithin(req.db_path, reviewRoots())) {
|
|
11821
|
+
return c.json({
|
|
11822
|
+
detail: "db_path \u4E0D\u5728\u5141\u8BB8\u8BFB\u53D6\u7684\u76EE\u5F55\u5185\uFF1B\u6807\u51C6 AgentGate \u90E8\u7F72\u65E0\u9700\u914D\u7F6E\uFF0C\u81EA\u5B9A\u4E49\u8DEF\u5F84\u8BF7\u52A0\u5165 AGENT_MOSS_REVIEW_PATHS"
|
|
11823
|
+
}, 403);
|
|
11824
|
+
}
|
|
11196
11825
|
let sessions = [];
|
|
11197
11826
|
if (req.db_path) {
|
|
11198
11827
|
sessions = loadAgentGateSessions(req.db_path);
|
|
@@ -11256,15 +11885,20 @@ async function reviewRoute(c) {
|
|
|
11256
11885
|
discovered_patterns: discovered
|
|
11257
11886
|
}));
|
|
11258
11887
|
} catch (err) {
|
|
11259
|
-
|
|
11260
|
-
|
|
11888
|
+
if (errorDetailMode() === "full") {
|
|
11889
|
+
console.error("[AgentMoss] Review error:", err);
|
|
11890
|
+
return c.json({ error: "Internal error", detail: String(err) }, 500);
|
|
11891
|
+
}
|
|
11892
|
+
const ref = randomBytes2(4).toString("hex");
|
|
11893
|
+
console.error(`[AgentMoss] Review internal error (ref=${ref}):`, err);
|
|
11894
|
+
return c.json({ error: "Internal error", detail: `Internal server error (ref: ${ref})` }, 500);
|
|
11261
11895
|
}
|
|
11262
11896
|
}
|
|
11263
11897
|
function getBrainRuleStore() {
|
|
11264
11898
|
return new BrainRuleStore();
|
|
11265
11899
|
}
|
|
11266
11900
|
function getBrainSkillManager() {
|
|
11267
|
-
const dir = resolveAssetDir(__dirname9, "skills") ??
|
|
11901
|
+
const dir = resolveAssetDir(__dirname9, "skills") ?? join18(__dirname9, "..", "..", "agent_moss", "skills");
|
|
11268
11902
|
return new BrainSkillManager(dir);
|
|
11269
11903
|
}
|
|
11270
11904
|
async function brainRulesRoute(c) {
|
|
@@ -11367,11 +12001,12 @@ init_rules_loader();
|
|
|
11367
12001
|
init_asset_dir();
|
|
11368
12002
|
init_rules_mode();
|
|
11369
12003
|
import { Hono } from "hono";
|
|
11370
|
-
import {
|
|
11371
|
-
import {
|
|
12004
|
+
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
12005
|
+
import { readFileSync as readFileSync20, existsSync as existsSync20, unlinkSync as unlinkSync4 } from "fs";
|
|
12006
|
+
import { join as join19, dirname as dirname13 } from "path";
|
|
11372
12007
|
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
11373
|
-
var __dirname10 =
|
|
11374
|
-
var STATIC_DIR =
|
|
12008
|
+
var __dirname10 = dirname13(fileURLToPath11(import.meta.url));
|
|
12009
|
+
var STATIC_DIR = join19(__dirname10, "static");
|
|
11375
12010
|
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
11376
12011
|
function getConsoleToken() {
|
|
11377
12012
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
@@ -11380,7 +12015,11 @@ function verifyAuth(req) {
|
|
|
11380
12015
|
const token = getConsoleToken();
|
|
11381
12016
|
if (!token) return true;
|
|
11382
12017
|
const auth = req.headers.get("Authorization") || "";
|
|
11383
|
-
if (auth.startsWith("Bearer "))
|
|
12018
|
+
if (auth.startsWith("Bearer ")) {
|
|
12019
|
+
const a = Buffer.from(auth.slice(7), "utf-8");
|
|
12020
|
+
const b = Buffer.from(token, "utf-8");
|
|
12021
|
+
return a.length === b.length && timingSafeEqual2(a, b);
|
|
12022
|
+
}
|
|
11384
12023
|
return false;
|
|
11385
12024
|
}
|
|
11386
12025
|
function authMiddleware() {
|
|
@@ -11409,9 +12048,9 @@ function createConsoleRouter() {
|
|
|
11409
12048
|
console2.use("/api/*", authMiddleware());
|
|
11410
12049
|
console2.use("/api/*", builtinOnlyReadOnlyMiddleware());
|
|
11411
12050
|
const serveIndex = (c) => {
|
|
11412
|
-
const indexFile =
|
|
11413
|
-
if (
|
|
11414
|
-
return c.html(
|
|
12051
|
+
const indexFile = join19(STATIC_DIR, "index.html");
|
|
12052
|
+
if (existsSync20(indexFile)) {
|
|
12053
|
+
return c.html(readFileSync20(indexFile, "utf-8"));
|
|
11415
12054
|
}
|
|
11416
12055
|
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
11417
12056
|
};
|
|
@@ -11606,15 +12245,15 @@ function createConsoleRouter() {
|
|
|
11606
12245
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
11607
12246
|
return c.json({ detail: "\u975E\u6CD5 skill id" }, 400);
|
|
11608
12247
|
}
|
|
11609
|
-
const userPath =
|
|
12248
|
+
const userPath = join19(dirname13(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
11610
12249
|
const builtinDir = resolveAssetDir(__dirname10, "skills");
|
|
11611
12250
|
const candidates = [
|
|
11612
12251
|
...isRuntimeConfigDisabled() ? [] : [userPath],
|
|
11613
|
-
builtinDir ?
|
|
12252
|
+
builtinDir ? join19(builtinDir, `${skillId}.md`) : ""
|
|
11614
12253
|
].filter(Boolean);
|
|
11615
12254
|
for (const p of candidates) {
|
|
11616
|
-
if (
|
|
11617
|
-
return c.json({ skill_id: skillId, path: p, content:
|
|
12255
|
+
if (existsSync20(p)) {
|
|
12256
|
+
return c.json({ skill_id: skillId, path: p, content: readFileSync20(p, "utf-8") });
|
|
11618
12257
|
}
|
|
11619
12258
|
}
|
|
11620
12259
|
return c.json({ detail: `skill ${skillId} \u4E0D\u5B58\u5728` }, 404);
|
|
@@ -11711,7 +12350,7 @@ function createConsoleRouter() {
|
|
|
11711
12350
|
const defaults = generateSourceDefaults();
|
|
11712
12351
|
try {
|
|
11713
12352
|
const p = getRuntimeConfigPath();
|
|
11714
|
-
if (
|
|
12353
|
+
if (existsSync20(p)) unlinkSync4(p);
|
|
11715
12354
|
} catch {
|
|
11716
12355
|
}
|
|
11717
12356
|
return c.json(withL22View(defaults));
|
|
@@ -11768,7 +12407,7 @@ function logDeploymentSelfCheck() {
|
|
|
11768
12407
|
console.log("\u5C42\u7EA7\u5F00\u5173: " + layerKeys.map((k) => `${k}=${isLayerEnabled(k) ? "on" : "OFF"}`).join(" "));
|
|
11769
12408
|
if (!builtinOnly) {
|
|
11770
12409
|
try {
|
|
11771
|
-
const dirs = rulesDirs().filter((d) =>
|
|
12410
|
+
const dirs = rulesDirs().filter((d) => existsSync21(d));
|
|
11772
12411
|
if (dirs.length > 0) {
|
|
11773
12412
|
console.log(`\u89C4\u5219\u76EE\u5F55: ${dirs.join(", ")}\uFF08\u53EA\u8BFB\u5176\u4E2D ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09`);
|
|
11774
12413
|
}
|
|
@@ -11806,11 +12445,134 @@ function logDeploymentSelfCheck() {
|
|
|
11806
12445
|
console.error(`\u90E8\u7F72\u81EA\u68C0\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u670D\u52A1\uFF09: ${exc instanceof Error ? exc.message : String(exc)}`);
|
|
11807
12446
|
}
|
|
11808
12447
|
}
|
|
12448
|
+
var PooledLimiter = class {
|
|
12449
|
+
counts = /* @__PURE__ */ new Map();
|
|
12450
|
+
tryAcquire(key, limit) {
|
|
12451
|
+
if (limit <= 0) return true;
|
|
12452
|
+
const cur = this.counts.get(key) || 0;
|
|
12453
|
+
if (cur >= limit) return false;
|
|
12454
|
+
this.counts.set(key, cur + 1);
|
|
12455
|
+
return true;
|
|
12456
|
+
}
|
|
12457
|
+
release(key) {
|
|
12458
|
+
const cur = this.counts.get(key) || 0;
|
|
12459
|
+
if (cur <= 1) this.counts.delete(key);
|
|
12460
|
+
else this.counts.set(key, cur - 1);
|
|
12461
|
+
}
|
|
12462
|
+
};
|
|
12463
|
+
var RateLimiter = class {
|
|
12464
|
+
constructor(qps, burst) {
|
|
12465
|
+
this.qps = qps;
|
|
12466
|
+
this.burst = burst;
|
|
12467
|
+
}
|
|
12468
|
+
qps;
|
|
12469
|
+
burst;
|
|
12470
|
+
buckets = /* @__PURE__ */ new Map();
|
|
12471
|
+
get enabled() {
|
|
12472
|
+
return this.qps > 0;
|
|
12473
|
+
}
|
|
12474
|
+
allow(key) {
|
|
12475
|
+
if (!this.enabled) return true;
|
|
12476
|
+
const burst = this.burst ?? Math.max(this.qps, 1) * 2;
|
|
12477
|
+
const now = Date.now() / 1e3;
|
|
12478
|
+
const [tokens, last] = this.buckets.get(key) ?? [burst, now];
|
|
12479
|
+
const next = Math.min(burst, tokens + (now - last) * this.qps);
|
|
12480
|
+
if (next < 1) {
|
|
12481
|
+
this.buckets.set(key, [next, now]);
|
|
12482
|
+
return false;
|
|
12483
|
+
}
|
|
12484
|
+
this.buckets.set(key, [next - 1, now]);
|
|
12485
|
+
if (this.buckets.size > 1e4) this.buckets.clear();
|
|
12486
|
+
return true;
|
|
12487
|
+
}
|
|
12488
|
+
};
|
|
11809
12489
|
function createApp() {
|
|
11810
12490
|
enforceRulesContract();
|
|
12491
|
+
hardenExistingPaths();
|
|
11811
12492
|
const app = new Hono2();
|
|
11812
|
-
app.use("*", cors({
|
|
12493
|
+
app.use("*", cors({
|
|
12494
|
+
origin: (o) => allowedOrigins().includes(o) ? o : null,
|
|
12495
|
+
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
12496
|
+
allowHeaders: ["*"]
|
|
12497
|
+
}));
|
|
11813
12498
|
app.use("*", logger());
|
|
12499
|
+
const pools = new PooledLimiter();
|
|
12500
|
+
const limiter = new RateLimiter(rateLimitQps());
|
|
12501
|
+
app.use("*", async (c, next) => {
|
|
12502
|
+
const path6 = new URL(c.req.url).pathname;
|
|
12503
|
+
const method = c.req.method.toUpperCase();
|
|
12504
|
+
if (method === "OPTIONS") return next();
|
|
12505
|
+
const maxBody = maxBodyBytes();
|
|
12506
|
+
const cl = c.req.header("content-length");
|
|
12507
|
+
if (cl && maxBody > 0) {
|
|
12508
|
+
const n = Number.parseInt(cl, 10);
|
|
12509
|
+
if (Number.isFinite(n) && n > maxBody) {
|
|
12510
|
+
return c.json({ detail: `\u8BF7\u6C42\u4F53\u8D85\u8FC7\u4E0A\u9650 ${maxBody} \u5B57\u8282` }, 413);
|
|
12511
|
+
}
|
|
12512
|
+
}
|
|
12513
|
+
if (originCheckEnabled()) {
|
|
12514
|
+
const origin = c.req.header("origin") || "";
|
|
12515
|
+
if (origin && !originAllowed(origin, c.req.header("host") || "")) {
|
|
12516
|
+
console.warn(`[AgentMoss] \u5DF2\u62E6\u622A\u8DE8\u6E90\u8BF7\u6C42: origin=${origin} path=${path6}`);
|
|
12517
|
+
return c.json({ detail: `\u8DE8\u6E90\u6765\u6E90\u672A\u88AB\u5141\u8BB8: ${origin}` }, 403);
|
|
12518
|
+
}
|
|
12519
|
+
}
|
|
12520
|
+
const [okAuth, principal] = checkRequestAuth(c.req.raw.headers, path6);
|
|
12521
|
+
const isPublicPage = !path6.startsWith("/console/api") && (path6 === "/console" || path6.startsWith("/console/"));
|
|
12522
|
+
if (!okAuth && !isPublicPage) {
|
|
12523
|
+
return c.json({ detail: "Unauthorized: invalid or missing Bearer token" }, 401);
|
|
12524
|
+
}
|
|
12525
|
+
if (limiter.enabled && path6.startsWith("/api/")) {
|
|
12526
|
+
const key = principal ? "p:" + principalHash(principal) : "c:" + (c.req.header("x-forwarded-for") || "local");
|
|
12527
|
+
if (!limiter.allow(key)) {
|
|
12528
|
+
return c.json({ detail: "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" }, 429);
|
|
12529
|
+
}
|
|
12530
|
+
}
|
|
12531
|
+
const ip = c.req.header("x-forwarded-for") || "local";
|
|
12532
|
+
const keys = [];
|
|
12533
|
+
const take = (key, limit) => {
|
|
12534
|
+
if (pools.tryAcquire(key, limit)) {
|
|
12535
|
+
keys.push(key);
|
|
12536
|
+
return true;
|
|
12537
|
+
}
|
|
12538
|
+
return false;
|
|
12539
|
+
};
|
|
12540
|
+
const busy = (detail, status = 503) => {
|
|
12541
|
+
c.header("Retry-After", "1");
|
|
12542
|
+
c.header("Connection", "close");
|
|
12543
|
+
return c.json({ detail }, status);
|
|
12544
|
+
};
|
|
12545
|
+
try {
|
|
12546
|
+
if (!principal && authEnabled()) {
|
|
12547
|
+
if (!take("unauth:total", unauthInflight())) {
|
|
12548
|
+
return busy("\u672A\u8BA4\u8BC1\u8BF7\u6C42\u5E76\u53D1\u8D85\u9650\uFF0C\u8BF7\u6301\u51ED\u8BC1\u8BBF\u95EE");
|
|
12549
|
+
}
|
|
12550
|
+
if (!take(`unauth:ip:${ip}`, unauthPerIp())) {
|
|
12551
|
+
return busy("\u8BE5\u6765\u6E90\u7684\u672A\u8BA4\u8BC1\u8BF7\u6C42\u5E76\u53D1\u8D85\u9650");
|
|
12552
|
+
}
|
|
12553
|
+
} else {
|
|
12554
|
+
const bound = boundAgentOf(principal);
|
|
12555
|
+
if (bound && !take(`agent:${bound}`, agentInflight())) {
|
|
12556
|
+
return busy(`agent '${bound}' \u7684\u5E76\u53D1\u8D85\u9650\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`, 429);
|
|
12557
|
+
}
|
|
12558
|
+
}
|
|
12559
|
+
const pool = poolOf(path6);
|
|
12560
|
+
if (!take(`pool:${pool}`, poolLimit(pool))) {
|
|
12561
|
+
const qkey = principal ? "p:" + principalHash(principal) : "c:local";
|
|
12562
|
+
const [qn, abusive] = noteQueueReject(qkey);
|
|
12563
|
+
if (abusive && queueAbuseAction() === "deny") {
|
|
12564
|
+
return busy("\u5E76\u53D1\u62D2\u7EDD\u8FC7\u4E8E\u9891\u7E41\uFF0C\u7591\u4F3C\u6076\u610F\u8BF7\u6C42", 429);
|
|
12565
|
+
}
|
|
12566
|
+
if (abusive) {
|
|
12567
|
+
console.warn(`[AgentMoss] \u26A0\uFE0F \u7591\u4F3C\u6076\u610F\u5E76\u53D1\u5360\u7528: key=${qkey} \u7A97\u53E3\u5185\u88AB\u62D2 ${qn} \u6B21`);
|
|
12568
|
+
}
|
|
12569
|
+
return busy("\u670D\u52A1\u7E41\u5FD9\uFF08\u5E76\u53D1\u5DF2\u6EE1\uFF09\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
|
|
12570
|
+
}
|
|
12571
|
+
return await next();
|
|
12572
|
+
} finally {
|
|
12573
|
+
while (keys.length) pools.release(keys.pop());
|
|
12574
|
+
}
|
|
12575
|
+
});
|
|
11814
12576
|
app.get("/api/v1/health", healthRoute);
|
|
11815
12577
|
app.post("/api/v1/analyze", analyzeRoute);
|
|
11816
12578
|
app.post("/api/v1/review", reviewRoute);
|
|
@@ -11828,12 +12590,12 @@ function createApp() {
|
|
|
11828
12590
|
}
|
|
11829
12591
|
async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
11830
12592
|
for (let port = startPort; port < startPort + scan; port++) {
|
|
11831
|
-
const free = await new Promise((
|
|
12593
|
+
const free = await new Promise((resolve12) => {
|
|
11832
12594
|
const server = net.createServer();
|
|
11833
12595
|
server.listen(port, host, () => {
|
|
11834
|
-
server.close(() =>
|
|
12596
|
+
server.close(() => resolve12(true));
|
|
11835
12597
|
});
|
|
11836
|
-
server.on("error", () =>
|
|
12598
|
+
server.on("error", () => resolve12(false));
|
|
11837
12599
|
});
|
|
11838
12600
|
if (free) {
|
|
11839
12601
|
if (port !== startPort) {
|
|
@@ -11876,6 +12638,7 @@ async function warmUpEngine() {
|
|
|
11876
12638
|
}
|
|
11877
12639
|
}
|
|
11878
12640
|
async function startServer(host = "127.0.0.1", port = 9090) {
|
|
12641
|
+
failFastOnAuthError();
|
|
11879
12642
|
const actualPort = await findFreePort(port, host);
|
|
11880
12643
|
const app = createApp();
|
|
11881
12644
|
return {
|
|
@@ -11885,7 +12648,12 @@ async function startServer(host = "127.0.0.1", port = 9090) {
|
|
|
11885
12648
|
logDeploymentSelfCheck();
|
|
11886
12649
|
await warmUpEngine();
|
|
11887
12650
|
const { serve: honoServe } = await import("@hono/node-server");
|
|
11888
|
-
honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
|
|
12651
|
+
const srv = honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
|
|
12652
|
+
try {
|
|
12653
|
+
srv.maxConnections = maxConnections();
|
|
12654
|
+
srv.keepAliveTimeout = keepAliveSec() * 1e3;
|
|
12655
|
+
} catch {
|
|
12656
|
+
}
|
|
11889
12657
|
}
|
|
11890
12658
|
};
|
|
11891
12659
|
}
|
|
@@ -11896,28 +12664,29 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
|
|
|
11896
12664
|
socketPath,
|
|
11897
12665
|
close: async () => {
|
|
11898
12666
|
if (!server) return;
|
|
11899
|
-
await new Promise((
|
|
11900
|
-
server.close(() =>
|
|
12667
|
+
await new Promise((resolve12) => {
|
|
12668
|
+
server.close(() => resolve12());
|
|
11901
12669
|
});
|
|
11902
12670
|
server = null;
|
|
11903
12671
|
},
|
|
11904
12672
|
serve: async () => {
|
|
11905
|
-
|
|
11906
|
-
const {
|
|
11907
|
-
const
|
|
12673
|
+
failFastOnAuthError();
|
|
12674
|
+
const { mkdirSync: mkdirSync8, existsSync: existsSync22, unlinkSync: unlinkSync5 } = await import("fs");
|
|
12675
|
+
const { dirname: dirname14 } = await import("path");
|
|
12676
|
+
const dir = dirname14(socketPath);
|
|
11908
12677
|
if (dir) {
|
|
11909
12678
|
try {
|
|
11910
12679
|
mkdirSync8(dir, { recursive: true });
|
|
11911
12680
|
} catch {
|
|
11912
12681
|
}
|
|
11913
12682
|
}
|
|
11914
|
-
if (
|
|
12683
|
+
if (existsSync22(socketPath)) {
|
|
11915
12684
|
const { connect } = await import("net");
|
|
11916
|
-
const inUse = await new Promise((
|
|
12685
|
+
const inUse = await new Promise((resolve12) => {
|
|
11917
12686
|
const probe = connect(socketPath);
|
|
11918
12687
|
const done = (v) => {
|
|
11919
12688
|
probe.destroy();
|
|
11920
|
-
|
|
12689
|
+
resolve12(v);
|
|
11921
12690
|
};
|
|
11922
12691
|
probe.once("connect", () => done(true));
|
|
11923
12692
|
probe.once("error", () => done(false));
|
|
@@ -11940,10 +12709,22 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
|
|
|
11940
12709
|
const { getRequestListener } = await import("@hono/node-server");
|
|
11941
12710
|
const { createServer } = await import("http");
|
|
11942
12711
|
server = createServer(getRequestListener(app.fetch));
|
|
11943
|
-
|
|
12712
|
+
try {
|
|
12713
|
+
server.maxConnections = maxConnections();
|
|
12714
|
+
server.keepAliveTimeout = keepAliveSec() * 1e3;
|
|
12715
|
+
} catch {
|
|
12716
|
+
}
|
|
12717
|
+
await new Promise((resolve12, reject) => {
|
|
11944
12718
|
server.once("error", reject);
|
|
11945
|
-
server.listen(socketPath, () =>
|
|
12719
|
+
server.listen(socketPath, () => resolve12());
|
|
11946
12720
|
});
|
|
12721
|
+
try {
|
|
12722
|
+
const { chmodSync: chmodSync2 } = await import("fs");
|
|
12723
|
+
chmodSync2(socketPath, socketMode());
|
|
12724
|
+
console.log(`[AgentMoss] socket \u6743\u9650: 0${socketMode().toString(8)}\uFF08AGENT_MOSS_SOCKET_MODE\uFF09`);
|
|
12725
|
+
} catch (e) {
|
|
12726
|
+
console.warn(`[AgentMoss] \u8BBE\u7F6E socket \u6743\u9650\u5931\u8D25: ${String(e)}`);
|
|
12727
|
+
}
|
|
11947
12728
|
console.log(`AgentMoss HTTP server on unix socket: ${socketPath}`);
|
|
11948
12729
|
}
|
|
11949
12730
|
};
|