@zqagent/core 0.2.20 → 0.3.7
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/README.md +1 -1
- package/README.oss.en.md +3 -3
- package/README.oss.md +8 -3
- package/README.oss.zh-TW.md +3 -3
- package/dist/McpClient-CEQBUPuC.d.cts +145 -0
- package/dist/McpClient-CEQBUPuC.d.ts +145 -0
- package/dist/{ToolRegistry-C3pTFiaJ.d.cts → ToolRegistry-Cy0fQxrb.d.ts} +2 -111
- package/dist/{ToolRegistry-C3pTFiaJ.d.ts → ToolRegistry-DouDvn9E.d.cts} +2 -111
- package/dist/{explore-RtZtb-V6.d.ts → TurnEngine-BPKhWuSI.d.cts} +108 -73
- package/dist/{explore-BgRhj6SP.d.cts → TurnEngine-DtGEuVLT.d.ts} +108 -73
- package/dist/conflictGuard-BBOOIxxK.d.cts +42 -0
- package/dist/conflictGuard-BBOOIxxK.d.ts +42 -0
- package/dist/index.cjs +5930 -1114
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +577 -195
- package/dist/index.d.ts +577 -195
- package/dist/index.js +5761 -979
- package/dist/index.js.map +1 -1
- package/dist/internal/crypto/secrets.cjs +25 -0
- package/dist/internal/crypto/secrets.cjs.map +1 -0
- package/dist/internal/crypto/secrets.d.cts +2 -0
- package/dist/internal/crypto/secrets.d.ts +2 -0
- package/dist/internal/crypto/secrets.js +3 -0
- package/dist/internal/crypto/secrets.js.map +1 -0
- package/dist/internal/engine/FileLogHook.cjs +179 -0
- package/dist/internal/engine/FileLogHook.cjs.map +1 -0
- package/dist/internal/engine/FileLogHook.d.cts +67 -0
- package/dist/internal/engine/FileLogHook.d.ts +67 -0
- package/dist/internal/engine/FileLogHook.js +144 -0
- package/dist/internal/engine/FileLogHook.js.map +1 -0
- package/dist/internal/engine/approvalView.cjs +25 -0
- package/dist/internal/engine/approvalView.cjs.map +1 -0
- package/dist/internal/engine/approvalView.d.cts +1 -0
- package/dist/internal/engine/approvalView.d.ts +1 -0
- package/dist/internal/engine/approvalView.js +3 -0
- package/dist/internal/engine/approvalView.js.map +1 -0
- package/dist/internal/engine/limits.cjs +59 -0
- package/dist/internal/engine/limits.cjs.map +1 -0
- package/dist/internal/engine/limits.d.cts +32 -0
- package/dist/internal/engine/limits.d.ts +32 -0
- package/dist/internal/engine/limits.js +31 -0
- package/dist/internal/engine/limits.js.map +1 -0
- package/dist/internal/engine/sessionNaming.cjs +47 -0
- package/dist/internal/engine/sessionNaming.cjs.map +1 -0
- package/dist/internal/engine/sessionNaming.d.cts +25 -0
- package/dist/internal/engine/sessionNaming.d.ts +25 -0
- package/dist/internal/engine/sessionNaming.js +20 -0
- package/dist/internal/engine/sessionNaming.js.map +1 -0
- package/dist/internal/llmConfig.cjs +561 -0
- package/dist/internal/llmConfig.cjs.map +1 -0
- package/dist/internal/llmConfig.d.cts +75 -0
- package/dist/internal/llmConfig.d.ts +75 -0
- package/dist/internal/llmConfig.js +535 -0
- package/dist/internal/llmConfig.js.map +1 -0
- package/dist/internal/mcp/builtin/index.cjs +685 -0
- package/dist/internal/mcp/builtin/index.cjs.map +1 -0
- package/dist/internal/mcp/builtin/index.d.cts +21 -0
- package/dist/internal/mcp/builtin/index.d.ts +21 -0
- package/dist/internal/mcp/builtin/index.js +652 -0
- package/dist/internal/mcp/builtin/index.js.map +1 -0
- package/dist/internal/mcp/config.cjs +278 -0
- package/dist/internal/mcp/config.cjs.map +1 -0
- package/dist/internal/mcp/config.d.cts +61 -0
- package/dist/internal/mcp/config.d.ts +61 -0
- package/dist/internal/mcp/config.js +235 -0
- package/dist/internal/mcp/config.js.map +1 -0
- package/dist/internal/mcp/presets.cjs +184 -0
- package/dist/internal/mcp/presets.cjs.map +1 -0
- package/dist/internal/mcp/presets.d.cts +39 -0
- package/dist/internal/mcp/presets.d.ts +39 -0
- package/dist/internal/mcp/presets.js +158 -0
- package/dist/internal/mcp/presets.js.map +1 -0
- package/dist/internal/node-name.cjs +45 -0
- package/dist/internal/node-name.cjs.map +1 -0
- package/dist/internal/node-name.d.cts +3 -0
- package/dist/internal/node-name.d.ts +3 -0
- package/dist/internal/node-name.js +10 -0
- package/dist/internal/node-name.js.map +1 -0
- package/dist/internal/paths.cjs +63 -0
- package/dist/internal/paths.cjs.map +1 -0
- package/dist/internal/paths.d.cts +17 -0
- package/dist/internal/paths.d.ts +17 -0
- package/dist/internal/paths.js +25 -0
- package/dist/internal/paths.js.map +1 -0
- package/dist/internal/providers/diagnostics.cjs +288 -0
- package/dist/internal/providers/diagnostics.cjs.map +1 -0
- package/dist/internal/providers/diagnostics.d.cts +103 -0
- package/dist/internal/providers/diagnostics.d.ts +103 -0
- package/dist/internal/providers/diagnostics.js +250 -0
- package/dist/internal/providers/diagnostics.js.map +1 -0
- package/dist/internal/providers/models.cjs +699 -0
- package/dist/internal/providers/models.cjs.map +1 -0
- package/dist/internal/providers/models.d.cts +136 -0
- package/dist/internal/providers/models.d.ts +136 -0
- package/dist/internal/providers/models.js +654 -0
- package/dist/internal/providers/models.js.map +1 -0
- package/dist/internal/terminal.cjs +161 -0
- package/dist/internal/terminal.cjs.map +1 -0
- package/dist/internal/terminal.d.cts +58 -0
- package/dist/internal/terminal.d.ts +58 -0
- package/dist/internal/terminal.js +128 -0
- package/dist/internal/terminal.js.map +1 -0
- package/dist/internal/tools/execAsync.cjs +187 -0
- package/dist/internal/tools/execAsync.cjs.map +1 -0
- package/dist/internal/tools/execAsync.d.cts +30 -0
- package/dist/internal/tools/execAsync.d.ts +30 -0
- package/dist/internal/tools/execAsync.js +162 -0
- package/dist/internal/tools/execAsync.js.map +1 -0
- package/dist/internal/tools/git.cjs +935 -0
- package/dist/internal/tools/git.cjs.map +1 -0
- package/dist/internal/tools/git.d.cts +206 -0
- package/dist/internal/tools/git.d.ts +206 -0
- package/dist/internal/tools/git.js +883 -0
- package/dist/internal/tools/git.js.map +1 -0
- package/dist/internal/tools/pathSafety.cjs +99 -0
- package/dist/internal/tools/pathSafety.cjs.map +1 -0
- package/dist/internal/tools/pathSafety.d.cts +44 -0
- package/dist/internal/tools/pathSafety.d.ts +44 -0
- package/dist/internal/tools/pathSafety.js +61 -0
- package/dist/internal/tools/pathSafety.js.map +1 -0
- package/dist/internal/tools/plan.cjs +146 -0
- package/dist/internal/tools/plan.cjs.map +1 -0
- package/dist/internal/tools/plan.d.cts +48 -0
- package/dist/internal/tools/plan.d.ts +48 -0
- package/dist/internal/tools/plan.js +117 -0
- package/dist/internal/tools/plan.js.map +1 -0
- package/dist/internal/tools/ripgrep.cjs +118 -0
- package/dist/internal/tools/ripgrep.cjs.map +1 -0
- package/dist/internal/tools/ripgrep.d.cts +6 -0
- package/dist/internal/tools/ripgrep.d.ts +6 -0
- package/dist/internal/tools/ripgrep.js +89 -0
- package/dist/internal/tools/ripgrep.js.map +1 -0
- package/dist/internal/tools/sandbox.cjs +253 -0
- package/dist/internal/tools/sandbox.cjs.map +1 -0
- package/dist/internal/tools/sandbox.d.cts +67 -0
- package/dist/internal/tools/sandbox.d.ts +67 -0
- package/dist/internal/tools/sandbox.js +213 -0
- package/dist/internal/tools/sandbox.js.map +1 -0
- package/dist/internal/tools/shell.cjs +771 -0
- package/dist/internal/tools/shell.cjs.map +1 -0
- package/dist/internal/tools/shell.d.cts +47 -0
- package/dist/internal/tools/shell.d.ts +47 -0
- package/dist/internal/tools/shell.js +731 -0
- package/dist/internal/tools/shell.js.map +1 -0
- package/dist/internal/tools/shellRuntime.cjs +220 -0
- package/dist/internal/tools/shellRuntime.cjs.map +1 -0
- package/dist/internal/tools/shellRuntime.d.cts +67 -0
- package/dist/internal/tools/shellRuntime.d.ts +67 -0
- package/dist/internal/tools/shellRuntime.js +181 -0
- package/dist/internal/tools/shellRuntime.js.map +1 -0
- package/dist/internal/update/controller.cjs +1035 -0
- package/dist/internal/update/controller.cjs.map +1 -0
- package/dist/internal/update/controller.d.cts +150 -0
- package/dist/internal/update/controller.d.ts +150 -0
- package/dist/internal/update/controller.js +991 -0
- package/dist/internal/update/controller.js.map +1 -0
- package/dist/internal/update/platform.cjs +234 -0
- package/dist/internal/update/platform.cjs.map +1 -0
- package/dist/internal/update/platform.d.cts +77 -0
- package/dist/internal/update/platform.d.ts +77 -0
- package/dist/internal/update/platform.js +184 -0
- package/dist/internal/update/platform.js.map +1 -0
- package/dist/tools/ask.d.cts +3 -1
- package/dist/tools/ask.d.ts +3 -1
- package/dist/tools/diff.cjs +17 -6
- package/dist/tools/diff.cjs.map +1 -1
- package/dist/tools/diff.d.cts +5 -2
- package/dist/tools/diff.d.ts +5 -2
- package/dist/tools/diff.js +17 -6
- package/dist/tools/diff.js.map +1 -1
- package/dist/tools/directories.d.cts +3 -1
- package/dist/tools/directories.d.ts +3 -1
- package/dist/tools/explore.cjs +1778 -204
- package/dist/tools/explore.cjs.map +1 -1
- package/dist/tools/explore.d.cts +82 -2
- package/dist/tools/explore.d.ts +82 -2
- package/dist/tools/explore.js +1780 -204
- package/dist/tools/explore.js.map +1 -1
- package/dist/tools/files.cjs +56 -21
- package/dist/tools/files.cjs.map +1 -1
- package/dist/tools/files.d.cts +6 -3
- package/dist/tools/files.d.ts +6 -3
- package/dist/tools/files.js +56 -21
- package/dist/tools/files.js.map +1 -1
- package/dist/tools/git.cjs +34 -25
- package/dist/tools/git.cjs.map +1 -1
- package/dist/tools/git.d.cts +4 -204
- package/dist/tools/git.d.ts +4 -204
- package/dist/tools/git.js +34 -25
- package/dist/tools/git.js.map +1 -1
- package/dist/tools/media.cjs +280 -3
- package/dist/tools/media.cjs.map +1 -1
- package/dist/tools/media.d.cts +3 -1
- package/dist/tools/media.d.ts +3 -1
- package/dist/tools/media.js +278 -1
- package/dist/tools/media.js.map +1 -1
- package/dist/tools/plan.cjs +62 -0
- package/dist/tools/plan.cjs.map +1 -1
- package/dist/tools/plan.d.cts +4 -17
- package/dist/tools/plan.d.ts +4 -17
- package/dist/tools/plan.js +59 -0
- package/dist/tools/plan.js.map +1 -1
- package/dist/tools/search.cjs +3 -0
- package/dist/tools/search.cjs.map +1 -1
- package/dist/tools/search.d.cts +3 -1
- package/dist/tools/search.d.ts +3 -1
- package/dist/tools/search.js +3 -0
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/shell.cjs +76 -11
- package/dist/tools/shell.cjs.map +1 -1
- package/dist/tools/shell.d.cts +5 -39
- package/dist/tools/shell.d.ts +5 -39
- package/dist/tools/shell.js +75 -11
- package/dist/tools/shell.js.map +1 -1
- package/dist/tools/todo.d.cts +3 -1
- package/dist/tools/todo.d.ts +3 -1
- package/dist/tools/web.cjs +279 -5
- package/dist/tools/web.cjs.map +1 -1
- package/dist/tools/web.d.cts +3 -1
- package/dist/tools/web.d.ts +3 -1
- package/dist/tools/web.js +275 -3
- package/dist/tools/web.js.map +1 -1
- package/dist/types-BYyXWWPW.d.cts +237 -0
- package/dist/types-BYyXWWPW.d.ts +237 -0
- package/package.json +26 -37
- package/bin/zqagent +0 -2
- package/dist/cli/index.cjs +0 -25218
- package/dist/cli/index.cjs.map +0 -1
- package/dist/cli/index.d.cts +0 -2
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -25201
- package/dist/cli/index.js.map +0 -1
- package/dist/server/index.cjs +0 -20510
- package/dist/server/index.cjs.map +0 -1
- package/dist/server/index.d.cts +0 -2
- package/dist/server/index.d.ts +0 -2
- package/dist/server/index.js +0 -20493
- package/dist/server/index.js.map +0 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// src/update/platform.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { spawnSync } from "child_process";
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
function currentVersion() {
|
|
8
|
+
const injected = "0.3.7"?.trim();
|
|
9
|
+
if (injected) return injected;
|
|
10
|
+
try {
|
|
11
|
+
const req = createRequire(import.meta.url);
|
|
12
|
+
const pkg = req("../../package.json");
|
|
13
|
+
if (pkg?.version) return pkg.version;
|
|
14
|
+
} catch {
|
|
15
|
+
}
|
|
16
|
+
return "dev";
|
|
17
|
+
}
|
|
18
|
+
function isSeaRuntime() {
|
|
19
|
+
if (process.env.ZQAGENT_SEA === "1") return true;
|
|
20
|
+
const base = path.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
|
|
21
|
+
return base !== "node" && base !== "nodejs";
|
|
22
|
+
}
|
|
23
|
+
var DEFAULT_SERVICE_NAME = "zqagent";
|
|
24
|
+
var DEFAULT_SERVICE_LABEL = "com.zqagent.node";
|
|
25
|
+
function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
|
|
26
|
+
const unitName = `${serviceName}.service`;
|
|
27
|
+
for (const line of cgroup.split("\n")) {
|
|
28
|
+
const idx = line.lastIndexOf(":");
|
|
29
|
+
if (idx < 0) continue;
|
|
30
|
+
const unitPath = line.slice(idx + 1).trim();
|
|
31
|
+
if (unitPath.split("/").includes(unitName)) return "systemd";
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function launchdDomain() {
|
|
36
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : -1;
|
|
37
|
+
return uid === 0 ? "system" : `gui/${uid}`;
|
|
38
|
+
}
|
|
39
|
+
function launchdLabel() {
|
|
40
|
+
return process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL;
|
|
41
|
+
}
|
|
42
|
+
function launchdPlistPath() {
|
|
43
|
+
const label = launchdLabel();
|
|
44
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : -1;
|
|
45
|
+
if (uid === 0) return `/Library/LaunchDaemons/${label}.plist`;
|
|
46
|
+
return path.join(os.homedir(), "Library", "LaunchAgents", `${label}.plist`);
|
|
47
|
+
}
|
|
48
|
+
function serviceManagerFromLaunchctl(output, pid, execPath, sea = true) {
|
|
49
|
+
const pidMatch = /^\s*pid = (\d+)\s*$/m.exec(output);
|
|
50
|
+
if (pidMatch && Number(pidMatch[1]) === pid) return "launchd";
|
|
51
|
+
const program = /^\s*program = (.+?)\s*$/m.exec(output);
|
|
52
|
+
if (program) {
|
|
53
|
+
const shown = program[1].replace(/^"|"$/g, "");
|
|
54
|
+
if (path.resolve(shown) === path.resolve(execPath)) return "launchd";
|
|
55
|
+
}
|
|
56
|
+
if (!sea && /^\s*state = /m.test(output)) return "launchd";
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function parseLaunchdEnvironment(xml) {
|
|
60
|
+
const out = {};
|
|
61
|
+
const marker = xml.indexOf("<key>EnvironmentVariables</key>");
|
|
62
|
+
if (marker < 0) return out;
|
|
63
|
+
const open = xml.indexOf("<dict>", marker);
|
|
64
|
+
if (open < 0) return out;
|
|
65
|
+
const close = xml.indexOf("</dict>", open);
|
|
66
|
+
if (close < 0) return out;
|
|
67
|
+
const body = xml.slice(open + "<dict>".length, close);
|
|
68
|
+
for (const m of body.matchAll(/<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g)) {
|
|
69
|
+
out[m[1]] = m[2];
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
function detectServiceManager() {
|
|
74
|
+
if (process.platform === "darwin") return detectLaunchd();
|
|
75
|
+
if (process.platform !== "linux") return null;
|
|
76
|
+
const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
|
|
77
|
+
try {
|
|
78
|
+
return serviceManagerFromCgroup(
|
|
79
|
+
fs.readFileSync("/proc/self/cgroup", "utf8"),
|
|
80
|
+
serviceName
|
|
81
|
+
);
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function detectLaunchd() {
|
|
87
|
+
try {
|
|
88
|
+
const res = spawnSync(
|
|
89
|
+
"launchctl",
|
|
90
|
+
["print", `${launchdDomain()}/${launchdLabel()}`],
|
|
91
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
92
|
+
);
|
|
93
|
+
if (res.status !== 0 || !res.stdout) return null;
|
|
94
|
+
return serviceManagerFromLaunchctl(
|
|
95
|
+
res.stdout,
|
|
96
|
+
process.pid,
|
|
97
|
+
process.execPath,
|
|
98
|
+
isSeaRuntime()
|
|
99
|
+
);
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function serviceManager() {
|
|
105
|
+
const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
|
|
106
|
+
if (v === "systemd" || v === "launchd") return v;
|
|
107
|
+
return detectServiceManager();
|
|
108
|
+
}
|
|
109
|
+
function autoUpdateDisabled() {
|
|
110
|
+
return process.env.ZQAGENT_AUTO_UPDATE?.trim() === "0";
|
|
111
|
+
}
|
|
112
|
+
function parseServiceEnvironment(raw) {
|
|
113
|
+
const out = {};
|
|
114
|
+
for (const token of raw.trim().split(/\s+/)) {
|
|
115
|
+
const idx = token.indexOf("=");
|
|
116
|
+
if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
var cachedServiceEnv = null;
|
|
121
|
+
function readServiceEnvironment() {
|
|
122
|
+
const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
|
|
123
|
+
try {
|
|
124
|
+
const res = spawnSync(
|
|
125
|
+
"systemctl",
|
|
126
|
+
["show", serviceName, "--property=Environment", "--value", "--no-pager"],
|
|
127
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
128
|
+
);
|
|
129
|
+
if (res.status !== 0 || !res.stdout) return {};
|
|
130
|
+
return parseServiceEnvironment(res.stdout);
|
|
131
|
+
} catch {
|
|
132
|
+
return {};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function serviceEnvValue(key) {
|
|
136
|
+
if (process.platform === "darwin") {
|
|
137
|
+
if (serviceManager() !== "launchd") return void 0;
|
|
138
|
+
cachedLaunchdEnv ??= readLaunchdEnvironment();
|
|
139
|
+
return cachedLaunchdEnv[key];
|
|
140
|
+
}
|
|
141
|
+
if (process.platform !== "linux") return void 0;
|
|
142
|
+
if (serviceManager() !== "systemd") return void 0;
|
|
143
|
+
cachedServiceEnv ??= readServiceEnvironment();
|
|
144
|
+
return cachedServiceEnv[key];
|
|
145
|
+
}
|
|
146
|
+
var cachedLaunchdEnv = null;
|
|
147
|
+
function readLaunchdEnvironment() {
|
|
148
|
+
try {
|
|
149
|
+
return parseLaunchdEnvironment(fs.readFileSync(launchdPlistPath(), "utf8"));
|
|
150
|
+
} catch {
|
|
151
|
+
return {};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function selfUpdateCapability(hasPublicKey) {
|
|
155
|
+
if (!isSeaRuntime()) {
|
|
156
|
+
return {
|
|
157
|
+
ok: false,
|
|
158
|
+
reason: "\u5F53\u524D\u4E0D\u662F\u5355\u6587\u4EF6\uFF08SEA\uFF09\u5F62\u6001\uFF1A\u8BF7\u7528 npm \u5347\u7EA7\uFF08npm i -g @zqagent/cli@latest\uFF09"
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (!serviceManager()) {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
reason: "\u672A\u88AB systemd/launchd \u6258\u7BA1\uFF1A\u8BF7\u7528 deploy/service/install.sh \u5B89\u88C5\uFF0C\u6216\u624B\u52A8\u5347\u7EA7"
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (!hasPublicKey) {
|
|
168
|
+
return { ok: false, reason: "\u672A\u914D\u7F6E ZQAGENT_UPDATE_PUBKEY\uFF0C\u65E0\u6CD5\u9A8C\u7B7E\uFF0C\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0" };
|
|
169
|
+
}
|
|
170
|
+
return { ok: true };
|
|
171
|
+
}
|
|
172
|
+
export {
|
|
173
|
+
autoUpdateDisabled,
|
|
174
|
+
currentVersion,
|
|
175
|
+
isSeaRuntime,
|
|
176
|
+
parseLaunchdEnvironment,
|
|
177
|
+
parseServiceEnvironment,
|
|
178
|
+
selfUpdateCapability,
|
|
179
|
+
serviceEnvValue,
|
|
180
|
+
serviceManager,
|
|
181
|
+
serviceManagerFromCgroup,
|
|
182
|
+
serviceManagerFromLaunchctl
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/update/platform.ts"],"sourcesContent":["/**\n * 运行形态与自更新能力判定。\n *\n * 只有「SEA 单文件 + 由 systemd/launchd 托管 + 配置了公钥」三者同时满足,\n * 才允许自动替换可执行文件并重启——否则退出后没人把它拉起来。\n */\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\n\n/**\n * 当前版本:构建期注入(SEA/tsup define)优先,其次读包内 package.json,\n * 最后回退 \"dev\"。后两条让源码运行(tsx)也能显示真实版本。\n */\nexport function currentVersion(): string {\n const injected = process.env.ZQAGENT_VERSION?.trim();\n if (injected) return injected;\n try {\n const req = createRequire(import.meta.url);\n const pkg = req(\"../../package.json\") as { version?: string };\n if (pkg?.version) return pkg.version;\n } catch {\n // SEA 内取不到包信息,忽略\n }\n return \"dev\";\n}\n\n/**\n * 是否为 SEA 单文件运行。\n *\n * 判据优先取 execPath 的文件名:SEA 产物是 `zqagent`,经 node 运行的则是 `node`。\n * 构建期还会注入 `ZQAGENT_SEA=1`(见 tsup.sea.config.ts)作为权威标记。\n */\nexport function isSeaRuntime(): boolean {\n if (process.env.ZQAGENT_SEA === \"1\") return true;\n const base = path.basename(process.execPath).toLowerCase().replace(/\\.exe$/, \"\");\n return base !== \"node\" && base !== \"nodejs\";\n}\n\nexport type ServiceManager = \"systemd\" | \"launchd\";\n\n/** 未指定 ZQAGENT_SERVICE_NAME 时的默认 systemd unit 名(与 install.sh 一致) */\nconst DEFAULT_SERVICE_NAME = \"zqagent\";\n\n/** 未指定 ZQAGENT_SERVICE_LABEL 时的默认 launchd 作业名(与 install.sh 一致) */\nconst DEFAULT_SERVICE_LABEL = \"com.zqagent.node\";\n\n/**\n * 从 `/proc/self/cgroup` 的内容判定进程是否属于某个 systemd unit。\n *\n * 抽取为纯函数以便测试。兼容 cgroup v1(`1:name=systemd:/system.slice/x.service`)\n * 与 v2(`0::/system.slice/x.service`)两种行格式。\n *\n * 只认路径中**独立成段**的 `<serviceName>.service`:`my-zqagent.service`、\n * `zqagent.service.bak` 这类名字相近的 unit 不会被误判成 `zqagent.service`。\n */\nexport function serviceManagerFromCgroup(\n cgroup: string,\n serviceName: string = DEFAULT_SERVICE_NAME,\n): ServiceManager | null {\n const unitName = `${serviceName}.service`;\n for (const line of cgroup.split(\"\\n\")) {\n // 路径在最后一个冒号之后;v1 的 `name=systemd` 不会干扰\n const idx = line.lastIndexOf(\":\");\n if (idx < 0) continue;\n const unitPath = line.slice(idx + 1).trim();\n if (unitPath.split(\"/\").includes(unitName)) return \"systemd\";\n }\n return null;\n}\n\n/** launchd 作业所在域:root 装的是 LaunchDaemon(system),普通用户是 LaunchAgent(gui/<uid>) */\nfunction launchdDomain(): string {\n const uid = typeof process.getuid === \"function\" ? process.getuid() : -1;\n return uid === 0 ? \"system\" : `gui/${uid}`;\n}\n\nfunction launchdLabel(): string {\n return process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL;\n}\n\n/** 与 install.sh 写入的位置保持一致 */\nfunction launchdPlistPath(): string {\n const label = launchdLabel();\n const uid = typeof process.getuid === \"function\" ? process.getuid() : -1;\n if (uid === 0) return `/Library/LaunchDaemons/${label}.plist`;\n return path.join(os.homedir(), \"Library\", \"LaunchAgents\", `${label}.plist`);\n}\n\n/**\n * 从 `launchctl print <domain>/<label>` 的输出判定是否为 launchd 托管。\n *\n * macOS 没有 cgroup 可用,改用作业描述里的两处信息:\n * - `pid = N` 与当前进程一致 → 本进程就是该作业拉起的;\n * - `program = /path` 与 execPath 相同 → 服务运行的正是这个可执行文件,\n * 在交互 shell 里执行 `zqagent update` 时靠这条判定(Linux 靠 cgroup 归属)。\n *\n * `sea` 为 false(npm/source 形态)时作业存在即算托管:这种形态本来就不允许\n * 自替换二进制(见 selfUpdateCapability),这里只影响 `update --status` 的显示。\n */\nexport function serviceManagerFromLaunchctl(\n output: string,\n pid: number,\n execPath: string,\n sea = true,\n): ServiceManager | null {\n const pidMatch = /^\\s*pid = (\\d+)\\s*$/m.exec(output);\n if (pidMatch && Number(pidMatch[1]) === pid) return \"launchd\";\n\n const program = /^\\s*program = (.+?)\\s*$/m.exec(output);\n if (program) {\n const shown = program[1].replace(/^\"|\"$/g, \"\");\n if (path.resolve(shown) === path.resolve(execPath)) return \"launchd\";\n }\n\n if (!sea && /^\\s*state = /m.test(output)) return \"launchd\";\n return null;\n}\n\n/**\n * 解析 launchd plist 中 `EnvironmentVariables` 字典的键值。\n *\n * 只处理本脚本会写出的 `<key>K</key><string>V</string>` 形式;`<true/>` 之类的\n * 非字符串值忽略(更新配置用不到)。\n */\nexport function parseLaunchdEnvironment(xml: string): Record<string, string> {\n const out: Record<string, string> = {};\n const marker = xml.indexOf(\"<key>EnvironmentVariables</key>\");\n if (marker < 0) return out;\n const open = xml.indexOf(\"<dict>\", marker);\n if (open < 0) return out;\n const close = xml.indexOf(\"</dict>\", open);\n if (close < 0) return out;\n const body = xml.slice(open + \"<dict>\".length, close);\n for (const m of body.matchAll(/<key>([^<]+)<\\/key>\\s*<string>([^<]*)<\\/string>/g)) {\n out[m[1]] = m[2];\n }\n return out;\n}\n\n/**\n * 未注入 ZQAGENT_SERVICE_MANAGER 时的兜底检测。\n *\n * 服务进程的环境变量由 unit 注入,但运维在交互 shell 里手动执行\n * `zqagent update --status/--check/--now` 时没有这些变量——此前会被判成\n * 「未托管」,导致诊断结论错误、`--now` 直接拒绝执行。这里用 cgroup 归属\n * 兜底:Linux 上进程落在 `/<service>.service` 里,说明 systemd 确实会拉起它。\n *\n * 刻意不使用「/run/systemd/system 存在即视为托管」这类判据:那样任何在\n * systemd 机器上裸跑的进程都会通过自检,替换二进制后 `exit(70)` 却没人拉起,\n * 节点会直接下线。\n */\nfunction detectServiceManager(): ServiceManager | null {\n if (process.platform === \"darwin\") return detectLaunchd();\n if (process.platform !== \"linux\") return null;\n const serviceName =\n process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;\n try {\n return serviceManagerFromCgroup(\n fs.readFileSync(\"/proc/self/cgroup\", \"utf8\"),\n serviceName,\n );\n } catch {\n return null;\n }\n}\n\n/**\n * macOS 兜底:读取 launchd 作业描述,按 pid / program 判定(见\n * `serviceManagerFromLaunchctl`)。作业不存在、launchctl 不可用时返回 null。\n */\nfunction detectLaunchd(): ServiceManager | null {\n try {\n const res = spawnSync(\n \"launchctl\",\n [\"print\", `${launchdDomain()}/${launchdLabel()}`],\n { encoding: \"utf8\", timeout: 3000 },\n );\n if (res.status !== 0 || !res.stdout) return null;\n return serviceManagerFromLaunchctl(\n res.stdout,\n process.pid,\n process.execPath,\n isSeaRuntime(),\n );\n } catch {\n return null;\n }\n}\n\n/**\n * 进程由哪个服务管理器托管;无托管返回 null。\n *\n * 优先采信 `deploy/service/install.sh` 注入的环境变量,缺失时在 Linux 上\n * 按 cgroup 归属兜底检测(见 `detectServiceManager`)。\n */\nexport function serviceManager(): ServiceManager | null {\n const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();\n if (v === \"systemd\" || v === \"launchd\") return v;\n return detectServiceManager();\n}\n\nexport function autoUpdateDisabled(): boolean {\n return process.env.ZQAGENT_AUTO_UPDATE?.trim() === \"0\";\n}\n\n/**\n * 解析 `systemctl show -p Environment --value` 的输出为键值表。\n *\n * 输出形如 `NODE_ENV=production PATH=/usr/local/bin:/usr/bin KEY=abc`。\n * 值里若含空格会被拆开——本函数只用于取公钥/通道/清单地址这类不含空格的\n * 变量,够用且无需处理 systemd 的引号规则。\n */\nexport function parseServiceEnvironment(raw: string): Record<string, string> {\n const out: Record<string, string> = {};\n for (const token of raw.trim().split(/\\s+/)) {\n const idx = token.indexOf(\"=\");\n if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);\n }\n return out;\n}\n\nlet cachedServiceEnv: Record<string, string> | null = null;\n\nfunction readServiceEnvironment(): Record<string, string> {\n const serviceName =\n process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;\n try {\n const res = spawnSync(\n \"systemctl\",\n [\"show\", serviceName, \"--property=Environment\", \"--value\", \"--no-pager\"],\n { encoding: \"utf8\", timeout: 3000 },\n );\n if (res.status !== 0 || !res.stdout) return {};\n return parseServiceEnvironment(res.stdout);\n } catch {\n return {};\n }\n}\n\n/**\n * 读取 systemd unit 注入的环境变量。\n *\n * 服务进程拿得到这些变量,但运维在交互 shell 里手动执行 `zqagent update`\n * 时拿不到——公钥缺失会让自更新被判为不可用。这里按需从 unit 补齐,\n * 取不到(非 systemd、无 systemctl、超时)时返回 undefined。\n */\nexport function serviceEnvValue(key: string): string | undefined {\n if (process.platform === \"darwin\") {\n if (serviceManager() !== \"launchd\") return undefined;\n cachedLaunchdEnv ??= readLaunchdEnvironment();\n return cachedLaunchdEnv[key];\n }\n if (process.platform !== \"linux\") return undefined;\n if (serviceManager() !== \"systemd\") return undefined;\n cachedServiceEnv ??= readServiceEnvironment();\n return cachedServiceEnv[key];\n}\n\nlet cachedLaunchdEnv: Record<string, string> | null = null;\n\n/**\n * 读取 launchd plist 注入的环境变量。\n *\n * 与 systemd 的 `systemctl show -p Environment` 对应:干净 shell 里执行\n * `zqagent update --status` 时,公钥/通道/清单地址只能从服务定义里取回。\n */\nfunction readLaunchdEnvironment(): Record<string, string> {\n try {\n return parseLaunchdEnvironment(fs.readFileSync(launchdPlistPath(), \"utf8\"));\n } catch {\n return {};\n }\n}\n\nexport interface Capability {\n ok: boolean;\n /** 不满足时的可读原因(用于日志/`update --status`) */\n reason?: string;\n}\n\n/**\n * 自更新能力判定。`hasPublicKey` 由调用方传入(公钥只在验签时解析一次)。\n */\nexport function selfUpdateCapability(hasPublicKey: boolean): Capability {\n if (!isSeaRuntime()) {\n return {\n ok: false,\n reason: \"当前不是单文件(SEA)形态:请用 npm 升级(npm i -g @zqagent/cli@latest)\",\n };\n }\n if (!serviceManager()) {\n return {\n ok: false,\n reason: \"未被 systemd/launchd 托管:请用 deploy/service/install.sh 安装,或手动升级\",\n };\n }\n if (!hasPublicKey) {\n return { ok: false, reason: \"未配置 ZQAGENT_UPDATE_PUBKEY,无法验签,已禁用自动更新\" };\n }\n return { ok: true };\n}\n"],"mappings":"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","names":[]}
|
package/dist/tools/ask.d.cts
CHANGED
package/dist/tools/ask.d.ts
CHANGED
package/dist/tools/diff.cjs
CHANGED
|
@@ -89,7 +89,7 @@ function resolveEntryWithinRoot(workspace, target) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// src/tools/diff.ts
|
|
92
|
-
function makeApplyPatch(workspace) {
|
|
92
|
+
function makeApplyPatch(workspace, guard) {
|
|
93
93
|
const root = path2.resolve(workspace);
|
|
94
94
|
return function apply_patch(args) {
|
|
95
95
|
const patch = String(args.patch ?? "");
|
|
@@ -150,6 +150,8 @@ function makeApplyPatch(workspace) {
|
|
|
150
150
|
if (!fs2.existsSync(target)) {
|
|
151
151
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
152
152
|
}
|
|
153
|
+
const delConflict = guard?.checkWrite(target, filePath);
|
|
154
|
+
if (delConflict) return { error: delConflict.message };
|
|
153
155
|
fs2.unlinkSync(target);
|
|
154
156
|
deletedFiles.push(path2.relative(entry.root, target));
|
|
155
157
|
fileCount++;
|
|
@@ -191,6 +193,8 @@ function makeApplyPatch(workspace) {
|
|
|
191
193
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
192
194
|
}
|
|
193
195
|
const oldLines = fs2.readFileSync(target, "utf-8").split("\n");
|
|
196
|
+
const conflict = guard?.checkWrite(target, filePath);
|
|
197
|
+
if (conflict) return { error: conflict.message };
|
|
194
198
|
const newLines = applyPatchHunks(oldLines, chunkLines, filePath);
|
|
195
199
|
let finalResolved = resolved;
|
|
196
200
|
if (moveTo) {
|
|
@@ -206,6 +210,7 @@ function makeApplyPatch(workspace) {
|
|
|
206
210
|
}
|
|
207
211
|
fs2.mkdirSync(path2.dirname(finalTarget), { recursive: true });
|
|
208
212
|
fs2.writeFileSync(finalTarget, newLines.join("\n"), "utf-8");
|
|
213
|
+
guard?.noteWrite(finalTarget, newLines.join("\n"));
|
|
209
214
|
if (moveTo && finalTarget !== target) {
|
|
210
215
|
const entry = resolveEntryWithinRoot(root, filePath);
|
|
211
216
|
if (!entry) {
|
|
@@ -253,7 +258,7 @@ function findBlockMatch(lines, block, start) {
|
|
|
253
258
|
}
|
|
254
259
|
return -1;
|
|
255
260
|
}
|
|
256
|
-
function makeApplyUnifiedDiff(workspace) {
|
|
261
|
+
function makeApplyUnifiedDiff(workspace, guard) {
|
|
257
262
|
const root = path2.resolve(workspace);
|
|
258
263
|
return function apply_unified_diff(args) {
|
|
259
264
|
const diff = String(args.diff ?? "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
@@ -274,6 +279,10 @@ function makeApplyUnifiedDiff(workspace) {
|
|
|
274
279
|
}
|
|
275
280
|
const target = resolved.real;
|
|
276
281
|
const oldLines = oldIsDevNull ? [] : fs2.existsSync(target) ? fs2.readFileSync(target, "utf-8").split("\n") : [];
|
|
282
|
+
if (!oldIsDevNull) {
|
|
283
|
+
const conflict = guard?.checkWrite(target, targetPath);
|
|
284
|
+
if (conflict) return { error: conflict.message };
|
|
285
|
+
}
|
|
277
286
|
const newLines = applyDiffHunks(oldLines, patch.hunks, targetPath);
|
|
278
287
|
hunkCount += patch.hunks.length;
|
|
279
288
|
if (newIsDevNull) {
|
|
@@ -291,7 +300,9 @@ function makeApplyUnifiedDiff(workspace) {
|
|
|
291
300
|
continue;
|
|
292
301
|
}
|
|
293
302
|
fs2.mkdirSync(path2.dirname(target), { recursive: true });
|
|
294
|
-
|
|
303
|
+
const written = newLines.join("\n");
|
|
304
|
+
fs2.writeFileSync(target, written, "utf-8");
|
|
305
|
+
guard?.noteWrite(target, written);
|
|
295
306
|
const rel = path2.relative(resolved.root, target);
|
|
296
307
|
changedFiles.push(rel);
|
|
297
308
|
if (oldIsDevNull) addedFiles.push(rel);
|
|
@@ -406,8 +417,8 @@ function applyDiffHunks(oldLines, hunks, filePath) {
|
|
|
406
417
|
newLines.push(...oldLines.slice(cursor));
|
|
407
418
|
return newLines;
|
|
408
419
|
}
|
|
409
|
-
function registerDiffTools(registry, workspace) {
|
|
410
|
-
const applyPatchFunc = makeApplyPatch(workspace);
|
|
420
|
+
function registerDiffTools(registry, workspace, guard) {
|
|
421
|
+
const applyPatchFunc = makeApplyPatch(workspace, guard);
|
|
411
422
|
registry.register(
|
|
412
423
|
(args) => applyPatchFunc(args),
|
|
413
424
|
{ category: "filesystem", riskLevel: "medium", capabilities: ["edit_file"], requiresApproval: true },
|
|
@@ -426,7 +437,7 @@ function registerDiffTools(registry, workspace) {
|
|
|
426
437
|
}
|
|
427
438
|
}
|
|
428
439
|
);
|
|
429
|
-
const applyUnifiedDiffFunc = makeApplyUnifiedDiff(workspace);
|
|
440
|
+
const applyUnifiedDiffFunc = makeApplyUnifiedDiff(workspace, guard);
|
|
430
441
|
registry.register(
|
|
431
442
|
(args) => applyUnifiedDiffFunc(args),
|
|
432
443
|
{ category: "filesystem", riskLevel: "medium", capabilities: ["edit_file"], requiresApproval: true },
|
package/dist/tools/diff.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tools/diff.ts","../../src/tools/pathSafety.ts"],"sourcesContent":["import { ToolRegistry } from \"./ToolRegistry.js\";\n\n/**\n * apply_patch + apply_unified_diff\n * 对齐 Python 版 aisuite.toolkits.files.FileToolkit\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { resolveEntryWithinRoot, resolveWithinRoot } from \"./pathSafety.js\";\n\n/** apply_patch — Codex 风格补丁 (对齐 Python 版) */\nfunction makeApplyPatch(workspace: string) {\n const root = path.resolve(workspace);\n\n return function apply_patch(args: Record<string, unknown>) {\n const patch = String(args.patch ?? \"\");\n if (!patch.trim()) return { error: \"补丁内容不能为空\" };\n\n // 归一化 CRLF,并去掉尾部空白:否则 `*** End Patch\\n` 会因最后一项是空串\n // 而被误判为「补丁必须以 *** End Patch 结尾」。\n const lines = patch\n .replace(/\\r\\n/g, \"\\n\")\n .replace(/\\r/g, \"\\n\")\n .trimEnd()\n .split(\"\\n\");\n if (!lines[0]?.trim().startsWith(\"*** Begin Patch\")) {\n return { error: \"补丁必须以 '*** Begin Patch' 开头\" };\n }\n if (!lines[lines.length - 1]?.trim().startsWith(\"*** End Patch\")) {\n return { error: \"补丁必须以 '*** End Patch' 结尾\" };\n }\n\n const changedFiles: string[] = [];\n const addedFiles: string[] = [];\n const deletedFiles: string[] = [];\n let fileCount = 0;\n\n let i = 1;\n while (i < lines.length - 1) {\n const line = lines[i].trim();\n if (!line) { i++; continue; }\n\n if (line.startsWith(\"*** Add File: \")) {\n const filePath = line.slice(\"*** Add File: \".length).trim();\n const resolved = resolveWithinRoot(root, filePath);\n if (!resolved) {\n return { error: `路径在工作区之外: ${filePath}` };\n }\n const target = resolved.real;\n i++;\n const contentLines: string[] = [];\n while (i < lines.length - 1 && !lines[i].trimStart().startsWith(\"*** \")) {\n const current = lines[i];\n if (!current.startsWith(\"+\")) {\n return { error: `无效的 add-file 补丁行: ${current}` };\n }\n contentLines.push(current.slice(1));\n i++;\n }\n if (fs.existsSync(target)) {\n return { error: `文件已存在: ${filePath}` };\n }\n fs.mkdirSync(path.dirname(target), { recursive: true });\n fs.writeFileSync(target, contentLines.join(\"\\n\") + \"\\n\", \"utf-8\");\n const rel = path.relative(resolved.root, target);\n changedFiles.push(rel);\n addedFiles.push(rel);\n fileCount++;\n continue;\n }\n\n if (line.startsWith(\"*** Delete File: \")) {\n const filePath = line.slice(\"*** Delete File: \".length).trim();\n // 删除作用于条目本身:父目录取 realpath,最后一段不解析\n const entry = resolveEntryWithinRoot(root, filePath);\n if (!entry) {\n return { error: `路径在工作区之外: ${filePath}` };\n }\n const target = entry.real;\n if (!fs.existsSync(target)) {\n return { error: `文件不存在: ${filePath}` };\n }\n fs.unlinkSync(target);\n deletedFiles.push(path.relative(entry.root, target));\n fileCount++;\n i++;\n continue;\n }\n\n if (line.startsWith(\"*** Update File: \")) {\n const filePath = line.slice(\"*** Update File: \".length).trim();\n const resolved = resolveWithinRoot(root, filePath);\n if (!resolved) {\n return { error: `路径在工作区之外: ${filePath}` };\n }\n const target = resolved.real;\n i++;\n let moveTo: string | null = null;\n if (i < lines.length - 1 && lines[i].trimStart().startsWith(\"*** Move to: \")) {\n moveTo = lines[i].trim().slice(\"*** Move to: \".length).trim();\n i++;\n }\n\n const chunkLines: string[][] = [];\n let currentChunk: string[] = [];\n while (i < lines.length - 1 && !lines[i].trimStart().startsWith(\"*** \")) {\n const ln = lines[i];\n if (ln.trimStart().startsWith(\"@@\")) {\n if (currentChunk.length) {\n chunkLines.push(currentChunk);\n currentChunk = [];\n }\n } else if (ln && \" +-\".includes(ln[0]!)) {\n currentChunk.push(ln);\n }\n i++;\n }\n if (currentChunk.length) chunkLines.push(currentChunk);\n if (!chunkLines.length) {\n return { error: `补丁更新没有 hunks: ${filePath}` };\n }\n\n // Read original file\n if (!fs.existsSync(target)) {\n return { error: `文件不存在: ${filePath}` };\n }\n const oldLines = fs.readFileSync(target, \"utf-8\").split(\"\\n\");\n // Apply hunks\n const newLines = applyPatchHunks(oldLines, chunkLines, filePath);\n\n let finalResolved = resolved;\n if (moveTo) {\n const moved = resolveWithinRoot(root, moveTo);\n if (!moved) {\n return { error: `路径在工作区之外: ${moveTo}` };\n }\n finalResolved = moved;\n }\n const finalTarget = finalResolved.real;\n if (moveTo && fs.existsSync(finalTarget) && finalTarget !== target) {\n return { error: `文件已存在: ${moveTo}` };\n }\n fs.mkdirSync(path.dirname(finalTarget), { recursive: true });\n fs.writeFileSync(finalTarget, newLines.join(\"\\n\"), \"utf-8\");\n if (moveTo && finalTarget !== target) {\n // 删除源条目本身:父目录取 realpath,最后一段不解析(符号链接删链接)\n const entry = resolveEntryWithinRoot(root, filePath);\n if (!entry) {\n return { error: `路径在工作区之外: ${filePath}` };\n }\n fs.unlinkSync(entry.real);\n deletedFiles.push(path.relative(resolved.root, target));\n }\n changedFiles.push(path.relative(finalResolved.root, finalTarget));\n fileCount++;\n continue;\n }\n\n return { error: `未知的补丁操作: ${line}` };\n }\n\n if (fileCount === 0) {\n return { error: \"补丁不包含任何文件操作\" };\n }\n\n return { changed_files: changedFiles, added_files: addedFiles, deleted_files: deletedFiles, file_count: fileCount };\n };\n}\n\n/** 应用补丁 hunks 到文件行 */\nfunction applyPatchHunks(oldLines: string[], chunks: string[][], path: string): string[] {\n let newLines = [...oldLines];\n let cursor = 0;\n\n for (const chunk of chunks) {\n const oldBlock = chunk.filter((l) => l[0] === \" \" || l[0] === \"-\").map((l) => l.slice(1));\n const newBlock = chunk.filter((l) => l[0] === \" \" || l[0] === \"+\").map((l) => l.slice(1));\n\n if (!oldBlock.length) {\n throw new Error(`补丁 hunk 必须包含上下文或删除行: ${path}`);\n }\n\n // Find match for old block\n const matchStart = findBlockMatch(newLines, oldBlock, cursor);\n if (matchStart === -1) {\n throw new Error(`补丁上下文不匹配: ${path}`);\n }\n\n const matchEnd = matchStart + oldBlock.length;\n newLines.splice(matchStart, oldBlock.length, ...newBlock);\n cursor = matchStart + newBlock.length;\n }\n\n return newLines;\n}\n\n/** 在行数组中查找块匹配 */\nfunction findBlockMatch(lines: string[], block: string[], start: number): number {\n for (let i = start; i <= lines.length - block.length; i++) {\n if (lines.slice(i, i + block.length).every((l, j) => l === block[j]!)) {\n return i;\n }\n }\n return -1;\n}\n\n// ==================== Unified Diff ====================\n\n/** apply_unified_diff — 标准 unified diff (对齐 Python 版) */\nfunction makeApplyUnifiedDiff(workspace: string) {\n const root = path.resolve(workspace);\n\n return function apply_unified_diff(args: Record<string, unknown>) {\n const diff = String(args.diff ?? \"\").replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n if (!diff.trim()) return { error: \"diff 内容不能为空\" };\n\n const patches = parseUnifiedDiff(diff);\n if (!patches.length) return { error: \"未找到 unified diff 文件补丁\" };\n\n const changedFiles: string[] = [];\n const addedFiles: string[] = [];\n const deletedFiles: string[] = [];\n let hunkCount = 0;\n\n for (const patch of patches) {\n const oldIsDevNull = patch.oldPath === \"/dev/null\";\n const newIsDevNull = patch.newPath === \"/dev/null\";\n const targetPath = newIsDevNull ? patch.oldPath : patch.newPath;\n const resolved = resolveWithinRoot(root, targetPath);\n if (!resolved) {\n return { error: `路径在工作区之外: ${targetPath}` };\n }\n const target = resolved.real;\n\n const oldLines = oldIsDevNull ? [] : (fs.existsSync(target) ? fs.readFileSync(target, \"utf-8\").split(\"\\n\") : []);\n const newLines = applyDiffHunks(oldLines, patch.hunks, targetPath);\n hunkCount += patch.hunks.length;\n\n if (newIsDevNull) {\n if (fs.existsSync(target)) {\n if (!fs.statSync(target).isFile()) {\n return { error: `路径不是文件: ${targetPath}` };\n }\n // 删除源条目本身:父目录取 realpath,最后一段不解析\n const entry = resolveEntryWithinRoot(root, targetPath);\n if (!entry) {\n return { error: `路径在工作区之外: ${targetPath}` };\n }\n fs.unlinkSync(entry.real);\n }\n deletedFiles.push(path.relative(resolved.root, target));\n continue;\n }\n\n fs.mkdirSync(path.dirname(target), { recursive: true });\n fs.writeFileSync(target, newLines.join(\"\\n\"), \"utf-8\");\n const rel = path.relative(resolved.root, target);\n changedFiles.push(rel);\n if (oldIsDevNull) addedFiles.push(rel);\n }\n\n return {\n changed_files: changedFiles,\n added_files: addedFiles,\n deleted_files: deletedFiles,\n file_count: patches.length,\n hunk_count: hunkCount,\n };\n };\n}\n\ninterface DiffHunk {\n oldStart: number;\n oldCount: number;\n newStart: number;\n newCount: number;\n lines: string[];\n}\n\ninterface FilePatch {\n oldPath: string;\n newPath: string;\n hunks: DiffHunk[];\n}\n\nfunction parseUnifiedDiff(diff: string): FilePatch[] {\n const lines = diff.split(\"\\n\");\n const patches: FilePatch[] = [];\n let i = 0;\n\n while (i < lines.length) {\n if (!lines[i]!.startsWith(\"--- \")) { i++; continue; }\n\n const oldPath = parseDiffFileLine(lines[i]!, \"---\");\n i++;\n if (i >= lines.length || !lines[i]!.startsWith(\"+++ \")) {\n throw new Error(\"Unified diff 缺少 +++ 文件头\");\n }\n const newPath = parseDiffFileLine(lines[i]!, \"+++\");\n i++;\n\n const hunks: DiffHunk[] = [];\n while (i < lines.length) {\n const current = lines[i]!;\n if (current.startsWith(\"--- \")) break;\n if (!current.startsWith(\"@@ \")) { i++; continue; }\n\n const [oldStart, oldCount, newStart, newCount] = parseHunkHeader(current);\n i++;\n const hunkLines: string[] = [];\n while (i < lines.length) {\n const ln = lines[i]!;\n if (ln.startsWith(\"@@ \") || ln.startsWith(\"--- \")) break;\n if (ln.startsWith(\"\\\\ No newline\")) { i++; continue; }\n if (ln && \" +-\".includes(ln[0]!)) {\n hunkLines.push(ln);\n i++;\n } else {\n break;\n }\n }\n hunks.push({ oldStart, oldCount, newStart, newCount, lines: hunkLines });\n }\n if (hunks.length) {\n patches.push({ oldPath, newPath, hunks });\n }\n }\n\n return patches;\n}\n\nfunction parseDiffFileLine(line: string, marker: string): string {\n let value = line.slice(marker.length).trim();\n if (value.includes(\"\\t\")) value = value.split(\"\\t\")[0]!;\n if (value.startsWith(\"a/\") || value.startsWith(\"b/\")) value = value.slice(2);\n return value;\n}\n\nfunction parseHunkHeader(line: string): [number, number, number, number] {\n const parts = line.split(\" \");\n if (parts.length < 3 || !parts[1]!.startsWith(\"-\") || !parts[2]!.startsWith(\"+\")) {\n throw new Error(`无效的 hunk header: ${line}`);\n }\n const [oldStart, oldCount] = parseRange(parts[1]!.slice(1));\n const [newStart, newCount] = parseRange(parts[2]!.slice(1));\n return [oldStart, oldCount, newStart, newCount];\n}\n\nfunction parseRange(value: string): [number, number] {\n if (!value.includes(\",\")) return [parseInt(value), 1];\n const [start, count] = value.split(\",\");\n return [parseInt(start!), parseInt(count!)];\n}\n\nfunction applyDiffHunks(oldLines: string[], hunks: DiffHunk[], filePath: string): string[] {\n const newLines: string[] = [];\n let cursor = 0;\n\n for (const hunk of hunks) {\n const start = Math.max(hunk.oldStart - 1, 0);\n if (start < cursor) {\n throw new Error(`Overlapping hunks: ${filePath}`);\n }\n newLines.push(...oldLines.slice(cursor, start));\n cursor = start;\n\n for (const line of hunk.lines) {\n const prefix = line[0]!;\n const content = line.slice(1);\n if (prefix === \" \") {\n if (cursor >= oldLines.length || oldLines[cursor] !== content) {\n throw new Error(`Diff context 不匹配: ${filePath}`);\n }\n newLines.push(oldLines[cursor]!);\n cursor++;\n } else if (prefix === \"-\") {\n if (cursor >= oldLines.length || oldLines[cursor] !== content) {\n throw new Error(`Diff removal 不匹配: ${filePath}`);\n }\n cursor++;\n } else if (prefix === \"+\") {\n newLines.push(content);\n }\n }\n }\n newLines.push(...oldLines.slice(cursor));\n return newLines;\n}\n\n/** 注册 diff 工具 */\nexport function registerDiffTools(registry: ToolRegistry, workspace: string): void {\n const applyPatchFunc = makeApplyPatch(workspace);\n registry.register(\n (args) => applyPatchFunc(args),\n { category: \"filesystem\", riskLevel: \"medium\", capabilities: [\"edit_file\"], requiresApproval: true },\n {\n type: \"function\",\n function: {\n name: \"apply_patch\",\n description: \"应用 Codex 风格的补丁信封(使用 *** Begin Patch / *** End Patch 段落)。支持 Add File、Delete File、Update File 操作。\",\n parameters: {\n type: \"object\",\n properties: {\n patch: { type: \"string\", description: \"补丁内容 (必须包含 *** Begin Patch 和 *** End Patch)\" },\n },\n required: [\"patch\"],\n },\n },\n },\n );\n\n const applyUnifiedDiffFunc = makeApplyUnifiedDiff(workspace);\n registry.register(\n (args) => applyUnifiedDiffFunc(args),\n { category: \"filesystem\", riskLevel: \"medium\", capabilities: [\"edit_file\"], requiresApproval: true },\n {\n type: \"function\",\n function: {\n name: \"apply_unified_diff\",\n description: \"应用标准 unified diff 补丁。支持 --- / +++ 文件头和 @@ hunk headers。\",\n parameters: {\n type: \"object\",\n properties: {\n diff: { type: \"string\", description: \"unified diff 格式的补丁内容\" },\n },\n required: [\"diff\"],\n },\n },\n },\n );\n}\n","/**\n * 路径安全辅助函数\n *\n * 判断目标路径是否真正位于工作区根目录之内。\n *\n * 仅做字符串比较(path.relative)无法防御符号链接逃逸:\n * 工作区内若存在指向外部的 symlink,`workspace/link/secret.txt`\n * 的字符串前缀合法,但实际读写发生在链接目标(工作区外)。\n *\n * 因此这里用 fs.realpathSync 解析符号链接后再与根目录的 realpath\n * 比较。目标尚不存在(如 write_file 新建文件)时,向上回溯到最近\n * 已存在的祖先目录解析,再拼回剩余路径段。\n *\n * 校验通过时把解析后的真实路径一并返回:调用方必须用它(而非原始\n * 相对路径)做实际 fs 操作,否则「校验后、操作前」路径组件被替换为\n * 指向外部的 symlink 仍会逃逸(TOCTOU)。\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\n/**\n * 判断 `path.relative(root, target)` 的结果是否落在 root 之内。\n *\n * 逃逸必然恰好是 `..` 或以 `..` + 分隔符开头,不能用 `startsWith(\"..\")`:\n * 工作区里名为 `..foo` 的合法条目会被误判为逃逸而拒绝。\n * 跨盘符时(Windows)path.relative 返回绝对路径,一并拒绝。\n */\nfunction isInsideRelative(rel: string): boolean {\n return (\n !path.isAbsolute(rel) && rel !== \"..\" && !rel.startsWith(`..${path.sep}`)\n );\n}\n\n/** 是否为文件系统根(\"/\"、\"C:\\\\\") */\nfunction isFilesystemRoot(p: string): boolean {\n return path.parse(p).root === p;\n}\n\n/** 校验通过的路径:root 为工作区根的真实路径,real 为目标的真实路径 */\nexport interface ResolvedPath {\n root: string;\n real: string;\n}\n\n/**\n * 校验 target(相对 workspace 的路径)是否位于 workspace 内,并返回\n * 解析符号链接后的真实路径;不在工作区内时返回 null。\n *\n * - 解析符号链接:路径中任何 symlink 组件都会先 realpath 再校验;\n * broken symlink(存在但无法解析)一律拒绝。\n * - 目标不存在时(写新文件):回溯到最近存在的祖先做 realpath,\n * 再拼接剩余段,仍能捕获祖先链中的 symlink 逃逸。\n * - 工作区根若就是文件系统根(`WORKSPACE=/`):此时任何绝对路径都在\n * 其内,等同没有沙箱,直接判为越界。\n */\nexport function resolveWithinRoot(\n workspace: string,\n target: string,\n): ResolvedPath | null {\n const abs = path.resolve(workspace, target);\n\n let rootReal: string;\n try {\n rootReal = fs.realpathSync(workspace);\n } catch {\n // 根目录不存在(如权限引擎尚未初始化工作区):\n // 没有实际目录结构即无 symlink 逃逸面,退化为纯字符串路径比较。\n const rootAbs = path.resolve(workspace);\n if (!isInsideRelative(path.relative(rootAbs, abs))) return null;\n return { root: rootAbs, real: abs };\n }\n if (isFilesystemRoot(rootReal)) return null;\n\n let real: string;\n try {\n real = fs.realpathSync(abs);\n } catch {\n // abs 不存在:向上回溯找最近存在的祖先\n let probe = abs;\n const tail: string[] = [];\n for (;;) {\n // 组件存在(含 broken symlink)但 realpath 失败 → 无法解析,拒绝\n const exists = fs.existsSync(probe) || fs.lstatSync(probe, { throwIfNoEntry: false }) !== undefined;\n if (exists) {\n try {\n real = path.join(fs.realpathSync(probe), ...tail);\n break;\n } catch {\n return null;\n }\n }\n tail.unshift(path.basename(probe));\n const parent = path.dirname(probe);\n if (parent === probe) return null; // 已到文件系统根\n probe = parent;\n }\n }\n\n if (!isInsideRelative(path.relative(rootReal, real))) return null;\n return { root: rootReal, real };\n}\n\n/**\n * 判断 target 是否安全地位于 workspace 内(不需要真实路径时用这个)。\n *\n * 注意:删除类操作不应据此改用 realpath 去 unlink —— 那会删掉符号链接\n * 指向的目标而非链接本身;这类操作请用 resolveEntryWithinRoot。\n */\nexport function isWithinRoot(workspace: string, target: string): boolean {\n return resolveWithinRoot(workspace, target) !== null;\n}\n\n/**\n * 解析「目录项本身」的路径:父目录取 realpath,最后一段保持原样。\n *\n * 供 unlink / rename 等作用于条目自身的操作用:父目录不可被 symlink\n * 替换,而最后一段若是符号链接,被操作的是链接本身,不会被跟随。\n */\nexport function resolveEntryWithinRoot(\n workspace: string,\n target: string,\n): ResolvedPath | null {\n const abs = path.resolve(workspace, target);\n const parent = resolveWithinRoot(workspace, path.dirname(abs));\n if (!parent) return null;\n return { root: parent.root, real: path.join(parent.real, path.basename(abs)) };\n}\n"],"mappings":"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","names":["fs","path","path"]}
|
|
1
|
+
{"version":3,"sources":["../../src/tools/diff.ts","../../src/tools/pathSafety.ts"],"sourcesContent":["import { ToolRegistry } from \"./ToolRegistry.js\";\n\n/**\n * apply_patch + apply_unified_diff\n * 对齐 Python 版 aisuite.toolkits.files.FileToolkit\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { resolveEntryWithinRoot, resolveWithinRoot } from \"./pathSafety.js\";\nimport type { ConflictGuard } from \"./conflictGuard.js\";\n\n/** apply_patch — Codex 风格补丁 (对齐 Python 版) */\nfunction makeApplyPatch(workspace: string, guard?: ConflictGuard) {\n const root = path.resolve(workspace);\n\n return function apply_patch(args: Record<string, unknown>) {\n const patch = String(args.patch ?? \"\");\n if (!patch.trim()) return { error: \"补丁内容不能为空\" };\n\n // 归一化 CRLF,并去掉尾部空白:否则 `*** End Patch\\n` 会因最后一项是空串\n // 而被误判为「补丁必须以 *** End Patch 结尾」。\n const lines = patch\n .replace(/\\r\\n/g, \"\\n\")\n .replace(/\\r/g, \"\\n\")\n .trimEnd()\n .split(\"\\n\");\n if (!lines[0]?.trim().startsWith(\"*** Begin Patch\")) {\n return { error: \"补丁必须以 '*** Begin Patch' 开头\" };\n }\n if (!lines[lines.length - 1]?.trim().startsWith(\"*** End Patch\")) {\n return { error: \"补丁必须以 '*** End Patch' 结尾\" };\n }\n\n const changedFiles: string[] = [];\n const addedFiles: string[] = [];\n const deletedFiles: string[] = [];\n let fileCount = 0;\n\n let i = 1;\n while (i < lines.length - 1) {\n const line = lines[i].trim();\n if (!line) { i++; continue; }\n\n if (line.startsWith(\"*** Add File: \")) {\n const filePath = line.slice(\"*** Add File: \".length).trim();\n const resolved = resolveWithinRoot(root, filePath);\n if (!resolved) {\n return { error: `路径在工作区之外: ${filePath}` };\n }\n const target = resolved.real;\n i++;\n const contentLines: string[] = [];\n while (i < lines.length - 1 && !lines[i].trimStart().startsWith(\"*** \")) {\n const current = lines[i];\n if (!current.startsWith(\"+\")) {\n return { error: `无效的 add-file 补丁行: ${current}` };\n }\n contentLines.push(current.slice(1));\n i++;\n }\n if (fs.existsSync(target)) {\n return { error: `文件已存在: ${filePath}` };\n }\n fs.mkdirSync(path.dirname(target), { recursive: true });\n fs.writeFileSync(target, contentLines.join(\"\\n\") + \"\\n\", \"utf-8\");\n const rel = path.relative(resolved.root, target);\n changedFiles.push(rel);\n addedFiles.push(rel);\n fileCount++;\n continue;\n }\n\n if (line.startsWith(\"*** Delete File: \")) {\n const filePath = line.slice(\"*** Delete File: \".length).trim();\n // 删除作用于条目本身:父目录取 realpath,最后一段不解析\n const entry = resolveEntryWithinRoot(root, filePath);\n if (!entry) {\n return { error: `路径在工作区之外: ${filePath}` };\n }\n const target = entry.real;\n if (!fs.existsSync(target)) {\n return { error: `文件不存在: ${filePath}` };\n }\n // 删除同样是破坏性操作:本会话读过之后被别人改过 → 先让人确认\n const delConflict = guard?.checkWrite(target, filePath);\n if (delConflict) return { error: delConflict.message };\n fs.unlinkSync(target);\n deletedFiles.push(path.relative(entry.root, target));\n fileCount++;\n i++;\n continue;\n }\n\n if (line.startsWith(\"*** Update File: \")) {\n const filePath = line.slice(\"*** Update File: \".length).trim();\n const resolved = resolveWithinRoot(root, filePath);\n if (!resolved) {\n return { error: `路径在工作区之外: ${filePath}` };\n }\n const target = resolved.real;\n i++;\n let moveTo: string | null = null;\n if (i < lines.length - 1 && lines[i].trimStart().startsWith(\"*** Move to: \")) {\n moveTo = lines[i].trim().slice(\"*** Move to: \".length).trim();\n i++;\n }\n\n const chunkLines: string[][] = [];\n let currentChunk: string[] = [];\n while (i < lines.length - 1 && !lines[i].trimStart().startsWith(\"*** \")) {\n const ln = lines[i];\n if (ln.trimStart().startsWith(\"@@\")) {\n if (currentChunk.length) {\n chunkLines.push(currentChunk);\n currentChunk = [];\n }\n } else if (ln && \" +-\".includes(ln[0]!)) {\n currentChunk.push(ln);\n }\n i++;\n }\n if (currentChunk.length) chunkLines.push(currentChunk);\n if (!chunkLines.length) {\n return { error: `补丁更新没有 hunks: ${filePath}` };\n }\n\n // Read original file\n if (!fs.existsSync(target)) {\n return { error: `文件不存在: ${filePath}` };\n }\n const oldLines = fs.readFileSync(target, \"utf-8\").split(\"\\n\");\n // 补丁是「基于读到的版本」生成的:先确认这个版本没被别人改过\n const conflict = guard?.checkWrite(target, filePath);\n if (conflict) return { error: conflict.message };\n // Apply hunks\n const newLines = applyPatchHunks(oldLines, chunkLines, filePath);\n\n let finalResolved = resolved;\n if (moveTo) {\n const moved = resolveWithinRoot(root, moveTo);\n if (!moved) {\n return { error: `路径在工作区之外: ${moveTo}` };\n }\n finalResolved = moved;\n }\n const finalTarget = finalResolved.real;\n if (moveTo && fs.existsSync(finalTarget) && finalTarget !== target) {\n return { error: `文件已存在: ${moveTo}` };\n }\n fs.mkdirSync(path.dirname(finalTarget), { recursive: true });\n fs.writeFileSync(finalTarget, newLines.join(\"\\n\"), \"utf-8\");\n guard?.noteWrite(finalTarget, newLines.join(\"\\n\"));\n if (moveTo && finalTarget !== target) {\n // 删除源条目本身:父目录取 realpath,最后一段不解析(符号链接删链接)\n const entry = resolveEntryWithinRoot(root, filePath);\n if (!entry) {\n return { error: `路径在工作区之外: ${filePath}` };\n }\n fs.unlinkSync(entry.real);\n deletedFiles.push(path.relative(resolved.root, target));\n }\n changedFiles.push(path.relative(finalResolved.root, finalTarget));\n fileCount++;\n continue;\n }\n\n return { error: `未知的补丁操作: ${line}` };\n }\n\n if (fileCount === 0) {\n return { error: \"补丁不包含任何文件操作\" };\n }\n\n return { changed_files: changedFiles, added_files: addedFiles, deleted_files: deletedFiles, file_count: fileCount };\n };\n}\n\n/** 应用补丁 hunks 到文件行 */\nfunction applyPatchHunks(oldLines: string[], chunks: string[][], path: string): string[] {\n let newLines = [...oldLines];\n let cursor = 0;\n\n for (const chunk of chunks) {\n const oldBlock = chunk.filter((l) => l[0] === \" \" || l[0] === \"-\").map((l) => l.slice(1));\n const newBlock = chunk.filter((l) => l[0] === \" \" || l[0] === \"+\").map((l) => l.slice(1));\n\n if (!oldBlock.length) {\n throw new Error(`补丁 hunk 必须包含上下文或删除行: ${path}`);\n }\n\n // Find match for old block\n const matchStart = findBlockMatch(newLines, oldBlock, cursor);\n if (matchStart === -1) {\n throw new Error(`补丁上下文不匹配: ${path}`);\n }\n\n const matchEnd = matchStart + oldBlock.length;\n newLines.splice(matchStart, oldBlock.length, ...newBlock);\n cursor = matchStart + newBlock.length;\n }\n\n return newLines;\n}\n\n/** 在行数组中查找块匹配 */\nfunction findBlockMatch(lines: string[], block: string[], start: number): number {\n for (let i = start; i <= lines.length - block.length; i++) {\n if (lines.slice(i, i + block.length).every((l, j) => l === block[j]!)) {\n return i;\n }\n }\n return -1;\n}\n\n// ==================== Unified Diff ====================\n\n/** apply_unified_diff — 标准 unified diff (对齐 Python 版) */\nfunction makeApplyUnifiedDiff(workspace: string, guard?: ConflictGuard) {\n const root = path.resolve(workspace);\n\n return function apply_unified_diff(args: Record<string, unknown>) {\n const diff = String(args.diff ?? \"\").replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n if (!diff.trim()) return { error: \"diff 内容不能为空\" };\n\n const patches = parseUnifiedDiff(diff);\n if (!patches.length) return { error: \"未找到 unified diff 文件补丁\" };\n\n const changedFiles: string[] = [];\n const addedFiles: string[] = [];\n const deletedFiles: string[] = [];\n let hunkCount = 0;\n\n for (const patch of patches) {\n const oldIsDevNull = patch.oldPath === \"/dev/null\";\n const newIsDevNull = patch.newPath === \"/dev/null\";\n const targetPath = newIsDevNull ? patch.oldPath : patch.newPath;\n const resolved = resolveWithinRoot(root, targetPath);\n if (!resolved) {\n return { error: `路径在工作区之外: ${targetPath}` };\n }\n const target = resolved.real;\n\n const oldLines = oldIsDevNull ? [] : (fs.existsSync(target) ? fs.readFileSync(target, \"utf-8\").split(\"\\n\") : []);\n // diff 基于某个版本生成:不校验就应用会把别人的改动覆盖掉\n if (!oldIsDevNull) {\n const conflict = guard?.checkWrite(target, targetPath);\n if (conflict) return { error: conflict.message };\n }\n const newLines = applyDiffHunks(oldLines, patch.hunks, targetPath);\n hunkCount += patch.hunks.length;\n\n if (newIsDevNull) {\n if (fs.existsSync(target)) {\n if (!fs.statSync(target).isFile()) {\n return { error: `路径不是文件: ${targetPath}` };\n }\n // 删除源条目本身:父目录取 realpath,最后一段不解析\n const entry = resolveEntryWithinRoot(root, targetPath);\n if (!entry) {\n return { error: `路径在工作区之外: ${targetPath}` };\n }\n fs.unlinkSync(entry.real);\n }\n deletedFiles.push(path.relative(resolved.root, target));\n continue;\n }\n\n fs.mkdirSync(path.dirname(target), { recursive: true });\n const written = newLines.join(\"\\n\");\n fs.writeFileSync(target, written, \"utf-8\");\n guard?.noteWrite(target, written);\n const rel = path.relative(resolved.root, target);\n changedFiles.push(rel);\n if (oldIsDevNull) addedFiles.push(rel);\n }\n\n return {\n changed_files: changedFiles,\n added_files: addedFiles,\n deleted_files: deletedFiles,\n file_count: patches.length,\n hunk_count: hunkCount,\n };\n };\n}\n\ninterface DiffHunk {\n oldStart: number;\n oldCount: number;\n newStart: number;\n newCount: number;\n lines: string[];\n}\n\ninterface FilePatch {\n oldPath: string;\n newPath: string;\n hunks: DiffHunk[];\n}\n\nfunction parseUnifiedDiff(diff: string): FilePatch[] {\n const lines = diff.split(\"\\n\");\n const patches: FilePatch[] = [];\n let i = 0;\n\n while (i < lines.length) {\n if (!lines[i]!.startsWith(\"--- \")) { i++; continue; }\n\n const oldPath = parseDiffFileLine(lines[i]!, \"---\");\n i++;\n if (i >= lines.length || !lines[i]!.startsWith(\"+++ \")) {\n throw new Error(\"Unified diff 缺少 +++ 文件头\");\n }\n const newPath = parseDiffFileLine(lines[i]!, \"+++\");\n i++;\n\n const hunks: DiffHunk[] = [];\n while (i < lines.length) {\n const current = lines[i]!;\n if (current.startsWith(\"--- \")) break;\n if (!current.startsWith(\"@@ \")) { i++; continue; }\n\n const [oldStart, oldCount, newStart, newCount] = parseHunkHeader(current);\n i++;\n const hunkLines: string[] = [];\n while (i < lines.length) {\n const ln = lines[i]!;\n if (ln.startsWith(\"@@ \") || ln.startsWith(\"--- \")) break;\n if (ln.startsWith(\"\\\\ No newline\")) { i++; continue; }\n if (ln && \" +-\".includes(ln[0]!)) {\n hunkLines.push(ln);\n i++;\n } else {\n break;\n }\n }\n hunks.push({ oldStart, oldCount, newStart, newCount, lines: hunkLines });\n }\n if (hunks.length) {\n patches.push({ oldPath, newPath, hunks });\n }\n }\n\n return patches;\n}\n\nfunction parseDiffFileLine(line: string, marker: string): string {\n let value = line.slice(marker.length).trim();\n if (value.includes(\"\\t\")) value = value.split(\"\\t\")[0]!;\n if (value.startsWith(\"a/\") || value.startsWith(\"b/\")) value = value.slice(2);\n return value;\n}\n\nfunction parseHunkHeader(line: string): [number, number, number, number] {\n const parts = line.split(\" \");\n if (parts.length < 3 || !parts[1]!.startsWith(\"-\") || !parts[2]!.startsWith(\"+\")) {\n throw new Error(`无效的 hunk header: ${line}`);\n }\n const [oldStart, oldCount] = parseRange(parts[1]!.slice(1));\n const [newStart, newCount] = parseRange(parts[2]!.slice(1));\n return [oldStart, oldCount, newStart, newCount];\n}\n\nfunction parseRange(value: string): [number, number] {\n if (!value.includes(\",\")) return [parseInt(value), 1];\n const [start, count] = value.split(\",\");\n return [parseInt(start!), parseInt(count!)];\n}\n\nfunction applyDiffHunks(oldLines: string[], hunks: DiffHunk[], filePath: string): string[] {\n const newLines: string[] = [];\n let cursor = 0;\n\n for (const hunk of hunks) {\n const start = Math.max(hunk.oldStart - 1, 0);\n if (start < cursor) {\n throw new Error(`Overlapping hunks: ${filePath}`);\n }\n newLines.push(...oldLines.slice(cursor, start));\n cursor = start;\n\n for (const line of hunk.lines) {\n const prefix = line[0]!;\n const content = line.slice(1);\n if (prefix === \" \") {\n if (cursor >= oldLines.length || oldLines[cursor] !== content) {\n throw new Error(`Diff context 不匹配: ${filePath}`);\n }\n newLines.push(oldLines[cursor]!);\n cursor++;\n } else if (prefix === \"-\") {\n if (cursor >= oldLines.length || oldLines[cursor] !== content) {\n throw new Error(`Diff removal 不匹配: ${filePath}`);\n }\n cursor++;\n } else if (prefix === \"+\") {\n newLines.push(content);\n }\n }\n }\n newLines.push(...oldLines.slice(cursor));\n return newLines;\n}\n\n/** 注册 diff 工具 */\nexport function registerDiffTools(\n registry: ToolRegistry,\n workspace: string,\n guard?: ConflictGuard,\n): void {\n const applyPatchFunc = makeApplyPatch(workspace, guard);\n registry.register(\n (args) => applyPatchFunc(args),\n { category: \"filesystem\", riskLevel: \"medium\", capabilities: [\"edit_file\"], requiresApproval: true },\n {\n type: \"function\",\n function: {\n name: \"apply_patch\",\n description: \"应用 Codex 风格的补丁信封(使用 *** Begin Patch / *** End Patch 段落)。支持 Add File、Delete File、Update File 操作。\",\n parameters: {\n type: \"object\",\n properties: {\n patch: { type: \"string\", description: \"补丁内容 (必须包含 *** Begin Patch 和 *** End Patch)\" },\n },\n required: [\"patch\"],\n },\n },\n },\n );\n\n const applyUnifiedDiffFunc = makeApplyUnifiedDiff(workspace, guard);\n registry.register(\n (args) => applyUnifiedDiffFunc(args),\n { category: \"filesystem\", riskLevel: \"medium\", capabilities: [\"edit_file\"], requiresApproval: true },\n {\n type: \"function\",\n function: {\n name: \"apply_unified_diff\",\n description: \"应用标准 unified diff 补丁。支持 --- / +++ 文件头和 @@ hunk headers。\",\n parameters: {\n type: \"object\",\n properties: {\n diff: { type: \"string\", description: \"unified diff 格式的补丁内容\" },\n },\n required: [\"diff\"],\n },\n },\n },\n );\n}\n","/**\n * 路径安全辅助函数\n *\n * 判断目标路径是否真正位于工作区根目录之内。\n *\n * 仅做字符串比较(path.relative)无法防御符号链接逃逸:\n * 工作区内若存在指向外部的 symlink,`workspace/link/secret.txt`\n * 的字符串前缀合法,但实际读写发生在链接目标(工作区外)。\n *\n * 因此这里用 fs.realpathSync 解析符号链接后再与根目录的 realpath\n * 比较。目标尚不存在(如 write_file 新建文件)时,向上回溯到最近\n * 已存在的祖先目录解析,再拼回剩余路径段。\n *\n * 校验通过时把解析后的真实路径一并返回:调用方必须用它(而非原始\n * 相对路径)做实际 fs 操作,否则「校验后、操作前」路径组件被替换为\n * 指向外部的 symlink 仍会逃逸(TOCTOU)。\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\n/**\n * 判断 `path.relative(root, target)` 的结果是否落在 root 之内。\n *\n * 逃逸必然恰好是 `..` 或以 `..` + 分隔符开头,不能用 `startsWith(\"..\")`:\n * 工作区里名为 `..foo` 的合法条目会被误判为逃逸而拒绝。\n * 跨盘符时(Windows)path.relative 返回绝对路径,一并拒绝。\n *\n * 导出供自行做 `path.relative` 校验的调用方复用(如目录浏览新建目录、\n * git 面板的 repo / path 参数),避免各处重复写 `startsWith(\"..\")`。\n */\nexport function isInsideRelative(rel: string): boolean {\n return (\n !path.isAbsolute(rel) && rel !== \"..\" && !rel.startsWith(`..${path.sep}`)\n );\n}\n\n/** 是否为文件系统根(\"/\"、\"C:\\\\\") */\nfunction isFilesystemRoot(p: string): boolean {\n return path.parse(p).root === p;\n}\n\n/** 校验通过的路径:root 为工作区根的真实路径,real 为目标的真实路径 */\nexport interface ResolvedPath {\n root: string;\n real: string;\n}\n\n/**\n * 校验 target(相对 workspace 的路径)是否位于 workspace 内,并返回\n * 解析符号链接后的真实路径;不在工作区内时返回 null。\n *\n * - 解析符号链接:路径中任何 symlink 组件都会先 realpath 再校验;\n * broken symlink(存在但无法解析)一律拒绝。\n * - 目标不存在时(写新文件):回溯到最近存在的祖先做 realpath,\n * 再拼接剩余段,仍能捕获祖先链中的 symlink 逃逸。\n * - 工作区根若就是文件系统根(`WORKSPACE=/`):此时任何绝对路径都在\n * 其内,等同没有沙箱,直接判为越界。\n */\nexport function resolveWithinRoot(\n workspace: string,\n target: string,\n): ResolvedPath | null {\n const abs = path.resolve(workspace, target);\n\n let rootReal: string;\n try {\n rootReal = fs.realpathSync(workspace);\n } catch {\n // 根目录不存在(如权限引擎尚未初始化工作区):\n // 没有实际目录结构即无 symlink 逃逸面,退化为纯字符串路径比较。\n const rootAbs = path.resolve(workspace);\n if (!isInsideRelative(path.relative(rootAbs, abs))) return null;\n return { root: rootAbs, real: abs };\n }\n if (isFilesystemRoot(rootReal)) return null;\n\n let real: string;\n try {\n real = fs.realpathSync(abs);\n } catch {\n // abs 不存在:向上回溯找最近存在的祖先\n let probe = abs;\n const tail: string[] = [];\n for (;;) {\n // 组件存在(含 broken symlink)但 realpath 失败 → 无法解析,拒绝\n const exists = fs.existsSync(probe) || fs.lstatSync(probe, { throwIfNoEntry: false }) !== undefined;\n if (exists) {\n try {\n real = path.join(fs.realpathSync(probe), ...tail);\n break;\n } catch {\n return null;\n }\n }\n tail.unshift(path.basename(probe));\n const parent = path.dirname(probe);\n if (parent === probe) return null; // 已到文件系统根\n probe = parent;\n }\n }\n\n if (!isInsideRelative(path.relative(rootReal, real))) return null;\n return { root: rootReal, real };\n}\n\n/**\n * 判断 target 是否安全地位于 workspace 内(不需要真实路径时用这个)。\n *\n * 注意:删除类操作不应据此改用 realpath 去 unlink —— 那会删掉符号链接\n * 指向的目标而非链接本身;这类操作请用 resolveEntryWithinRoot。\n */\nexport function isWithinRoot(workspace: string, target: string): boolean {\n return resolveWithinRoot(workspace, target) !== null;\n}\n\n/**\n * 解析「目录项本身」的路径:父目录取 realpath,最后一段保持原样。\n *\n * 供 unlink / rename 等作用于条目自身的操作用:父目录不可被 symlink\n * 替换,而最后一段若是符号链接,被操作的是链接本身,不会被跟随。\n */\nexport function resolveEntryWithinRoot(\n workspace: string,\n target: string,\n): ResolvedPath | null {\n const abs = path.resolve(workspace, target);\n const parent = resolveWithinRoot(workspace, path.dirname(abs));\n if (!parent) return null;\n return { root: parent.root, real: path.join(parent.real, path.basename(abs)) };\n}\n"],"mappings":"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","names":["fs","path","path"]}
|
package/dist/tools/diff.d.cts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { T as ToolRegistry } from '../ToolRegistry-
|
|
1
|
+
import { T as ToolRegistry } from '../ToolRegistry-DouDvn9E.cjs';
|
|
2
|
+
import { C as ConflictGuard } from '../conflictGuard-BBOOIxxK.cjs';
|
|
3
|
+
import '../types-BYyXWWPW.cjs';
|
|
4
|
+
import '@zqagent/shared/model-capabilities';
|
|
2
5
|
|
|
3
6
|
/** 注册 diff 工具 */
|
|
4
|
-
declare function registerDiffTools(registry: ToolRegistry, workspace: string): void;
|
|
7
|
+
declare function registerDiffTools(registry: ToolRegistry, workspace: string, guard?: ConflictGuard): void;
|
|
5
8
|
|
|
6
9
|
export { registerDiffTools };
|
package/dist/tools/diff.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { T as ToolRegistry } from '../ToolRegistry-
|
|
1
|
+
import { T as ToolRegistry } from '../ToolRegistry-Cy0fQxrb.js';
|
|
2
|
+
import { C as ConflictGuard } from '../conflictGuard-BBOOIxxK.js';
|
|
3
|
+
import '../types-BYyXWWPW.js';
|
|
4
|
+
import '@zqagent/shared/model-capabilities';
|
|
2
5
|
|
|
3
6
|
/** 注册 diff 工具 */
|
|
4
|
-
declare function registerDiffTools(registry: ToolRegistry, workspace: string): void;
|
|
7
|
+
declare function registerDiffTools(registry: ToolRegistry, workspace: string, guard?: ConflictGuard): void;
|
|
5
8
|
|
|
6
9
|
export { registerDiffTools };
|
package/dist/tools/diff.js
CHANGED
|
@@ -55,7 +55,7 @@ function resolveEntryWithinRoot(workspace, target) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// src/tools/diff.ts
|
|
58
|
-
function makeApplyPatch(workspace) {
|
|
58
|
+
function makeApplyPatch(workspace, guard) {
|
|
59
59
|
const root = path2.resolve(workspace);
|
|
60
60
|
return function apply_patch(args) {
|
|
61
61
|
const patch = String(args.patch ?? "");
|
|
@@ -116,6 +116,8 @@ function makeApplyPatch(workspace) {
|
|
|
116
116
|
if (!fs2.existsSync(target)) {
|
|
117
117
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
118
118
|
}
|
|
119
|
+
const delConflict = guard?.checkWrite(target, filePath);
|
|
120
|
+
if (delConflict) return { error: delConflict.message };
|
|
119
121
|
fs2.unlinkSync(target);
|
|
120
122
|
deletedFiles.push(path2.relative(entry.root, target));
|
|
121
123
|
fileCount++;
|
|
@@ -157,6 +159,8 @@ function makeApplyPatch(workspace) {
|
|
|
157
159
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
158
160
|
}
|
|
159
161
|
const oldLines = fs2.readFileSync(target, "utf-8").split("\n");
|
|
162
|
+
const conflict = guard?.checkWrite(target, filePath);
|
|
163
|
+
if (conflict) return { error: conflict.message };
|
|
160
164
|
const newLines = applyPatchHunks(oldLines, chunkLines, filePath);
|
|
161
165
|
let finalResolved = resolved;
|
|
162
166
|
if (moveTo) {
|
|
@@ -172,6 +176,7 @@ function makeApplyPatch(workspace) {
|
|
|
172
176
|
}
|
|
173
177
|
fs2.mkdirSync(path2.dirname(finalTarget), { recursive: true });
|
|
174
178
|
fs2.writeFileSync(finalTarget, newLines.join("\n"), "utf-8");
|
|
179
|
+
guard?.noteWrite(finalTarget, newLines.join("\n"));
|
|
175
180
|
if (moveTo && finalTarget !== target) {
|
|
176
181
|
const entry = resolveEntryWithinRoot(root, filePath);
|
|
177
182
|
if (!entry) {
|
|
@@ -219,7 +224,7 @@ function findBlockMatch(lines, block, start) {
|
|
|
219
224
|
}
|
|
220
225
|
return -1;
|
|
221
226
|
}
|
|
222
|
-
function makeApplyUnifiedDiff(workspace) {
|
|
227
|
+
function makeApplyUnifiedDiff(workspace, guard) {
|
|
223
228
|
const root = path2.resolve(workspace);
|
|
224
229
|
return function apply_unified_diff(args) {
|
|
225
230
|
const diff = String(args.diff ?? "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
@@ -240,6 +245,10 @@ function makeApplyUnifiedDiff(workspace) {
|
|
|
240
245
|
}
|
|
241
246
|
const target = resolved.real;
|
|
242
247
|
const oldLines = oldIsDevNull ? [] : fs2.existsSync(target) ? fs2.readFileSync(target, "utf-8").split("\n") : [];
|
|
248
|
+
if (!oldIsDevNull) {
|
|
249
|
+
const conflict = guard?.checkWrite(target, targetPath);
|
|
250
|
+
if (conflict) return { error: conflict.message };
|
|
251
|
+
}
|
|
243
252
|
const newLines = applyDiffHunks(oldLines, patch.hunks, targetPath);
|
|
244
253
|
hunkCount += patch.hunks.length;
|
|
245
254
|
if (newIsDevNull) {
|
|
@@ -257,7 +266,9 @@ function makeApplyUnifiedDiff(workspace) {
|
|
|
257
266
|
continue;
|
|
258
267
|
}
|
|
259
268
|
fs2.mkdirSync(path2.dirname(target), { recursive: true });
|
|
260
|
-
|
|
269
|
+
const written = newLines.join("\n");
|
|
270
|
+
fs2.writeFileSync(target, written, "utf-8");
|
|
271
|
+
guard?.noteWrite(target, written);
|
|
261
272
|
const rel = path2.relative(resolved.root, target);
|
|
262
273
|
changedFiles.push(rel);
|
|
263
274
|
if (oldIsDevNull) addedFiles.push(rel);
|
|
@@ -372,8 +383,8 @@ function applyDiffHunks(oldLines, hunks, filePath) {
|
|
|
372
383
|
newLines.push(...oldLines.slice(cursor));
|
|
373
384
|
return newLines;
|
|
374
385
|
}
|
|
375
|
-
function registerDiffTools(registry, workspace) {
|
|
376
|
-
const applyPatchFunc = makeApplyPatch(workspace);
|
|
386
|
+
function registerDiffTools(registry, workspace, guard) {
|
|
387
|
+
const applyPatchFunc = makeApplyPatch(workspace, guard);
|
|
377
388
|
registry.register(
|
|
378
389
|
(args) => applyPatchFunc(args),
|
|
379
390
|
{ category: "filesystem", riskLevel: "medium", capabilities: ["edit_file"], requiresApproval: true },
|
|
@@ -392,7 +403,7 @@ function registerDiffTools(registry, workspace) {
|
|
|
392
403
|
}
|
|
393
404
|
}
|
|
394
405
|
);
|
|
395
|
-
const applyUnifiedDiffFunc = makeApplyUnifiedDiff(workspace);
|
|
406
|
+
const applyUnifiedDiffFunc = makeApplyUnifiedDiff(workspace, guard);
|
|
396
407
|
registry.register(
|
|
397
408
|
(args) => applyUnifiedDiffFunc(args),
|
|
398
409
|
{ category: "filesystem", riskLevel: "medium", capabilities: ["edit_file"], requiresApproval: true },
|