alemonjs-aichat 1.0.37 → 1.0.39

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.
@@ -313,6 +313,36 @@ const tools = [
313
313
  },
314
314
  },
315
315
  },
316
+ {
317
+ type: "function",
318
+ function: {
319
+ name: "RunServerCommand",
320
+ description: "直接在服务器上执行运维指令,可用于查看状态、管理服务、Docker、PM2、日志和部署流程。执行前必须通过本地基础拦截和 AI 自动审核;审核不可用或未通过时不会执行。不要用它操作 public/{guid} 项目文件,AI agent 项目文件操作仍使用 Agent* 工具。",
321
+ parameters: {
322
+ type: "object",
323
+ properties: {
324
+ command: {
325
+ type: "string",
326
+ description: "要执行的服务器指令,例如 systemctl status nginx、docker ps、pm2 list、tail -n 100 app.log",
327
+ },
328
+ workingDirectory: {
329
+ type: "string",
330
+ description: "可选。服务器上的执行目录,可以是绝对路径或相对当前进程目录,默认为服务进程当前目录",
331
+ },
332
+ reviewGuid: {
333
+ type: "string",
334
+ description: "必填。传入当前群号,也就是系统提示词里的“当前群号(reviewGuid)”;私聊时也传当前会话的群号/guid。用于读取当前群 AI 配置并执行 AI 自动审核,不是 public/{guid} 项目工作目录ID。",
335
+ },
336
+ timeoutSeconds: {
337
+ type: "number",
338
+ description: "可选。命令超时时间,默认 120 秒,最大 600 秒",
339
+ default: 120,
340
+ },
341
+ },
342
+ required: ["command", "reviewGuid"],
343
+ },
344
+ },
345
+ },
316
346
  {
317
347
  type: "function",
318
348
  function: {
@@ -333,7 +363,12 @@ const tools = [
333
363
  action: {
334
364
  type: "string",
335
365
  description: "Git 操作类型:currentBranch 当前分支;listBranches 分支列表;listRemotes 远端列表;switchBranch 切换到已有分支",
336
- enum: ["currentBranch", "listBranches", "listRemotes", "switchBranch"],
366
+ enum: [
367
+ "currentBranch",
368
+ "listBranches",
369
+ "listRemotes",
370
+ "switchBranch",
371
+ ],
337
372
  },
338
373
  branch: {
339
374
  type: "string",
@@ -708,14 +743,14 @@ const tools = [
708
743
  type: "function",
709
744
  function: {
710
745
  name: "ping",
711
- description: `
712
- 检测目标主机是否可达。
713
-
714
- 【限制】
715
- - 仅允许公网域名
716
- - 禁止 IP 地址(防止内网扫描)
717
- - 自动限制次数(例如 3 次)
718
-
746
+ description: `
747
+ 检测目标主机是否可达。
748
+
749
+ 【限制】
750
+ - 仅允许公网域名
751
+ - 禁止 IP 地址(防止内网扫描)
752
+ - 自动限制次数(例如 3 次)
753
+
719
754
  `,
720
755
  parameters: {
721
756
  type: "object",
@@ -730,22 +765,22 @@ const tools = [
730
765
  type: "function",
731
766
  function: {
732
767
  name: "http_request",
733
- description: `
734
- 发送 HTTP 请求以获取网页或 API 数据。
735
-
736
- 【能力范围】
737
- - 支持 GET / POST 请求
738
- - 返回文本或 JSON 内容
739
- - 自动处理常见编码
740
-
741
- 【限制】
742
- - 禁止访问云元数据地址(如 169.254.169.254)
743
- - 请求超时限制为 5 秒
744
- - 响应大小限制(例如 1MB)
745
-
746
- 【使用建议】
747
- - 优先使用此工具获取网页内容,而不是使用 shell
748
- - 适合抓取网页、调用 API、获取数据
768
+ description: `
769
+ 发送 HTTP 请求以获取网页或 API 数据。
770
+
771
+ 【能力范围】
772
+ - 支持 GET / POST 请求
773
+ - 返回文本或 JSON 内容
774
+ - 自动处理常见编码
775
+
776
+ 【限制】
777
+ - 禁止访问云元数据地址(如 169.254.169.254)
778
+ - 请求超时限制为 5 秒
779
+ - 响应大小限制(例如 1MB)
780
+
781
+ 【使用建议】
782
+ - 优先使用此工具获取网页内容,而不是使用 shell
783
+ - 适合抓取网页、调用 API、获取数据
749
784
  `,
750
785
  parameters: {
751
786
  type: "object",
@@ -830,11 +865,44 @@ const ALLOWED_DOTNET_PROJECT_SUBCOMMANDS = new Set([
830
865
  "sln",
831
866
  ]);
832
867
  const BLOCKED_PROJECT_COMMAND_PATTERNS = [
833
- { pattern: /&&|\|\||[;|<>`]|[$][(]/, reason: "不允许使用 shell 控制符、重定向、命令替换或反引号" },
834
- { pattern: /(^|[\s])(rm|del|erase|rmdir|rd)([\s]|$)/i, reason: "不允许使用删除命令" },
835
- { pattern: /(^|[\s])(powershell|pwsh|cmd|bash|sh|docker|ssh|scp|sftp|curl|wget)([\s]|$)/i, reason: "不允许执行受限外部命令" },
868
+ {
869
+ pattern: /&&|\|\||[;|<>`]|[$][(]/,
870
+ reason: "不允许使用 shell 控制符、重定向、命令替换或反引号",
871
+ },
872
+ {
873
+ pattern: /(^|[\s])(rm|del|erase|rmdir|rd)([\s]|$)/i,
874
+ reason: "不允许使用删除命令",
875
+ },
876
+ {
877
+ pattern: /(^|[\s])(powershell|pwsh|cmd|bash|sh|docker|ssh|scp|sftp|curl|wget)([\s]|$)/i,
878
+ reason: "不允许执行受限外部命令",
879
+ },
836
880
  { pattern: /\.\.[\\/]/, reason: "不允许通过 .. 离开 public 工作目录" },
837
881
  ];
882
+ const SERVER_COMMAND_DEFAULT_TIMEOUT_SECONDS = 120;
883
+ const SERVER_COMMAND_MAX_TIMEOUT_SECONDS = 600;
884
+ const BLOCKED_SERVER_COMMAND_PATTERNS = [
885
+ {
886
+ pattern: /\0/,
887
+ reason: "命令包含非法空字符",
888
+ },
889
+ {
890
+ pattern: /:\s*\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/,
891
+ reason: "不允许执行 fork bomb",
892
+ },
893
+ {
894
+ pattern: /(^|[\s;&|])rm\s+(-[^\s]*r[^\s]*f|-f[^\s]*r|-[^\s]*rf[^\s]*)\s+(\/|\/\*|~|~\/|\$HOME)(\s|$)/i,
895
+ reason: "不允许递归强制删除系统根目录或用户主目录",
896
+ },
897
+ {
898
+ pattern: /(^|[\s;&|])(mkfs|mkswap|fdisk|parted|wipefs)([\s]|$)/i,
899
+ reason: "不允许执行磁盘格式化或分区破坏类命令",
900
+ },
901
+ {
902
+ pattern: /(^|[\s;&|])dd\s+[\s\S]*\bof=\/dev\//i,
903
+ reason: "不允许使用 dd 写入块设备",
904
+ },
905
+ ];
838
906
  const toToolError = (error) => ({
839
907
  success: false,
840
908
  error: error instanceof Error ? error.message : String(error),
@@ -1408,7 +1476,15 @@ const buildProjectCommandReview = (command) => {
1408
1476
  tokens,
1409
1477
  };
1410
1478
  }
1411
- risk = ["new", "restore", "build", "run", "test", "publish", "clean"].includes(subcommand)
1479
+ risk = [
1480
+ "new",
1481
+ "restore",
1482
+ "build",
1483
+ "run",
1484
+ "test",
1485
+ "publish",
1486
+ "clean",
1487
+ ].includes(subcommand)
1412
1488
  ? "medium"
1413
1489
  : "low";
1414
1490
  reasons.push(risk === "medium"
@@ -1490,7 +1566,8 @@ const buildProjectCommandReview = (command) => {
1490
1566
  tokens,
1491
1567
  };
1492
1568
  }
1493
- if (["create", "init", "dlx", "exec"].includes(subcommand) || ["npx", "npx.cmd", "bunx"].includes(baseCommand)) {
1569
+ if (["create", "init", "dlx", "exec"].includes(subcommand) ||
1570
+ ["npx", "npx.cmd", "bunx"].includes(baseCommand)) {
1494
1571
  risk = "medium";
1495
1572
  reasons.push("脚手架或外部执行命令会下载并运行第三方包");
1496
1573
  }
@@ -1498,7 +1575,8 @@ const buildProjectCommandReview = (command) => {
1498
1575
  risk = "medium";
1499
1576
  reasons.push("依赖安装命令会写入项目依赖和锁文件");
1500
1577
  }
1501
- else if (["run", "test", "build"].includes(subcommand) || subcommand === "") {
1578
+ else if (["run", "test", "build"].includes(subcommand) ||
1579
+ subcommand === "") {
1502
1580
  risk = "low";
1503
1581
  reasons.push("项目内脚本或常规包管理命令");
1504
1582
  }
@@ -1514,6 +1592,56 @@ const buildProjectCommandReview = (command) => {
1514
1592
  tokens,
1515
1593
  };
1516
1594
  };
1595
+ const buildServerCommandLocalReview = (command) => {
1596
+ const trimmed = command.trim();
1597
+ if (!trimmed) {
1598
+ return {
1599
+ allowed: false,
1600
+ risk: "blocked",
1601
+ reasons: ["命令不能为空"],
1602
+ };
1603
+ }
1604
+ for (const item of BLOCKED_SERVER_COMMAND_PATTERNS) {
1605
+ if (item.pattern.test(trimmed)) {
1606
+ return {
1607
+ allowed: false,
1608
+ risk: "blocked",
1609
+ reasons: [item.reason],
1610
+ };
1611
+ }
1612
+ }
1613
+ const highRiskPatterns = [
1614
+ /(^|[\s;&|])(sudo|su)([\s]|$)/i,
1615
+ /(^|[\s;&|])(systemctl|service|supervisorctl|pm2)([\s]|$)/i,
1616
+ /(^|[\s;&|])(docker|docker-compose|kubectl)([\s]|$)/i,
1617
+ /(^|[\s;&|])(apt|apt-get|yum|dnf|pacman|apk)([\s]|$)/i,
1618
+ /(^|[\s;&|])(iptables|ufw|firewall-cmd)([\s]|$)/i,
1619
+ /(^|[\s;&|])(rm|mv|cp|chmod|chown|kill|pkill)([\s]|$)/i,
1620
+ ];
1621
+ const mediumRiskPatterns = [
1622
+ /(^|[\s;&|])(curl|wget|git|tar|unzip|npm|pnpm|yarn|node)([\s]|$)/i,
1623
+ /[;&|<>`]|[$][(]/,
1624
+ ];
1625
+ if (highRiskPatterns.some((pattern) => pattern.test(trimmed))) {
1626
+ return {
1627
+ allowed: true,
1628
+ risk: "high",
1629
+ reasons: ["服务器管理或文件/进程/网络配置相关命令,必须经过 AI 审核"],
1630
+ };
1631
+ }
1632
+ if (mediumRiskPatterns.some((pattern) => pattern.test(trimmed))) {
1633
+ return {
1634
+ allowed: true,
1635
+ risk: "medium",
1636
+ reasons: ["可能修改服务状态、下载内容或使用 shell 控制符,必须经过 AI 审核"],
1637
+ };
1638
+ }
1639
+ return {
1640
+ allowed: true,
1641
+ risk: "low",
1642
+ reasons: ["常规服务器查询或低风险命令,仍需 AI 审核确认"],
1643
+ };
1644
+ };
1517
1645
  const getReviewAIConfig = async (reviewGuid) => {
1518
1646
  const guid = reviewGuid?.trim();
1519
1647
  const config = await redisClient.getAIConfig(guid || undefined);
@@ -1548,8 +1676,8 @@ const reviewProjectCommandWithAI = async ({ command, localReview, reviewGuid, })
1548
1676
  messages: [
1549
1677
  {
1550
1678
  role: "system",
1551
- content: "你是一个命令安全审查器。请只输出 JSON,格式为 {\"allowed\":boolean,\"risk\":\"low|medium|high|blocked\",\"reason\":\"简短中文原因\"}。审核标准:命令必须只适用于 public 工作目录内的安全项目操作,不得包含权限提升、删除系统文件、远程控制、数据外传、全局环境修改或危险 shell 行为。"
1552
- + `\n性格约束:${aiConfig.config.systemPrompt || ""}`,
1679
+ content: '你是一个命令安全审查器。请只输出 JSON,格式为 {"allowed":boolean,"risk":"low|medium|high|blocked","reason":"简短中文原因"}。审核标准:命令必须只适用于 public 工作目录内的安全项目操作,不得包含权限提升、删除系统文件、远程控制、数据外传、全局环境修改或危险 shell 行为。' +
1680
+ `\n性格约束:${aiConfig.config.systemPrompt || ""}`,
1553
1681
  },
1554
1682
  {
1555
1683
  role: "user",
@@ -1598,6 +1726,143 @@ const reviewProjectCommandWithAI = async ({ command, localReview, reviewGuid, })
1598
1726
  };
1599
1727
  }
1600
1728
  };
1729
+ const reviewServerCommandWithAI = async ({ command, localReview, reviewGuid, workingDirectory, }) => {
1730
+ const aiConfig = await getReviewAIConfig(reviewGuid);
1731
+ if (!aiConfig) {
1732
+ return {
1733
+ status: "skipped",
1734
+ reason: "未找到可用的当前 AI 配置,服务器指令必须完成 AI 审核后才能执行",
1735
+ };
1736
+ }
1737
+ try {
1738
+ const openai = new OpenAi({
1739
+ baseURL: aiConfig.config.host,
1740
+ apiKey: aiConfig.config.key,
1741
+ timeout: 30000,
1742
+ });
1743
+ const response = await openai.chat.completions.create({
1744
+ model: aiConfig.config.model,
1745
+ temperature: 0,
1746
+ max_tokens: 220,
1747
+ messages: [
1748
+ {
1749
+ role: "system",
1750
+ content: '你是服务器运维指令安全审核器。请只输出 JSON,格式为 {"allowed":boolean,"risk":"low|medium|high|blocked","reason":"简短中文原因"}。可以允许合理的服务器管理、状态查看、日志查看、服务重启、Docker/PM2/systemctl 操作、部署和包管理命令;必须阻止清空数据、破坏系统、植入后门、泄露密钥、反弹 shell、未知脚本下载后直接执行、越权访问或明显不可恢复的危险操作。' +
1751
+ `\n性格约束:${aiConfig.config.systemPrompt || ""}`,
1752
+ },
1753
+ {
1754
+ role: "user",
1755
+ content: JSON.stringify({
1756
+ command,
1757
+ workingDirectory,
1758
+ localReview: {
1759
+ allowed: localReview.allowed,
1760
+ risk: localReview.risk,
1761
+ reasons: localReview.reasons,
1762
+ },
1763
+ }),
1764
+ },
1765
+ ],
1766
+ });
1767
+ const text = response.choices[0]?.message?.content || "";
1768
+ const match = text.match(/\{[\s\S]*\}/);
1769
+ if (!match) {
1770
+ return {
1771
+ status: "failed",
1772
+ reason: "AI 审核未返回可解析的 JSON",
1773
+ raw: text,
1774
+ };
1775
+ }
1776
+ const parsed = JSON.parse(match[0]);
1777
+ return {
1778
+ status: "completed",
1779
+ currentAI: aiConfig.currentAI || null,
1780
+ model: aiConfig.config.model,
1781
+ guid: aiConfig.guid,
1782
+ allowed: Boolean(parsed.allowed),
1783
+ risk: parsed.risk === "low" ||
1784
+ parsed.risk === "medium" ||
1785
+ parsed.risk === "high" ||
1786
+ parsed.risk === "blocked"
1787
+ ? parsed.risk
1788
+ : "high",
1789
+ reason: typeof parsed.reason === "string" && parsed.reason.trim()
1790
+ ? parsed.reason.trim()
1791
+ : "AI 未提供明确原因",
1792
+ };
1793
+ }
1794
+ catch (error) {
1795
+ return {
1796
+ status: "failed",
1797
+ reason: error instanceof Error ? error.message : String(error),
1798
+ };
1799
+ }
1800
+ };
1801
+ const resolveServerWorkingDirectory = (workingDirectory) => {
1802
+ if (workingDirectory?.includes("\0")) {
1803
+ throw new Error("workingDirectory 包含非法空字符");
1804
+ }
1805
+ const cwd = path.resolve(workingDirectory?.trim() || process.cwd());
1806
+ if (!fs.existsSync(cwd)) {
1807
+ throw new Error("workingDirectory 不存在");
1808
+ }
1809
+ if (!fs.statSync(cwd).isDirectory()) {
1810
+ throw new Error("workingDirectory 不是目录");
1811
+ }
1812
+ return cwd;
1813
+ };
1814
+ const normalizeServerCommandTimeoutMs = (timeoutSeconds) => {
1815
+ const safeSeconds = Math.min(normalizePositiveInteger(timeoutSeconds, SERVER_COMMAND_DEFAULT_TIMEOUT_SECONDS, "timeoutSeconds"), SERVER_COMMAND_MAX_TIMEOUT_SECONDS);
1816
+ return safeSeconds * 1000;
1817
+ };
1818
+ const runServerShellCommand = ({ command, cwd, timeoutMs, }) => {
1819
+ return new Promise((resolve, reject) => {
1820
+ const powershellCommand = [
1821
+ "chcp 65001 | Out-Null",
1822
+ "[Console]::InputEncoding = [System.Text.UTF8Encoding]::new()",
1823
+ "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()",
1824
+ "$OutputEncoding = [System.Text.UTF8Encoding]::new()",
1825
+ command,
1826
+ ].join("; ");
1827
+ const shell = process.platform === "win32"
1828
+ ? {
1829
+ file: "powershell.exe",
1830
+ args: [
1831
+ "-NoProfile",
1832
+ "-ExecutionPolicy",
1833
+ "Bypass",
1834
+ "-Command",
1835
+ powershellCommand,
1836
+ ],
1837
+ }
1838
+ : { file: "sh", args: ["-lc", command] };
1839
+ const child = spawn(shell.file, shell.args, { cwd });
1840
+ let output = "";
1841
+ const appendOutput = (data) => {
1842
+ output += data.toString();
1843
+ if (output.length > PROJECT_COMMAND_MAX_OUTPUT) {
1844
+ output = output.slice(-PROJECT_COMMAND_MAX_OUTPUT);
1845
+ }
1846
+ };
1847
+ const timeout = setTimeout(() => {
1848
+ child.kill();
1849
+ reject(new Error("服务器指令执行超时"));
1850
+ }, timeoutMs);
1851
+ child.stdout.on("data", appendOutput);
1852
+ child.stderr.on("data", appendOutput);
1853
+ child.on("error", (error) => {
1854
+ clearTimeout(timeout);
1855
+ reject(new Error(`服务器指令启动失败: ${error.message}`));
1856
+ });
1857
+ child.on("close", (exitCode) => {
1858
+ clearTimeout(timeout);
1859
+ resolve({
1860
+ output: trimProjectCommandOutput(output.trim()),
1861
+ exitCode: exitCode ?? -1,
1862
+ });
1863
+ });
1864
+ });
1865
+ };
1601
1866
  const runSpawnCommand = ({ file, args, cwd, }) => {
1602
1867
  return new Promise((resolve, reject) => {
1603
1868
  const child = spawn(file, args, { cwd });
@@ -1662,14 +1927,22 @@ const findExecutablePaths = async (name, cwd) => {
1662
1927
  };
1663
1928
  const inspectProjectEnvironment = async (cwd) => {
1664
1929
  const versionCommands = [
1665
- { name: "dotnet", file: process.platform === "win32" ? "dotnet.exe" : "dotnet", args: ["--version"] },
1930
+ {
1931
+ name: "dotnet",
1932
+ file: process.platform === "win32" ? "dotnet.exe" : "dotnet",
1933
+ args: ["--version"],
1934
+ },
1666
1935
  process.platform === "win32"
1667
1936
  ? { name: "npm", file: "npm.cmd", args: ["-v"] }
1668
1937
  : { name: "npm", file: "npm", args: ["-v"] },
1669
1938
  process.platform === "win32"
1670
1939
  ? { name: "npx", file: "npx.cmd", args: ["-v"] }
1671
1940
  : { name: "npx", file: "npx", args: ["-v"] },
1672
- { name: "node", file: process.platform === "win32" ? "node.exe" : "node", args: ["-v"] },
1941
+ {
1942
+ name: "node",
1943
+ file: process.platform === "win32" ? "node.exe" : "node",
1944
+ args: ["-v"],
1945
+ },
1673
1946
  { name: "git", file: "git", args: ["--version"] },
1674
1947
  ];
1675
1948
  const versions = {};
@@ -1682,11 +1955,15 @@ const inspectProjectEnvironment = async (cwd) => {
1682
1955
  cwd,
1683
1956
  });
1684
1957
  versions[command.name] =
1685
- result.exitCode === 0 ? result.output || "ok" : `exit ${result.exitCode}`;
1958
+ result.exitCode === 0
1959
+ ? result.output || "ok"
1960
+ : `exit ${result.exitCode}`;
1686
1961
  }
1687
1962
  catch (error) {
1688
1963
  versions[command.name] =
1689
- error instanceof Error ? `unavailable: ${error.message}` : String(error);
1964
+ error instanceof Error
1965
+ ? `unavailable: ${error.message}`
1966
+ : String(error);
1690
1967
  }
1691
1968
  paths[command.name] = await findExecutablePaths(command.name, cwd);
1692
1969
  }
@@ -1694,7 +1971,10 @@ const inspectProjectEnvironment = async (cwd) => {
1694
1971
  cwd,
1695
1972
  versions,
1696
1973
  paths,
1697
- entries: fs.readdirSync(cwd, { withFileTypes: true }).slice(0, 50).map((item) => ({
1974
+ entries: fs
1975
+ .readdirSync(cwd, { withFileTypes: true })
1976
+ .slice(0, 50)
1977
+ .map((item) => ({
1698
1978
  name: item.name,
1699
1979
  type: item.isDirectory() ? "directory" : "file",
1700
1980
  })),
@@ -1722,7 +2002,9 @@ const extractArchiveWithinWorkspace = async ({ archive, destination, }) => {
1722
2002
  cwd: destination,
1723
2003
  });
1724
2004
  }
1725
- if (lower.endsWith(".tar") || lower.endsWith(".tar.gz") || lower.endsWith(".tgz")) {
2005
+ if (lower.endsWith(".tar") ||
2006
+ lower.endsWith(".tar.gz") ||
2007
+ lower.endsWith(".tgz")) {
1726
2008
  return runSpawnCommand({
1727
2009
  file: "tar",
1728
2010
  args: ["-xf", archive, "-C", destination],
@@ -1750,15 +2032,50 @@ const performProjectCommandReview = async ({ command, reviewGuid, }) => {
1750
2032
  aiReview,
1751
2033
  };
1752
2034
  };
2035
+ const performServerCommandReview = async ({ command, reviewGuid, workingDirectory, }) => {
2036
+ const localReview = buildServerCommandLocalReview(command);
2037
+ if (!localReview.allowed) {
2038
+ return {
2039
+ allowed: false,
2040
+ risk: localReview.risk,
2041
+ localReview,
2042
+ aiReview: {
2043
+ status: "skipped",
2044
+ reason: "本地基础拦截未通过,未继续调用 AI 审核",
2045
+ },
2046
+ };
2047
+ }
2048
+ const aiReview = await reviewServerCommandWithAI({
2049
+ command,
2050
+ localReview,
2051
+ reviewGuid,
2052
+ workingDirectory,
2053
+ });
2054
+ const allowed = aiReview.status === "completed" && aiReview.allowed === true;
2055
+ const risk = aiReview.status === "completed" && aiReview.risk
2056
+ ? aiReview.risk
2057
+ : localReview.risk;
2058
+ return {
2059
+ allowed,
2060
+ risk,
2061
+ localReview,
2062
+ aiReview,
2063
+ };
2064
+ };
1753
2065
  /**
1754
2066
  * 确保 Docker 容器已创建并运行
1755
2067
  */
1756
2068
  async function ensureContainerRunning(config) {
1757
- const { containerName, image, memory, cpus, pidsLimit, networkMode, workspacePath } = config;
2069
+ const { containerName, image, memory, cpus, pidsLimit, networkMode, workspacePath, } = config;
1758
2070
  if (!containerName)
1759
2071
  return;
1760
2072
  return new Promise((resolve, reject) => {
1761
- const check = spawn("docker", ["inspect", "-f", "{{.State.Running}}", containerName]);
2073
+ const check = spawn("docker", [
2074
+ "inspect",
2075
+ "-f",
2076
+ "{{.State.Running}}",
2077
+ containerName,
2078
+ ]);
1762
2079
  let stdout = "";
1763
2080
  check.stdout.on("data", (d) => (stdout += d.toString()));
1764
2081
  check.on("error", (error) => {
@@ -1787,12 +2104,18 @@ async function ensureContainerRunning(config) {
1787
2104
  return;
1788
2105
  }
1789
2106
  const args = [
1790
- "run", "-d",
1791
- "--name", containerName,
1792
- "--memory", memory || "256m",
1793
- "--cpus", cpus || "0.5",
1794
- "--pids-limit", String(pidsLimit || 64),
1795
- "--network", networkMode || "none",
2107
+ "run",
2108
+ "-d",
2109
+ "--name",
2110
+ containerName,
2111
+ "--memory",
2112
+ memory || "256m",
2113
+ "--cpus",
2114
+ cpus || "0.5",
2115
+ "--pids-limit",
2116
+ String(pidsLimit || 64),
2117
+ "--network",
2118
+ networkMode || "none",
1796
2119
  ];
1797
2120
  if (workspacePath && fs.existsSync(workspacePath)) {
1798
2121
  args.push("-v", `${workspacePath}:/workspace`, "-w", "/workspace");
@@ -2224,7 +2547,7 @@ const availableTools = {
2224
2547
  * @param command 命令字符串
2225
2548
  * @returns 命令执行结果
2226
2549
  */
2227
- exec: async ({ userId, groupId, command }) => {
2550
+ exec: async ({ userId, groupId, command, }) => {
2228
2551
  const workspace = getWorkspace(userId);
2229
2552
  if (commandTargetsAgentWorkspace(command || "")) {
2230
2553
  return [
@@ -2385,6 +2708,46 @@ const availableTools = {
2385
2708
  return toToolError(error);
2386
2709
  }
2387
2710
  },
2711
+ RunServerCommand: async ({ command, workingDirectory, reviewGuid, timeoutSeconds = SERVER_COMMAND_DEFAULT_TIMEOUT_SECONDS, }) => {
2712
+ try {
2713
+ if (!reviewGuid?.trim()) {
2714
+ throw new Error("reviewGuid 必填,请传入当前群号");
2715
+ }
2716
+ const cwd = resolveServerWorkingDirectory(workingDirectory);
2717
+ const timeoutMs = normalizeServerCommandTimeoutMs(timeoutSeconds);
2718
+ const review = await performServerCommandReview({
2719
+ command,
2720
+ reviewGuid,
2721
+ workingDirectory: cwd,
2722
+ });
2723
+ if (!review.allowed) {
2724
+ return {
2725
+ success: false,
2726
+ command,
2727
+ workingDirectory: cwd,
2728
+ error: "服务器指令未通过 AI 自动审核",
2729
+ review,
2730
+ };
2731
+ }
2732
+ const result = await runServerShellCommand({
2733
+ command,
2734
+ cwd,
2735
+ timeoutMs,
2736
+ });
2737
+ return {
2738
+ success: result.exitCode === 0,
2739
+ command,
2740
+ workingDirectory: cwd,
2741
+ review,
2742
+ output: result.output,
2743
+ exitCode: result.exitCode,
2744
+ error: result.exitCode === 0 ? undefined : "服务器指令执行失败",
2745
+ };
2746
+ }
2747
+ catch (error) {
2748
+ return toToolError(error);
2749
+ }
2750
+ },
2388
2751
  AgentGitOperation: async ({ guid, action, workingDirectory = ".", branch, includeRemote = true, }) => {
2389
2752
  try {
2390
2753
  const { target: cwd, relativePath } = resolveAgentPath(guid, workingDirectory);
@@ -2494,7 +2857,8 @@ const availableTools = {
2494
2857
  throw new Error("action=extract 时必须提供 archivePath");
2495
2858
  }
2496
2859
  const archive = resolveAgentPath(guid, archivePath);
2497
- if (!fs.existsSync(archive.target) || !fs.statSync(archive.target).isFile()) {
2860
+ if (!fs.existsSync(archive.target) ||
2861
+ !fs.statSync(archive.target).isFile()) {
2498
2862
  throw new Error("压缩包不存在或不是文件");
2499
2863
  }
2500
2864
  const destination = resolveAgentPath(guid, destinationPath || workingDirectory || ".");
@@ -2757,9 +3121,7 @@ const availableTools = {
2757
3121
  fileName: fileName || null,
2758
3122
  uploadType,
2759
3123
  url,
2760
- sendText: uploadType === "image"
2761
- ? `<img=${url}>`
2762
- : `文件链接: ${url}`,
3124
+ sendText: uploadType === "image" ? `<img=${url}>` : `文件链接: ${url}`,
2763
3125
  };
2764
3126
  }
2765
3127
  catch (error) {
@@ -0,0 +1 @@
1
+ *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}.absolute{position:absolute}.relative{position:relative}.-left-4{left:-1rem}.-top-4{top:-1rem}.-z-0{z-index:0}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.box-border{box-sizing:border-box}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-0\.5{height:.125rem}.h-1\.5{height:.375rem}.h-24{height:6rem}.min-h-\[120px\]{min-height:120px}.w-1\.5{width:.375rem}.w-1\/3{width:33.333333%}.w-12{width:3rem}.w-20{width:5rem}.w-24{width:6rem}.auto-rows-auto{grid-auto-rows:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.self-start{align-self:flex-start}.overflow-hidden{overflow:hidden}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-amber-400\/20{border-color:rgba(251,191,36,.2)}.border-amber-400\/30{border-color:rgba(251,191,36,.3)}.border-blue-500\/30{border-color:rgba(59,130,246,.3)}.border-cyan-500\/30{border-color:rgba(6,182,212,.3)}.border-green-500\/30{border-color:rgba(34,197,94,.3)}.border-purple-400\/30{border-color:rgba(192,132,252,.3)}.border-red-500\/20{border-color:rgba(239,68,68,.2)}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.border-yellow-500\/30{border-color:rgba(234,179,8,.3)}.bg-amber-400{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity,1))}.bg-amber-500\/10{background-color:rgba(245,158,11,.1)}.bg-amber-500\/20{background-color:rgba(245,158,11,.2)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/60{background-color:rgba(0,0,0,.6)}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity,1))}.bg-blue-500\/20{background-color:rgba(59,130,246,.2)}.bg-cyan-400{--tw-bg-opacity:1;background-color:rgb(34 211 238/var(--tw-bg-opacity,1))}.bg-cyan-500\/20{background-color:rgba(6,182,212,.2)}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(74 222 128/var(--tw-bg-opacity,1))}.bg-green-500\/20{background-color:rgba(34,197,94,.2)}.bg-purple-400\/20{background-color:rgba(192,132,252,.2)}.bg-red-500\/10{background-color:rgba(239,68,68,.1)}.bg-white\/30{background-color:hsla(0,0%,100%,.3)}.bg-white\/5{background-color:hsla(0,0%,100%,.05)}.bg-yellow-500\/20{background-color:rgba(234,179,8,.2)}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-amber-300{--tw-gradient-from:#fcd34d var(--tw-gradient-from-position);--tw-gradient-to:rgba(252,211,77,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-amber-400{--tw-gradient-from:#fbbf24 var(--tw-gradient-from-position);--tw-gradient-to:rgba(251,191,36,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-900\/90{--tw-gradient-from:rgba(17,24,39,.9) var(--tw-gradient-from-position);--tw-gradient-to:rgba(17,24,39,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-500\/20{--tw-gradient-from:rgba(168,85,247,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(168,85,247,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-yellow-200{--tw-gradient-to:hsla(53,98%,77%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#fef08a var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-amber-300{--tw-gradient-to:#fcd34d var(--tw-gradient-to-position)}.to-black\/80{--tw-gradient-to:rgba(0,0,0,.8) var(--tw-gradient-to-position)}.to-pink-500\/20{--tw-gradient-to:rgba(236,72,153,.2) var(--tw-gradient-to-position)}.to-purple-400{--tw-gradient-to:#c084fc var(--tw-gradient-to-position)}.bg-cover{background-size:cover}.bg-clip-text{background-clip:text}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.pl-1{padding-left:.25rem}.pt-2{padding-top:.5rem}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.italic{font-style:italic}.leading-relaxed{line-height:1.625}.tracking-wide{letter-spacing:.025em}.text-\[\#B8AE8E\]{--tw-text-opacity:1;color:rgb(184 174 142/var(--tw-text-opacity,1))}.text-amber-100{--tw-text-opacity:1;color:rgb(254 243 199/var(--tw-text-opacity,1))}.text-amber-200{--tw-text-opacity:1;color:rgb(253 230 138/var(--tw-text-opacity,1))}.text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.text-blue-300{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.text-cyan-200{--tw-text-opacity:1;color:rgb(165 243 252/var(--tw-text-opacity,1))}.text-cyan-300{--tw-text-opacity:1;color:rgb(103 232 249/var(--tw-text-opacity,1))}.text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.text-purple-200{--tw-text-opacity:1;color:rgb(233 213 255/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-transparent{color:transparent}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/40{color:hsla(0,0%,100%,.4)}.text-white\/50{color:hsla(0,0%,100%,.5)}.text-white\/60{color:hsla(0,0%,100%,.6)}.text-white\/70{color:hsla(0,0%,100%,.7)}.text-white\/80{color:hsla(0,0%,100%,.8)}.text-yellow-300{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity,1))}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.blur-2xl{--tw-blur:blur(40px)}.blur-2xl,.drop-shadow-lg{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow-lg{--tw-drop-shadow:drop-shadow(0 10px 8px rgba(0,0,0,.04)) drop-shadow(0 4px 3px rgba(0,0,0,.1))}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-md,.backdrop-blur-sm{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}body{display:flex;flex-direction:column;margin:0;padding:0}.backdrop-blur-xs{backdrop-filter:blur(4px)}.last\:border-0:last-child{border-width:0}.hover\:bg-white\/5:hover{background-color:hsla(0,0%,100%,.05)}@media (min-width:640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:768px){.md\:block{display:block}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:border-l{border-left-width:1px}.md\:border-r-0{border-right-width:0}}
@@ -1,4 +1,4 @@
1
1
  const reg = ['win32'].includes(process.platform) ? /^file:\/\/\// : /^file:\/\// ;
2
- const fileUrl = new URL('main.css-BXtCDFIq.css', import.meta.url).href.replace(reg, '');
2
+ const fileUrl = new URL('main.css-Dysn6Zt3.css', import.meta.url).href.replace(reg, '');
3
3
 
4
4
  export { fileUrl as default };