@kenhkl/agent-moss 0.17.12 → 0.17.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1964,9 +1964,9 @@ function runLogicCheck(input, opts) {
1964
1964
  }
1965
1965
  }
1966
1966
  }
1967
- const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType);
1967
+ const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1968
1968
  if (pwdResult) return pwdResult;
1969
- const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType);
1969
+ const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1970
1970
  if (userDelResult) return userDelResult;
1971
1971
  const comboResult = checkComboChain(actionHistory, aNext.actionDetail);
1972
1972
  if (comboResult) return comboResult;
@@ -2162,28 +2162,44 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
2162
2162
  }
2163
2163
  return null;
2164
2164
  }
2165
- function hasUserConsent(actionHistory) {
2166
- return actionHistory.some((h) => {
2167
- const t = String(h.actionType || "").toLowerCase();
2168
- return t === "ask_user_question" && String(h.output || "").trim().length > 0;
2169
- });
2170
- }
2171
- function hasWeakConsent(actionHistory) {
2172
- return actionHistory.some((h) => {
2173
- if (String(h.actionType || "").toLowerCase() !== "ask_user_question") return false;
2174
- return CONSENT_TOPIC.test(String(h.actionDetail || ""));
2175
- });
2165
+ function consentWithinTtl(asked, aNext) {
2166
+ const raw = String(process.env.AGENT_MOSS_CONSENT_TTL_SECONDS || "").trim();
2167
+ const parsed = raw ? Number(raw) : CONSENT_TTL_SECONDS;
2168
+ const ttl = Number.isFinite(parsed) ? parsed : CONSENT_TTL_SECONDS;
2169
+ if (ttl <= 0) return true;
2170
+ const askedAt = String(asked.calledAt || "").trim();
2171
+ const nextAt = String(aNext?.calledAt || "").trim();
2172
+ if (!askedAt || !nextAt) return true;
2173
+ const a = Date.parse(askedAt);
2174
+ const b = Date.parse(nextAt);
2175
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return true;
2176
+ return Math.abs(b - a) <= ttl * 1e3;
2177
+ }
2178
+ function hasValidConsent(actionHistory, aNext) {
2179
+ if (!actionHistory.length) return false;
2180
+ const tail = actionHistory.slice(-CONSENT_MAX_DISTANCE);
2181
+ for (let i = tail.length - 1; i >= 0; i--) {
2182
+ const h = tail[i];
2183
+ if (String(h.actionType || "").toLowerCase() !== "ask_user_question") continue;
2184
+ const output = String(h.output || "").trim();
2185
+ if (!output) return false;
2186
+ const question = String(h.actionDetail || "");
2187
+ if (!CONSENT_TOPIC.test(question)) return false;
2188
+ if (CONSENT_NEGATIVE.test(output)) return false;
2189
+ if (!CONSENT_INPUT_REQUEST.test(question) && !CONSENT_AFFIRMATIVE.test(output)) return false;
2190
+ return consentWithinTtl(h, aNext);
2191
+ }
2192
+ return false;
2176
2193
  }
2177
- function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
2194
+ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType, aNext) {
2178
2195
  if (actionType !== "bash") return null;
2179
2196
  const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
2180
2197
  const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
2181
2198
  if (!isPasswordModify) return null;
2182
- if (hasUserConsent(actionHistory)) return null;
2183
- if (hasWeakConsent(actionHistory)) return null;
2199
+ if (hasValidConsent(actionHistory, aNext)) return null;
2184
2200
  return {
2185
2201
  allowed: false,
2186
- reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u5BC6\u7801\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
2202
+ reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
2187
2203
  riskLevel: "high",
2188
2204
  riskType: "consent_missing",
2189
2205
  confidence: 85,
@@ -2191,16 +2207,15 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
2191
2207
  violatedLayers: ["L2.1"]
2192
2208
  };
2193
2209
  }
2194
- function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
2210
+ function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType, aNext) {
2195
2211
  if (actionType !== "bash") return null;
2196
2212
  const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
2197
2213
  const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
2198
2214
  if (!isUserDeletion) return null;
2199
- if (hasUserConsent(actionHistory)) return null;
2200
- if (hasWeakConsent(actionHistory)) return null;
2215
+ if (hasValidConsent(actionHistory, aNext)) return null;
2201
2216
  return {
2202
2217
  allowed: false,
2203
- reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
2218
+ reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
2204
2219
  riskLevel: "high",
2205
2220
  riskType: "consent_missing",
2206
2221
  confidence: 85,
@@ -2299,7 +2314,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
2299
2314
  violatedLayers: ["L2.1"]
2300
2315
  };
2301
2316
  }
2302
- 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, CONSENT_TOPIC, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
2317
+ 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, CONSENT_TOPIC, CONSENT_NEGATIVE, CONSENT_AFFIRMATIVE, CONSENT_INPUT_REQUEST, CONSENT_MAX_DISTANCE, CONSENT_TTL_SECONDS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
2303
2318
  var init_logic_rules = __esm({
2304
2319
  "src/engine/logic-rules.ts"() {
2305
2320
  "use strict";
@@ -2387,6 +2402,11 @@ var init_logic_rules = __esm({
2387
2402
  }));
2388
2403
  })();
2389
2404
  CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
2405
+ CONSENT_NEGATIVE = /(?:否|不|别|取消|拒绝|停止|终止|算了|\bno\b|\bnope\b|\bcancel\b|\babort\b|\bstop\b|\bdeny\b|\breject\b|\bdecline\b|\bdon'?t\b|\bdo\s+not\b)/i;
2406
+ CONSENT_AFFIRMATIVE = /(?:是|好|可以|行|嗯|对|确认|同意|继续|执行|删除|删掉|移除|修改|重置|改密|\byes\b|\by\b|\byep\b|\byeah\b|\bok\b|\bokay\b|\bconfirm(?:ed)?\b|\bproceed\b|\bcontinue\b|\bsure\b|\bapprove\b|\baccept\b|\bagree\b|\bdo\s+it\b|\bgo\s+ahead\b)/i;
2407
+ CONSENT_INPUT_REQUEST = /(?:请|输入|提供|填写|给出|告诉我).{0,8}(?:新的?)?(?:密码|口令|passwd|password)/i;
2408
+ CONSENT_MAX_DISTANCE = 5;
2409
+ CONSENT_TTL_SECONDS = 300;
2390
2410
  INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
2391
2411
  DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
2392
2412
  _IFA = loadLogicRules().indirect_file_access ?? {};
@@ -12459,7 +12479,10 @@ async function analyzeRoute(c) {
12459
12479
  aNext: {
12460
12480
  actionType: req.a_next.action_type,
12461
12481
  actionDetail: req.a_next.action_detail,
12462
- instanceId
12482
+ instanceId,
12483
+ // called_at 一并透传:L2 授权(consent)的时效窗需要「提问时刻」与
12484
+ // 「本次动作时刻」两侧都有时间戳才生效(对齐 Python routes.py)。
12485
+ calledAt: req.a_next.called_at
12463
12486
  },
12464
12487
  reason: req.reason,
12465
12488
  osType,
@@ -66,6 +66,7 @@ interface AnalysisInput {
66
66
  actionType: string;
67
67
  actionDetail: string;
68
68
  output?: string;
69
+ calledAt?: string;
69
70
  }[];
70
71
  /** 完整的交错历史 prompt(可选),每轮 {text, actions} 或纯文本 string(AgentGate v3 旧格式),按时间序。
71
72
  * 供 L2 意图一致性与 L3 语义分析使用;空则回退单条 promptSession。 */
@@ -92,6 +93,7 @@ interface AnalysisInput {
92
93
  actionType: string;
93
94
  actionDetail: string;
94
95
  instanceId?: string;
96
+ calledAt?: string;
95
97
  };
96
98
  reason: string;
97
99
  osType: string;
@@ -1141,9 +1141,9 @@ function runLogicCheck(input, opts) {
1141
1141
  }
1142
1142
  }
1143
1143
  }
1144
- const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType);
1144
+ const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1145
1145
  if (pwdResult) return pwdResult;
1146
- const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType);
1146
+ const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1147
1147
  if (userDelResult) return userDelResult;
1148
1148
  const comboResult = checkComboChain(actionHistory, aNext.actionDetail);
1149
1149
  if (comboResult) return comboResult;
@@ -1339,28 +1339,44 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
1339
1339
  }
1340
1340
  return null;
1341
1341
  }
1342
- function hasUserConsent(actionHistory) {
1343
- return actionHistory.some((h) => {
1344
- const t = String(h.actionType || "").toLowerCase();
1345
- return t === "ask_user_question" && String(h.output || "").trim().length > 0;
1346
- });
1347
- }
1348
- function hasWeakConsent(actionHistory) {
1349
- return actionHistory.some((h) => {
1350
- if (String(h.actionType || "").toLowerCase() !== "ask_user_question") return false;
1351
- return CONSENT_TOPIC.test(String(h.actionDetail || ""));
1352
- });
1342
+ function consentWithinTtl(asked, aNext) {
1343
+ const raw = String(process.env.AGENT_MOSS_CONSENT_TTL_SECONDS || "").trim();
1344
+ const parsed = raw ? Number(raw) : CONSENT_TTL_SECONDS;
1345
+ const ttl = Number.isFinite(parsed) ? parsed : CONSENT_TTL_SECONDS;
1346
+ if (ttl <= 0) return true;
1347
+ const askedAt = String(asked.calledAt || "").trim();
1348
+ const nextAt = String(aNext?.calledAt || "").trim();
1349
+ if (!askedAt || !nextAt) return true;
1350
+ const a = Date.parse(askedAt);
1351
+ const b = Date.parse(nextAt);
1352
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return true;
1353
+ return Math.abs(b - a) <= ttl * 1e3;
1354
+ }
1355
+ function hasValidConsent(actionHistory, aNext) {
1356
+ if (!actionHistory.length) return false;
1357
+ const tail = actionHistory.slice(-CONSENT_MAX_DISTANCE);
1358
+ for (let i = tail.length - 1; i >= 0; i--) {
1359
+ const h = tail[i];
1360
+ if (String(h.actionType || "").toLowerCase() !== "ask_user_question") continue;
1361
+ const output = String(h.output || "").trim();
1362
+ if (!output) return false;
1363
+ const question = String(h.actionDetail || "");
1364
+ if (!CONSENT_TOPIC.test(question)) return false;
1365
+ if (CONSENT_NEGATIVE.test(output)) return false;
1366
+ if (!CONSENT_INPUT_REQUEST.test(question) && !CONSENT_AFFIRMATIVE.test(output)) return false;
1367
+ return consentWithinTtl(h, aNext);
1368
+ }
1369
+ return false;
1353
1370
  }
1354
- function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
1371
+ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType, aNext) {
1355
1372
  if (actionType !== "bash") return null;
1356
1373
  const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
1357
1374
  const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
1358
1375
  if (!isPasswordModify) return null;
1359
- if (hasUserConsent(actionHistory)) return null;
1360
- if (hasWeakConsent(actionHistory)) return null;
1376
+ if (hasValidConsent(actionHistory, aNext)) return null;
1361
1377
  return {
1362
1378
  allowed: false,
1363
- reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u5BC6\u7801\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
1379
+ reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
1364
1380
  riskLevel: "high",
1365
1381
  riskType: "consent_missing",
1366
1382
  confidence: 85,
@@ -1368,16 +1384,15 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
1368
1384
  violatedLayers: ["L2.1"]
1369
1385
  };
1370
1386
  }
1371
- function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
1387
+ function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType, aNext) {
1372
1388
  if (actionType !== "bash") return null;
1373
1389
  const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
1374
1390
  const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
1375
1391
  if (!isUserDeletion) return null;
1376
- if (hasUserConsent(actionHistory)) return null;
1377
- if (hasWeakConsent(actionHistory)) return null;
1392
+ if (hasValidConsent(actionHistory, aNext)) return null;
1378
1393
  return {
1379
1394
  allowed: false,
1380
- reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
1395
+ reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
1381
1396
  riskLevel: "high",
1382
1397
  riskType: "consent_missing",
1383
1398
  confidence: 85,
@@ -1476,7 +1491,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
1476
1491
  violatedLayers: ["L2.1"]
1477
1492
  };
1478
1493
  }
1479
- 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, CONSENT_TOPIC, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
1494
+ 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, CONSENT_TOPIC, CONSENT_NEGATIVE, CONSENT_AFFIRMATIVE, CONSENT_INPUT_REQUEST, CONSENT_MAX_DISTANCE, CONSENT_TTL_SECONDS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
1480
1495
  var init_logic_rules = __esm({
1481
1496
  "src/engine/logic-rules.ts"() {
1482
1497
  "use strict";
@@ -1564,6 +1579,11 @@ var init_logic_rules = __esm({
1564
1579
  }));
1565
1580
  })();
1566
1581
  CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
1582
+ CONSENT_NEGATIVE = /(?:否|不|别|取消|拒绝|停止|终止|算了|\bno\b|\bnope\b|\bcancel\b|\babort\b|\bstop\b|\bdeny\b|\breject\b|\bdecline\b|\bdon'?t\b|\bdo\s+not\b)/i;
1583
+ CONSENT_AFFIRMATIVE = /(?:是|好|可以|行|嗯|对|确认|同意|继续|执行|删除|删掉|移除|修改|重置|改密|\byes\b|\by\b|\byep\b|\byeah\b|\bok\b|\bokay\b|\bconfirm(?:ed)?\b|\bproceed\b|\bcontinue\b|\bsure\b|\bapprove\b|\baccept\b|\bagree\b|\bdo\s+it\b|\bgo\s+ahead\b)/i;
1584
+ CONSENT_INPUT_REQUEST = /(?:请|输入|提供|填写|给出|告诉我).{0,8}(?:新的?)?(?:密码|口令|passwd|password)/i;
1585
+ CONSENT_MAX_DISTANCE = 5;
1586
+ CONSENT_TTL_SECONDS = 300;
1567
1587
  INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
1568
1588
  DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
1569
1589
  _IFA = loadLogicRules().indirect_file_access ?? {};
@@ -697,14 +697,14 @@
697
697
  "reason": "检测到 MongoDB 删除集合 (db.<col>.drop())"
698
698
  },
699
699
  {
700
- "pattern": "(?:del|rmdir|rd)\\s+[\\\\/][a-zA-Z]:",
700
+ "pattern": "(?:del|rmdir|rd)\\s+[\\\\/]?[a-zA-Z]:",
701
701
  "risk_level": "critical",
702
702
  "risk_type": "script_execution",
703
703
  "reason": "检测到递归删除关键路径 (del/rmdir C:\\)",
704
704
  "os": "windows"
705
705
  },
706
706
  {
707
- "pattern": "del\\s+[\\\\/][a-zA-Z]:",
707
+ "pattern": "del\\s+[\\\\/]?[a-zA-Z]:",
708
708
  "risk_level": "critical",
709
709
  "risk_type": "script_execution",
710
710
  "reason": "检测到删除卷根目录文件 (del C:\\)",
@@ -816,7 +816,7 @@
816
816
  "os": "windows"
817
817
  },
818
818
  {
819
- "pattern": "\\bSet-Content\\b[^;&|\\n]*\\b-[A-Za-z]*Stream\\b",
819
+ "pattern": "\\bSet-Content\\b[^;&|\\n]*\\s-[A-Za-z]*Stream\\b",
820
820
  "risk_level": "high",
821
821
  "risk_type": "script_execution",
822
822
  "reason": "检测到写入 NTFS 备用数据流(用于隐藏载荷)",
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "scenario_id": "bank",
3
- "description": "银行沙箱场景定制规则。客户约束:沙箱用户(sandbox)仅允许读写 /home/sandbox、/opt/sandbox、/tmp 三个目录,其他根目录一律禁止读写;禁止远程访问/横向移动(ssh 家族 + scp/sftp/sshpass/ssh-copy-id + 用 ssh 的 rsync)。本地操作(ssh-keygen/ssh-add/ssh-agent、本地 rsync)不受影响。另放行 /dev/null、/dev/stdout、/dev/stderr —— 这三个是 666/可写、仅作流丢弃与输出重定向用的标准设备文件(`2>/dev/null` 是最常见的运维写法),不放行会让大量正常命令被误拦;/dev 下**其余**文件(/dev/mem、/dev/sda、/dev/shm…)照常拦。",
3
+ "description": "银行沙箱场景定制规则。客户约束:沙箱用户(sandbox)仅允许读写 /home/sandbox、/opt/sandbox、/tmp 三个目录,其他根目录一律禁止读写;禁止远程访问/横向移动(ssh 家族 + scp/sftp/sshpass/ssh-copy-id + 用 ssh 的 rsync)。本地操作(ssh-keygen/ssh-add/ssh-agent、本地 rsync)不受影响。另放行 /dev/null、/dev/stdout、/dev/stderr —— 这三个是 666/可写、仅作流丢弃与输出重定向用的标准设备文件(`2>/dev/null` 是最常见的运维写法),不放行会让大量正常命令被误拦;/dev 下**其余**文件(/dev/mem、/dev/sda、/dev/shm…)照常拦。。另:`/bin/ls`、`/usr/bin/python3` 这类**命令的绝对路径写法**属正常执行,不算路径访问(2026-10 修误报);但用 `>/cp/mv/tee/install/ln` 往这些目录**写文件**仍然拦。",
4
4
  "extra_command_patterns": [
5
5
  {
6
- "pattern": "(?<![\\w.:~/-])/(?:(?!home/sandbox(?:\\s|/|$)|opt/sandbox(?:\\s|/|$)|tmp(?:\\s|/|$)|dev/null(?=[\\s;&|<>)\"'`]|$)|dev/stdout(?=[\\s;&|<>)\"'`]|$)|dev/stderr(?=[\\s;&|<>)\"'`]|$))[^\\s;&|>\"'`]*|[^\\s;&|>\"'`]*/\\.\\.[^\\s;&|>\"'`]*)|(?:^|[\\s;&|<>=(`'\"!/])(?:\\.\\./|\\.\\.(?=\\s|$|[;&|]))|(?:^|[\\s;&|<>=(`'\"!])~(?!sandbox\\b)[\\w-]+",
6
+ "pattern": "(?<![\\w.:~/-])/(?:(?!home/sandbox(?:\\s|/|$)|opt/sandbox(?:\\s|/|$)|tmp(?:\\s|/|$)|dev/null(?=[\\s;&|<>)\"'`]|$)|dev/stdout(?=[\\s;&|<>)\"'`]|$)|dev/stderr(?=[\\s;&|<>)\"'`]|$)|(?:usr/)?(?:local/)?s?bin/(?!.*\\.\\.)[^\\s;&|>\"'`]*(?=[\\s;&|<>)\"'`]|$))[^\\s;&|>\"'`]*|[^\\s;&|>\"'`]*/\\.\\.[^\\s;&|>\"'`]*)|(?:^|[\\s;&|<>=(`'\"!/])(?:\\.\\./|\\.\\.(?=\\s|$|[;&|]))|(?:^|[\\s;&|<>=(`'\"!])~(?!sandbox\\b)[\\w-]+",
7
7
  "risk_level": "high",
8
8
  "risk_type": "file_access",
9
9
  "reason": "bank 场景:访问允许目录(/home/sandbox、/opt/sandbox、/tmp,以及 /dev/null、/dev/stdout、/dev/stderr 三个标准流重定向目标)之外的路径(含根目录 /),或用 .. 逃逸 / ~其它用户"
@@ -13,6 +13,18 @@
13
13
  "risk_level": "critical",
14
14
  "risk_type": "network_access",
15
15
  "reason": "bank 场景:禁止远程访问 / 横向移动(ssh / scp / sftp / sshpass / ssh-copy-id / rsync -e ssh)"
16
+ },
17
+ {
18
+ "pattern": "(?:^|[\\s;&|(])(?:>|>>|\\btee\\b|\\bcp\\b|\\bmv\\b|\\binstall\\b|\\bln\\b)[^;&|\\n]*?/(?:usr/)?(?:local/)?s?bin/",
19
+ "risk_level": "high",
20
+ "risk_type": "file_access",
21
+ "reason": "bank 场景:向系统可执行目录(/bin、/sbin、/usr/bin、/usr/local/bin 等)写入文件 —— 可执行路径本身作为命令运行是放行的,但把它当写目标不行"
22
+ },
23
+ {
24
+ "pattern": "(?:^|[;&|(\\n])\\s*(?:sudo\\s+)?crontab\\s+(?!-l\\b)\\S",
25
+ "risk_level": "high",
26
+ "risk_type": "persistence",
27
+ "reason": "bank 场景:禁止写系统定时任务(crontab 安装/编辑/删除)——写操作落在 /var/spool/cron,属白名单外,且是持久化后门;`crontab -l` 只读放行"
16
28
  }
17
29
  ],
18
30
  "extra_injection_keywords": [],
package/dist/server.js CHANGED
@@ -1672,9 +1672,9 @@ function runLogicCheck(input, opts) {
1672
1672
  }
1673
1673
  }
1674
1674
  }
1675
- const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType);
1675
+ const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1676
1676
  if (pwdResult) return pwdResult;
1677
- const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType);
1677
+ const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1678
1678
  if (userDelResult) return userDelResult;
1679
1679
  const comboResult = checkComboChain(actionHistory, aNext.actionDetail);
1680
1680
  if (comboResult) return comboResult;
@@ -1870,28 +1870,44 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
1870
1870
  }
1871
1871
  return null;
1872
1872
  }
1873
- function hasUserConsent(actionHistory) {
1874
- return actionHistory.some((h) => {
1875
- const t = String(h.actionType || "").toLowerCase();
1876
- return t === "ask_user_question" && String(h.output || "").trim().length > 0;
1877
- });
1878
- }
1879
- function hasWeakConsent(actionHistory) {
1880
- return actionHistory.some((h) => {
1881
- if (String(h.actionType || "").toLowerCase() !== "ask_user_question") return false;
1882
- return CONSENT_TOPIC.test(String(h.actionDetail || ""));
1883
- });
1873
+ function consentWithinTtl(asked, aNext) {
1874
+ const raw = String(process.env.AGENT_MOSS_CONSENT_TTL_SECONDS || "").trim();
1875
+ const parsed = raw ? Number(raw) : CONSENT_TTL_SECONDS;
1876
+ const ttl = Number.isFinite(parsed) ? parsed : CONSENT_TTL_SECONDS;
1877
+ if (ttl <= 0) return true;
1878
+ const askedAt = String(asked.calledAt || "").trim();
1879
+ const nextAt = String(aNext?.calledAt || "").trim();
1880
+ if (!askedAt || !nextAt) return true;
1881
+ const a = Date.parse(askedAt);
1882
+ const b = Date.parse(nextAt);
1883
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return true;
1884
+ return Math.abs(b - a) <= ttl * 1e3;
1885
+ }
1886
+ function hasValidConsent(actionHistory, aNext) {
1887
+ if (!actionHistory.length) return false;
1888
+ const tail = actionHistory.slice(-CONSENT_MAX_DISTANCE);
1889
+ for (let i = tail.length - 1; i >= 0; i--) {
1890
+ const h = tail[i];
1891
+ if (String(h.actionType || "").toLowerCase() !== "ask_user_question") continue;
1892
+ const output = String(h.output || "").trim();
1893
+ if (!output) return false;
1894
+ const question = String(h.actionDetail || "");
1895
+ if (!CONSENT_TOPIC.test(question)) return false;
1896
+ if (CONSENT_NEGATIVE.test(output)) return false;
1897
+ if (!CONSENT_INPUT_REQUEST.test(question) && !CONSENT_AFFIRMATIVE.test(output)) return false;
1898
+ return consentWithinTtl(h, aNext);
1899
+ }
1900
+ return false;
1884
1901
  }
1885
- function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
1902
+ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType, aNext) {
1886
1903
  if (actionType !== "bash") return null;
1887
1904
  const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
1888
1905
  const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
1889
1906
  if (!isPasswordModify) return null;
1890
- if (hasUserConsent(actionHistory)) return null;
1891
- if (hasWeakConsent(actionHistory)) return null;
1907
+ if (hasValidConsent(actionHistory, aNext)) return null;
1892
1908
  return {
1893
1909
  allowed: false,
1894
- reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u5BC6\u7801\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
1910
+ reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
1895
1911
  riskLevel: "high",
1896
1912
  riskType: "consent_missing",
1897
1913
  confidence: 85,
@@ -1899,16 +1915,15 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
1899
1915
  violatedLayers: ["L2.1"]
1900
1916
  };
1901
1917
  }
1902
- function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
1918
+ function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType, aNext) {
1903
1919
  if (actionType !== "bash") return null;
1904
1920
  const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
1905
1921
  const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
1906
1922
  if (!isUserDeletion) return null;
1907
- if (hasUserConsent(actionHistory)) return null;
1908
- if (hasWeakConsent(actionHistory)) return null;
1923
+ if (hasValidConsent(actionHistory, aNext)) return null;
1909
1924
  return {
1910
1925
  allowed: false,
1911
- reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
1926
+ reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
1912
1927
  riskLevel: "high",
1913
1928
  riskType: "consent_missing",
1914
1929
  confidence: 85,
@@ -2007,7 +2022,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
2007
2022
  violatedLayers: ["L2.1"]
2008
2023
  };
2009
2024
  }
2010
- 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, CONSENT_TOPIC, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
2025
+ 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, CONSENT_TOPIC, CONSENT_NEGATIVE, CONSENT_AFFIRMATIVE, CONSENT_INPUT_REQUEST, CONSENT_MAX_DISTANCE, CONSENT_TTL_SECONDS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
2011
2026
  var init_logic_rules = __esm({
2012
2027
  "src/engine/logic-rules.ts"() {
2013
2028
  "use strict";
@@ -2095,6 +2110,11 @@ var init_logic_rules = __esm({
2095
2110
  }));
2096
2111
  })();
2097
2112
  CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
2113
+ CONSENT_NEGATIVE = /(?:否|不|别|取消|拒绝|停止|终止|算了|\bno\b|\bnope\b|\bcancel\b|\babort\b|\bstop\b|\bdeny\b|\breject\b|\bdecline\b|\bdon'?t\b|\bdo\s+not\b)/i;
2114
+ CONSENT_AFFIRMATIVE = /(?:是|好|可以|行|嗯|对|确认|同意|继续|执行|删除|删掉|移除|修改|重置|改密|\byes\b|\by\b|\byep\b|\byeah\b|\bok\b|\bokay\b|\bconfirm(?:ed)?\b|\bproceed\b|\bcontinue\b|\bsure\b|\bapprove\b|\baccept\b|\bagree\b|\bdo\s+it\b|\bgo\s+ahead\b)/i;
2115
+ CONSENT_INPUT_REQUEST = /(?:请|输入|提供|填写|给出|告诉我).{0,8}(?:新的?)?(?:密码|口令|passwd|password)/i;
2116
+ CONSENT_MAX_DISTANCE = 5;
2117
+ CONSENT_TTL_SECONDS = 300;
2098
2118
  INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
2099
2119
  DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
2100
2120
  _IFA = loadLogicRules().indirect_file_access ?? {};
@@ -12130,7 +12150,10 @@ async function analyzeRoute(c) {
12130
12150
  aNext: {
12131
12151
  actionType: req.a_next.action_type,
12132
12152
  actionDetail: req.a_next.action_detail,
12133
- instanceId
12153
+ instanceId,
12154
+ // called_at 一并透传:L2 授权(consent)的时效窗需要「提问时刻」与
12155
+ // 「本次动作时刻」两侧都有时间戳才生效(对齐 Python routes.py)。
12156
+ calledAt: req.a_next.called_at
12134
12157
  },
12135
12158
  reason: req.reason,
12136
12159
  osType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenhkl/agent-moss",
3
- "version": "0.17.12",
3
+ "version": "0.17.13",
4
4
  "description": "Multi-layer security analysis engine for AI agents (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",