@zqagent/core 0.2.5 → 0.2.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 +3 -0
- package/README.oss.en.md +5 -0
- package/README.oss.md +5 -0
- package/README.oss.zh-TW.md +5 -0
- package/dist/cli/index.cjs +1161 -600
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +1167 -606
- package/dist/cli/index.js.map +1 -1
- package/dist/{explore-D4lCKXbA.d.ts → explore-B_bIPzmB.d.ts} +49 -1
- package/dist/{explore-Dj1j_GyN.d.cts → explore-COqHKDoK.d.cts} +49 -1
- package/dist/index.cjs +654 -258
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +654 -258
- package/dist/index.js.map +1 -1
- package/dist/server/index.cjs +1000 -443
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +1006 -449
- package/dist/server/index.js.map +1 -1
- package/dist/tools/explore.cjs +528 -141
- package/dist/tools/explore.cjs.map +1 -1
- package/dist/tools/explore.d.cts +1 -1
- package/dist/tools/explore.d.ts +1 -1
- package/dist/tools/explore.js +528 -141
- package/dist/tools/explore.js.map +1 -1
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -572,8 +572,8 @@ var import_config = require("dotenv/config");
|
|
|
572
572
|
|
|
573
573
|
// src/server/runtime.ts
|
|
574
574
|
init_cjs_shims();
|
|
575
|
-
var
|
|
576
|
-
var
|
|
575
|
+
var fs29 = __toESM(require("fs"), 1);
|
|
576
|
+
var path33 = __toESM(require("path"), 1);
|
|
577
577
|
|
|
578
578
|
// src/server/store.ts
|
|
579
579
|
init_cjs_shims();
|
|
@@ -1062,6 +1062,10 @@ function configDir() {
|
|
|
1062
1062
|
const base = process.env.XDG_CONFIG_HOME || (IS_WINDOWS ? process.env.APPDATA : void 0) || path2.join(homeDir(), ".config");
|
|
1063
1063
|
return path2.join(base, APP_DIR_NAME);
|
|
1064
1064
|
}
|
|
1065
|
+
function stateDir() {
|
|
1066
|
+
const base = process.env.XDG_STATE_HOME || (IS_WINDOWS ? process.env.LOCALAPPDATA : void 0) || path2.join(homeDir(), ".local", "state");
|
|
1067
|
+
return path2.join(base, APP_DIR_NAME);
|
|
1068
|
+
}
|
|
1065
1069
|
|
|
1066
1070
|
// ../shared/src/llm-presets.ts
|
|
1067
1071
|
init_cjs_shims();
|
|
@@ -1625,6 +1629,7 @@ var Store = class {
|
|
|
1625
1629
|
|
|
1626
1630
|
// src/server/session.ts
|
|
1627
1631
|
init_cjs_shims();
|
|
1632
|
+
var path20 = __toESM(require("path"), 1);
|
|
1628
1633
|
var import_async_hooks = require("async_hooks");
|
|
1629
1634
|
|
|
1630
1635
|
// src/engine/TurnEngine.ts
|
|
@@ -1752,8 +1757,11 @@ function lookupContextWindow(modelId) {
|
|
|
1752
1757
|
return DEFAULT_CONTEXT_WINDOW;
|
|
1753
1758
|
}
|
|
1754
1759
|
var KEEP_RECENT_FRACTION = 0.25;
|
|
1755
|
-
var
|
|
1756
|
-
var
|
|
1760
|
+
var SOFT_THRESHOLD_RATIO = 0.75;
|
|
1761
|
+
var ELIDE_KEEP_FRACTION = 0.15;
|
|
1762
|
+
var SUMMARY_MAX_TOKENS = 3e3;
|
|
1763
|
+
var SPAN_TOOL_RESULT_CLIP = 200;
|
|
1764
|
+
var SPAN_BUDGET_CHARS = 8e4;
|
|
1757
1765
|
var USER_MESSAGE_CLIP = 600;
|
|
1758
1766
|
var USER_MESSAGES_MAX = 40;
|
|
1759
1767
|
var TRIM_FRACTION = 0.1;
|
|
@@ -1776,9 +1784,22 @@ function triggerTokens(contextWindow, thresholdPct = DEFAULT_THRESHOLD_PCT, capT
|
|
|
1776
1784
|
}
|
|
1777
1785
|
return base;
|
|
1778
1786
|
}
|
|
1787
|
+
function softTriggerTokens(contextWindow, thresholdPct = DEFAULT_THRESHOLD_PCT, capTokens, softThresholdPct) {
|
|
1788
|
+
const hard = triggerTokens(contextWindow, thresholdPct, capTokens);
|
|
1789
|
+
if (softThresholdPct != null && Number.isFinite(softThresholdPct) && softThresholdPct > 0) {
|
|
1790
|
+
return Math.min(
|
|
1791
|
+
triggerTokens(contextWindow, softThresholdPct, capTokens),
|
|
1792
|
+
Math.floor(hard * 0.95)
|
|
1793
|
+
);
|
|
1794
|
+
}
|
|
1795
|
+
return Math.floor(hard * SOFT_THRESHOLD_RATIO);
|
|
1796
|
+
}
|
|
1779
1797
|
function shouldCompact(signal, contextWindow, thresholdPct, capTokens) {
|
|
1780
1798
|
return signal >= triggerTokens(contextWindow, thresholdPct, capTokens);
|
|
1781
1799
|
}
|
|
1800
|
+
function elisionKeepChars(hardTriggerTokens, fraction = ELIDE_KEEP_FRACTION) {
|
|
1801
|
+
return Math.max(4e3, Math.floor(hardTriggerTokens * fraction * 4));
|
|
1802
|
+
}
|
|
1782
1803
|
function turnStarts(messages, start) {
|
|
1783
1804
|
const users = [];
|
|
1784
1805
|
const assistants = [];
|
|
@@ -1928,10 +1949,11 @@ var SUMMARY_SYSTEM_PROMPT = `\u4F60\u6B63\u5728\u538B\u7F29\u4E00\u4E2A AI \u534
|
|
|
1928
1949
|
|
|
1929
1950
|
\u89C4\u5219\uFF1A
|
|
1930
1951
|
- \u4E0D\u8981\u5C06\u5B8C\u6574\u7684\u6587\u4EF6\u5185\u5BB9\u5F53\u4F5C\u771F\u7406\u3002\u53EA\u8BB0\u5F55\u67D0\u4E2A\u6587\u4EF6\u88AB\u8BFB\u53D6/\u7F16\u8F91\u8FC7\u8FD9\u4E00\u4E8B\u5B9E\uFF1B\u534F\u4F5C\u8005\u5982\u679C\u9700\u8981\u518D\u6B21\u83B7\u53D6\u5185\u5BB9\u4F1A\u91CD\u65B0\u8BFB\u53D6\u3002\u9648\u65E7\u7684\u6587\u4EF6\u8BB0\u5FC6\u6BD4\u6CA1\u6709\u8BB0\u5FC6\u66F4\u7CDF\u7CD5\u3002
|
|
1952
|
+
- \u82E5\u63D0\u4F9B\u4E86\u4E0A\u4E00\u7248\u6458\u8981\uFF1A\u53EA\u628A\u5B83\u4ECD\u7136\u6709\u6548\u7684\u7ED3\u8BBA\u5E76\u5165\u5BF9\u5E94\u7AE0\u8282\uFF0C\u4E0D\u8981\u6574\u6BB5\u590D\u8FF0\u6216\u9010\u6761\u8F6C\u6284\u3002
|
|
1931
1953
|
- \u8981\u5177\u4F53\uFF1A\u8DEF\u5F84\u3001\u540D\u79F0\u3001\u547D\u4EE4\u3001id\u2014\u2014\u800C\u4E0D\u662F\u6A21\u7CCA\u7684\u5F15\u7528\u3002
|
|
1932
1954
|
- \u4EC5\u8F93\u51FA\u6458\u8981\u7AE0\u8282\uFF0C\u4E0D\u9644\u52A0\u524D\u8A00\u3002`;
|
|
1933
1955
|
var CONTINUATION_CONTRACT = "\u4ECE\u4F60\u4E4B\u524D\u4E2D\u65AD\u7684\u5730\u65B9\u7EE7\u7EED\uFF1A\u5B8C\u5168\u6309\u7167\u63CF\u8FF0\u63A5\u7EED\u5F53\u524D\u5DE5\u4F5C\u548C\u4E0B\u4E00\u6B65\u3002\u4E0D\u8981\u91CD\u65B0\u8BE2\u95EE\u5DF2\u56DE\u7B54\u7684\u95EE\u9898\uFF0C\u4E0D\u8981\u56DE\u987E\uFF0C\u4E0D\u8981\u63D0\u53CA\u4E0A\u4E0B\u6587\u5DF2\u88AB\u538B\u7F29\u3002\u5982\u679C\u4F60\u9700\u8981\u4E0A\u8FF0\u63D0\u5230\u7684\u6587\u4EF6\u7684\u5185\u5BB9\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u5B83\u3002";
|
|
1934
|
-
function renderSpan(span,
|
|
1956
|
+
function renderSpan(span, budgetChars2 = SPAN_BUDGET_CHARS) {
|
|
1935
1957
|
const lines = [];
|
|
1936
1958
|
for (const m of span) {
|
|
1937
1959
|
const role = m.role;
|
|
@@ -1955,8 +1977,8 @@ function renderSpan(span, budgetChars = SPAN_BUDGET_CHARS) {
|
|
|
1955
1977
|
if (text) lines.push(`[${role}] ${text}`);
|
|
1956
1978
|
}
|
|
1957
1979
|
let rendered = lines.join("\n");
|
|
1958
|
-
if (rendered.length >
|
|
1959
|
-
rendered = "(\u2026oldest turns elided\u2026)\n" + rendered.slice(-
|
|
1980
|
+
if (rendered.length > budgetChars2) {
|
|
1981
|
+
rendered = "(\u2026oldest turns elided\u2026)\n" + rendered.slice(-budgetChars2);
|
|
1960
1982
|
}
|
|
1961
1983
|
return rendered;
|
|
1962
1984
|
}
|
|
@@ -1970,9 +1992,17 @@ function summarizerMessages(span, priorSummary = "") {
|
|
|
1970
1992
|
{ role: "user", content: body }
|
|
1971
1993
|
];
|
|
1972
1994
|
}
|
|
1995
|
+
function summarizerReasoningEffort() {
|
|
1996
|
+
const v = process.env.COMPACTION_REASONING_EFFORT?.toLowerCase();
|
|
1997
|
+
if (v === "low" || v === "medium" || v === "high") return v;
|
|
1998
|
+
return "low";
|
|
1999
|
+
}
|
|
1973
2000
|
async function summarizeSpan(provider, model, span, priorSummary = "", opts) {
|
|
1974
2001
|
const msgs = summarizerMessages(span, priorSummary);
|
|
1975
|
-
const chunks = await provider.stream(model, msgs, [],
|
|
2002
|
+
const chunks = await provider.stream(model, msgs, [], {
|
|
2003
|
+
...opts,
|
|
2004
|
+
maxTokens: SUMMARY_MAX_TOKENS
|
|
2005
|
+
});
|
|
1976
2006
|
let text = "";
|
|
1977
2007
|
for await (const chunk of chunks) {
|
|
1978
2008
|
if (chunk.content) text += chunk.content;
|
|
@@ -2083,6 +2113,166 @@ function applyToOutbound(messages, state) {
|
|
|
2083
2113
|
head.push({ role: "user", content: compactedBlock(state) });
|
|
2084
2114
|
return [...head, ...messages.slice(boundary)];
|
|
2085
2115
|
}
|
|
2116
|
+
var ELIDE_EXEMPT_TOOLS = /* @__PURE__ */ new Set([
|
|
2117
|
+
"explore",
|
|
2118
|
+
"ask_user",
|
|
2119
|
+
"propose_plan",
|
|
2120
|
+
"request_directory",
|
|
2121
|
+
"todo_write"
|
|
2122
|
+
]);
|
|
2123
|
+
function elidedToolContent(m) {
|
|
2124
|
+
const name = m.tool_call_name || "tool";
|
|
2125
|
+
const size = m.content.length;
|
|
2126
|
+
const saved = /full output saved to ([^\]]+)/.exec(m.content)?.[1]?.trim();
|
|
2127
|
+
return saved ? `[old tool result elided: ${name}, ${size} chars; full output saved to ${saved}]` : `[old tool result elided: ${name}, ${size} chars; re-run the tool if its content is still needed]`;
|
|
2128
|
+
}
|
|
2129
|
+
function isPlaceholder(content) {
|
|
2130
|
+
return content.startsWith("[old tool result elided") || content.startsWith("[superseded by a later read_file");
|
|
2131
|
+
}
|
|
2132
|
+
var ONE_SHOT_TOOLS = /* @__PURE__ */ new Set(["git_diff"]);
|
|
2133
|
+
var ONE_SHOT_KEEP_RECENT = 1;
|
|
2134
|
+
function applyOneShotElision(messages, keepRecent = ONE_SHOT_KEEP_RECENT, tools = ONE_SHOT_TOOLS) {
|
|
2135
|
+
const out = [...messages];
|
|
2136
|
+
let seen = 0;
|
|
2137
|
+
let elided = 0;
|
|
2138
|
+
let freed = 0;
|
|
2139
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2140
|
+
const m = messages[i];
|
|
2141
|
+
if (m.role !== "tool" || !tools.has(m.tool_call_name ?? "")) continue;
|
|
2142
|
+
if (isPlaceholder(m.content)) continue;
|
|
2143
|
+
seen++;
|
|
2144
|
+
if (seen <= keepRecent) continue;
|
|
2145
|
+
const placeholder = elidedToolContent(m);
|
|
2146
|
+
freed += m.content.length - placeholder.length;
|
|
2147
|
+
out[i] = { ...m, content: placeholder };
|
|
2148
|
+
elided++;
|
|
2149
|
+
}
|
|
2150
|
+
return elided > 0 ? { messages: out, elided, freedChars: freed } : { messages, elided: 0, freedChars: 0 };
|
|
2151
|
+
}
|
|
2152
|
+
function applyToolResultElision(messages, keepChars, exempt = ELIDE_EXEMPT_TOOLS) {
|
|
2153
|
+
let total = 0;
|
|
2154
|
+
for (const m of messages) {
|
|
2155
|
+
if (m.role === "tool" && !exempt.has(m.tool_call_name ?? "")) {
|
|
2156
|
+
total += m.content.length;
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
if (total <= keepChars) return { messages, elided: 0, freedChars: 0 };
|
|
2160
|
+
const out = [...messages];
|
|
2161
|
+
let budget = 0;
|
|
2162
|
+
let elided = 0;
|
|
2163
|
+
let freed = 0;
|
|
2164
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2165
|
+
const m = messages[i];
|
|
2166
|
+
if (m.role !== "tool") continue;
|
|
2167
|
+
if (exempt.has(m.tool_call_name ?? "")) continue;
|
|
2168
|
+
if (elided === 0 && budget === 0 && m.content.length > keepChars) {
|
|
2169
|
+
const kept = m.content.slice(0, keepChars) + "\n\u2026(truncated)";
|
|
2170
|
+
freed += m.content.length - kept.length;
|
|
2171
|
+
out[i] = { ...m, content: kept };
|
|
2172
|
+
budget = keepChars;
|
|
2173
|
+
continue;
|
|
2174
|
+
}
|
|
2175
|
+
if (budget + m.content.length <= keepChars) {
|
|
2176
|
+
budget += m.content.length;
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
const placeholder = elidedToolContent(m);
|
|
2180
|
+
freed += m.content.length - placeholder.length;
|
|
2181
|
+
out[i] = { ...m, content: placeholder };
|
|
2182
|
+
elided++;
|
|
2183
|
+
}
|
|
2184
|
+
return { messages: out, elided, freedChars: freed };
|
|
2185
|
+
}
|
|
2186
|
+
function normalizeToolPath(p) {
|
|
2187
|
+
let s = p.trim().replace(/\\/g, "/");
|
|
2188
|
+
while (s.startsWith("./")) s = s.slice(2);
|
|
2189
|
+
return s.replace(/\/{2,}/g, "/");
|
|
2190
|
+
}
|
|
2191
|
+
var MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
2192
|
+
"write_file",
|
|
2193
|
+
"replace_in_file",
|
|
2194
|
+
"apply_patch",
|
|
2195
|
+
"apply_unified_diff",
|
|
2196
|
+
"run_shell"
|
|
2197
|
+
]);
|
|
2198
|
+
function parseReadFileSpan(content) {
|
|
2199
|
+
const body = content.replace(/^\[tool output truncated:[^\n]*\]\n\n/, "");
|
|
2200
|
+
let path35;
|
|
2201
|
+
let start;
|
|
2202
|
+
let end;
|
|
2203
|
+
try {
|
|
2204
|
+
const obj = JSON.parse(body);
|
|
2205
|
+
path35 = obj.path;
|
|
2206
|
+
start = obj.start_line;
|
|
2207
|
+
end = obj.end_line;
|
|
2208
|
+
} catch {
|
|
2209
|
+
const str2 = (key) => new RegExp(`"${key}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`).exec(body)?.[1];
|
|
2210
|
+
const num2 = (key) => {
|
|
2211
|
+
const m = new RegExp(`"${key}"\\s*:\\s*(\\d+)`).exec(body);
|
|
2212
|
+
return m ? Number(m[1]) : void 0;
|
|
2213
|
+
};
|
|
2214
|
+
path35 = str2("path");
|
|
2215
|
+
start = num2("start_line");
|
|
2216
|
+
end = num2("end_line");
|
|
2217
|
+
}
|
|
2218
|
+
if (typeof path35 !== "string" || !path35) return null;
|
|
2219
|
+
if (typeof start !== "number" || typeof end !== "number") return null;
|
|
2220
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return null;
|
|
2221
|
+
return { path: path35, start, end };
|
|
2222
|
+
}
|
|
2223
|
+
function addSpan(spans, start, end) {
|
|
2224
|
+
spans.push([start, end]);
|
|
2225
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
2226
|
+
const merged = [];
|
|
2227
|
+
for (const cur of spans) {
|
|
2228
|
+
const last = merged[merged.length - 1];
|
|
2229
|
+
if (last && cur[0] <= last[1] + 1) {
|
|
2230
|
+
last[1] = Math.max(last[1], cur[1]);
|
|
2231
|
+
} else {
|
|
2232
|
+
merged.push([...cur]);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
spans.length = 0;
|
|
2236
|
+
spans.push(...merged);
|
|
2237
|
+
}
|
|
2238
|
+
function coversSpans(spans, start, end) {
|
|
2239
|
+
return spans.some(([a, b]) => a <= start && b >= end);
|
|
2240
|
+
}
|
|
2241
|
+
function applyReadFileDedup(messages) {
|
|
2242
|
+
let candidates = 0;
|
|
2243
|
+
for (const m of messages) {
|
|
2244
|
+
if (m.role === "tool" && m.tool_call_name === "read_file") candidates++;
|
|
2245
|
+
}
|
|
2246
|
+
if (candidates < 2) return { messages, elided: 0 };
|
|
2247
|
+
const out = [...messages];
|
|
2248
|
+
const covered = /* @__PURE__ */ new Map();
|
|
2249
|
+
let elided = 0;
|
|
2250
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2251
|
+
const m = messages[i];
|
|
2252
|
+
if (m.role === "assistant") {
|
|
2253
|
+
if (m.tool_calls?.some((tc) => MUTATING_TOOLS.has(tc.function.name))) {
|
|
2254
|
+
covered.clear();
|
|
2255
|
+
}
|
|
2256
|
+
continue;
|
|
2257
|
+
}
|
|
2258
|
+
if (m.role !== "tool" || m.tool_call_name !== "read_file") continue;
|
|
2259
|
+
const span = parseReadFileSpan(m.content);
|
|
2260
|
+
if (!span) continue;
|
|
2261
|
+
const key = normalizeToolPath(span.path);
|
|
2262
|
+
const seen = covered.get(key) ?? [];
|
|
2263
|
+
if (coversSpans(seen, span.start, span.end)) {
|
|
2264
|
+
out[i] = {
|
|
2265
|
+
...m,
|
|
2266
|
+
content: `[superseded by a later read_file of ${span.path} (lines ${span.start}-${span.end})]`
|
|
2267
|
+
};
|
|
2268
|
+
elided++;
|
|
2269
|
+
continue;
|
|
2270
|
+
}
|
|
2271
|
+
addSpan(seen, span.start, span.end);
|
|
2272
|
+
covered.set(key, seen);
|
|
2273
|
+
}
|
|
2274
|
+
return { messages: elided > 0 ? out : messages, elided };
|
|
2275
|
+
}
|
|
2086
2276
|
var OVERFLOW_MARKERS = [
|
|
2087
2277
|
"context_length_exceeded",
|
|
2088
2278
|
"maximum context length",
|
|
@@ -2098,9 +2288,94 @@ function isContextOverflow(exc) {
|
|
|
2098
2288
|
return OVERFLOW_MARKERS.some((m) => text.includes(m));
|
|
2099
2289
|
}
|
|
2100
2290
|
|
|
2291
|
+
// src/engine/toolResultBudget.ts
|
|
2292
|
+
init_cjs_shims();
|
|
2293
|
+
var fs4 = __toESM(require("fs"), 1);
|
|
2294
|
+
var path5 = __toESM(require("path"), 1);
|
|
2295
|
+
var DEFAULT_TOOL_RESULT_MAX_CHARS = 24e3;
|
|
2296
|
+
var HEAD_RATIO = 0.6;
|
|
2297
|
+
function budgetChars() {
|
|
2298
|
+
const n = Number(process.env.TOOL_RESULT_MAX_CHARS);
|
|
2299
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_TOOL_RESULT_MAX_CHARS;
|
|
2300
|
+
}
|
|
2301
|
+
function toolResultDir() {
|
|
2302
|
+
const env3 = process.env.TOOL_RESULT_DIR?.trim();
|
|
2303
|
+
return env3 || path5.join(stateDir(), "tool-results");
|
|
2304
|
+
}
|
|
2305
|
+
function clipText(text, budget) {
|
|
2306
|
+
if (text.length <= budget) return text;
|
|
2307
|
+
const room = Math.max(
|
|
2308
|
+
0,
|
|
2309
|
+
budget - `
|
|
2310
|
+
|
|
2311
|
+
\u2026 [${text.length} characters omitted] \u2026
|
|
2312
|
+
|
|
2313
|
+
`.length
|
|
2314
|
+
);
|
|
2315
|
+
const headLen = Math.floor(room * HEAD_RATIO);
|
|
2316
|
+
const tailLen = room - headLen;
|
|
2317
|
+
const omitted = text.length - headLen - tailLen;
|
|
2318
|
+
return text.slice(0, headLen) + `
|
|
2319
|
+
|
|
2320
|
+
\u2026 [${omitted} characters omitted] \u2026
|
|
2321
|
+
|
|
2322
|
+
` + text.slice(-tailLen);
|
|
2323
|
+
}
|
|
2324
|
+
function clipDeep(value, perField) {
|
|
2325
|
+
if (typeof value === "string") {
|
|
2326
|
+
return value.length <= perField ? value : clipText(value, perField);
|
|
2327
|
+
}
|
|
2328
|
+
if (Array.isArray(value)) {
|
|
2329
|
+
return value.map((v) => clipDeep(v, perField));
|
|
2330
|
+
}
|
|
2331
|
+
if (value && typeof value === "object") {
|
|
2332
|
+
const out = {};
|
|
2333
|
+
for (const [k, v] of Object.entries(value)) out[k] = clipDeep(v, perField);
|
|
2334
|
+
return out;
|
|
2335
|
+
}
|
|
2336
|
+
return value;
|
|
2337
|
+
}
|
|
2338
|
+
function saveRaw(tool, raw, dir) {
|
|
2339
|
+
try {
|
|
2340
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
2341
|
+
const safe = tool.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40) || "tool";
|
|
2342
|
+
const name = `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}-${safe}.txt`;
|
|
2343
|
+
const file = path5.join(dir, name);
|
|
2344
|
+
fs4.writeFileSync(file, raw, "utf-8");
|
|
2345
|
+
return file;
|
|
2346
|
+
} catch {
|
|
2347
|
+
return void 0;
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
function applyToolResultBudget(tool, result, opts = {}) {
|
|
2351
|
+
const budget = opts.maxChars ?? budgetChars();
|
|
2352
|
+
const raw = typeof result === "string" ? result : JSON.stringify(result, null, 2) ?? String(result);
|
|
2353
|
+
if (raw.length <= budget) {
|
|
2354
|
+
return { content: raw, truncated: false };
|
|
2355
|
+
}
|
|
2356
|
+
let content = raw;
|
|
2357
|
+
if (typeof result === "object" && result !== null) {
|
|
2358
|
+
let perField = Math.floor(budget * 0.7);
|
|
2359
|
+
let clipped = JSON.stringify(clipDeep(result, perField), null, 2);
|
|
2360
|
+
for (let i = 0; i < 6 && clipped && clipped.length > budget && perField > 128; i++) {
|
|
2361
|
+
perField = Math.floor(perField / 2);
|
|
2362
|
+
clipped = JSON.stringify(clipDeep(result, perField), null, 2);
|
|
2363
|
+
}
|
|
2364
|
+
if (clipped && clipped.length <= budget) content = clipped;
|
|
2365
|
+
}
|
|
2366
|
+
if (content.length > budget) content = clipText(content, budget);
|
|
2367
|
+
const savedPath = saveRaw(tool, raw, opts.dir ?? toolResultDir());
|
|
2368
|
+
if (savedPath) {
|
|
2369
|
+
content = `[tool output truncated: ${raw.length} chars, full output saved to ${savedPath}]
|
|
2370
|
+
|
|
2371
|
+
` + content;
|
|
2372
|
+
}
|
|
2373
|
+
return { content, truncated: true, savedPath };
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2101
2376
|
// src/engine/TurnEngine.ts
|
|
2102
2377
|
var crypto3 = __toESM(require("crypto"), 1);
|
|
2103
|
-
var
|
|
2378
|
+
var fs5 = __toESM(require("fs"), 1);
|
|
2104
2379
|
var _path = __toESM(require("path"), 1);
|
|
2105
2380
|
|
|
2106
2381
|
// src/prompts/index.ts
|
|
@@ -2226,13 +2501,17 @@ var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
2226
2501
|
"propose_plan",
|
|
2227
2502
|
"request_directory"
|
|
2228
2503
|
]);
|
|
2229
|
-
|
|
2230
|
-
|
|
2504
|
+
var MAX_COMPACT_ROUNDS = 4;
|
|
2505
|
+
var OVERFLOW_TARGET_RATIO = 0.5;
|
|
2506
|
+
var OVERFLOW_TRIM_FRACTION = 0.5;
|
|
2507
|
+
var COMPACT_STALL_RETRY_DELTA = 20;
|
|
2508
|
+
function toolResultMessage(tc, result, taskId, toolResultDir2) {
|
|
2509
|
+
const budgeted = applyToolResultBudget(tc.name, result, { dir: toolResultDir2 });
|
|
2231
2510
|
return {
|
|
2232
2511
|
role: "tool",
|
|
2233
2512
|
tool_call_id: tc.id,
|
|
2234
2513
|
tool_call_name: tc.name,
|
|
2235
|
-
content,
|
|
2514
|
+
content: budgeted.content,
|
|
2236
2515
|
taskId
|
|
2237
2516
|
};
|
|
2238
2517
|
}
|
|
@@ -2343,12 +2622,22 @@ var TurnEngine = class _TurnEngine {
|
|
|
2343
2622
|
// 上下文压缩
|
|
2344
2623
|
compactionState = null;
|
|
2345
2624
|
_lastContextTokens = null;
|
|
2625
|
+
/** 压缩无进展时的消息条数快照;-1 表示无冷却 */
|
|
2626
|
+
_compactStalledAt = -1;
|
|
2346
2627
|
/** 显式配置的上下文窗口大小(token 数),未设置时为 undefined(走自动检测) */
|
|
2347
2628
|
_contextWindow;
|
|
2348
2629
|
/** 压缩触发阈值比例(默认 0.8) */
|
|
2349
2630
|
_thresholdPct;
|
|
2631
|
+
/** 软阈值比例(undefined = 取硬阈值的 75%) */
|
|
2632
|
+
_softThresholdPct;
|
|
2633
|
+
/** 是否启用软阈值淘汰(COMPACTION_ELIDE=0 可关闭) */
|
|
2634
|
+
_elisionEnabled;
|
|
2635
|
+
/** 是否启用重复读取去重(COMPACTION_DEDUP=0 可关闭) */
|
|
2636
|
+
_dedupEnabled;
|
|
2350
2637
|
/** 压缩触发阈值绝对上限(null = 不限制) */
|
|
2351
2638
|
_capTokens;
|
|
2639
|
+
/** 超预算工具结果的落盘目录(undefined = 使用默认状态目录) */
|
|
2640
|
+
_toolResultDir;
|
|
2352
2641
|
// 产出文件追踪(本轮 Turn 中成功写入的文件)
|
|
2353
2642
|
_producedFiles = [];
|
|
2354
2643
|
_turnStartTime = 0;
|
|
@@ -2400,7 +2689,15 @@ var TurnEngine = class _TurnEngine {
|
|
|
2400
2689
|
this._hook = opts.hook;
|
|
2401
2690
|
this._contextWindow = opts.contextWindow;
|
|
2402
2691
|
this._thresholdPct = opts.thresholdPct ?? DEFAULT_THRESHOLD_PCT;
|
|
2692
|
+
this._softThresholdPct = opts.softThresholdPct;
|
|
2693
|
+
this._elisionEnabled = !/^(0|false|off|no)$/i.test(
|
|
2694
|
+
(process.env.COMPACTION_ELIDE ?? "").trim()
|
|
2695
|
+
);
|
|
2696
|
+
this._dedupEnabled = !/^(0|false|off|no)$/i.test(
|
|
2697
|
+
(process.env.COMPACTION_DEDUP ?? "").trim()
|
|
2698
|
+
);
|
|
2403
2699
|
this._capTokens = opts.capTokens ?? null;
|
|
2700
|
+
this._toolResultDir = opts.toolResultDir;
|
|
2404
2701
|
this._reasoningEffort = opts.reasoningEffort;
|
|
2405
2702
|
if (this.messages.length === 0 || this.messages[0].role !== "system") {
|
|
2406
2703
|
this.messages.unshift({ role: "system", content: this.instructions });
|
|
@@ -2456,6 +2753,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
2456
2753
|
this._cancel = false;
|
|
2457
2754
|
this._producedFiles = [];
|
|
2458
2755
|
this._turnStartTime = Date.now();
|
|
2756
|
+
this._compactStalledAt = -1;
|
|
2459
2757
|
this._emitHook((h) => h.onTurnStart?.(userInput));
|
|
2460
2758
|
yield {
|
|
2461
2759
|
type: "turn_start" /* TURN_START */,
|
|
@@ -3172,7 +3470,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
3172
3470
|
for (let i = 0; i < concurrent.length; i++) {
|
|
3173
3471
|
const tc = concurrent[i];
|
|
3174
3472
|
const { result, status } = results[i];
|
|
3175
|
-
this.messages.push(
|
|
3473
|
+
this.messages.push(
|
|
3474
|
+
toolResultMessage(tc, result, this.taskId, this._toolResultDir)
|
|
3475
|
+
);
|
|
3176
3476
|
this._trackProducedFile(
|
|
3177
3477
|
tc,
|
|
3178
3478
|
result,
|
|
@@ -3235,7 +3535,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
3235
3535
|
};
|
|
3236
3536
|
status = "error";
|
|
3237
3537
|
}
|
|
3238
|
-
this.messages.push(
|
|
3538
|
+
this.messages.push(
|
|
3539
|
+
toolResultMessage(tc, result, this.taskId, this._toolResultDir)
|
|
3540
|
+
);
|
|
3239
3541
|
this._trackProducedFile(
|
|
3240
3542
|
tc,
|
|
3241
3543
|
result,
|
|
@@ -3270,13 +3572,41 @@ var TurnEngine = class _TurnEngine {
|
|
|
3270
3572
|
* 过滤 notice 消息;tool 消息只保留必要字段;
|
|
3271
3573
|
* 其余消息剥离 display-only sidecar 字段(source/_display/ts/
|
|
3272
3574
|
* reasoning/usage),避免污染模型上下文。
|
|
3575
|
+
*
|
|
3576
|
+
* 达到软阈值时先淘汰旧工具结果(零 LLM 成本),再产出最终视图。
|
|
3273
3577
|
*/
|
|
3274
3578
|
_outboundMessages() {
|
|
3275
|
-
const
|
|
3276
|
-
const
|
|
3277
|
-
|
|
3278
|
-
|
|
3579
|
+
const base = applyToOutbound(this.messages, this.compactionState);
|
|
3580
|
+
const deduped = this._dedupEnabled ? applyReadFileDedup(base).messages : base;
|
|
3581
|
+
let view = this._finalizeOutbound(deduped);
|
|
3582
|
+
if (!this._elisionEnabled) return view;
|
|
3583
|
+
if (estimateTokens(view) < this._softTriggerTokens()) {
|
|
3584
|
+
return view;
|
|
3585
|
+
}
|
|
3586
|
+
const oneShot = applyOneShotElision(deduped);
|
|
3587
|
+
if (oneShot.freedChars > 0) {
|
|
3588
|
+
view = this._finalizeOutbound(oneShot.messages);
|
|
3589
|
+
if (estimateTokens(view) < this._softTriggerTokens()) {
|
|
3590
|
+
return view;
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
const keepChars = elisionKeepChars(
|
|
3594
|
+
triggerTokens(
|
|
3595
|
+
this._getContextWindow(),
|
|
3596
|
+
this._thresholdPct,
|
|
3597
|
+
this._capTokens
|
|
3598
|
+
)
|
|
3599
|
+
);
|
|
3600
|
+
const elided = applyToolResultElision(
|
|
3601
|
+
oneShot.messages,
|
|
3602
|
+
keepChars
|
|
3279
3603
|
);
|
|
3604
|
+
if (elided.freedChars === 0) return view;
|
|
3605
|
+
return this._finalizeOutbound(elided.messages);
|
|
3606
|
+
}
|
|
3607
|
+
/** 过滤 notice + 剥离 display-only 字段,得到最终发送视图 */
|
|
3608
|
+
_finalizeOutbound(sourceMessages) {
|
|
3609
|
+
const _STRIP = /* @__PURE__ */ new Set(["source", "_display", "ts", "reasoning", "usage"]);
|
|
3280
3610
|
return sourceMessages.filter((m) => m.role !== "notice").map((m) => {
|
|
3281
3611
|
if (m.role === "tool") {
|
|
3282
3612
|
return {
|
|
@@ -3294,6 +3624,15 @@ var TurnEngine = class _TurnEngine {
|
|
|
3294
3624
|
return stripped;
|
|
3295
3625
|
});
|
|
3296
3626
|
}
|
|
3627
|
+
/** 软阈值(token 数):达到它只淘汰旧工具结果 */
|
|
3628
|
+
_softTriggerTokens() {
|
|
3629
|
+
return softTriggerTokens(
|
|
3630
|
+
this._getContextWindow(),
|
|
3631
|
+
this._thresholdPct,
|
|
3632
|
+
this._capTokens,
|
|
3633
|
+
this._softThresholdPct
|
|
3634
|
+
);
|
|
3635
|
+
}
|
|
3297
3636
|
/** 通知消息:仅用于日志/展示,不发给模型 */
|
|
3298
3637
|
_appendNotice(kind, text) {
|
|
3299
3638
|
this.messages.push({
|
|
@@ -3335,18 +3674,36 @@ var TurnEngine = class _TurnEngine {
|
|
|
3335
3674
|
return false;
|
|
3336
3675
|
}
|
|
3337
3676
|
// ===================== 自动压缩 =====================
|
|
3338
|
-
/**
|
|
3677
|
+
/**
|
|
3678
|
+
* 当前上下文 token 用量。
|
|
3679
|
+
*
|
|
3680
|
+
* API 返回的 `usage.input` 是精确值,但反映的是「上一次请求」——工具结果
|
|
3681
|
+
* 与淘汰/压缩都会让它过时。因此同时估算当前 outbound 视图,取较大者:
|
|
3682
|
+
* 宁可早一点压缩,也不要漏判导致溢出。
|
|
3683
|
+
*/
|
|
3339
3684
|
_compactionTokens() {
|
|
3340
|
-
|
|
3341
|
-
|
|
3685
|
+
const estimated = estimateTokens(this._outboundMessages());
|
|
3686
|
+
if (this._lastContextTokens == null) {
|
|
3687
|
+
return { tokens: estimated, estimated: true };
|
|
3342
3688
|
}
|
|
3343
|
-
|
|
3344
|
-
tokens:
|
|
3345
|
-
|
|
3346
|
-
};
|
|
3689
|
+
if (estimated > this._lastContextTokens) {
|
|
3690
|
+
return { tokens: estimated, estimated: true };
|
|
3691
|
+
}
|
|
3692
|
+
return { tokens: this._lastContextTokens, estimated: false };
|
|
3693
|
+
}
|
|
3694
|
+
/** 压缩后重置精确计数:后续按估算值判断 */
|
|
3695
|
+
_resetContextTokens() {
|
|
3696
|
+
this._lastContextTokens = null;
|
|
3697
|
+
}
|
|
3698
|
+
/** 当前 outbound 视图的估算 token 数 */
|
|
3699
|
+
_estimateContextTokens() {
|
|
3700
|
+
return estimateTokens(this._outboundMessages());
|
|
3347
3701
|
}
|
|
3348
3702
|
/** 检查是否需要压缩(每轮 LLM 调用前) */
|
|
3349
3703
|
_compactionDue() {
|
|
3704
|
+
if (this._compactStalledAt >= 0 && this.messages.length < this._compactStalledAt + COMPACT_STALL_RETRY_DELTA) {
|
|
3705
|
+
return false;
|
|
3706
|
+
}
|
|
3350
3707
|
const { tokens } = this._compactionTokens();
|
|
3351
3708
|
return shouldCompact(
|
|
3352
3709
|
tokens,
|
|
@@ -3393,63 +3750,82 @@ var TurnEngine = class _TurnEngine {
|
|
|
3393
3750
|
async _compactNow(reqId, force = false) {
|
|
3394
3751
|
const contextWindow = this._getContextWindow();
|
|
3395
3752
|
const messagesBefore = this._outboundMessages().length;
|
|
3396
|
-
const
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
this._capTokens
|
|
3401
|
-
)
|
|
3753
|
+
const trigger = triggerTokens(
|
|
3754
|
+
contextWindow,
|
|
3755
|
+
this._thresholdPct,
|
|
3756
|
+
this._capTokens
|
|
3402
3757
|
);
|
|
3403
|
-
const
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3758
|
+
const target = Math.floor(
|
|
3759
|
+
trigger * (force ? OVERFLOW_TARGET_RATIO : 0.9)
|
|
3760
|
+
);
|
|
3761
|
+
const model = process.env.COMPACTION_MODEL?.trim() || this.model;
|
|
3762
|
+
const reasoningEffort = summarizerReasoningEffort();
|
|
3763
|
+
let keep = Math.floor(KEEP_RECENT_FRACTION * trigger);
|
|
3764
|
+
let method = null;
|
|
3765
|
+
let lastState = null;
|
|
3766
|
+
let progressed = false;
|
|
3767
|
+
for (let round = 0; round < MAX_COMPACT_ROUNDS; round++) {
|
|
3768
|
+
if (round > 0 && this._estimateContextTokens() <= target) break;
|
|
3769
|
+
let state = null;
|
|
3770
|
+
for (let attempt = 0; attempt < 2 && !state; attempt++) {
|
|
3771
|
+
try {
|
|
3772
|
+
const logTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
3773
|
+
state = await buildState(
|
|
3774
|
+
this.messages,
|
|
3775
|
+
this.provider,
|
|
3776
|
+
model,
|
|
3777
|
+
keep,
|
|
3778
|
+
this.compactionState,
|
|
3779
|
+
{ reqId, logTime, reasoningEffort }
|
|
3780
|
+
);
|
|
3781
|
+
} catch {
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
if (state) {
|
|
3785
|
+
this.compactionState = state;
|
|
3786
|
+
lastState = state;
|
|
3787
|
+
method = "summarized";
|
|
3788
|
+
progressed = true;
|
|
3789
|
+
} else {
|
|
3790
|
+
const trimmed = trimState(
|
|
3410
3791
|
this.messages,
|
|
3411
|
-
this.provider,
|
|
3412
|
-
model,
|
|
3413
|
-
keep,
|
|
3414
3792
|
this.compactionState,
|
|
3415
|
-
|
|
3793
|
+
force ? OVERFLOW_TRIM_FRACTION : 0.25
|
|
3416
3794
|
);
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
this.
|
|
3425
|
-
|
|
3795
|
+
if (!trimmed) break;
|
|
3796
|
+
this.compactionState = trimmed;
|
|
3797
|
+
lastState = trimmed;
|
|
3798
|
+
method = "trimmed";
|
|
3799
|
+
progressed = true;
|
|
3800
|
+
}
|
|
3801
|
+
this._resetContextTokens();
|
|
3802
|
+
if (this._estimateContextTokens() <= trigger) break;
|
|
3803
|
+
keep = Math.max(Math.floor(trigger * 0.05), Math.floor(keep / 2));
|
|
3804
|
+
}
|
|
3805
|
+
if (!progressed || !lastState) return { notice: null, method: null };
|
|
3806
|
+
this._compactStalledAt = this._estimateContextTokens() >= trigger ? this.messages.length : -1;
|
|
3807
|
+
const messagesAfter = this._outboundMessages().length;
|
|
3808
|
+
const common = {
|
|
3809
|
+
boundaryIndex: lastState.boundaryIndex,
|
|
3810
|
+
messagesBefore,
|
|
3811
|
+
messagesAfter
|
|
3812
|
+
};
|
|
3813
|
+
if (method === "summarized") {
|
|
3426
3814
|
return {
|
|
3427
3815
|
notice: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29 \u2014 \u65E9\u671F\u8F6E\u6B21\u5DF2\u6458\u8981",
|
|
3428
3816
|
method: "summarized",
|
|
3429
|
-
modelUsed:
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
messagesBefore,
|
|
3435
|
-
messagesAfter: this._outboundMessages().length
|
|
3817
|
+
modelUsed: lastState.modelUsed,
|
|
3818
|
+
summaryPreview: lastState.summaryText.slice(0, 200),
|
|
3819
|
+
keptUserMessages: lastState.userMessages.length,
|
|
3820
|
+
droppedUserMessages: lastState.userMessagesDropped,
|
|
3821
|
+
...common
|
|
3436
3822
|
};
|
|
3437
3823
|
}
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
return {
|
|
3444
|
-
notice: "\u4E0A\u4E0B\u6587\u5DF2\u88C1\u526A \u2014 \u6700\u65E7\u7684\u8F6E\u6B21\u5DF2\u4E22\u5F03\uFF08\u6458\u8981\u4E0D\u53EF\u7528\uFF09",
|
|
3445
|
-
method: "trimmed",
|
|
3446
|
-
boundaryIndex: trimmed.boundaryIndex,
|
|
3447
|
-
messagesBefore,
|
|
3448
|
-
messagesAfter: this._outboundMessages().length
|
|
3449
|
-
};
|
|
3450
|
-
}
|
|
3451
|
-
}
|
|
3452
|
-
return { notice: null, method: null };
|
|
3824
|
+
return {
|
|
3825
|
+
notice: "\u4E0A\u4E0B\u6587\u5DF2\u88C1\u526A \u2014 \u6700\u65E7\u7684\u8F6E\u6B21\u5DF2\u4E22\u5F03\uFF08\u6458\u8981\u4E0D\u53EF\u7528\uFF09",
|
|
3826
|
+
method: "trimmed",
|
|
3827
|
+
...common
|
|
3828
|
+
};
|
|
3453
3829
|
}
|
|
3454
3830
|
/** 追踪文件写入工具成功产生的文件 */
|
|
3455
3831
|
_trackProducedFile(tc, result, status) {
|
|
@@ -3459,9 +3835,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
3459
3835
|
return;
|
|
3460
3836
|
}
|
|
3461
3837
|
if (result && typeof result === "object") {
|
|
3462
|
-
const
|
|
3463
|
-
if (
|
|
3464
|
-
this._producedFiles.push(
|
|
3838
|
+
const path35 = result.path;
|
|
3839
|
+
if (path35) {
|
|
3840
|
+
this._producedFiles.push(path35);
|
|
3465
3841
|
return;
|
|
3466
3842
|
}
|
|
3467
3843
|
const changed = result.changed_files;
|
|
@@ -3485,7 +3861,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
3485
3861
|
_fileExists(f, ws) {
|
|
3486
3862
|
if (!ws && !_path.isAbsolute(f)) return true;
|
|
3487
3863
|
try {
|
|
3488
|
-
return
|
|
3864
|
+
return fs5.existsSync(_path.isAbsolute(f) ? f : _path.join(ws, f));
|
|
3489
3865
|
} catch {
|
|
3490
3866
|
return false;
|
|
3491
3867
|
}
|
|
@@ -3508,7 +3884,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
3508
3884
|
if (depth > 4 || results.length >= 50) return;
|
|
3509
3885
|
let entries;
|
|
3510
3886
|
try {
|
|
3511
|
-
entries =
|
|
3887
|
+
entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
3512
3888
|
} catch {
|
|
3513
3889
|
return;
|
|
3514
3890
|
}
|
|
@@ -3523,7 +3899,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
3523
3899
|
const ext = dotIdx !== -1 ? e.name.slice(dotIdx).toLowerCase() : "";
|
|
3524
3900
|
if (!_TurnEngine.SCAN_SUFFIXES.has(ext)) continue;
|
|
3525
3901
|
try {
|
|
3526
|
-
const st =
|
|
3902
|
+
const st = fs5.statSync(full);
|
|
3527
3903
|
if (st.mtimeMs >= since) {
|
|
3528
3904
|
results.push(
|
|
3529
3905
|
_path.relative(_path.resolve(ws), _path.resolve(full))
|
|
@@ -4020,62 +4396,62 @@ init_cjs_shims();
|
|
|
4020
4396
|
|
|
4021
4397
|
// src/tools/pathSafety.ts
|
|
4022
4398
|
init_cjs_shims();
|
|
4023
|
-
var
|
|
4024
|
-
var
|
|
4399
|
+
var fs7 = __toESM(require("fs"), 1);
|
|
4400
|
+
var path7 = __toESM(require("path"), 1);
|
|
4025
4401
|
function isInsideRelative(rel) {
|
|
4026
|
-
return !
|
|
4402
|
+
return !path7.isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${path7.sep}`);
|
|
4027
4403
|
}
|
|
4028
4404
|
function isFilesystemRoot(p) {
|
|
4029
|
-
return
|
|
4405
|
+
return path7.parse(p).root === p;
|
|
4030
4406
|
}
|
|
4031
4407
|
function resolveWithinRoot(workspace, target) {
|
|
4032
|
-
const abs =
|
|
4408
|
+
const abs = path7.resolve(workspace, target);
|
|
4033
4409
|
let rootReal;
|
|
4034
4410
|
try {
|
|
4035
|
-
rootReal =
|
|
4411
|
+
rootReal = fs7.realpathSync(workspace);
|
|
4036
4412
|
} catch {
|
|
4037
|
-
const rootAbs =
|
|
4038
|
-
if (!isInsideRelative(
|
|
4413
|
+
const rootAbs = path7.resolve(workspace);
|
|
4414
|
+
if (!isInsideRelative(path7.relative(rootAbs, abs))) return null;
|
|
4039
4415
|
return { root: rootAbs, real: abs };
|
|
4040
4416
|
}
|
|
4041
4417
|
if (isFilesystemRoot(rootReal)) return null;
|
|
4042
4418
|
let real;
|
|
4043
4419
|
try {
|
|
4044
|
-
real =
|
|
4420
|
+
real = fs7.realpathSync(abs);
|
|
4045
4421
|
} catch {
|
|
4046
4422
|
let probe = abs;
|
|
4047
4423
|
const tail = [];
|
|
4048
4424
|
for (; ; ) {
|
|
4049
|
-
const exists =
|
|
4425
|
+
const exists = fs7.existsSync(probe) || fs7.lstatSync(probe, { throwIfNoEntry: false }) !== void 0;
|
|
4050
4426
|
if (exists) {
|
|
4051
4427
|
try {
|
|
4052
|
-
real =
|
|
4428
|
+
real = path7.join(fs7.realpathSync(probe), ...tail);
|
|
4053
4429
|
break;
|
|
4054
4430
|
} catch {
|
|
4055
4431
|
return null;
|
|
4056
4432
|
}
|
|
4057
4433
|
}
|
|
4058
|
-
tail.unshift(
|
|
4059
|
-
const parent =
|
|
4434
|
+
tail.unshift(path7.basename(probe));
|
|
4435
|
+
const parent = path7.dirname(probe);
|
|
4060
4436
|
if (parent === probe) return null;
|
|
4061
4437
|
probe = parent;
|
|
4062
4438
|
}
|
|
4063
4439
|
}
|
|
4064
|
-
if (!isInsideRelative(
|
|
4440
|
+
if (!isInsideRelative(path7.relative(rootReal, real))) return null;
|
|
4065
4441
|
return { root: rootReal, real };
|
|
4066
4442
|
}
|
|
4067
4443
|
function isWithinRoot(workspace, target) {
|
|
4068
4444
|
return resolveWithinRoot(workspace, target) !== null;
|
|
4069
4445
|
}
|
|
4070
4446
|
function resolveEntryWithinRoot(workspace, target) {
|
|
4071
|
-
const abs =
|
|
4072
|
-
const parent = resolveWithinRoot(workspace,
|
|
4447
|
+
const abs = path7.resolve(workspace, target);
|
|
4448
|
+
const parent = resolveWithinRoot(workspace, path7.dirname(abs));
|
|
4073
4449
|
if (!parent) return null;
|
|
4074
|
-
return { root: parent.root, real:
|
|
4450
|
+
return { root: parent.root, real: path7.join(parent.real, path7.basename(abs)) };
|
|
4075
4451
|
}
|
|
4076
4452
|
|
|
4077
4453
|
// src/permissions/PermissionEngine.ts
|
|
4078
|
-
var
|
|
4454
|
+
var path8 = __toESM(require("path"), 1);
|
|
4079
4455
|
var READ_ONLY_MODES = /* @__PURE__ */ new Set(["discuss" /* DISCUSS */, "plan" /* PLAN */]);
|
|
4080
4456
|
var WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
4081
4457
|
"write_file",
|
|
@@ -4125,7 +4501,7 @@ var PermissionEngine = class {
|
|
|
4125
4501
|
/** 会话级「总是允许此命令」集合 */
|
|
4126
4502
|
sessionAllowCommands;
|
|
4127
4503
|
constructor(opts) {
|
|
4128
|
-
this.workspaceRoot =
|
|
4504
|
+
this.workspaceRoot = path8.resolve(opts.workspaceRoot);
|
|
4129
4505
|
this.mode = opts.mode;
|
|
4130
4506
|
this.allowedCommands = Array.isArray(opts.allowedCommands) ? opts.allowedCommands : [];
|
|
4131
4507
|
this.sessionAllowTools = /* @__PURE__ */ new Set();
|
|
@@ -4218,8 +4594,8 @@ var PermissionEngine = class {
|
|
|
4218
4594
|
|
|
4219
4595
|
// src/providers/OpenAIProvider.ts
|
|
4220
4596
|
init_cjs_shims();
|
|
4221
|
-
var
|
|
4222
|
-
var
|
|
4597
|
+
var fs8 = __toESM(require("fs"), 1);
|
|
4598
|
+
var path9 = __toESM(require("path"), 1);
|
|
4223
4599
|
var import_openai = __toESM(require("openai"), 1);
|
|
4224
4600
|
|
|
4225
4601
|
// src/terminal.ts
|
|
@@ -4357,7 +4733,7 @@ var OpenAIProvider = class {
|
|
|
4357
4733
|
});
|
|
4358
4734
|
if (opts.logDir) {
|
|
4359
4735
|
this._logDir = opts.logDir;
|
|
4360
|
-
|
|
4736
|
+
fs8.mkdirSync(opts.logDir, { recursive: true });
|
|
4361
4737
|
}
|
|
4362
4738
|
}
|
|
4363
4739
|
// =====================================================================
|
|
@@ -4510,6 +4886,8 @@ var OpenAIProvider = class {
|
|
|
4510
4886
|
tools: this._toResponsesTools(tools, model),
|
|
4511
4887
|
// 推理强度:DeepSeek 不支持,跳过
|
|
4512
4888
|
...opts.reasoningEffort && !this._isDeepSeek(model) ? { reasoning: { effort: opts.reasoningEffort } } : {},
|
|
4889
|
+
// 限制输出长度(摘要器等轻量请求)
|
|
4890
|
+
...opts.maxTokens ? { max_output_tokens: opts.maxTokens } : {},
|
|
4513
4891
|
stream: true
|
|
4514
4892
|
}, requestOptions);
|
|
4515
4893
|
return this._responsesStream(reqId, startTime, rawStream, {
|
|
@@ -4608,6 +4986,8 @@ var OpenAIProvider = class {
|
|
|
4608
4986
|
})),
|
|
4609
4987
|
// 推理强度:DeepSeek 不支持,跳过
|
|
4610
4988
|
...opts.reasoningEffort && !this._isDeepSeek(model) ? { reasoning_effort: opts.reasoningEffort } : {},
|
|
4989
|
+
// 限制输出长度:o 系列 / gpt-5 推理模型仅支持 max_completion_tokens
|
|
4990
|
+
...opts.maxTokens ? isReasoningModel(model) ? { max_completion_tokens: opts.maxTokens } : { max_tokens: opts.maxTokens } : {},
|
|
4611
4991
|
stream: true,
|
|
4612
4992
|
// 要求返回 Token 用量统计(在流结束前的一个 chunk 中携带)
|
|
4613
4993
|
stream_options: { include_usage: true }
|
|
@@ -4721,8 +5101,8 @@ var OpenAIProvider = class {
|
|
|
4721
5101
|
if (!this._logDir) return;
|
|
4722
5102
|
try {
|
|
4723
5103
|
const today = logTime?.split("T")[0];
|
|
4724
|
-
const dir =
|
|
4725
|
-
|
|
5104
|
+
const dir = path9.join(this._logDir, today);
|
|
5105
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
4726
5106
|
const entry = {
|
|
4727
5107
|
reqId,
|
|
4728
5108
|
ts: logTime,
|
|
@@ -4734,8 +5114,8 @@ var OpenAIProvider = class {
|
|
|
4734
5114
|
toolNames: tools?.map((t) => t.function.name) ?? []
|
|
4735
5115
|
}
|
|
4736
5116
|
};
|
|
4737
|
-
|
|
4738
|
-
|
|
5117
|
+
fs8.writeFileSync(
|
|
5118
|
+
path9.join(dir, `${reqId}.json`),
|
|
4739
5119
|
JSON.stringify(entry, null, 2),
|
|
4740
5120
|
"utf-8"
|
|
4741
5121
|
);
|
|
@@ -4754,9 +5134,9 @@ var OpenAIProvider = class {
|
|
|
4754
5134
|
if (!this._logDir) return;
|
|
4755
5135
|
try {
|
|
4756
5136
|
const today = logTime?.split("T")[0];
|
|
4757
|
-
const filePath =
|
|
4758
|
-
if (!
|
|
4759
|
-
const entry = JSON.parse(
|
|
5137
|
+
const filePath = path9.join(this._logDir, today, `${reqId}.json`);
|
|
5138
|
+
if (!fs8.existsSync(filePath)) return;
|
|
5139
|
+
const entry = JSON.parse(fs8.readFileSync(filePath, "utf-8"));
|
|
4760
5140
|
entry.elapsedMs = Date.now() - startTime;
|
|
4761
5141
|
if (data.response) {
|
|
4762
5142
|
entry.response = data.response;
|
|
@@ -4764,7 +5144,7 @@ var OpenAIProvider = class {
|
|
|
4764
5144
|
if (data.error) {
|
|
4765
5145
|
entry.error = data.error;
|
|
4766
5146
|
}
|
|
4767
|
-
|
|
5147
|
+
fs8.writeFileSync(filePath, JSON.stringify(entry, null, 2), "utf-8");
|
|
4768
5148
|
} catch (e) {
|
|
4769
5149
|
console.error(e);
|
|
4770
5150
|
}
|
|
@@ -5122,12 +5502,12 @@ var OpenAIProvider = class {
|
|
|
5122
5502
|
|
|
5123
5503
|
// src/tools/files.ts
|
|
5124
5504
|
init_cjs_shims();
|
|
5125
|
-
var
|
|
5126
|
-
var
|
|
5505
|
+
var fs9 = __toESM(require("fs"), 1);
|
|
5506
|
+
var path10 = __toESM(require("path"), 1);
|
|
5127
5507
|
var DEFAULT_MAX_LINES = 2e3;
|
|
5128
5508
|
var MAX_LINE_CHARS = 500;
|
|
5129
5509
|
function makeReadFile(workspace) {
|
|
5130
|
-
const root =
|
|
5510
|
+
const root = path10.resolve(workspace);
|
|
5131
5511
|
return function read_file(args) {
|
|
5132
5512
|
const filePath = String(args.path ?? "");
|
|
5133
5513
|
const startLine = Math.max(1, Number(args.start_line ?? 1));
|
|
@@ -5137,13 +5517,13 @@ function makeReadFile(workspace) {
|
|
|
5137
5517
|
return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5138
5518
|
}
|
|
5139
5519
|
const target = resolved.real;
|
|
5140
|
-
if (!
|
|
5520
|
+
if (!fs9.existsSync(target) || !fs9.statSync(target).isFile()) {
|
|
5141
5521
|
return { error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
|
|
5142
5522
|
}
|
|
5143
5523
|
const selected = [];
|
|
5144
5524
|
let total = 0;
|
|
5145
5525
|
try {
|
|
5146
|
-
const content =
|
|
5526
|
+
const content = fs9.readFileSync(target, "utf-8");
|
|
5147
5527
|
const lines = content.split("\n");
|
|
5148
5528
|
total = lines.length;
|
|
5149
5529
|
for (let i = startLine - 1; i < Math.min(startLine - 1 + maxLines, lines.length); i++) {
|
|
@@ -5158,7 +5538,7 @@ function makeReadFile(workspace) {
|
|
|
5158
5538
|
}
|
|
5159
5539
|
const end = startLine + selected.length - 1;
|
|
5160
5540
|
const result = {
|
|
5161
|
-
path:
|
|
5541
|
+
path: path10.relative(resolved.root, target),
|
|
5162
5542
|
start_line: startLine,
|
|
5163
5543
|
end_line: end,
|
|
5164
5544
|
total_lines: total,
|
|
@@ -5171,7 +5551,7 @@ function makeReadFile(workspace) {
|
|
|
5171
5551
|
};
|
|
5172
5552
|
}
|
|
5173
5553
|
function makeWriteFile(workspace) {
|
|
5174
|
-
const root =
|
|
5554
|
+
const root = path10.resolve(workspace);
|
|
5175
5555
|
return function write_file(args) {
|
|
5176
5556
|
const filePath = String(args.path ?? "");
|
|
5177
5557
|
const content = String(args.content ?? "");
|
|
@@ -5181,34 +5561,34 @@ function makeWriteFile(workspace) {
|
|
|
5181
5561
|
return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5182
5562
|
}
|
|
5183
5563
|
const target = resolved.real;
|
|
5184
|
-
if (
|
|
5564
|
+
if (fs9.existsSync(target) && fs9.statSync(target).isDirectory()) {
|
|
5185
5565
|
return { error: "\u8DEF\u5F84\u662F\u76EE\u5F55\uFF0C\u4E0D\u662F\u6587\u4EF6" };
|
|
5186
5566
|
}
|
|
5187
|
-
if (
|
|
5567
|
+
if (fs9.existsSync(target) && !overwrite) {
|
|
5188
5568
|
return { error: "\u6587\u4EF6\u5DF2\u5B58\u5728 (overwrite=false)" };
|
|
5189
5569
|
}
|
|
5190
5570
|
try {
|
|
5191
|
-
|
|
5192
|
-
|
|
5571
|
+
fs9.mkdirSync(path10.dirname(target), { recursive: true });
|
|
5572
|
+
fs9.writeFileSync(target, content, "utf-8");
|
|
5193
5573
|
} catch (exc) {
|
|
5194
5574
|
return { error: `\u5199\u5165\u5931\u8D25: ${String(exc)}` };
|
|
5195
5575
|
}
|
|
5196
|
-
return
|
|
5576
|
+
return path10.relative(resolved.root, target);
|
|
5197
5577
|
};
|
|
5198
5578
|
}
|
|
5199
5579
|
function makeListFiles(workspace) {
|
|
5200
|
-
const root =
|
|
5580
|
+
const root = path10.resolve(workspace);
|
|
5201
5581
|
return function list_files(args) {
|
|
5202
5582
|
const dirPath = String(args.path ?? ".");
|
|
5203
5583
|
const maxResults = Math.min(Number(args.max_results ?? 100), 100);
|
|
5204
5584
|
const resolved = resolveWithinRoot(root, dirPath);
|
|
5205
5585
|
if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5206
5586
|
const target = resolved.real;
|
|
5207
|
-
if (!
|
|
5587
|
+
if (!fs9.existsSync(target) || !fs9.statSync(target).isDirectory()) {
|
|
5208
5588
|
return { error: `\u4E0D\u662F\u76EE\u5F55: ${dirPath}` };
|
|
5209
5589
|
}
|
|
5210
5590
|
try {
|
|
5211
|
-
const entries =
|
|
5591
|
+
const entries = fs9.readdirSync(target, { withFileTypes: true });
|
|
5212
5592
|
const results = [];
|
|
5213
5593
|
for (const e of entries) {
|
|
5214
5594
|
if (results.length >= maxResults) break;
|
|
@@ -5217,14 +5597,14 @@ function makeListFiles(workspace) {
|
|
|
5217
5597
|
results.push(e.isDirectory() ? `${e.name}/` : e.name);
|
|
5218
5598
|
}
|
|
5219
5599
|
results.sort();
|
|
5220
|
-
return { path:
|
|
5600
|
+
return { path: path10.relative(resolved.root, target), entries: results.slice(0, maxResults) };
|
|
5221
5601
|
} catch (exc) {
|
|
5222
5602
|
return { error: `\u5217\u51FA\u6587\u4EF6\u5931\u8D25: ${String(exc)}` };
|
|
5223
5603
|
}
|
|
5224
5604
|
};
|
|
5225
5605
|
}
|
|
5226
5606
|
function makeReplaceInFile(workspace) {
|
|
5227
|
-
const root =
|
|
5607
|
+
const root = path10.resolve(workspace);
|
|
5228
5608
|
return function replace_in_file(args) {
|
|
5229
5609
|
const filePath = String(args.path ?? "");
|
|
5230
5610
|
const oldStr = String(args.old ?? "");
|
|
@@ -5234,19 +5614,19 @@ function makeReplaceInFile(workspace) {
|
|
|
5234
5614
|
const resolved = resolveWithinRoot(root, filePath);
|
|
5235
5615
|
if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5236
5616
|
const target = resolved.real;
|
|
5237
|
-
if (!
|
|
5617
|
+
if (!fs9.existsSync(target) || !fs9.statSync(target).isFile()) {
|
|
5238
5618
|
return { error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
|
|
5239
5619
|
}
|
|
5240
5620
|
try {
|
|
5241
|
-
const content =
|
|
5621
|
+
const content = fs9.readFileSync(target, "utf-8");
|
|
5242
5622
|
const count = content.split(oldStr).length - 1;
|
|
5243
5623
|
if (count !== expectedReplacements) {
|
|
5244
5624
|
return { error: `\u671F\u671B ${expectedReplacements} \u5904\u66FF\u6362\uFF0C\u5B9E\u9645\u627E\u5230 ${count} \u5904: ${filePath}` };
|
|
5245
5625
|
}
|
|
5246
5626
|
const updated = content.replaceAll(oldStr, newStr);
|
|
5247
|
-
|
|
5627
|
+
fs9.writeFileSync(target, updated, "utf-8");
|
|
5248
5628
|
return {
|
|
5249
|
-
path:
|
|
5629
|
+
path: path10.relative(resolved.root, target),
|
|
5250
5630
|
replacements: count,
|
|
5251
5631
|
chars_before: content.length,
|
|
5252
5632
|
chars_after: updated.length
|
|
@@ -5347,8 +5727,8 @@ function registerFileTools(registry, workspace) {
|
|
|
5347
5727
|
|
|
5348
5728
|
// src/tools/shell.ts
|
|
5349
5729
|
init_cjs_shims();
|
|
5350
|
-
var
|
|
5351
|
-
var
|
|
5730
|
+
var fs11 = __toESM(require("fs"), 1);
|
|
5731
|
+
var path12 = __toESM(require("path"), 1);
|
|
5352
5732
|
|
|
5353
5733
|
// src/tools/execAsync.ts
|
|
5354
5734
|
init_cjs_shims();
|
|
@@ -5513,15 +5893,15 @@ function execAsync(file, argv, opts = {}) {
|
|
|
5513
5893
|
|
|
5514
5894
|
// src/tools/shellRuntime.ts
|
|
5515
5895
|
init_cjs_shims();
|
|
5516
|
-
var
|
|
5517
|
-
var
|
|
5896
|
+
var fs10 = __toESM(require("fs"), 1);
|
|
5897
|
+
var path11 = __toESM(require("path"), 1);
|
|
5518
5898
|
var WINDOWS_SUFFIXES = [".exe", ".cmd", ".bat", ".com", ""];
|
|
5519
5899
|
function pathApi(platform) {
|
|
5520
|
-
return platform === "win32" ?
|
|
5900
|
+
return platform === "win32" ? path11.win32 : path11.posix;
|
|
5521
5901
|
}
|
|
5522
5902
|
function defaultFileExists(p) {
|
|
5523
5903
|
try {
|
|
5524
|
-
return
|
|
5904
|
+
return fs10.statSync(p).isFile();
|
|
5525
5905
|
} catch {
|
|
5526
5906
|
return false;
|
|
5527
5907
|
}
|
|
@@ -5552,7 +5932,7 @@ function kindFromBasename(base) {
|
|
|
5552
5932
|
return "sh";
|
|
5553
5933
|
}
|
|
5554
5934
|
function busyboxArgs(file, command) {
|
|
5555
|
-
const base = (file ?
|
|
5935
|
+
const base = (file ? path11.win32.basename(file) : "busybox").toLowerCase().replace(/\.(exe|cmd|bat|com)$/, "");
|
|
5556
5936
|
return base === "busybox" ? ["sh", "-c", command] : ["-c", command];
|
|
5557
5937
|
}
|
|
5558
5938
|
function argsFor(kind, command) {
|
|
@@ -5819,7 +6199,7 @@ var MAX_STDERR = 5e3;
|
|
|
5819
6199
|
var MAX_BUFFER = 1024 * 1024 * 5;
|
|
5820
6200
|
var shellSessions = /* @__PURE__ */ new Map();
|
|
5821
6201
|
function sessionFor(workspace) {
|
|
5822
|
-
const key =
|
|
6202
|
+
const key = path12.resolve(workspace);
|
|
5823
6203
|
let s = shellSessions.get(key);
|
|
5824
6204
|
if (!s) {
|
|
5825
6205
|
s = { cwd: key };
|
|
@@ -5876,8 +6256,8 @@ function makeRunShell(workspace, spec, tasks, scope) {
|
|
|
5876
6256
|
let toRun = command;
|
|
5877
6257
|
if (target) {
|
|
5878
6258
|
try {
|
|
5879
|
-
const resolved =
|
|
5880
|
-
if (
|
|
6259
|
+
const resolved = path12.resolve(session.cwd, target);
|
|
6260
|
+
if (fs11.existsSync(resolved) && fs11.statSync(resolved).isDirectory()) {
|
|
5881
6261
|
session.cwd = resolved;
|
|
5882
6262
|
}
|
|
5883
6263
|
} catch {
|
|
@@ -6012,8 +6392,8 @@ init_cjs_shims();
|
|
|
6012
6392
|
|
|
6013
6393
|
// src/tools/nodeGrep.ts
|
|
6014
6394
|
init_cjs_shims();
|
|
6015
|
-
var
|
|
6016
|
-
var
|
|
6395
|
+
var fs12 = __toESM(require("fs"), 1);
|
|
6396
|
+
var path13 = __toESM(require("path"), 1);
|
|
6017
6397
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
6018
6398
|
".git",
|
|
6019
6399
|
"node_modules",
|
|
@@ -6038,27 +6418,27 @@ function nodeGrep(opts) {
|
|
|
6038
6418
|
} catch (err) {
|
|
6039
6419
|
throw new Error(`\u65E0\u6548\u7684\u6B63\u5219\u8868\u8FBE\u5F0F: ${opts.pattern}\uFF08${String(err)}\uFF09`);
|
|
6040
6420
|
}
|
|
6041
|
-
const target =
|
|
6042
|
-
if (!
|
|
6421
|
+
const target = path13.resolve(opts.workspace, opts.target);
|
|
6422
|
+
if (!fs12.existsSync(target)) {
|
|
6043
6423
|
throw new Error(`\u641C\u7D22\u8DEF\u5F84\u4E0D\u5B58\u5728: ${opts.target}`);
|
|
6044
6424
|
}
|
|
6045
6425
|
const matches = [];
|
|
6046
6426
|
const scanFile = (file) => {
|
|
6047
6427
|
let stat2;
|
|
6048
6428
|
try {
|
|
6049
|
-
stat2 =
|
|
6429
|
+
stat2 = fs12.statSync(file);
|
|
6050
6430
|
} catch {
|
|
6051
6431
|
return false;
|
|
6052
6432
|
}
|
|
6053
6433
|
if (!stat2.isFile() || stat2.size > maxFileBytes) return false;
|
|
6054
6434
|
let buf;
|
|
6055
6435
|
try {
|
|
6056
|
-
buf =
|
|
6436
|
+
buf = fs12.readFileSync(file);
|
|
6057
6437
|
} catch {
|
|
6058
6438
|
return false;
|
|
6059
6439
|
}
|
|
6060
6440
|
if (looksBinary(buf)) return false;
|
|
6061
|
-
const rel =
|
|
6441
|
+
const rel = path13.relative(opts.workspace, file) || path13.basename(file);
|
|
6062
6442
|
const lines = buf.toString("utf-8").split("\n");
|
|
6063
6443
|
for (let i = 0; i < lines.length; i++) {
|
|
6064
6444
|
const raw = lines[i].replace(/\r$/, "");
|
|
@@ -6069,7 +6449,7 @@ function nodeGrep(opts) {
|
|
|
6069
6449
|
}
|
|
6070
6450
|
return false;
|
|
6071
6451
|
};
|
|
6072
|
-
const stat =
|
|
6452
|
+
const stat = fs12.statSync(target);
|
|
6073
6453
|
if (stat.isFile()) {
|
|
6074
6454
|
scanFile(target);
|
|
6075
6455
|
return matches;
|
|
@@ -6079,12 +6459,12 @@ function nodeGrep(opts) {
|
|
|
6079
6459
|
const dir = stack.pop();
|
|
6080
6460
|
let entries;
|
|
6081
6461
|
try {
|
|
6082
|
-
entries =
|
|
6462
|
+
entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
6083
6463
|
} catch {
|
|
6084
6464
|
continue;
|
|
6085
6465
|
}
|
|
6086
6466
|
for (const entry of entries) {
|
|
6087
|
-
const full =
|
|
6467
|
+
const full = path13.join(dir, entry.name);
|
|
6088
6468
|
if (entry.isDirectory()) {
|
|
6089
6469
|
if (!SKIP_DIRS.has(entry.name)) stack.push(full);
|
|
6090
6470
|
} else if (entry.isFile()) {
|
|
@@ -6097,22 +6477,22 @@ function nodeGrep(opts) {
|
|
|
6097
6477
|
|
|
6098
6478
|
// src/tools/ripgrep.ts
|
|
6099
6479
|
init_cjs_shims();
|
|
6100
|
-
var
|
|
6480
|
+
var fs13 = __toESM(require("fs"), 1);
|
|
6101
6481
|
var os2 = __toESM(require("os"), 1);
|
|
6102
|
-
var
|
|
6482
|
+
var path14 = __toESM(require("path"), 1);
|
|
6103
6483
|
var import_node_module = require("module");
|
|
6104
6484
|
var cached2;
|
|
6105
6485
|
function extractBundledRipgrep() {
|
|
6106
6486
|
try {
|
|
6107
|
-
const req2 = typeof require === "function" ? require : (0, import_node_module.createRequire)(
|
|
6487
|
+
const req2 = typeof require === "function" ? require : (0, import_node_module.createRequire)(path14.join(process.cwd(), "__zqagent__.js"));
|
|
6108
6488
|
const sea = req2("node:sea");
|
|
6109
6489
|
if (!sea?.isSea?.() || typeof sea.getAsset !== "function") return null;
|
|
6110
6490
|
const asset = sea.getAsset("rg");
|
|
6111
6491
|
if (!asset) return null;
|
|
6112
|
-
const dir =
|
|
6113
|
-
const file =
|
|
6114
|
-
|
|
6115
|
-
|
|
6492
|
+
const dir = fs13.mkdtempSync(path14.join(os2.tmpdir(), "zqagent-rg-"));
|
|
6493
|
+
const file = path14.join(dir, process.platform === "win32" ? "rg.exe" : "rg");
|
|
6494
|
+
fs13.writeFileSync(file, Buffer.from(asset));
|
|
6495
|
+
fs13.chmodSync(file, 493);
|
|
6116
6496
|
return file;
|
|
6117
6497
|
} catch {
|
|
6118
6498
|
return null;
|
|
@@ -6121,7 +6501,7 @@ function extractBundledRipgrep() {
|
|
|
6121
6501
|
function resolveRipgrep() {
|
|
6122
6502
|
if (cached2 !== void 0) return cached2;
|
|
6123
6503
|
const explicit = (process.env.ZQAGENT_RG ?? "").trim();
|
|
6124
|
-
if (explicit &&
|
|
6504
|
+
if (explicit && fs13.existsSync(explicit)) {
|
|
6125
6505
|
cached2 = explicit;
|
|
6126
6506
|
return cached2;
|
|
6127
6507
|
}
|
|
@@ -6135,7 +6515,7 @@ function resolveRipgrep() {
|
|
|
6135
6515
|
env: process.env,
|
|
6136
6516
|
fileExists: (p) => {
|
|
6137
6517
|
try {
|
|
6138
|
-
return
|
|
6518
|
+
return fs13.statSync(p).isFile();
|
|
6139
6519
|
} catch {
|
|
6140
6520
|
return false;
|
|
6141
6521
|
}
|
|
@@ -6684,8 +7064,8 @@ function registerWebTools(registry) {
|
|
|
6684
7064
|
|
|
6685
7065
|
// src/tools/git.ts
|
|
6686
7066
|
init_cjs_shims();
|
|
6687
|
-
var
|
|
6688
|
-
var
|
|
7067
|
+
var fs14 = __toESM(require("fs"), 1);
|
|
7068
|
+
var path15 = __toESM(require("path"), 1);
|
|
6689
7069
|
async function runGit(repoDir, args, timeout = 15e3) {
|
|
6690
7070
|
const res = await execAsync("git", ["-c", "core.quotepath=false", ...args], {
|
|
6691
7071
|
cwd: repoDir,
|
|
@@ -6702,9 +7082,9 @@ async function runGit(repoDir, args, timeout = 15e3) {
|
|
|
6702
7082
|
async function resolveRepoRoot(workspace, pathArg) {
|
|
6703
7083
|
let dir = workspace;
|
|
6704
7084
|
if (pathArg) {
|
|
6705
|
-
const abs =
|
|
7085
|
+
const abs = path15.resolve(workspace, pathArg);
|
|
6706
7086
|
try {
|
|
6707
|
-
dir =
|
|
7087
|
+
dir = fs14.statSync(abs).isDirectory() ? abs : path15.dirname(abs);
|
|
6708
7088
|
} catch {
|
|
6709
7089
|
dir = abs;
|
|
6710
7090
|
}
|
|
@@ -6717,24 +7097,24 @@ function repoDirArg(workspace, raw) {
|
|
|
6717
7097
|
if (typeof raw !== "string") return void 0;
|
|
6718
7098
|
const v = raw.trim();
|
|
6719
7099
|
if (!v) return void 0;
|
|
6720
|
-
const base =
|
|
6721
|
-
const abs =
|
|
6722
|
-
const rel =
|
|
6723
|
-
if (rel.startsWith("..") ||
|
|
7100
|
+
const base = path15.resolve(workspace);
|
|
7101
|
+
const abs = path15.resolve(base, v);
|
|
7102
|
+
const rel = path15.relative(base, abs);
|
|
7103
|
+
if (rel.startsWith("..") || path15.isAbsolute(rel)) return void 0;
|
|
6724
7104
|
return abs;
|
|
6725
7105
|
}
|
|
6726
7106
|
function toPathspec(repoRoot, workspace, pathArg) {
|
|
6727
7107
|
if (!pathArg) return null;
|
|
6728
|
-
const abs =
|
|
6729
|
-
let rel =
|
|
7108
|
+
const abs = path15.resolve(workspace, pathArg);
|
|
7109
|
+
let rel = path15.relative(repoRoot, abs);
|
|
6730
7110
|
if (rel === "") return ".";
|
|
6731
|
-
if (rel.startsWith("..") ||
|
|
7111
|
+
if (rel.startsWith("..") || path15.isAbsolute(rel)) {
|
|
6732
7112
|
try {
|
|
6733
|
-
const realRepo =
|
|
6734
|
-
const realAbs =
|
|
6735
|
-
const rel2 =
|
|
7113
|
+
const realRepo = fs14.realpathSync(repoRoot);
|
|
7114
|
+
const realAbs = fs14.realpathSync(abs);
|
|
7115
|
+
const rel2 = path15.relative(realRepo, realAbs);
|
|
6736
7116
|
if (rel2 === "") return ".";
|
|
6737
|
-
if (!rel2.startsWith("..") && !
|
|
7117
|
+
if (!rel2.startsWith("..") && !path15.isAbsolute(rel2)) return rel2;
|
|
6738
7118
|
} catch {
|
|
6739
7119
|
}
|
|
6740
7120
|
return null;
|
|
@@ -7160,18 +7540,18 @@ var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".s
|
|
|
7160
7540
|
var SCAN_DEFAULT_DEPTH = 3;
|
|
7161
7541
|
var SCAN_MAX_DIRS = 2e3;
|
|
7162
7542
|
function readRepoHead(repoDir) {
|
|
7163
|
-
const dotGit =
|
|
7543
|
+
const dotGit = path15.join(repoDir, ".git");
|
|
7164
7544
|
try {
|
|
7165
7545
|
let headFile;
|
|
7166
|
-
if (
|
|
7167
|
-
headFile =
|
|
7546
|
+
if (fs14.statSync(dotGit).isDirectory()) {
|
|
7547
|
+
headFile = path15.join(dotGit, "HEAD");
|
|
7168
7548
|
} else {
|
|
7169
|
-
const raw =
|
|
7549
|
+
const raw = fs14.readFileSync(dotGit, "utf-8");
|
|
7170
7550
|
const m = /^gitdir:\s*(.+)$/m.exec(raw);
|
|
7171
7551
|
if (!m) return {};
|
|
7172
|
-
headFile =
|
|
7552
|
+
headFile = path15.join(path15.resolve(repoDir, m[1].trim()), "HEAD");
|
|
7173
7553
|
}
|
|
7174
|
-
const head =
|
|
7554
|
+
const head = fs14.readFileSync(headFile, "utf-8").trim();
|
|
7175
7555
|
const prefix = "ref: refs/heads/";
|
|
7176
7556
|
if (head.startsWith(prefix)) return { branch: head.slice(prefix.length), detached: false };
|
|
7177
7557
|
if (/^[0-9a-f]{7,40}$/i.test(head)) return { branch: head.slice(0, 7), detached: true };
|
|
@@ -7180,7 +7560,7 @@ function readRepoHead(repoDir) {
|
|
|
7180
7560
|
return {};
|
|
7181
7561
|
}
|
|
7182
7562
|
function scanGitRepos(workspace, depth = SCAN_DEFAULT_DEPTH) {
|
|
7183
|
-
const root =
|
|
7563
|
+
const root = path15.resolve(workspace);
|
|
7184
7564
|
const maxDepth = Number.isFinite(depth) ? Math.min(Math.max(Math.floor(depth), 0), 6) : SCAN_DEFAULT_DEPTH;
|
|
7185
7565
|
const repos = [];
|
|
7186
7566
|
const stack = [{ dir: root, depth: 0 }];
|
|
@@ -7193,24 +7573,24 @@ function scanGitRepos(workspace, depth = SCAN_DEFAULT_DEPTH) {
|
|
|
7193
7573
|
truncated = true;
|
|
7194
7574
|
break;
|
|
7195
7575
|
}
|
|
7196
|
-
const rel =
|
|
7197
|
-
if (
|
|
7576
|
+
const rel = path15.relative(root, dir).split(path15.sep).join("/");
|
|
7577
|
+
if (fs14.existsSync(path15.join(dir, ".git"))) {
|
|
7198
7578
|
repos.push({
|
|
7199
7579
|
path: rel,
|
|
7200
|
-
name: rel ?
|
|
7580
|
+
name: rel ? path15.basename(dir) : path15.basename(root),
|
|
7201
7581
|
...readRepoHead(dir)
|
|
7202
7582
|
});
|
|
7203
7583
|
}
|
|
7204
7584
|
if (d >= maxDepth) continue;
|
|
7205
7585
|
let entries;
|
|
7206
7586
|
try {
|
|
7207
|
-
entries =
|
|
7587
|
+
entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
7208
7588
|
} catch {
|
|
7209
7589
|
continue;
|
|
7210
7590
|
}
|
|
7211
7591
|
for (const e of entries) {
|
|
7212
7592
|
if (!e.isDirectory() || SCAN_SKIP_DIRS.has(e.name)) continue;
|
|
7213
|
-
stack.push({ dir:
|
|
7593
|
+
stack.push({ dir: path15.join(dir, e.name), depth: d + 1 });
|
|
7214
7594
|
}
|
|
7215
7595
|
}
|
|
7216
7596
|
repos.sort(
|
|
@@ -7220,9 +7600,9 @@ function scanGitRepos(workspace, depth = SCAN_DEFAULT_DEPTH) {
|
|
|
7220
7600
|
}
|
|
7221
7601
|
function makeGitRepos(workspace) {
|
|
7222
7602
|
return async function git_repos(args) {
|
|
7223
|
-
const root =
|
|
7603
|
+
const root = path15.resolve(workspace);
|
|
7224
7604
|
try {
|
|
7225
|
-
if (!
|
|
7605
|
+
if (!fs14.statSync(root).isDirectory()) return { root, repos: [], error: "\u5DE5\u4F5C\u533A\u4E0D\u662F\u76EE\u5F55" };
|
|
7226
7606
|
} catch {
|
|
7227
7607
|
return { root, repos: [], error: "\u5DE5\u4F5C\u533A\u76EE\u5F55\u4E0D\u5B58\u5728" };
|
|
7228
7608
|
}
|
|
@@ -7234,10 +7614,10 @@ function makeGitRepos(workspace) {
|
|
|
7234
7614
|
|
|
7235
7615
|
// src/tools/diff.ts
|
|
7236
7616
|
init_cjs_shims();
|
|
7237
|
-
var
|
|
7238
|
-
var
|
|
7617
|
+
var fs15 = __toESM(require("fs"), 1);
|
|
7618
|
+
var path16 = __toESM(require("path"), 1);
|
|
7239
7619
|
function makeApplyPatch(workspace) {
|
|
7240
|
-
const root =
|
|
7620
|
+
const root = path16.resolve(workspace);
|
|
7241
7621
|
return function apply_patch(args) {
|
|
7242
7622
|
const patch = String(args.patch ?? "");
|
|
7243
7623
|
if (!patch.trim()) return { error: "\u8865\u4E01\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
@@ -7276,12 +7656,12 @@ function makeApplyPatch(workspace) {
|
|
|
7276
7656
|
contentLines.push(current.slice(1));
|
|
7277
7657
|
i++;
|
|
7278
7658
|
}
|
|
7279
|
-
if (
|
|
7659
|
+
if (fs15.existsSync(target)) {
|
|
7280
7660
|
return { error: `\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}` };
|
|
7281
7661
|
}
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
const rel =
|
|
7662
|
+
fs15.mkdirSync(path16.dirname(target), { recursive: true });
|
|
7663
|
+
fs15.writeFileSync(target, contentLines.join("\n") + "\n", "utf-8");
|
|
7664
|
+
const rel = path16.relative(resolved.root, target);
|
|
7285
7665
|
changedFiles.push(rel);
|
|
7286
7666
|
addedFiles.push(rel);
|
|
7287
7667
|
fileCount++;
|
|
@@ -7294,11 +7674,11 @@ function makeApplyPatch(workspace) {
|
|
|
7294
7674
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${filePath}` };
|
|
7295
7675
|
}
|
|
7296
7676
|
const target = entry.real;
|
|
7297
|
-
if (!
|
|
7677
|
+
if (!fs15.existsSync(target)) {
|
|
7298
7678
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
7299
7679
|
}
|
|
7300
|
-
|
|
7301
|
-
deletedFiles.push(
|
|
7680
|
+
fs15.unlinkSync(target);
|
|
7681
|
+
deletedFiles.push(path16.relative(entry.root, target));
|
|
7302
7682
|
fileCount++;
|
|
7303
7683
|
i++;
|
|
7304
7684
|
continue;
|
|
@@ -7334,10 +7714,10 @@ function makeApplyPatch(workspace) {
|
|
|
7334
7714
|
if (!chunkLines.length) {
|
|
7335
7715
|
return { error: `\u8865\u4E01\u66F4\u65B0\u6CA1\u6709 hunks: ${filePath}` };
|
|
7336
7716
|
}
|
|
7337
|
-
if (!
|
|
7717
|
+
if (!fs15.existsSync(target)) {
|
|
7338
7718
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
7339
7719
|
}
|
|
7340
|
-
const oldLines =
|
|
7720
|
+
const oldLines = fs15.readFileSync(target, "utf-8").split("\n");
|
|
7341
7721
|
const newLines = applyPatchHunks(oldLines, chunkLines, filePath);
|
|
7342
7722
|
let finalResolved = resolved;
|
|
7343
7723
|
if (moveTo) {
|
|
@@ -7348,20 +7728,20 @@ function makeApplyPatch(workspace) {
|
|
|
7348
7728
|
finalResolved = moved;
|
|
7349
7729
|
}
|
|
7350
7730
|
const finalTarget = finalResolved.real;
|
|
7351
|
-
if (moveTo &&
|
|
7731
|
+
if (moveTo && fs15.existsSync(finalTarget) && finalTarget !== target) {
|
|
7352
7732
|
return { error: `\u6587\u4EF6\u5DF2\u5B58\u5728: ${moveTo}` };
|
|
7353
7733
|
}
|
|
7354
|
-
|
|
7355
|
-
|
|
7734
|
+
fs15.mkdirSync(path16.dirname(finalTarget), { recursive: true });
|
|
7735
|
+
fs15.writeFileSync(finalTarget, newLines.join("\n"), "utf-8");
|
|
7356
7736
|
if (moveTo && finalTarget !== target) {
|
|
7357
7737
|
const entry = resolveEntryWithinRoot(root, filePath);
|
|
7358
7738
|
if (!entry) {
|
|
7359
7739
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${filePath}` };
|
|
7360
7740
|
}
|
|
7361
|
-
|
|
7362
|
-
deletedFiles.push(
|
|
7741
|
+
fs15.unlinkSync(entry.real);
|
|
7742
|
+
deletedFiles.push(path16.relative(resolved.root, target));
|
|
7363
7743
|
}
|
|
7364
|
-
changedFiles.push(
|
|
7744
|
+
changedFiles.push(path16.relative(finalResolved.root, finalTarget));
|
|
7365
7745
|
fileCount++;
|
|
7366
7746
|
continue;
|
|
7367
7747
|
}
|
|
@@ -7373,18 +7753,18 @@ function makeApplyPatch(workspace) {
|
|
|
7373
7753
|
return { changed_files: changedFiles, added_files: addedFiles, deleted_files: deletedFiles, file_count: fileCount };
|
|
7374
7754
|
};
|
|
7375
7755
|
}
|
|
7376
|
-
function applyPatchHunks(oldLines, chunks,
|
|
7756
|
+
function applyPatchHunks(oldLines, chunks, path35) {
|
|
7377
7757
|
let newLines = [...oldLines];
|
|
7378
7758
|
let cursor = 0;
|
|
7379
7759
|
for (const chunk of chunks) {
|
|
7380
7760
|
const oldBlock = chunk.filter((l) => l[0] === " " || l[0] === "-").map((l) => l.slice(1));
|
|
7381
7761
|
const newBlock = chunk.filter((l) => l[0] === " " || l[0] === "+").map((l) => l.slice(1));
|
|
7382
7762
|
if (!oldBlock.length) {
|
|
7383
|
-
throw new Error(`\u8865\u4E01 hunk \u5FC5\u987B\u5305\u542B\u4E0A\u4E0B\u6587\u6216\u5220\u9664\u884C: ${
|
|
7763
|
+
throw new Error(`\u8865\u4E01 hunk \u5FC5\u987B\u5305\u542B\u4E0A\u4E0B\u6587\u6216\u5220\u9664\u884C: ${path35}`);
|
|
7384
7764
|
}
|
|
7385
7765
|
const matchStart = findBlockMatch(newLines, oldBlock, cursor);
|
|
7386
7766
|
if (matchStart === -1) {
|
|
7387
|
-
throw new Error(`\u8865\u4E01\u4E0A\u4E0B\u6587\u4E0D\u5339\u914D: ${
|
|
7767
|
+
throw new Error(`\u8865\u4E01\u4E0A\u4E0B\u6587\u4E0D\u5339\u914D: ${path35}`);
|
|
7388
7768
|
}
|
|
7389
7769
|
const matchEnd = matchStart + oldBlock.length;
|
|
7390
7770
|
newLines.splice(matchStart, oldBlock.length, ...newBlock);
|
|
@@ -7401,7 +7781,7 @@ function findBlockMatch(lines, block, start) {
|
|
|
7401
7781
|
return -1;
|
|
7402
7782
|
}
|
|
7403
7783
|
function makeApplyUnifiedDiff(workspace) {
|
|
7404
|
-
const root =
|
|
7784
|
+
const root = path16.resolve(workspace);
|
|
7405
7785
|
return function apply_unified_diff(args) {
|
|
7406
7786
|
const diff = String(args.diff ?? "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
7407
7787
|
if (!diff.trim()) return { error: "diff \u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
@@ -7420,26 +7800,26 @@ function makeApplyUnifiedDiff(workspace) {
|
|
|
7420
7800
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${targetPath}` };
|
|
7421
7801
|
}
|
|
7422
7802
|
const target = resolved.real;
|
|
7423
|
-
const oldLines = oldIsDevNull ? [] :
|
|
7803
|
+
const oldLines = oldIsDevNull ? [] : fs15.existsSync(target) ? fs15.readFileSync(target, "utf-8").split("\n") : [];
|
|
7424
7804
|
const newLines = applyDiffHunks(oldLines, patch.hunks, targetPath);
|
|
7425
7805
|
hunkCount += patch.hunks.length;
|
|
7426
7806
|
if (newIsDevNull) {
|
|
7427
|
-
if (
|
|
7428
|
-
if (!
|
|
7807
|
+
if (fs15.existsSync(target)) {
|
|
7808
|
+
if (!fs15.statSync(target).isFile()) {
|
|
7429
7809
|
return { error: `\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${targetPath}` };
|
|
7430
7810
|
}
|
|
7431
7811
|
const entry = resolveEntryWithinRoot(root, targetPath);
|
|
7432
7812
|
if (!entry) {
|
|
7433
7813
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${targetPath}` };
|
|
7434
7814
|
}
|
|
7435
|
-
|
|
7815
|
+
fs15.unlinkSync(entry.real);
|
|
7436
7816
|
}
|
|
7437
|
-
deletedFiles.push(
|
|
7817
|
+
deletedFiles.push(path16.relative(resolved.root, target));
|
|
7438
7818
|
continue;
|
|
7439
7819
|
}
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
const rel =
|
|
7820
|
+
fs15.mkdirSync(path16.dirname(target), { recursive: true });
|
|
7821
|
+
fs15.writeFileSync(target, newLines.join("\n"), "utf-8");
|
|
7822
|
+
const rel = path16.relative(resolved.root, target);
|
|
7443
7823
|
changedFiles.push(rel);
|
|
7444
7824
|
if (oldIsDevNull) addedFiles.push(rel);
|
|
7445
7825
|
}
|
|
@@ -7840,7 +8220,8 @@ function buildExplorerEngine(opts) {
|
|
|
7840
8220
|
model: opts.model,
|
|
7841
8221
|
instructions: EXPLORER_INSTRUCTIONS,
|
|
7842
8222
|
maxIterations: parseMaxIterations(process.env.EXPLORE_MAX_ITERATIONS, 30),
|
|
7843
|
-
hook: opts.hook
|
|
8223
|
+
hook: opts.hook,
|
|
8224
|
+
toolResultDir: opts.toolResultDir
|
|
7844
8225
|
});
|
|
7845
8226
|
}
|
|
7846
8227
|
function makeExploreTool(opts) {
|
|
@@ -7852,7 +8233,8 @@ function makeExploreTool(opts) {
|
|
|
7852
8233
|
provider: opts.provider,
|
|
7853
8234
|
model: opts.model,
|
|
7854
8235
|
readonlyRegistrars: opts.readonlyRegistrars,
|
|
7855
|
-
hook: opts.hookFactory?.()
|
|
8236
|
+
hook: opts.hookFactory?.(),
|
|
8237
|
+
toolResultDir: opts.toolResultDir
|
|
7856
8238
|
});
|
|
7857
8239
|
let report = "";
|
|
7858
8240
|
let status = "unknown";
|
|
@@ -7913,14 +8295,14 @@ function registerExploreTool(registry, opts) {
|
|
|
7913
8295
|
|
|
7914
8296
|
// src/tools/media.ts
|
|
7915
8297
|
init_cjs_shims();
|
|
7916
|
-
var
|
|
7917
|
-
var
|
|
8298
|
+
var fs17 = __toESM(require("fs"), 1);
|
|
8299
|
+
var path18 = __toESM(require("path"), 1);
|
|
7918
8300
|
|
|
7919
8301
|
// src/tools/ffmpegRuntime.ts
|
|
7920
8302
|
init_cjs_shims();
|
|
7921
|
-
var
|
|
8303
|
+
var fs16 = __toESM(require("fs"), 1);
|
|
7922
8304
|
var os3 = __toESM(require("os"), 1);
|
|
7923
|
-
var
|
|
8305
|
+
var path17 = __toESM(require("path"), 1);
|
|
7924
8306
|
var import_node_module2 = require("module");
|
|
7925
8307
|
var import_node_crypto = require("crypto");
|
|
7926
8308
|
var import_promises = require("stream/promises");
|
|
@@ -7943,29 +8325,29 @@ if (PROXY_ENV2) {
|
|
|
7943
8325
|
}
|
|
7944
8326
|
var DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
7945
8327
|
function agentBinDir(workspace) {
|
|
7946
|
-
return
|
|
8328
|
+
return path17.join(path17.resolve(workspace), ".agent", "bin");
|
|
7947
8329
|
}
|
|
7948
8330
|
function exeName(name, platform) {
|
|
7949
8331
|
return platform === "win32" ? `${name}.exe` : name;
|
|
7950
8332
|
}
|
|
7951
8333
|
function defaultFileExists2(p) {
|
|
7952
8334
|
try {
|
|
7953
|
-
return
|
|
8335
|
+
return fs16.statSync(p).isFile();
|
|
7954
8336
|
} catch {
|
|
7955
8337
|
return false;
|
|
7956
8338
|
}
|
|
7957
8339
|
}
|
|
7958
8340
|
function extractBundled(name, platform) {
|
|
7959
8341
|
try {
|
|
7960
|
-
const req2 = typeof require === "function" ? require : (0, import_node_module2.createRequire)(
|
|
8342
|
+
const req2 = typeof require === "function" ? require : (0, import_node_module2.createRequire)(path17.join(process.cwd(), "__zqagent__.js"));
|
|
7961
8343
|
const sea = req2("node:sea");
|
|
7962
8344
|
if (!sea?.isSea?.() || typeof sea.getAsset !== "function") return null;
|
|
7963
8345
|
const asset = sea.getAsset(name);
|
|
7964
8346
|
if (!asset) return null;
|
|
7965
|
-
const dir =
|
|
7966
|
-
const file =
|
|
7967
|
-
|
|
7968
|
-
|
|
8347
|
+
const dir = fs16.mkdtempSync(path17.join(os3.tmpdir(), `zqagent-${name}-`));
|
|
8348
|
+
const file = path17.join(dir, exeName(name, platform));
|
|
8349
|
+
fs16.writeFileSync(file, Buffer.from(asset));
|
|
8350
|
+
fs16.chmodSync(file, 493);
|
|
7969
8351
|
return file;
|
|
7970
8352
|
} catch {
|
|
7971
8353
|
return null;
|
|
@@ -7975,7 +8357,7 @@ function resolveOne(name, workspace, o) {
|
|
|
7975
8357
|
const envKey = name === "ffmpeg" ? ENV_FFMPEG : ENV_FFPROBE;
|
|
7976
8358
|
const explicit = (o.env[envKey] ?? "").trim();
|
|
7977
8359
|
if (explicit && o.fileExists(explicit)) return { path: explicit, source: "env" };
|
|
7978
|
-
const local =
|
|
8360
|
+
const local = path17.join(agentBinDir(workspace), exeName(name, o.platform));
|
|
7979
8361
|
if (o.fileExists(local)) return { path: local, source: "agent-bin" };
|
|
7980
8362
|
const bundled = extractBundled(name, o.platform);
|
|
7981
8363
|
if (bundled) return { path: bundled, source: "bundled" };
|
|
@@ -7989,7 +8371,7 @@ function resolveOne(name, workspace, o) {
|
|
|
7989
8371
|
var cache = /* @__PURE__ */ new Map();
|
|
7990
8372
|
function resolveMedia(workspace, opts = {}) {
|
|
7991
8373
|
const platform = opts.platform ?? process.platform;
|
|
7992
|
-
const key = `${platform}:${
|
|
8374
|
+
const key = `${platform}:${path17.resolve(workspace)}`;
|
|
7993
8375
|
const hit = cache.get(key);
|
|
7994
8376
|
if (hit) return hit;
|
|
7995
8377
|
const ctx = {
|
|
@@ -8095,7 +8477,7 @@ function parseChecksumFile(text, filename) {
|
|
|
8095
8477
|
if (!m) continue;
|
|
8096
8478
|
const hash = m[1].toLowerCase();
|
|
8097
8479
|
const name = m[2]?.trim() ?? "";
|
|
8098
|
-
if (!name || name === filename ||
|
|
8480
|
+
if (!name || name === filename || path17.basename(name) === filename) return hash;
|
|
8099
8481
|
}
|
|
8100
8482
|
const single = lines[0]?.trim();
|
|
8101
8483
|
if (lines.length === 1 && single && /^[0-9a-f]{32,128}$/i.test(single)) {
|
|
@@ -8105,7 +8487,7 @@ function parseChecksumFile(text, filename) {
|
|
|
8105
8487
|
}
|
|
8106
8488
|
async function computeChecksum(file, algo) {
|
|
8107
8489
|
const hash = (0, import_node_crypto.createHash)(algo);
|
|
8108
|
-
await (0, import_promises.pipeline)(
|
|
8490
|
+
await (0, import_promises.pipeline)(fs16.createReadStream(file), hash);
|
|
8109
8491
|
return hash.digest("hex");
|
|
8110
8492
|
}
|
|
8111
8493
|
function fetchInit() {
|
|
@@ -8120,7 +8502,7 @@ async function downloadToFile(url, dest) {
|
|
|
8120
8502
|
const resp = await (0, import_node_fetch3.default)(url, fetchInit());
|
|
8121
8503
|
if (!resp.ok) throw new Error(`HTTP ${resp.status} ${resp.statusText}`);
|
|
8122
8504
|
if (!resp.body) throw new Error("\u54CD\u5E94\u6CA1\u6709\u5185\u5BB9");
|
|
8123
|
-
await (0, import_promises.pipeline)(resp.body,
|
|
8505
|
+
await (0, import_promises.pipeline)(resp.body, fs16.createWriteStream(dest));
|
|
8124
8506
|
}
|
|
8125
8507
|
async function resolveComponentUrl(c) {
|
|
8126
8508
|
if (c.url) return c.url;
|
|
@@ -8137,12 +8519,12 @@ function findBinaryInTree(dir, names) {
|
|
|
8137
8519
|
const cur = stack.pop();
|
|
8138
8520
|
let entries;
|
|
8139
8521
|
try {
|
|
8140
|
-
entries =
|
|
8522
|
+
entries = fs16.readdirSync(cur, { withFileTypes: true });
|
|
8141
8523
|
} catch {
|
|
8142
8524
|
continue;
|
|
8143
8525
|
}
|
|
8144
8526
|
for (const e of entries) {
|
|
8145
|
-
const full =
|
|
8527
|
+
const full = path17.join(cur, e.name);
|
|
8146
8528
|
if (e.isDirectory()) {
|
|
8147
8529
|
stack.push(full);
|
|
8148
8530
|
} else if (names.includes(e.name)) {
|
|
@@ -8153,7 +8535,7 @@ function findBinaryInTree(dir, names) {
|
|
|
8153
8535
|
return null;
|
|
8154
8536
|
}
|
|
8155
8537
|
async function extractArchive(archive, dest, kind, platform) {
|
|
8156
|
-
|
|
8538
|
+
fs16.mkdirSync(dest, { recursive: true });
|
|
8157
8539
|
let res;
|
|
8158
8540
|
if (kind === "tar.xz") {
|
|
8159
8541
|
res = await execAsync("tar", ["-xJf", archive, "-C", dest]);
|
|
@@ -8189,7 +8571,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8189
8571
|
error: `\u6682\u4E0D\u652F\u6301\u81EA\u52A8\u4E0B\u8F7D\u7684\u5E73\u53F0: ${platform}/${arch}`
|
|
8190
8572
|
};
|
|
8191
8573
|
}
|
|
8192
|
-
const tmp =
|
|
8574
|
+
const tmp = fs16.mkdtempSync(path17.join(os3.tmpdir(), "zqagent-ffmpeg-"));
|
|
8193
8575
|
const binDir = agentBinDir(workspace);
|
|
8194
8576
|
const installed = {};
|
|
8195
8577
|
try {
|
|
@@ -8203,7 +8585,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8203
8585
|
}
|
|
8204
8586
|
let archive = fetched.get(url);
|
|
8205
8587
|
if (!archive) {
|
|
8206
|
-
const archivePath =
|
|
8588
|
+
const archivePath = path17.join(tmp, path17.basename(new URL(url).pathname) || `${c.name}.${c.archive}`);
|
|
8207
8589
|
log(`\u4E0B\u8F7D ${url}`);
|
|
8208
8590
|
try {
|
|
8209
8591
|
await downloadToFile(url, archivePath);
|
|
@@ -8213,7 +8595,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8213
8595
|
if (c.checksumUrl) {
|
|
8214
8596
|
const algo = c.checksumAlgo ?? "sha256";
|
|
8215
8597
|
try {
|
|
8216
|
-
const expected = parseChecksumFile(await fetchText(c.checksumUrl),
|
|
8598
|
+
const expected = parseChecksumFile(await fetchText(c.checksumUrl), path17.basename(archivePath));
|
|
8217
8599
|
if (!expected) {
|
|
8218
8600
|
notes.push(`\u672A\u80FD\u4ECE ${c.checksumUrl} \u89E3\u6790\u51FA\u6821\u9A8C\u548C\uFF0C\u5DF2\u8DF3\u8FC7\u6821\u9A8C`);
|
|
8219
8601
|
} else {
|
|
@@ -8230,24 +8612,24 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8230
8612
|
notes.push(`\u6821\u9A8C\u548C\u83B7\u53D6\u5931\u8D25 (${c.checksumUrl}): ${String(e instanceof Error ? e.message : e)}`);
|
|
8231
8613
|
}
|
|
8232
8614
|
} else {
|
|
8233
|
-
notes.push(`${
|
|
8615
|
+
notes.push(`${path17.basename(archivePath)} \u672A\u63D0\u4F9B\u6821\u9A8C\u548C\uFF08\u4EC5 HTTPS \u4F20\u8F93\u4FDD\u62A4\uFF09`);
|
|
8234
8616
|
}
|
|
8235
8617
|
archive = { file: archivePath, kind: c.archive };
|
|
8236
8618
|
fetched.set(url, archive);
|
|
8237
8619
|
}
|
|
8238
|
-
const extractDir =
|
|
8620
|
+
const extractDir = fs16.mkdtempSync(path17.join(tmp, "x-"));
|
|
8239
8621
|
const err = await extractArchive(archive.file, extractDir, archive.kind, platform);
|
|
8240
8622
|
if (err) return { ok: false, error: err };
|
|
8241
8623
|
const found = findBinaryInTree(extractDir, [exeName(c.name, platform), c.name]);
|
|
8242
8624
|
if (!found) {
|
|
8243
|
-
return { ok: false, error: `\u5F52\u6863\u5185\u672A\u627E\u5230 ${c.name}: ${
|
|
8625
|
+
return { ok: false, error: `\u5F52\u6863\u5185\u672A\u627E\u5230 ${c.name}: ${path17.basename(archive.file)}` };
|
|
8244
8626
|
}
|
|
8245
|
-
|
|
8246
|
-
const target =
|
|
8627
|
+
fs16.mkdirSync(binDir, { recursive: true });
|
|
8628
|
+
const target = path17.join(binDir, exeName(c.name, platform));
|
|
8247
8629
|
const staging = `${target}.tmp-${process.pid}`;
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8630
|
+
fs16.copyFileSync(found, staging);
|
|
8631
|
+
fs16.chmodSync(staging, 493);
|
|
8632
|
+
fs16.renameSync(staging, target);
|
|
8251
8633
|
installed[c.name] = target;
|
|
8252
8634
|
}
|
|
8253
8635
|
resetFfmpegCache();
|
|
@@ -8256,7 +8638,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8256
8638
|
} catch (e) {
|
|
8257
8639
|
return { ok: false, error: `\u5B89\u88C5\u5931\u8D25: ${String(e instanceof Error ? e.message : e)}` };
|
|
8258
8640
|
} finally {
|
|
8259
|
-
|
|
8641
|
+
fs16.rmSync(tmp, { recursive: true, force: true });
|
|
8260
8642
|
}
|
|
8261
8643
|
}
|
|
8262
8644
|
function canAutoDownload(platform, arch) {
|
|
@@ -8521,13 +8903,13 @@ function resolveTarget(workspace, value) {
|
|
|
8521
8903
|
return { abs: resolved.real, root: resolved.root };
|
|
8522
8904
|
}
|
|
8523
8905
|
function relOut(workspace, abs) {
|
|
8524
|
-
return
|
|
8906
|
+
return path18.relative(workspace, abs) || abs;
|
|
8525
8907
|
}
|
|
8526
8908
|
function registerMediaTool(registry, workspace, opts = {}) {
|
|
8527
8909
|
const platform = opts.platform ?? process.platform;
|
|
8528
8910
|
const arch = process.arch;
|
|
8529
8911
|
const media = async (args) => {
|
|
8530
|
-
const root =
|
|
8912
|
+
const root = path18.resolve(workspace);
|
|
8531
8913
|
const op = str(args["op"]);
|
|
8532
8914
|
if (!op) {
|
|
8533
8915
|
return { error: `\u7F3A\u5C11\u53C2\u6570 op\u3002\u53EF\u9009\u503C\uFF1A${OPS.join(" / ")}` };
|
|
@@ -8572,7 +8954,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
8572
8954
|
if (!input) return { error: `\u64CD\u4F5C ${op} \u9700\u8981\u53C2\u6570 input\uFF08\u5DE5\u4F5C\u533A\u5185\u76F8\u5BF9\u8DEF\u5F84\uFF09` };
|
|
8573
8955
|
const r = resolveTarget(root, input);
|
|
8574
8956
|
if ("error" in r) return { error: r.error };
|
|
8575
|
-
if (!
|
|
8957
|
+
if (!fs17.existsSync(r.abs) || !fs17.statSync(r.abs).isFile()) {
|
|
8576
8958
|
return { error: `\u8F93\u5165\u6587\u4EF6\u4E0D\u5B58\u5728: ${input}` };
|
|
8577
8959
|
}
|
|
8578
8960
|
inputAbs = r.abs;
|
|
@@ -8584,7 +8966,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
8584
8966
|
if (!output) return { error: `\u64CD\u4F5C ${op} \u9700\u8981\u53C2\u6570 output\uFF08\u5DE5\u4F5C\u533A\u5185\u76F8\u5BF9\u8DEF\u5F84\uFF09` };
|
|
8585
8967
|
const r = resolveTarget(root, output);
|
|
8586
8968
|
if ("error" in r) return { error: r.error };
|
|
8587
|
-
|
|
8969
|
+
fs17.mkdirSync(path18.dirname(r.abs), { recursive: true });
|
|
8588
8970
|
outputAbs = r.abs;
|
|
8589
8971
|
relRoot = r.root;
|
|
8590
8972
|
}
|
|
@@ -8634,7 +9016,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
8634
9016
|
};
|
|
8635
9017
|
if (outputAbs) {
|
|
8636
9018
|
try {
|
|
8637
|
-
result["size_bytes"] =
|
|
9019
|
+
result["size_bytes"] = fs17.statSync(outputAbs).size;
|
|
8638
9020
|
} catch {
|
|
8639
9021
|
}
|
|
8640
9022
|
}
|
|
@@ -8825,8 +9207,8 @@ function resolveSessionProvider(store, tenant, providerId) {
|
|
|
8825
9207
|
|
|
8826
9208
|
// src/server/usage.ts
|
|
8827
9209
|
init_cjs_shims();
|
|
8828
|
-
var
|
|
8829
|
-
var
|
|
9210
|
+
var fs18 = __toESM(require("fs"), 1);
|
|
9211
|
+
var path19 = __toESM(require("path"), 1);
|
|
8830
9212
|
var LEDGER_RETENTION_MONTHS = 12;
|
|
8831
9213
|
var USAGE_SCHEMA = 1;
|
|
8832
9214
|
var DEFAULT_PRICING = {
|
|
@@ -8907,15 +9289,15 @@ function retainedMonths(now = /* @__PURE__ */ new Date()) {
|
|
|
8907
9289
|
return out;
|
|
8908
9290
|
}
|
|
8909
9291
|
function atomicWrite(file, content) {
|
|
8910
|
-
|
|
9292
|
+
fs18.mkdirSync(path19.dirname(file), { recursive: true });
|
|
8911
9293
|
const tmp = `${file}.tmp`;
|
|
8912
|
-
|
|
8913
|
-
|
|
9294
|
+
fs18.writeFileSync(tmp, content, "utf-8");
|
|
9295
|
+
fs18.renameSync(tmp, file);
|
|
8914
9296
|
}
|
|
8915
9297
|
var UsageLedger = class {
|
|
8916
9298
|
constructor(root) {
|
|
8917
9299
|
this.root = root;
|
|
8918
|
-
this.dir =
|
|
9300
|
+
this.dir = path19.join(root, "usage");
|
|
8919
9301
|
}
|
|
8920
9302
|
root;
|
|
8921
9303
|
dir;
|
|
@@ -8928,18 +9310,18 @@ var UsageLedger = class {
|
|
|
8928
9310
|
return this.dir;
|
|
8929
9311
|
}
|
|
8930
9312
|
ledgerFile(month) {
|
|
8931
|
-
return
|
|
9313
|
+
return path19.join(this.ledgerDir, `ledger-${month}.jsonl`);
|
|
8932
9314
|
}
|
|
8933
9315
|
get rollupFile() {
|
|
8934
|
-
return
|
|
9316
|
+
return path19.join(this.ledgerDir, "rollup.json");
|
|
8935
9317
|
}
|
|
8936
9318
|
get pricingFile() {
|
|
8937
|
-
return
|
|
9319
|
+
return path19.join(this.ledgerDir, "pricing.json");
|
|
8938
9320
|
}
|
|
8939
9321
|
// ===================== 初始化 =====================
|
|
8940
9322
|
init(now = /* @__PURE__ */ new Date()) {
|
|
8941
9323
|
if (this.inited) return;
|
|
8942
|
-
|
|
9324
|
+
fs18.mkdirSync(this.ledgerDir, { recursive: true });
|
|
8943
9325
|
this.pricing = this.loadPricing();
|
|
8944
9326
|
this.rollup = this.loadRollup(now);
|
|
8945
9327
|
this.pruneLedgers(now);
|
|
@@ -8947,7 +9329,7 @@ var UsageLedger = class {
|
|
|
8947
9329
|
}
|
|
8948
9330
|
loadPricing() {
|
|
8949
9331
|
try {
|
|
8950
|
-
const raw =
|
|
9332
|
+
const raw = fs18.readFileSync(this.pricingFile, "utf-8");
|
|
8951
9333
|
const parsed = JSON.parse(raw);
|
|
8952
9334
|
const vendors = {};
|
|
8953
9335
|
for (const v of /* @__PURE__ */ new Set([
|
|
@@ -8976,7 +9358,7 @@ var UsageLedger = class {
|
|
|
8976
9358
|
}
|
|
8977
9359
|
loadRollup(now) {
|
|
8978
9360
|
try {
|
|
8979
|
-
const raw =
|
|
9361
|
+
const raw = fs18.readFileSync(this.rollupFile, "utf-8");
|
|
8980
9362
|
const parsed = JSON.parse(raw);
|
|
8981
9363
|
return {
|
|
8982
9364
|
schema: USAGE_SCHEMA,
|
|
@@ -9003,10 +9385,10 @@ var UsageLedger = class {
|
|
|
9003
9385
|
const keep = retainedMonths(now);
|
|
9004
9386
|
let changed = false;
|
|
9005
9387
|
try {
|
|
9006
|
-
for (const f of
|
|
9388
|
+
for (const f of fs18.readdirSync(this.ledgerDir)) {
|
|
9007
9389
|
const m = /^ledger-(\d{4}-\d{2})\.jsonl$/.exec(f);
|
|
9008
9390
|
if (m && !keep.has(m[1])) {
|
|
9009
|
-
|
|
9391
|
+
fs18.unlinkSync(path19.join(this.ledgerDir, f));
|
|
9010
9392
|
}
|
|
9011
9393
|
}
|
|
9012
9394
|
} catch {
|
|
@@ -9083,7 +9465,7 @@ var UsageLedger = class {
|
|
|
9083
9465
|
};
|
|
9084
9466
|
const line = JSON.stringify(rec) + "\n";
|
|
9085
9467
|
try {
|
|
9086
|
-
|
|
9468
|
+
fs18.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
|
|
9087
9469
|
} catch (e) {
|
|
9088
9470
|
console.error(`[usage] \u5199\u8D26\u672C\u5931\u8D25: ${String(e)}`);
|
|
9089
9471
|
}
|
|
@@ -9255,10 +9637,10 @@ var UsageLedger = class {
|
|
|
9255
9637
|
const rows = [];
|
|
9256
9638
|
for (const month of months) {
|
|
9257
9639
|
const file = this.ledgerFile(month);
|
|
9258
|
-
if (!
|
|
9640
|
+
if (!fs18.existsSync(file)) continue;
|
|
9259
9641
|
let content = "";
|
|
9260
9642
|
try {
|
|
9261
|
-
content =
|
|
9643
|
+
content = fs18.readFileSync(file, "utf-8");
|
|
9262
9644
|
} catch {
|
|
9263
9645
|
continue;
|
|
9264
9646
|
}
|
|
@@ -9367,10 +9749,10 @@ var UsageLedger = class {
|
|
|
9367
9749
|
const seen = /* @__PURE__ */ new Set();
|
|
9368
9750
|
for (const month of this.monthsInRange("", "")) {
|
|
9369
9751
|
const file = this.ledgerFile(month);
|
|
9370
|
-
if (!
|
|
9752
|
+
if (!fs18.existsSync(file)) continue;
|
|
9371
9753
|
let content = "";
|
|
9372
9754
|
try {
|
|
9373
|
-
content =
|
|
9755
|
+
content = fs18.readFileSync(file, "utf-8");
|
|
9374
9756
|
} catch {
|
|
9375
9757
|
continue;
|
|
9376
9758
|
}
|
|
@@ -10678,7 +11060,10 @@ data: ${JSON.stringify(entry.data)}
|
|
|
10678
11060
|
// 上下文窗口:环境变量显式覆盖 > 供应商 /models 提供的真实长度 > 引擎内置表
|
|
10679
11061
|
contextWindow: parseOptionalPositiveNumber(process.env.CONTEXT_WINDOW) ?? modelContextWindow(eff.modelMeta, activeModel),
|
|
10680
11062
|
thresholdPct: parseRatio(process.env.COMPACTION_THRESHOLD_PCT),
|
|
10681
|
-
|
|
11063
|
+
softThresholdPct: parseRatio(process.env.COMPACTION_SOFT_THRESHOLD_PCT),
|
|
11064
|
+
capTokens: parseOptionalPositiveNumber(process.env.COMPACTION_CAP_TOKENS) ?? null,
|
|
11065
|
+
// 超预算工具结果按租户/工作区隔离落盘(多租户共用一个状态目录会混内容)
|
|
11066
|
+
toolResultDir: path20.join(workspace, ".agent", "tool-results")
|
|
10682
11067
|
});
|
|
10683
11068
|
engine.setApprover(this._makeApprover(rt));
|
|
10684
11069
|
const mcpConfigs = loadServerMcpConfigs(tenant);
|
|
@@ -10859,11 +11244,11 @@ var ApiError = class extends Error {
|
|
|
10859
11244
|
// src/server/routes.ts
|
|
10860
11245
|
init_cjs_shims();
|
|
10861
11246
|
var import_http = require("http");
|
|
10862
|
-
var
|
|
11247
|
+
var path25 = __toESM(require("path"), 1);
|
|
10863
11248
|
|
|
10864
11249
|
// src/server/projects.ts
|
|
10865
11250
|
init_cjs_shims();
|
|
10866
|
-
var
|
|
11251
|
+
var path21 = __toESM(require("path"), 1);
|
|
10867
11252
|
var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
|
|
10868
11253
|
function toProjectSummary(store, tenant) {
|
|
10869
11254
|
return {
|
|
@@ -10996,9 +11381,9 @@ function createProject(store, input) {
|
|
|
10996
11381
|
if (store.loadTenant(id)) {
|
|
10997
11382
|
throw new ApiError(409, "project_exists", `\u9879\u76EE ${id} \u5DF2\u5B58\u5728`);
|
|
10998
11383
|
}
|
|
10999
|
-
const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ?
|
|
11384
|
+
const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ? path21.resolve(raw.workspaceRoot.trim()) : void 0;
|
|
11000
11385
|
const explicitName = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : "";
|
|
11001
|
-
const defaultName = workspaceRoot ?
|
|
11386
|
+
const defaultName = workspaceRoot ? path21.basename(workspaceRoot) : id;
|
|
11002
11387
|
const tenant = {
|
|
11003
11388
|
id,
|
|
11004
11389
|
name: explicitName || defaultName,
|
|
@@ -11028,7 +11413,7 @@ function buildNodeInfo(store, opts) {
|
|
|
11028
11413
|
nodeId: opts.nodeId ?? ensureNodeId(store),
|
|
11029
11414
|
platform: process.platform,
|
|
11030
11415
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
11031
|
-
version: "0.2.
|
|
11416
|
+
version: "0.2.7",
|
|
11032
11417
|
admin: opts.admin,
|
|
11033
11418
|
projects: opts.projects
|
|
11034
11419
|
};
|
|
@@ -11112,8 +11497,8 @@ function hubProxyEnabled(host) {
|
|
|
11112
11497
|
const h = (host ?? "127.0.0.1").trim();
|
|
11113
11498
|
return h === "127.0.0.1" || h === "localhost" || h === "::1";
|
|
11114
11499
|
}
|
|
11115
|
-
function isHubProxyPath(
|
|
11116
|
-
return
|
|
11500
|
+
function isHubProxyPath(path35) {
|
|
11501
|
+
return path35.startsWith(HUB_PROXY_PREFIX);
|
|
11117
11502
|
}
|
|
11118
11503
|
function decodeTarget(enc) {
|
|
11119
11504
|
try {
|
|
@@ -11207,34 +11592,34 @@ async function handleHubProxy(req2, res) {
|
|
|
11207
11592
|
|
|
11208
11593
|
// src/server/fsBrowser.ts
|
|
11209
11594
|
init_cjs_shims();
|
|
11210
|
-
var
|
|
11595
|
+
var fs19 = __toESM(require("fs"), 1);
|
|
11211
11596
|
var os4 = __toESM(require("os"), 1);
|
|
11212
|
-
var
|
|
11597
|
+
var path22 = __toESM(require("path"), 1);
|
|
11213
11598
|
var MAX_ENTRIES = 2e3;
|
|
11214
11599
|
var DEFAULT_SEARCH_LIMIT = 200;
|
|
11215
11600
|
var MAX_SEARCH_LIMIT = 1e3;
|
|
11216
11601
|
function listDirs(input) {
|
|
11217
11602
|
const base = (input ?? "").trim() || os4.homedir();
|
|
11218
|
-
const target =
|
|
11603
|
+
const target = path22.resolve(base);
|
|
11219
11604
|
let st;
|
|
11220
11605
|
try {
|
|
11221
|
-
st =
|
|
11606
|
+
st = fs19.statSync(target);
|
|
11222
11607
|
} catch {
|
|
11223
11608
|
throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
|
|
11224
11609
|
}
|
|
11225
11610
|
if (!st.isDirectory()) throw new Error(`\u4E0D\u662F\u76EE\u5F55: ${target}`);
|
|
11226
11611
|
let dirents;
|
|
11227
11612
|
try {
|
|
11228
|
-
dirents =
|
|
11613
|
+
dirents = fs19.readdirSync(target, { withFileTypes: true });
|
|
11229
11614
|
} catch {
|
|
11230
11615
|
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
|
|
11231
11616
|
}
|
|
11232
11617
|
const entries = dirents.map((d) => {
|
|
11233
|
-
const full =
|
|
11618
|
+
const full = path22.join(target, d.name);
|
|
11234
11619
|
let isDir = d.isDirectory();
|
|
11235
11620
|
if (d.isSymbolicLink()) {
|
|
11236
11621
|
try {
|
|
11237
|
-
isDir =
|
|
11622
|
+
isDir = fs19.statSync(full).isDirectory();
|
|
11238
11623
|
} catch {
|
|
11239
11624
|
}
|
|
11240
11625
|
}
|
|
@@ -11243,7 +11628,7 @@ function listDirs(input) {
|
|
|
11243
11628
|
entries.sort(
|
|
11244
11629
|
(a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
|
|
11245
11630
|
);
|
|
11246
|
-
const parentDir =
|
|
11631
|
+
const parentDir = path22.dirname(target);
|
|
11247
11632
|
return {
|
|
11248
11633
|
path: target,
|
|
11249
11634
|
parent: parentDir !== target ? parentDir : null,
|
|
@@ -11274,7 +11659,7 @@ var NAME_CACHE_MAX_DIRS = 4;
|
|
|
11274
11659
|
var NAME_CACHE_MAX_ENTRIES = 5e4;
|
|
11275
11660
|
var nameCache = /* @__PURE__ */ new Map();
|
|
11276
11661
|
function invalidateDirNames(target) {
|
|
11277
|
-
nameCache.delete(
|
|
11662
|
+
nameCache.delete(path22.resolve(target));
|
|
11278
11663
|
}
|
|
11279
11664
|
async function loadDirNames(target) {
|
|
11280
11665
|
const now = Date.now();
|
|
@@ -11286,7 +11671,7 @@ async function loadDirNames(target) {
|
|
|
11286
11671
|
}
|
|
11287
11672
|
let dirents;
|
|
11288
11673
|
try {
|
|
11289
|
-
dirents = await
|
|
11674
|
+
dirents = await fs19.promises.readdir(target, { withFileTypes: true });
|
|
11290
11675
|
} catch {
|
|
11291
11676
|
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
|
|
11292
11677
|
}
|
|
@@ -11309,11 +11694,11 @@ async function loadDirNames(target) {
|
|
|
11309
11694
|
return entries;
|
|
11310
11695
|
}
|
|
11311
11696
|
async function toDirEntry(dir, e) {
|
|
11312
|
-
const full =
|
|
11697
|
+
const full = path22.join(dir, e.name);
|
|
11313
11698
|
let isDir = e.isDir;
|
|
11314
11699
|
if (e.isLink) {
|
|
11315
11700
|
try {
|
|
11316
|
-
isDir = (await
|
|
11701
|
+
isDir = (await fs19.promises.stat(full)).isDirectory();
|
|
11317
11702
|
} catch {
|
|
11318
11703
|
}
|
|
11319
11704
|
}
|
|
@@ -11321,10 +11706,10 @@ async function toDirEntry(dir, e) {
|
|
|
11321
11706
|
}
|
|
11322
11707
|
async function searchDirs(input, query, limit) {
|
|
11323
11708
|
const base = (input ?? "").trim() || os4.homedir();
|
|
11324
|
-
const target =
|
|
11709
|
+
const target = path22.resolve(base);
|
|
11325
11710
|
let st;
|
|
11326
11711
|
try {
|
|
11327
|
-
st = await
|
|
11712
|
+
st = await fs19.promises.stat(target);
|
|
11328
11713
|
} catch {
|
|
11329
11714
|
throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
|
|
11330
11715
|
}
|
|
@@ -11354,7 +11739,7 @@ async function searchDirs(input, query, limit) {
|
|
|
11354
11739
|
);
|
|
11355
11740
|
const hit = scored.slice(0, max);
|
|
11356
11741
|
const entries = await Promise.all(hit.map(({ entry }) => toDirEntry(target, entry)));
|
|
11357
|
-
const parentDir =
|
|
11742
|
+
const parentDir = path22.dirname(target);
|
|
11358
11743
|
return {
|
|
11359
11744
|
path: target,
|
|
11360
11745
|
parent: parentDir !== target ? parentDir : null,
|
|
@@ -11365,52 +11750,52 @@ async function searchDirs(input, query, limit) {
|
|
|
11365
11750
|
};
|
|
11366
11751
|
}
|
|
11367
11752
|
function makeDirIn(parentInput, name) {
|
|
11368
|
-
const parent =
|
|
11753
|
+
const parent = path22.resolve((parentInput ?? "").trim() || os4.homedir());
|
|
11369
11754
|
const clean = (name ?? "").trim();
|
|
11370
11755
|
if (!clean) throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u4E3A\u7A7A");
|
|
11371
11756
|
if (clean === "." || clean === "..") throw new Error("\u76EE\u5F55\u540D\u975E\u6CD5");
|
|
11372
|
-
const target =
|
|
11373
|
-
const rel =
|
|
11374
|
-
if (!rel || rel.startsWith("..") ||
|
|
11757
|
+
const target = path22.resolve(parent, clean);
|
|
11758
|
+
const rel = path22.relative(parent, target);
|
|
11759
|
+
if (!rel || rel.startsWith("..") || path22.isAbsolute(rel)) {
|
|
11375
11760
|
throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u8D8A\u51FA\u5F53\u524D\u76EE\u5F55");
|
|
11376
11761
|
}
|
|
11377
11762
|
let st = null;
|
|
11378
11763
|
try {
|
|
11379
|
-
st =
|
|
11764
|
+
st = fs19.statSync(target);
|
|
11380
11765
|
} catch {
|
|
11381
11766
|
st = null;
|
|
11382
11767
|
}
|
|
11383
11768
|
if (st && !st.isDirectory()) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u6587\u4EF6: ${target}`);
|
|
11384
11769
|
try {
|
|
11385
|
-
|
|
11770
|
+
fs19.mkdirSync(target, { recursive: true });
|
|
11386
11771
|
} catch (e) {
|
|
11387
11772
|
throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
|
|
11388
11773
|
}
|
|
11389
11774
|
invalidateDirNames(parent);
|
|
11390
|
-
return { name:
|
|
11775
|
+
return { name: path22.basename(target), path: target, type: "dir" };
|
|
11391
11776
|
}
|
|
11392
11777
|
|
|
11393
11778
|
// src/server/projectFiles.ts
|
|
11394
11779
|
init_cjs_shims();
|
|
11395
|
-
var
|
|
11396
|
-
var
|
|
11780
|
+
var fs20 = __toESM(require("fs"), 1);
|
|
11781
|
+
var path23 = __toESM(require("path"), 1);
|
|
11397
11782
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
11398
11783
|
var MAX_DOWNLOAD_BYTES = 16 * 1024 * 1024;
|
|
11399
11784
|
var MAX_ENTRIES2 = 2e3;
|
|
11400
11785
|
function toPosix(p) {
|
|
11401
|
-
return p.split(
|
|
11786
|
+
return p.split(path23.sep).join("/");
|
|
11402
11787
|
}
|
|
11403
11788
|
function projectRoot(workspaceRoot) {
|
|
11404
11789
|
const r = (workspaceRoot ?? "").trim();
|
|
11405
11790
|
if (!r) {
|
|
11406
11791
|
throw new Error("\u9879\u76EE\u672A\u8BBE\u7F6E\u5DE5\u4F5C\u533A\u76EE\u5F55\uFF08workspaceRoot\uFF09\uFF0C\u65E0\u6CD5\u6D4F\u89C8\u6587\u4EF6");
|
|
11407
11792
|
}
|
|
11408
|
-
const abs =
|
|
11409
|
-
if (!
|
|
11793
|
+
const abs = path23.resolve(r);
|
|
11794
|
+
if (!fs20.existsSync(abs)) {
|
|
11410
11795
|
throw new Error(`\u5DE5\u4F5C\u533A\u76EE\u5F55\u4E0D\u5B58\u5728: ${abs}`);
|
|
11411
11796
|
}
|
|
11412
11797
|
try {
|
|
11413
|
-
return
|
|
11798
|
+
return fs20.realpathSync(abs);
|
|
11414
11799
|
} catch {
|
|
11415
11800
|
return abs;
|
|
11416
11801
|
}
|
|
@@ -11428,37 +11813,37 @@ function listProjectFiles(workspaceRoot, relPath) {
|
|
|
11428
11813
|
const dir = resolveWithin(base, relPath ?? "");
|
|
11429
11814
|
let st;
|
|
11430
11815
|
try {
|
|
11431
|
-
st =
|
|
11816
|
+
st = fs20.statSync(dir);
|
|
11432
11817
|
} catch {
|
|
11433
|
-
throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(
|
|
11818
|
+
throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(path23.relative(base, dir)) || "/"}`);
|
|
11434
11819
|
}
|
|
11435
11820
|
if (!st.isDirectory()) throw new Error("\u76EE\u6807\u4E0D\u662F\u76EE\u5F55");
|
|
11436
11821
|
let dirents;
|
|
11437
11822
|
try {
|
|
11438
|
-
dirents =
|
|
11823
|
+
dirents = fs20.readdirSync(dir, { withFileTypes: true });
|
|
11439
11824
|
} catch {
|
|
11440
11825
|
throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09");
|
|
11441
11826
|
}
|
|
11442
11827
|
const entries = dirents.map((d) => {
|
|
11443
|
-
const full =
|
|
11828
|
+
const full = path23.join(dir, d.name);
|
|
11444
11829
|
let isDir = d.isDirectory();
|
|
11445
11830
|
if (d.isSymbolicLink()) {
|
|
11446
11831
|
try {
|
|
11447
|
-
isDir =
|
|
11832
|
+
isDir = fs20.statSync(full).isDirectory();
|
|
11448
11833
|
} catch {
|
|
11449
11834
|
}
|
|
11450
11835
|
}
|
|
11451
11836
|
let size = 0;
|
|
11452
11837
|
let mtime = "";
|
|
11453
11838
|
try {
|
|
11454
|
-
const s =
|
|
11839
|
+
const s = fs20.statSync(full);
|
|
11455
11840
|
size = s.size;
|
|
11456
11841
|
mtime = s.mtime.toISOString();
|
|
11457
11842
|
} catch {
|
|
11458
11843
|
}
|
|
11459
11844
|
return {
|
|
11460
11845
|
name: d.name,
|
|
11461
|
-
path: toPosix(
|
|
11846
|
+
path: toPosix(path23.relative(base, full)),
|
|
11462
11847
|
type: isDir ? "dir" : "file",
|
|
11463
11848
|
size,
|
|
11464
11849
|
mtime
|
|
@@ -11467,12 +11852,12 @@ function listProjectFiles(workspaceRoot, relPath) {
|
|
|
11467
11852
|
entries.sort(
|
|
11468
11853
|
(a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
|
|
11469
11854
|
);
|
|
11470
|
-
const relDir = toPosix(
|
|
11471
|
-
const parentAbs =
|
|
11855
|
+
const relDir = toPosix(path23.relative(base, dir));
|
|
11856
|
+
const parentAbs = path23.dirname(dir);
|
|
11472
11857
|
return {
|
|
11473
11858
|
root: base,
|
|
11474
11859
|
path: relDir,
|
|
11475
|
-
parent: isWithinRoot(base, parentAbs) ? toPosix(
|
|
11860
|
+
parent: isWithinRoot(base, parentAbs) ? toPosix(path23.relative(base, parentAbs)) : null,
|
|
11476
11861
|
entries: entries.slice(0, MAX_ENTRIES2)
|
|
11477
11862
|
};
|
|
11478
11863
|
}
|
|
@@ -11481,17 +11866,17 @@ function readProjectFile(workspaceRoot, relPath) {
|
|
|
11481
11866
|
const file = resolveWithin(base, relPath ?? "");
|
|
11482
11867
|
let st;
|
|
11483
11868
|
try {
|
|
11484
|
-
st =
|
|
11869
|
+
st = fs20.statSync(file);
|
|
11485
11870
|
} catch {
|
|
11486
11871
|
throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
11487
11872
|
}
|
|
11488
11873
|
if (!st.isFile()) throw new Error("\u76EE\u6807\u4E0D\u662F\u6587\u4EF6");
|
|
11489
|
-
const buf =
|
|
11874
|
+
const buf = fs20.readFileSync(file);
|
|
11490
11875
|
const binary = isBinary(buf);
|
|
11491
11876
|
const truncated = buf.length > MAX_FILE_BYTES;
|
|
11492
11877
|
const slice = truncated ? buf.subarray(0, MAX_FILE_BYTES) : buf;
|
|
11493
11878
|
return {
|
|
11494
|
-
path: toPosix(
|
|
11879
|
+
path: toPosix(path23.relative(base, file)),
|
|
11495
11880
|
size: st.size,
|
|
11496
11881
|
binary,
|
|
11497
11882
|
truncated,
|
|
@@ -11503,7 +11888,7 @@ function downloadProjectFile(workspaceRoot, relPath) {
|
|
|
11503
11888
|
const file = resolveWithin(base, relPath ?? "");
|
|
11504
11889
|
let st;
|
|
11505
11890
|
try {
|
|
11506
|
-
st =
|
|
11891
|
+
st = fs20.statSync(file);
|
|
11507
11892
|
} catch {
|
|
11508
11893
|
throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
11509
11894
|
}
|
|
@@ -11511,10 +11896,10 @@ function downloadProjectFile(workspaceRoot, relPath) {
|
|
|
11511
11896
|
if (st.size > MAX_DOWNLOAD_BYTES) {
|
|
11512
11897
|
throw new Error(`\u6587\u4EF6\u8D85\u8FC7\u4E0B\u8F7D\u4E0A\u9650\uFF08${MAX_DOWNLOAD_BYTES / (1024 * 1024)}MB\uFF09`);
|
|
11513
11898
|
}
|
|
11514
|
-
const buf =
|
|
11899
|
+
const buf = fs20.readFileSync(file);
|
|
11515
11900
|
return {
|
|
11516
|
-
path: toPosix(
|
|
11517
|
-
name:
|
|
11901
|
+
path: toPosix(path23.relative(base, file)),
|
|
11902
|
+
name: path23.basename(file),
|
|
11518
11903
|
size: buf.length,
|
|
11519
11904
|
base64: buf.toString("base64")
|
|
11520
11905
|
};
|
|
@@ -11536,12 +11921,12 @@ function writeProjectFile(workspaceRoot, relPath, content) {
|
|
|
11536
11921
|
throw new Error("\u5185\u5BB9\u8D85\u8FC7 2MB \u4E0A\u9650\uFF0C\u65E0\u6CD5\u4FDD\u5B58");
|
|
11537
11922
|
}
|
|
11538
11923
|
try {
|
|
11539
|
-
|
|
11540
|
-
|
|
11924
|
+
fs20.mkdirSync(path23.dirname(file), { recursive: true });
|
|
11925
|
+
fs20.writeFileSync(file, buf);
|
|
11541
11926
|
} catch (e) {
|
|
11542
11927
|
throw new Error(`\u5199\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
11543
11928
|
}
|
|
11544
|
-
return { path: toPosix(
|
|
11929
|
+
return { path: toPosix(path23.relative(base, file)), size: buf.length };
|
|
11545
11930
|
}
|
|
11546
11931
|
function createProjectDir(workspaceRoot, relPath) {
|
|
11547
11932
|
const base = projectRoot(workspaceRoot);
|
|
@@ -11549,26 +11934,26 @@ function createProjectDir(workspaceRoot, relPath) {
|
|
|
11549
11934
|
if (!rel) throw new Error("\u76EE\u5F55\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
|
|
11550
11935
|
const dir = resolveWithin(base, rel);
|
|
11551
11936
|
try {
|
|
11552
|
-
|
|
11937
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
11553
11938
|
} catch (e) {
|
|
11554
11939
|
throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
11555
11940
|
}
|
|
11556
|
-
return { path: toPosix(
|
|
11941
|
+
return { path: toPosix(path23.relative(base, dir)) };
|
|
11557
11942
|
}
|
|
11558
11943
|
function createProjectFile(workspaceRoot, relPath, content = "") {
|
|
11559
11944
|
const base = projectRoot(workspaceRoot);
|
|
11560
11945
|
const rel = (relPath ?? "").trim();
|
|
11561
11946
|
if (!rel) throw new Error("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
|
|
11562
11947
|
const file = resolveWithin(base, rel);
|
|
11563
|
-
if (
|
|
11948
|
+
if (fs20.existsSync(file)) throw new Error("\u6587\u4EF6\u5DF2\u5B58\u5728");
|
|
11564
11949
|
const buf = Buffer.from(String(content ?? ""), "utf-8");
|
|
11565
11950
|
try {
|
|
11566
|
-
|
|
11567
|
-
|
|
11951
|
+
fs20.mkdirSync(path23.dirname(file), { recursive: true });
|
|
11952
|
+
fs20.writeFileSync(file, buf);
|
|
11568
11953
|
} catch (e) {
|
|
11569
11954
|
throw new Error(`\u521B\u5EFA\u6587\u4EF6\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
11570
11955
|
}
|
|
11571
|
-
return { path: toPosix(
|
|
11956
|
+
return { path: toPosix(path23.relative(base, file)), size: buf.length };
|
|
11572
11957
|
}
|
|
11573
11958
|
function searchProjectFiles(workspaceRoot, query) {
|
|
11574
11959
|
const base = projectRoot(workspaceRoot);
|
|
@@ -11591,7 +11976,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
11591
11976
|
if (out.length >= LIMIT) return;
|
|
11592
11977
|
let dirents;
|
|
11593
11978
|
try {
|
|
11594
|
-
dirents =
|
|
11979
|
+
dirents = fs20.readdirSync(dir, { withFileTypes: true });
|
|
11595
11980
|
} catch {
|
|
11596
11981
|
return;
|
|
11597
11982
|
}
|
|
@@ -11602,7 +11987,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
11602
11987
|
let isDir = d.isDirectory();
|
|
11603
11988
|
if (d.isSymbolicLink()) {
|
|
11604
11989
|
try {
|
|
11605
|
-
isDir =
|
|
11990
|
+
isDir = fs20.statSync(path23.join(dir, d.name)).isDirectory();
|
|
11606
11991
|
} catch {
|
|
11607
11992
|
isDir = false;
|
|
11608
11993
|
}
|
|
@@ -11610,7 +11995,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
11610
11995
|
if (d.name.toLowerCase().includes(q)) {
|
|
11611
11996
|
out.push({ path: rel, type: isDir ? "dir" : "file" });
|
|
11612
11997
|
}
|
|
11613
|
-
if (isDir) walk(
|
|
11998
|
+
if (isDir) walk(path23.join(dir, d.name), rel);
|
|
11614
11999
|
}
|
|
11615
12000
|
};
|
|
11616
12001
|
walk(base, "");
|
|
@@ -11620,7 +12005,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
11620
12005
|
// src/server/projectOps.ts
|
|
11621
12006
|
init_cjs_shims();
|
|
11622
12007
|
var os5 = __toESM(require("os"), 1);
|
|
11623
|
-
var
|
|
12008
|
+
var path24 = __toESM(require("path"), 1);
|
|
11624
12009
|
var MAX_STDOUT2 = 2e4;
|
|
11625
12010
|
var MAX_STDERR2 = 1e4;
|
|
11626
12011
|
var MAX_BUFFER3 = 1024 * 1024 * 5;
|
|
@@ -11634,7 +12019,7 @@ function resolveInvocation2(spec, prepared) {
|
|
|
11634
12019
|
}
|
|
11635
12020
|
function workRoot(workspaceRoot) {
|
|
11636
12021
|
const r = (workspaceRoot ?? "").trim();
|
|
11637
|
-
return r ?
|
|
12022
|
+
return r ? path24.resolve(r) : os5.homedir();
|
|
11638
12023
|
}
|
|
11639
12024
|
async function runProjectCommand(workspaceRoot, command, timeoutSeconds) {
|
|
11640
12025
|
const cmd = String(command ?? "");
|
|
@@ -11754,6 +12139,92 @@ async function projectGitPush(workspaceRoot, repoPath) {
|
|
|
11754
12139
|
});
|
|
11755
12140
|
}
|
|
11756
12141
|
|
|
12142
|
+
// src/server/gitCommitMessage.ts
|
|
12143
|
+
init_cjs_shims();
|
|
12144
|
+
var crypto5 = __toESM(require("crypto"), 1);
|
|
12145
|
+
var MAX_DIFF_CHARS = 12e3;
|
|
12146
|
+
var MAX_MESSAGE_CHARS = 200;
|
|
12147
|
+
var GEN_COMMIT_MESSAGE_INSTRUCTIONS = `\u4F60\u662F Git \u63D0\u4EA4\u8BF4\u660E\u751F\u6210\u52A9\u624B\u3002\u6839\u636E\u7ED9\u5B9A\u7684\u4EE3\u7801\u5DEE\u5F02\uFF0C\u751F\u6210\u4E00\u6761\u51C6\u786E\u3001\u7B80\u6D01\u7684\u63D0\u4EA4\u8BF4\u660E\u3002
|
|
12148
|
+
|
|
12149
|
+
\u8981\u6C42\uFF1A
|
|
12150
|
+
1. \u4F7F\u7528 Conventional Commits \u98CE\u683C\uFF1Atype(scope): \u63CF\u8FF0\uFF08type \u5982 feat / fix / refactor / docs / test / chore / perf / build / ci\uFF09\uFF1B
|
|
12151
|
+
2. scope \u53D6\u53D8\u66F4\u6700\u96C6\u4E2D\u7684\u6A21\u5757\u6216\u76EE\u5F55\uFF0C\u65E0\u6CD5\u5224\u65AD\u65F6\u7701\u7565\uFF1B
|
|
12152
|
+
3. \u63CF\u8FF0\u4F7F\u7528\u4E2D\u6587\uFF0C\u6982\u62EC\u672C\u6B21\u53D8\u66F4\u7684\u76EE\u7684\u6216\u6548\u679C\uFF0C\u4E0D\u8981\u9010\u6587\u4EF6\u7F57\u5217\uFF1B
|
|
12153
|
+
4. \u53EA\u8F93\u51FA\u63D0\u4EA4\u8BF4\u660E\u672C\u8EAB\uFF0C\u4E0D\u8981\u5F15\u53F7\u3001\u4EE3\u7801\u5757\u3001\u6362\u884C\u6216\u4EFB\u4F55\u89E3\u91CA\uFF1B
|
|
12154
|
+
5. \u4E0D\u8D85\u8FC7 72 \u4E2A\u5B57\u7B26\u3002`;
|
|
12155
|
+
function genReqId2() {
|
|
12156
|
+
return crypto5.randomBytes(6).toString("base64url");
|
|
12157
|
+
}
|
|
12158
|
+
function cleanCommitMessage(raw) {
|
|
12159
|
+
let text = String(raw ?? "").trim();
|
|
12160
|
+
if (!text) return "";
|
|
12161
|
+
text = text.replace(/^```[^\n]*\n?/, "").replace(/\n?```$/, "").trim();
|
|
12162
|
+
const first = text.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0) ?? "";
|
|
12163
|
+
return first.replace(/^(commit message|提交说明|提交信息|message)\s*[::]\s*/i, "").replace(/^["'“”`]+|["'“”`]+$/g, "").replace(/\s+/g, " ").trim().slice(0, MAX_MESSAGE_CHARS);
|
|
12164
|
+
}
|
|
12165
|
+
async function collectDiff(workspaceRoot, repo) {
|
|
12166
|
+
const staged = await projectGitDiff(workspaceRoot, void 0, true, repo);
|
|
12167
|
+
if (!staged.error && String(staged.diff ?? "").trim()) {
|
|
12168
|
+
return { text: String(staged.diff), source: "staged" };
|
|
12169
|
+
}
|
|
12170
|
+
const worktree = await projectGitDiff(workspaceRoot, void 0, false, repo);
|
|
12171
|
+
if (!worktree.error && String(worktree.diff ?? "").trim()) {
|
|
12172
|
+
return { text: String(worktree.diff), source: "worktree" };
|
|
12173
|
+
}
|
|
12174
|
+
const status = await projectGitStatus(workspaceRoot, void 0, repo);
|
|
12175
|
+
if (status.error) throw new ApiError(400, "git_error", String(status.error));
|
|
12176
|
+
const entries = status.entries ?? [];
|
|
12177
|
+
if (entries.length === 0) throw new ApiError(400, "no_changes", "\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4");
|
|
12178
|
+
return {
|
|
12179
|
+
text: entries.map((e) => `${e.status ?? ""} ${e.file ?? ""}`.trim()).join("\n"),
|
|
12180
|
+
source: "worktree"
|
|
12181
|
+
};
|
|
12182
|
+
}
|
|
12183
|
+
async function generateGitCommitMessage(opts) {
|
|
12184
|
+
const { store, tenant } = opts;
|
|
12185
|
+
const { text, source } = await collectDiff(opts.workspaceRoot, opts.repo);
|
|
12186
|
+
const truncated = text.length > MAX_DIFF_CHARS;
|
|
12187
|
+
const clipped = text.slice(0, MAX_DIFF_CHARS);
|
|
12188
|
+
const eff = resolveSessionProvider(store, tenant);
|
|
12189
|
+
const apiKey = eff.apiKey || process.env.OPENAI_API_KEY || "";
|
|
12190
|
+
const model = (eff.model || tenant.model || process.env.OPENAI_MODEL || process.env.MODEL || "").trim();
|
|
12191
|
+
if (!apiKey) {
|
|
12192
|
+
throw new ApiError(
|
|
12193
|
+
400,
|
|
12194
|
+
"llm_not_configured",
|
|
12195
|
+
"\u5F53\u524D\u8282\u70B9\u672A\u914D\u7F6E\u53EF\u7528\u7684 LLM\uFF0C\u8BF7\u5148\u5728\u8282\u70B9\u914D\u7F6E\u4E2D\u6DFB\u52A0\u6A21\u578B\u4E0E API Key"
|
|
12196
|
+
);
|
|
12197
|
+
}
|
|
12198
|
+
if (!model) {
|
|
12199
|
+
throw new ApiError(
|
|
12200
|
+
400,
|
|
12201
|
+
"llm_not_configured",
|
|
12202
|
+
"\u5F53\u524D\u8282\u70B9\u672A\u914D\u7F6E\u6A21\u578B\uFF0C\u8BF7\u5148\u5728\u8282\u70B9\u914D\u7F6E\u4E2D\u9009\u62E9\u6A21\u578B"
|
|
12203
|
+
);
|
|
12204
|
+
}
|
|
12205
|
+
const provider = new OpenAIProvider({ apiKey, baseURL: eff.baseURL });
|
|
12206
|
+
const raw = await provider.chat(
|
|
12207
|
+
model,
|
|
12208
|
+
[
|
|
12209
|
+
{ role: "system", content: GEN_COMMIT_MESSAGE_INSTRUCTIONS },
|
|
12210
|
+
{
|
|
12211
|
+
role: "user",
|
|
12212
|
+
content: `\u4EE5\u4E0B\u662F\u672C\u6B21\u5F85\u63D0\u4EA4\u7684\u53D8\u66F4${source === "staged" ? "\uFF08\u5DF2\u6682\u5B58\uFF09" : "\uFF08\u5DE5\u4F5C\u533A\uFF0C\u5C1A\u672A\u6682\u5B58\uFF09"}${truncated ? "\uFF0C\u5185\u5BB9\u8FC7\u957F\u5DF2\u622A\u65AD" : ""}\uFF1A
|
|
12213
|
+
|
|
12214
|
+
${clipped}`
|
|
12215
|
+
}
|
|
12216
|
+
],
|
|
12217
|
+
{},
|
|
12218
|
+
{ reqId: `llm_commit_${Date.now()}_${genReqId2()}`, logTime: (/* @__PURE__ */ new Date()).toISOString() },
|
|
12219
|
+
// 不设 maxTokens:推理模型的思考会占用输出配额,过小会导致 content 为空;
|
|
12220
|
+
// 提示词已约束只输出一行短说明。
|
|
12221
|
+
{}
|
|
12222
|
+
);
|
|
12223
|
+
const message = cleanCommitMessage(raw ?? "");
|
|
12224
|
+
if (!message) throw new ApiError(502, "llm_empty", "AI \u672A\u8FD4\u56DE\u6709\u6548\u7684\u63D0\u4EA4\u8BF4\u660E\uFF0C\u8BF7\u91CD\u8BD5");
|
|
12225
|
+
return message;
|
|
12226
|
+
}
|
|
12227
|
+
|
|
11757
12228
|
// src/server/toolsCatalog.ts
|
|
11758
12229
|
init_cjs_shims();
|
|
11759
12230
|
function buildToolCatalog(workspace) {
|
|
@@ -11859,11 +12330,11 @@ function projectStats(manager, tenantId) {
|
|
|
11859
12330
|
|
|
11860
12331
|
// src/server/nodeHost.ts
|
|
11861
12332
|
init_cjs_shims();
|
|
11862
|
-
var
|
|
12333
|
+
var fs21 = __toESM(require("fs"), 1);
|
|
11863
12334
|
var os6 = __toESM(require("os"), 1);
|
|
11864
12335
|
function diskInfo(pathStr) {
|
|
11865
12336
|
try {
|
|
11866
|
-
const st =
|
|
12337
|
+
const st = fs21.statfsSync(pathStr);
|
|
11867
12338
|
return {
|
|
11868
12339
|
path: pathStr,
|
|
11869
12340
|
total: Number(st.blocks) * Number(st.bsize),
|
|
@@ -11935,7 +12406,7 @@ function buildNodeHost(store, manager, diskPath) {
|
|
|
11935
12406
|
arch: process.arch,
|
|
11936
12407
|
release: os6.release(),
|
|
11937
12408
|
uptimeSec: Math.round(os6.uptime()),
|
|
11938
|
-
version: "0.2.
|
|
12409
|
+
version: "0.2.7",
|
|
11939
12410
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
11940
12411
|
cpu: {
|
|
11941
12412
|
count: cpus2.length,
|
|
@@ -12510,7 +12981,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
12510
12981
|
applyProvidersPatch(tenant, raw);
|
|
12511
12982
|
if (typeof raw.workspaceRoot === "string") {
|
|
12512
12983
|
const ws = raw.workspaceRoot.trim();
|
|
12513
|
-
if (ws) tenant.workspaceRoot =
|
|
12984
|
+
if (ws) tenant.workspaceRoot = path25.resolve(ws);
|
|
12514
12985
|
else delete tenant.workspaceRoot;
|
|
12515
12986
|
}
|
|
12516
12987
|
if (typeof raw.name === "string" && raw.name.trim()) {
|
|
@@ -12919,6 +13390,25 @@ function createApp(store, manager, opts = {}) {
|
|
|
12919
13390
|
}
|
|
12920
13391
|
}
|
|
12921
13392
|
},
|
|
13393
|
+
{
|
|
13394
|
+
method: "POST",
|
|
13395
|
+
pattern: ["git", "commit-message"],
|
|
13396
|
+
fn: async (_req, res, { tenant, body }) => {
|
|
13397
|
+
const raw = body ?? {};
|
|
13398
|
+
try {
|
|
13399
|
+
const message = await generateGitCommitMessage({
|
|
13400
|
+
store,
|
|
13401
|
+
tenant,
|
|
13402
|
+
workspaceRoot: tenant.workspaceRoot ?? null,
|
|
13403
|
+
repo: String(raw.repo ?? "") || void 0
|
|
13404
|
+
});
|
|
13405
|
+
ok(res, { message });
|
|
13406
|
+
} catch (e) {
|
|
13407
|
+
if (e instanceof ApiError) throw e;
|
|
13408
|
+
throw new ApiError(400, "bad_request", e instanceof Error ? e.message : String(e));
|
|
13409
|
+
}
|
|
13410
|
+
}
|
|
13411
|
+
},
|
|
12922
13412
|
{
|
|
12923
13413
|
method: "POST",
|
|
12924
13414
|
pattern: ["git", "checkout"],
|
|
@@ -13442,10 +13932,10 @@ function createApp(store, manager, opts = {}) {
|
|
|
13442
13932
|
function handle(req2, res) {
|
|
13443
13933
|
const startedAt = Date.now();
|
|
13444
13934
|
const url = new URL(req2.url ?? "/", "http://localhost");
|
|
13445
|
-
const
|
|
13935
|
+
const path35 = url.pathname;
|
|
13446
13936
|
const method = req2.method ?? "GET";
|
|
13447
13937
|
console.log(
|
|
13448
|
-
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2192 ${method} ${
|
|
13938
|
+
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2192 ${method} ${path35}${url.search}`
|
|
13449
13939
|
);
|
|
13450
13940
|
let tenantId = null;
|
|
13451
13941
|
let logged = false;
|
|
@@ -13453,7 +13943,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
13453
13943
|
if (logged) return;
|
|
13454
13944
|
logged = true;
|
|
13455
13945
|
console.log(
|
|
13456
|
-
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2190 ${method} ${
|
|
13946
|
+
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2190 ${method} ${path35} ${extra} (${Date.now() - startedAt}ms) tenant=${tenantId ?? "-"}`
|
|
13457
13947
|
);
|
|
13458
13948
|
};
|
|
13459
13949
|
res.on("finish", () => logDone(`HTTP ${res.statusCode}`));
|
|
@@ -13464,16 +13954,16 @@ function createApp(store, manager, opts = {}) {
|
|
|
13464
13954
|
res.end();
|
|
13465
13955
|
return;
|
|
13466
13956
|
}
|
|
13467
|
-
if (req2.method === "GET" &&
|
|
13957
|
+
if (req2.method === "GET" && path35 === "/healthz") {
|
|
13468
13958
|
json(res, 200, {
|
|
13469
13959
|
status: "ok",
|
|
13470
13960
|
// 版本让运维/探针能确认线上跑的是哪个构建(更新与回滚据此判断)
|
|
13471
|
-
version: "0.2.
|
|
13961
|
+
version: "0.2.7",
|
|
13472
13962
|
features: { mcp: hasServerMcpConfig() }
|
|
13473
13963
|
});
|
|
13474
13964
|
return;
|
|
13475
13965
|
}
|
|
13476
|
-
if (hubProxyEnabled(opts.host) && isHubProxyPath(
|
|
13966
|
+
if (hubProxyEnabled(opts.host) && isHubProxyPath(path35)) {
|
|
13477
13967
|
void handleHubProxy(req2, res).catch(() => {
|
|
13478
13968
|
if (!res.headersSent) {
|
|
13479
13969
|
res.writeHead(502, { "Content-Type": "application/json; charset=utf-8" });
|
|
@@ -13484,11 +13974,11 @@ function createApp(store, manager, opts = {}) {
|
|
|
13484
13974
|
});
|
|
13485
13975
|
return;
|
|
13486
13976
|
}
|
|
13487
|
-
if (!
|
|
13977
|
+
if (!path35.startsWith(API_PREFIX)) {
|
|
13488
13978
|
json(res, 404, { error: { code: "not_found", message: "\u672A\u77E5\u8DEF\u5F84" } });
|
|
13489
13979
|
return;
|
|
13490
13980
|
}
|
|
13491
|
-
const segs =
|
|
13981
|
+
const segs = path35.slice(API_PREFIX.length).split("/").filter(Boolean);
|
|
13492
13982
|
const apiKey = extractApiKey(req2);
|
|
13493
13983
|
if (!apiKey) {
|
|
13494
13984
|
json(res, 401, {
|
|
@@ -13553,7 +14043,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
13553
14043
|
).catch((err) => {
|
|
13554
14044
|
const msg = err instanceof Error ? err.message : String(err);
|
|
13555
14045
|
console.log(
|
|
13556
|
-
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} ! ${method} ${
|
|
14046
|
+
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} ! ${method} ${path35} ERROR: ${msg}`
|
|
13557
14047
|
);
|
|
13558
14048
|
error(res, err);
|
|
13559
14049
|
});
|
|
@@ -14068,6 +14558,17 @@ async function dispatchFleetCommand(deps, env3) {
|
|
|
14068
14558
|
)
|
|
14069
14559
|
);
|
|
14070
14560
|
}
|
|
14561
|
+
case "git_commit_message": {
|
|
14562
|
+
const t = project();
|
|
14563
|
+
return ok2({
|
|
14564
|
+
message: await generateGitCommitMessage({
|
|
14565
|
+
store,
|
|
14566
|
+
tenant: t,
|
|
14567
|
+
workspaceRoot: t.workspaceRoot ?? null,
|
|
14568
|
+
repo: gitRepo(payload.repo)
|
|
14569
|
+
})
|
|
14570
|
+
});
|
|
14571
|
+
}
|
|
14071
14572
|
case "git_checkout": {
|
|
14072
14573
|
const t = project();
|
|
14073
14574
|
return ok2(
|
|
@@ -14773,6 +15274,18 @@ var READONLY_CMD_TYPES = /* @__PURE__ */ new Set([
|
|
|
14773
15274
|
"git_show"
|
|
14774
15275
|
]);
|
|
14775
15276
|
var CMD_CACHE_TTL_MS = 1500;
|
|
15277
|
+
var STATE_CHANGING_EVENTS = /* @__PURE__ */ new Set([
|
|
15278
|
+
"turn_start",
|
|
15279
|
+
"turn_end",
|
|
15280
|
+
"permission_required",
|
|
15281
|
+
"ask_user_question",
|
|
15282
|
+
"plan_proposed",
|
|
15283
|
+
"directory_requested",
|
|
15284
|
+
"error",
|
|
15285
|
+
"interrupted",
|
|
15286
|
+
"queue_steering",
|
|
15287
|
+
"session_named"
|
|
15288
|
+
]);
|
|
14776
15289
|
var READONLY_CONCURRENCY = 4;
|
|
14777
15290
|
var BATCH_MAX_SUBS = 64;
|
|
14778
15291
|
var BATCH_SUB_TIMEOUT_MS = 7e3;
|
|
@@ -15008,6 +15521,20 @@ var FleetNodeBridge = class {
|
|
|
15008
15521
|
this.writeEpoch += 1;
|
|
15009
15522
|
this.cmdResultCache.clear();
|
|
15010
15523
|
}
|
|
15524
|
+
/**
|
|
15525
|
+
* 会话快照缓存失效(事件/通知驱动的状态变化)。
|
|
15526
|
+
*
|
|
15527
|
+
* 与写操作不同,这些变化只影响会话相关查询;文件树 / Git 等重查询的缓存
|
|
15528
|
+
* 保持有效,避免多会话并行运行时把整个只读缓存反复清空。
|
|
15529
|
+
*/
|
|
15530
|
+
_invalidateSessionCache() {
|
|
15531
|
+
this.writeEpoch += 1;
|
|
15532
|
+
for (const key of [...this.cmdResultCache.keys()]) {
|
|
15533
|
+
if (/^(get_state|get_events|list_sessions|get_session_states)\|/.test(key)) {
|
|
15534
|
+
this.cmdResultCache.delete(key);
|
|
15535
|
+
}
|
|
15536
|
+
}
|
|
15537
|
+
}
|
|
15011
15538
|
close() {
|
|
15012
15539
|
this.closing = true;
|
|
15013
15540
|
if (this.usageTimer) {
|
|
@@ -15036,6 +15563,7 @@ var FleetNodeBridge = class {
|
|
|
15036
15563
|
}
|
|
15037
15564
|
/** 通知广播(SessionManager 注入的 notify 回调) */
|
|
15038
15565
|
notify(tenantId, ev) {
|
|
15566
|
+
this._invalidateSessionCache();
|
|
15039
15567
|
const payload = ev.kind === "approval_request" ? {
|
|
15040
15568
|
...ev,
|
|
15041
15569
|
projectId: tenantId,
|
|
@@ -15048,6 +15576,7 @@ var FleetNodeBridge = class {
|
|
|
15048
15576
|
}
|
|
15049
15577
|
/** 事件桥接(SessionManager 注入的 onEvent 回调);发布按窗口批量合并 */
|
|
15050
15578
|
onEvent(tenantId, sessionId, entry) {
|
|
15579
|
+
if (STATE_CHANGING_EVENTS.has(entry.type)) this._invalidateSessionCache();
|
|
15051
15580
|
if (entry.type === "turn_end") this.stats.turns += 1;
|
|
15052
15581
|
else if (entry.type === "tool_finished") this.stats.toolCalls += 1;
|
|
15053
15582
|
else if (entry.type === "error") this.stats.errors += 1;
|
|
@@ -15132,7 +15661,7 @@ var FleetNodeBridge = class {
|
|
|
15132
15661
|
label: this.opts.deps.store.loadNodeLabel() ?? void 0,
|
|
15133
15662
|
platform: process.platform,
|
|
15134
15663
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
15135
|
-
version: "0.2.
|
|
15664
|
+
version: "0.2.7",
|
|
15136
15665
|
ip: primaryIp()
|
|
15137
15666
|
};
|
|
15138
15667
|
}
|
|
@@ -15166,7 +15695,10 @@ var FleetNodeBridge = class {
|
|
|
15166
15695
|
sessionId: s.sessionId,
|
|
15167
15696
|
name: s.name ?? null,
|
|
15168
15697
|
status: s.status,
|
|
15169
|
-
lastUserMessage: lastUserMessage(s)
|
|
15698
|
+
lastUserMessage: lastUserMessage(s),
|
|
15699
|
+
// 控制面的聚合邮件靠它判断「这台机器最近有没有在干活」;
|
|
15700
|
+
// 状态跃迁可能被 info 节流跳过,完成时间不会。
|
|
15701
|
+
lastCompletedAt: s.lastCompletedAt ?? null
|
|
15170
15702
|
});
|
|
15171
15703
|
}
|
|
15172
15704
|
} catch {
|
|
@@ -15184,7 +15716,7 @@ var FleetNodeBridge = class {
|
|
|
15184
15716
|
label: this.opts.deps.store.loadNodeLabel() ?? void 0,
|
|
15185
15717
|
platform: process.platform,
|
|
15186
15718
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
15187
|
-
version: "0.2.
|
|
15719
|
+
version: "0.2.7",
|
|
15188
15720
|
ip: primaryIp(),
|
|
15189
15721
|
projects,
|
|
15190
15722
|
totals,
|
|
@@ -15953,8 +16485,8 @@ init_cjs_shims();
|
|
|
15953
16485
|
|
|
15954
16486
|
// src/server/qq/bindings.ts
|
|
15955
16487
|
init_cjs_shims();
|
|
15956
|
-
var
|
|
15957
|
-
var
|
|
16488
|
+
var fs22 = __toESM(require("fs"), 1);
|
|
16489
|
+
var path26 = __toESM(require("path"), 1);
|
|
15958
16490
|
function conversationKey(target) {
|
|
15959
16491
|
switch (target.kind) {
|
|
15960
16492
|
case "c2c":
|
|
@@ -15993,12 +16525,12 @@ var QqBindingsStore = class _QqBindingsStore {
|
|
|
15993
16525
|
cache = null;
|
|
15994
16526
|
/** 基于 DATA_DIR 构造 */
|
|
15995
16527
|
static in(dataDir) {
|
|
15996
|
-
return new _QqBindingsStore(
|
|
16528
|
+
return new _QqBindingsStore(path26.join(dataDir, "qq", "bindings.json"));
|
|
15997
16529
|
}
|
|
15998
16530
|
load() {
|
|
15999
16531
|
if (this.cache) return this.cache;
|
|
16000
16532
|
try {
|
|
16001
|
-
const raw = JSON.parse(
|
|
16533
|
+
const raw = JSON.parse(fs22.readFileSync(this.file, "utf-8"));
|
|
16002
16534
|
this.cache = {
|
|
16003
16535
|
conversations: raw.conversations ?? {}
|
|
16004
16536
|
};
|
|
@@ -16030,8 +16562,8 @@ var QqBindingsStore = class _QqBindingsStore {
|
|
|
16030
16562
|
return null;
|
|
16031
16563
|
}
|
|
16032
16564
|
persist() {
|
|
16033
|
-
|
|
16034
|
-
|
|
16565
|
+
fs22.mkdirSync(path26.dirname(this.file), { recursive: true });
|
|
16566
|
+
fs22.writeFileSync(this.file, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
16035
16567
|
}
|
|
16036
16568
|
};
|
|
16037
16569
|
|
|
@@ -16443,8 +16975,8 @@ ${lines.join("\n")}`, msgId);
|
|
|
16443
16975
|
|
|
16444
16976
|
// src/server/qq/QqManager.ts
|
|
16445
16977
|
init_cjs_shims();
|
|
16446
|
-
var
|
|
16447
|
-
var
|
|
16978
|
+
var fs23 = __toESM(require("fs"), 1);
|
|
16979
|
+
var path27 = __toESM(require("path"), 1);
|
|
16448
16980
|
var QqManager = class {
|
|
16449
16981
|
constructor(opts) {
|
|
16450
16982
|
this.opts = opts;
|
|
@@ -16453,7 +16985,7 @@ var QqManager = class {
|
|
|
16453
16985
|
adapter = null;
|
|
16454
16986
|
config = null;
|
|
16455
16987
|
get configFile() {
|
|
16456
|
-
return
|
|
16988
|
+
return path27.join(this.opts.dataDir, "qq", "config.json");
|
|
16457
16989
|
}
|
|
16458
16990
|
/** 启动:读配置(env 优先,其次文件),有则建连接 */
|
|
16459
16991
|
start() {
|
|
@@ -16491,7 +17023,7 @@ var QqManager = class {
|
|
|
16491
17023
|
/** 清空配置:删除文件 + 断开 */
|
|
16492
17024
|
clearConfig() {
|
|
16493
17025
|
try {
|
|
16494
|
-
|
|
17026
|
+
fs23.rmSync(this.configFile, { force: true });
|
|
16495
17027
|
} catch {
|
|
16496
17028
|
}
|
|
16497
17029
|
this.stop();
|
|
@@ -16533,7 +17065,7 @@ var QqManager = class {
|
|
|
16533
17065
|
}
|
|
16534
17066
|
_readConfigFile() {
|
|
16535
17067
|
try {
|
|
16536
|
-
const raw = JSON.parse(
|
|
17068
|
+
const raw = JSON.parse(fs23.readFileSync(this.configFile, "utf-8"));
|
|
16537
17069
|
if (raw.appId && raw.appSecret) {
|
|
16538
17070
|
return {
|
|
16539
17071
|
appId: raw.appId,
|
|
@@ -16546,18 +17078,18 @@ var QqManager = class {
|
|
|
16546
17078
|
return null;
|
|
16547
17079
|
}
|
|
16548
17080
|
_persistConfig(cfg) {
|
|
16549
|
-
|
|
16550
|
-
|
|
17081
|
+
fs23.mkdirSync(path27.dirname(this.configFile), { recursive: true });
|
|
17082
|
+
fs23.writeFileSync(this.configFile, JSON.stringify(cfg, null, 2), "utf-8");
|
|
16551
17083
|
}
|
|
16552
17084
|
};
|
|
16553
17085
|
|
|
16554
17086
|
// src/server/qq/pairingCodes.ts
|
|
16555
17087
|
init_cjs_shims();
|
|
16556
|
-
var
|
|
17088
|
+
var crypto6 = __toESM(require("crypto"), 1);
|
|
16557
17089
|
var PAIRING_CODE_TTL_MS = 5 * 60 * 1e3;
|
|
16558
17090
|
function randomCode(len = 8) {
|
|
16559
17091
|
const alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
16560
|
-
const bytes =
|
|
17092
|
+
const bytes = crypto6.randomBytes(len);
|
|
16561
17093
|
let out = "";
|
|
16562
17094
|
for (let i = 0; i < len; i++) {
|
|
16563
17095
|
out += alphabet[bytes[i] % alphabet.length];
|
|
@@ -16599,13 +17131,13 @@ var baseUrlResolver = null;
|
|
|
16599
17131
|
function resolveBase(baseUrl) {
|
|
16600
17132
|
return baseUrlResolver ? baseUrlResolver(baseUrl) : baseUrl;
|
|
16601
17133
|
}
|
|
16602
|
-
async function req(baseUrl, token,
|
|
17134
|
+
async function req(baseUrl, token, path35, init = {}) {
|
|
16603
17135
|
const headers = {
|
|
16604
17136
|
...init.body ? { "Content-Type": "application/json" } : {},
|
|
16605
17137
|
...init.headers ?? {}
|
|
16606
17138
|
};
|
|
16607
17139
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
16608
|
-
const res = await fetch(`${resolveBase(baseUrl).replace(/\/$/, "")}${
|
|
17140
|
+
const res = await fetch(`${resolveBase(baseUrl).replace(/\/$/, "")}${path35}`, {
|
|
16609
17141
|
...init,
|
|
16610
17142
|
headers
|
|
16611
17143
|
});
|
|
@@ -16647,17 +17179,17 @@ async function hubEnrollByAccount(baseUrl, input) {
|
|
|
16647
17179
|
|
|
16648
17180
|
// src/update/controller.ts
|
|
16649
17181
|
init_cjs_shims();
|
|
16650
|
-
var
|
|
16651
|
-
var
|
|
17182
|
+
var fs28 = __toESM(require("fs"), 1);
|
|
17183
|
+
var path32 = __toESM(require("path"), 1);
|
|
16652
17184
|
|
|
16653
17185
|
// src/update/downloader.ts
|
|
16654
17186
|
init_cjs_shims();
|
|
16655
17187
|
var import_node_crypto5 = require("crypto");
|
|
16656
|
-
var
|
|
17188
|
+
var fs24 = __toESM(require("fs"), 1);
|
|
16657
17189
|
var import_node_stream2 = require("stream");
|
|
16658
17190
|
var import_promises2 = require("stream/promises");
|
|
16659
17191
|
var import_node_child_process4 = require("child_process");
|
|
16660
|
-
var
|
|
17192
|
+
var path28 = __toESM(require("path"), 1);
|
|
16661
17193
|
|
|
16662
17194
|
// src/update/manifest.ts
|
|
16663
17195
|
init_cjs_shims();
|
|
@@ -16832,8 +17364,8 @@ async function fetchManifest(url, publicKey, opts = {}) {
|
|
|
16832
17364
|
async function downloadBinary(bin, destPath, log = () => {
|
|
16833
17365
|
}) {
|
|
16834
17366
|
const partPath = `${destPath}.part`;
|
|
16835
|
-
|
|
16836
|
-
|
|
17367
|
+
fs24.mkdirSync(path28.dirname(destPath), { recursive: true });
|
|
17368
|
+
fs24.rmSync(partPath, { force: true });
|
|
16837
17369
|
let res;
|
|
16838
17370
|
try {
|
|
16839
17371
|
res = await fetch(bin.url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS2) });
|
|
@@ -16858,25 +17390,25 @@ async function downloadBinary(bin, destPath, log = () => {
|
|
|
16858
17390
|
yield chunk;
|
|
16859
17391
|
}
|
|
16860
17392
|
},
|
|
16861
|
-
|
|
17393
|
+
fs24.createWriteStream(partPath, { mode: 493 })
|
|
16862
17394
|
);
|
|
16863
17395
|
} catch (err) {
|
|
16864
|
-
|
|
17396
|
+
fs24.rmSync(partPath, { force: true });
|
|
16865
17397
|
return { error: `\u4E0B\u8F7D\u4E2D\u65AD\uFF1A${err.message}` };
|
|
16866
17398
|
}
|
|
16867
17399
|
const actual = hash.digest("hex");
|
|
16868
17400
|
if (actual !== bin.sha256) {
|
|
16869
|
-
|
|
17401
|
+
fs24.rmSync(partPath, { force: true });
|
|
16870
17402
|
return { error: `SHA-256 \u4E0D\u5339\u914D\uFF08\u671F\u671B ${bin.sha256.slice(0, 12)}\u2026\uFF0C\u5B9E\u9645 ${actual.slice(0, 12)}\u2026\uFF09` };
|
|
16871
17403
|
}
|
|
16872
17404
|
if (expected && bytes !== expected) {
|
|
16873
|
-
|
|
17405
|
+
fs24.rmSync(partPath, { force: true });
|
|
16874
17406
|
return { error: `\u4EA7\u7269\u5927\u5C0F\u4E0D\u5339\u914D\uFF08\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${bytes}\uFF09` };
|
|
16875
17407
|
}
|
|
16876
|
-
|
|
16877
|
-
|
|
16878
|
-
|
|
16879
|
-
log(`\u5DF2\u4E0B\u8F7D ${
|
|
17408
|
+
fs24.rmSync(destPath, { force: true });
|
|
17409
|
+
fs24.renameSync(partPath, destPath);
|
|
17410
|
+
fs24.chmodSync(destPath, 493);
|
|
17411
|
+
log(`\u5DF2\u4E0B\u8F7D ${path28.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
|
|
16880
17412
|
return { bytes };
|
|
16881
17413
|
}
|
|
16882
17414
|
function selfCheck(binPath, expectedVersion) {
|
|
@@ -16900,10 +17432,11 @@ function selfCheck(binPath, expectedVersion) {
|
|
|
16900
17432
|
|
|
16901
17433
|
// src/update/platform.ts
|
|
16902
17434
|
init_cjs_shims();
|
|
16903
|
-
var
|
|
17435
|
+
var fs25 = __toESM(require("fs"), 1);
|
|
17436
|
+
var path29 = __toESM(require("path"), 1);
|
|
16904
17437
|
var import_node_module3 = require("module");
|
|
16905
17438
|
function currentVersion() {
|
|
16906
|
-
const injected = "0.2.
|
|
17439
|
+
const injected = "0.2.7"?.trim();
|
|
16907
17440
|
if (injected) return injected;
|
|
16908
17441
|
try {
|
|
16909
17442
|
const req2 = (0, import_node_module3.createRequire)(importMetaUrl);
|
|
@@ -16915,12 +17448,36 @@ function currentVersion() {
|
|
|
16915
17448
|
}
|
|
16916
17449
|
function isSeaRuntime() {
|
|
16917
17450
|
if (process.env.ZQAGENT_SEA === "1") return true;
|
|
16918
|
-
const base =
|
|
17451
|
+
const base = path29.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
|
|
16919
17452
|
return base !== "node" && base !== "nodejs";
|
|
16920
17453
|
}
|
|
17454
|
+
var DEFAULT_SERVICE_NAME = "zqagent";
|
|
17455
|
+
function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
|
|
17456
|
+
const unitName = `${serviceName}.service`;
|
|
17457
|
+
for (const line of cgroup.split("\n")) {
|
|
17458
|
+
const idx = line.lastIndexOf(":");
|
|
17459
|
+
if (idx < 0) continue;
|
|
17460
|
+
const unitPath = line.slice(idx + 1).trim();
|
|
17461
|
+
if (unitPath.split("/").includes(unitName)) return "systemd";
|
|
17462
|
+
}
|
|
17463
|
+
return null;
|
|
17464
|
+
}
|
|
17465
|
+
function detectServiceManager() {
|
|
17466
|
+
if (process.platform !== "linux") return null;
|
|
17467
|
+
const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
|
|
17468
|
+
try {
|
|
17469
|
+
return serviceManagerFromCgroup(
|
|
17470
|
+
fs25.readFileSync("/proc/self/cgroup", "utf8"),
|
|
17471
|
+
serviceName
|
|
17472
|
+
);
|
|
17473
|
+
} catch {
|
|
17474
|
+
return null;
|
|
17475
|
+
}
|
|
17476
|
+
}
|
|
16921
17477
|
function serviceManager() {
|
|
16922
17478
|
const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
|
|
16923
|
-
|
|
17479
|
+
if (v === "systemd" || v === "launchd") return v;
|
|
17480
|
+
return detectServiceManager();
|
|
16924
17481
|
}
|
|
16925
17482
|
function selfUpdateCapability(hasPublicKey) {
|
|
16926
17483
|
if (!isSeaRuntime()) {
|
|
@@ -16943,22 +17500,22 @@ function selfUpdateCapability(hasPublicKey) {
|
|
|
16943
17500
|
|
|
16944
17501
|
// src/update/installer.ts
|
|
16945
17502
|
init_cjs_shims();
|
|
16946
|
-
var
|
|
16947
|
-
var
|
|
17503
|
+
var fs27 = __toESM(require("fs"), 1);
|
|
17504
|
+
var path31 = __toESM(require("path"), 1);
|
|
16948
17505
|
|
|
16949
17506
|
// src/update/state.ts
|
|
16950
17507
|
init_cjs_shims();
|
|
16951
|
-
var
|
|
16952
|
-
var
|
|
17508
|
+
var fs26 = __toESM(require("fs"), 1);
|
|
17509
|
+
var path30 = __toESM(require("path"), 1);
|
|
16953
17510
|
function updatesDir(dataDir) {
|
|
16954
|
-
return
|
|
17511
|
+
return path30.join(dataDir, "updates");
|
|
16955
17512
|
}
|
|
16956
17513
|
function statePath(dataDir) {
|
|
16957
|
-
return
|
|
17514
|
+
return path30.join(updatesDir(dataDir), "state.json");
|
|
16958
17515
|
}
|
|
16959
17516
|
function loadState(dataDir) {
|
|
16960
17517
|
try {
|
|
16961
|
-
const raw =
|
|
17518
|
+
const raw = fs26.readFileSync(statePath(dataDir), "utf-8");
|
|
16962
17519
|
const parsed = JSON.parse(raw);
|
|
16963
17520
|
if (typeof parsed === "object" && parsed !== null) return parsed;
|
|
16964
17521
|
} catch {
|
|
@@ -16967,11 +17524,11 @@ function loadState(dataDir) {
|
|
|
16967
17524
|
}
|
|
16968
17525
|
function saveState(dataDir, state) {
|
|
16969
17526
|
const dir = updatesDir(dataDir);
|
|
16970
|
-
|
|
17527
|
+
fs26.mkdirSync(dir, { recursive: true });
|
|
16971
17528
|
const file = statePath(dataDir);
|
|
16972
17529
|
const tmp = `${file}.tmp`;
|
|
16973
|
-
|
|
16974
|
-
|
|
17530
|
+
fs26.writeFileSync(tmp, JSON.stringify(state, null, 2), "utf-8");
|
|
17531
|
+
fs26.renameSync(tmp, file);
|
|
16975
17532
|
}
|
|
16976
17533
|
function patchState(dataDir, patch) {
|
|
16977
17534
|
const next = { ...loadState(dataDir), ...patch };
|
|
@@ -16981,36 +17538,36 @@ function patchState(dataDir, patch) {
|
|
|
16981
17538
|
|
|
16982
17539
|
// src/update/installer.ts
|
|
16983
17540
|
function backupDir(dataDir) {
|
|
16984
|
-
return
|
|
17541
|
+
return path31.join(updatesDir(dataDir), "backup");
|
|
16985
17542
|
}
|
|
16986
17543
|
function backupCurrent(dataDir, execPath, version) {
|
|
16987
17544
|
const dir = backupDir(dataDir);
|
|
16988
|
-
|
|
16989
|
-
const target =
|
|
16990
|
-
|
|
16991
|
-
|
|
17545
|
+
fs27.mkdirSync(dir, { recursive: true });
|
|
17546
|
+
const target = path31.join(dir, `zqagent-${version}${process.platform === "win32" ? ".exe" : ""}`);
|
|
17547
|
+
fs27.copyFileSync(execPath, target);
|
|
17548
|
+
fs27.chmodSync(target, 493);
|
|
16992
17549
|
return target;
|
|
16993
17550
|
}
|
|
16994
17551
|
function replaceExecutable(execPath, stagedPath) {
|
|
16995
17552
|
if (process.platform === "win32") {
|
|
16996
17553
|
const oldPath = `${execPath}.old`;
|
|
16997
|
-
|
|
16998
|
-
|
|
16999
|
-
|
|
17000
|
-
|
|
17554
|
+
fs27.rmSync(oldPath, { force: true });
|
|
17555
|
+
fs27.renameSync(execPath, oldPath);
|
|
17556
|
+
fs27.copyFileSync(stagedPath, execPath);
|
|
17557
|
+
fs27.chmodSync(execPath, 493);
|
|
17001
17558
|
return;
|
|
17002
17559
|
}
|
|
17003
17560
|
const tmp = `${execPath}.new`;
|
|
17004
|
-
|
|
17561
|
+
fs27.rmSync(tmp, { force: true });
|
|
17005
17562
|
try {
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17563
|
+
fs27.copyFileSync(stagedPath, tmp);
|
|
17564
|
+
fs27.chmodSync(tmp, 493);
|
|
17565
|
+
fs27.renameSync(tmp, execPath);
|
|
17009
17566
|
} catch (err) {
|
|
17010
|
-
|
|
17567
|
+
fs27.rmSync(tmp, { force: true });
|
|
17011
17568
|
throw err;
|
|
17012
17569
|
}
|
|
17013
|
-
|
|
17570
|
+
fs27.rmSync(stagedPath, { force: true });
|
|
17014
17571
|
}
|
|
17015
17572
|
function installStaged(opts) {
|
|
17016
17573
|
const log = opts.log ?? (() => {
|
|
@@ -17018,7 +17575,7 @@ function installStaged(opts) {
|
|
|
17018
17575
|
const from = currentVersion();
|
|
17019
17576
|
log(`\u6B63\u5728\u5B89\u88C5 v${opts.version}\uFF08\u5F53\u524D v${from}\uFF09`);
|
|
17020
17577
|
const backupPath = backupCurrent(opts.dataDir, opts.execPath, from);
|
|
17021
|
-
log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${
|
|
17578
|
+
log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${path31.basename(backupPath)}`);
|
|
17022
17579
|
replaceExecutable(opts.execPath, opts.stagedPath);
|
|
17023
17580
|
return patchState(opts.dataDir, {
|
|
17024
17581
|
stagedVersion: void 0,
|
|
@@ -17037,18 +17594,18 @@ function rollback(dataDir, execPath, log = () => {
|
|
|
17037
17594
|
}) {
|
|
17038
17595
|
const state = readRollbackTarget(dataDir);
|
|
17039
17596
|
if (!state?.backupPath || !state.backupVersion) return null;
|
|
17040
|
-
if (!
|
|
17597
|
+
if (!fs27.existsSync(state.backupPath)) return null;
|
|
17041
17598
|
log(`\u6B63\u5728\u56DE\u6EDA\u5230 v${state.backupVersion}`);
|
|
17042
17599
|
if (process.platform === "win32") {
|
|
17043
|
-
|
|
17044
|
-
|
|
17045
|
-
|
|
17046
|
-
|
|
17600
|
+
fs27.rmSync(`${execPath}.old`, { force: true });
|
|
17601
|
+
fs27.renameSync(execPath, `${execPath}.old`);
|
|
17602
|
+
fs27.copyFileSync(state.backupPath, execPath);
|
|
17603
|
+
fs27.chmodSync(execPath, 493);
|
|
17047
17604
|
} else {
|
|
17048
17605
|
const tmp = `${execPath}.rollback`;
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
|
|
17606
|
+
fs27.copyFileSync(state.backupPath, tmp);
|
|
17607
|
+
fs27.chmodSync(tmp, 493);
|
|
17608
|
+
fs27.renameSync(tmp, execPath);
|
|
17052
17609
|
}
|
|
17053
17610
|
patchState(dataDir, {
|
|
17054
17611
|
pendingVersion: state.backupVersion,
|
|
@@ -17062,7 +17619,7 @@ function rollback(dataDir, execPath, log = () => {
|
|
|
17062
17619
|
}
|
|
17063
17620
|
function readRollbackTarget(dataDir) {
|
|
17064
17621
|
try {
|
|
17065
|
-
const raw =
|
|
17622
|
+
const raw = fs27.readFileSync(path31.join(updatesDir(dataDir), "state.json"), "utf-8");
|
|
17066
17623
|
const s = JSON.parse(raw);
|
|
17067
17624
|
if (s.backupPath && s.backupVersion) return s;
|
|
17068
17625
|
} catch {
|
|
@@ -17145,7 +17702,7 @@ var UpdateController = class {
|
|
|
17145
17702
|
reconcile() {
|
|
17146
17703
|
const state = loadState(this.opts.dataDir);
|
|
17147
17704
|
const current = this.currentVersion;
|
|
17148
|
-
if (state.stagedPath && !
|
|
17705
|
+
if (state.stagedPath && !fs28.existsSync(state.stagedPath)) {
|
|
17149
17706
|
patchState(this.opts.dataDir, { stagedVersion: void 0, stagedPath: void 0 });
|
|
17150
17707
|
}
|
|
17151
17708
|
const patch = { runningVersion: current };
|
|
@@ -17160,7 +17717,7 @@ var UpdateController = class {
|
|
|
17160
17717
|
}
|
|
17161
17718
|
patchState(this.opts.dataDir, patch);
|
|
17162
17719
|
if (process.platform === "win32") {
|
|
17163
|
-
|
|
17720
|
+
fs28.rmSync(`${process.execPath}.old`, { force: true });
|
|
17164
17721
|
}
|
|
17165
17722
|
}
|
|
17166
17723
|
schedule(delayMs) {
|
|
@@ -17236,8 +17793,8 @@ var UpdateController = class {
|
|
|
17236
17793
|
const bin = manifest.binaries[platformKey()];
|
|
17237
17794
|
if (!bin) return `\u6E05\u5355\u6CA1\u6709\u5F53\u524D\u5E73\u53F0\uFF08${platformKey()}\uFF09\u7684\u4EA7\u7269`;
|
|
17238
17795
|
let stagedPath = state.stagedPath;
|
|
17239
|
-
if (!(state.stagedVersion === manifest.version && stagedPath &&
|
|
17240
|
-
stagedPath =
|
|
17796
|
+
if (!(state.stagedVersion === manifest.version && stagedPath && fs28.existsSync(stagedPath))) {
|
|
17797
|
+
stagedPath = path32.join(
|
|
17241
17798
|
updatesDir(this.opts.dataDir),
|
|
17242
17799
|
"staged",
|
|
17243
17800
|
`zqagent-${manifest.version}${process.platform === "win32" ? ".exe" : ""}`
|
|
@@ -17249,7 +17806,7 @@ var UpdateController = class {
|
|
|
17249
17806
|
}
|
|
17250
17807
|
const selfTest = selfCheck(stagedPath, manifest.version);
|
|
17251
17808
|
if (!selfTest.ok) {
|
|
17252
|
-
|
|
17809
|
+
fs28.rmSync(stagedPath, { force: true });
|
|
17253
17810
|
this.recordFailure(manifest.version, selfTest.error ?? "\u81EA\u68C0\u5931\u8D25");
|
|
17254
17811
|
return `\u5B89\u88C5\u5305\u81EA\u68C0\u5931\u8D25\uFF1A${selfTest.error}`;
|
|
17255
17812
|
}
|
|
@@ -17390,7 +17947,7 @@ function pemFile(key) {
|
|
|
17390
17947
|
const p = env2(key);
|
|
17391
17948
|
if (!p) return void 0;
|
|
17392
17949
|
try {
|
|
17393
|
-
return
|
|
17950
|
+
return fs29.readFileSync(p);
|
|
17394
17951
|
} catch (e) {
|
|
17395
17952
|
console.warn(`[tls] \u8BFB\u53D6 ${key}=${p} \u5931\u8D25: ${e instanceof Error ? e.message : e}`);
|
|
17396
17953
|
return void 0;
|
|
@@ -17412,7 +17969,7 @@ function seedDemoTenant(store) {
|
|
|
17412
17969
|
apiKey: env2("OPENAI_API_KEY"),
|
|
17413
17970
|
baseURL: env2("OPENAI_BASE_URL")
|
|
17414
17971
|
},
|
|
17415
|
-
workspaceRoot: workspace ?
|
|
17972
|
+
workspaceRoot: workspace ? path33.resolve(workspace) : void 0,
|
|
17416
17973
|
allowedCommands: []
|
|
17417
17974
|
};
|
|
17418
17975
|
store.saveTenant(demo);
|
|
@@ -17466,9 +18023,9 @@ function syncTenantModels(store) {
|
|
|
17466
18023
|
tenant.model = model;
|
|
17467
18024
|
changed = true;
|
|
17468
18025
|
}
|
|
17469
|
-
if (workspace && tenant.workspaceRoot !==
|
|
17470
|
-
console.log(`[sync] \u79DF\u6237 ${id} workspaceRoot \u5DF2\u540C\u6B65: ${tenant.workspaceRoot ?? "(\u6C99\u7BB1)"} \u2192 ${
|
|
17471
|
-
tenant.workspaceRoot =
|
|
18026
|
+
if (workspace && tenant.workspaceRoot !== path33.resolve(workspace)) {
|
|
18027
|
+
console.log(`[sync] \u79DF\u6237 ${id} workspaceRoot \u5DF2\u540C\u6B65: ${tenant.workspaceRoot ?? "(\u6C99\u7BB1)"} \u2192 ${path33.resolve(workspace)}`);
|
|
18028
|
+
tenant.workspaceRoot = path33.resolve(workspace);
|
|
17472
18029
|
changed = true;
|
|
17473
18030
|
}
|
|
17474
18031
|
if (apiKey && tenant.provider.apiKey !== apiKey) {
|
|
@@ -17500,7 +18057,7 @@ function exitWhenOrphaned() {
|
|
|
17500
18057
|
timer.unref();
|
|
17501
18058
|
}
|
|
17502
18059
|
function startServer(opts = {}) {
|
|
17503
|
-
const dataDir =
|
|
18060
|
+
const dataDir = path33.resolve(opts.dataDir ?? env2("DATA_DIR") ?? DEFAULT_DATA_DIR);
|
|
17504
18061
|
const port = opts.port ?? Number(env2("PORT") ?? 8787);
|
|
17505
18062
|
const host = opts.host ?? env2("HOST") ?? "127.0.0.1";
|
|
17506
18063
|
exitWhenOrphaned();
|
|
@@ -17530,7 +18087,7 @@ function startServer(opts = {}) {
|
|
|
17530
18087
|
}
|
|
17531
18088
|
const adminKey = ensureAdminKey();
|
|
17532
18089
|
const secretKey = env2("SECRET_KEY");
|
|
17533
|
-
const secretBox = secretKey ? SecretBox.fromHex(secretKey) : SecretBox.fromFile(
|
|
18090
|
+
const secretBox = secretKey ? SecretBox.fromHex(secretKey) : SecretBox.fromFile(path33.join(dataDir, "secret.key"));
|
|
17534
18091
|
if (secretKey && !secretBox) {
|
|
17535
18092
|
console.warn("[crypto] SECRET_KEY \u975E\u6CD5\uFF08\u9700 64 \u4F4D hex\uFF09\uFF0C\u901A\u77E5\u51ED\u636E\u5C06\u9000\u5316\u4E3A\u660E\u6587");
|
|
17536
18093
|
}
|
|
@@ -17759,7 +18316,7 @@ function printReady(info, qq) {
|
|
|
17759
18316
|
`zqagent server \u76D1\u542C http://${info.host}:${info.port}`,
|
|
17760
18317
|
`\u6570\u636E\u76EE\u5F55: ${info.dataDir}`,
|
|
17761
18318
|
`\u5BA1\u6279\u6302\u8D77\u8D85\u65F6: ${env2("APPROVAL_TIMEOUT_SECONDS") ?? 900}s\uFF08\u8D85\u65F6\u540E\u56DE\u6536\u5185\u5B58\u4EFB\u52A1\uFF0C\u53EF\u7ECF resume \u6062\u590D\uFF09`,
|
|
17762
|
-
env2("ZQAGENT_LOG_ADMIN_KEY") === "0" ? `\u7BA1\u7406\u5458\u5BC6\u94A5: ${maskKey(info.adminKey)}\uFF08\u5DF2\u8131\u654F\uFF0C\u5B8C\u6574\u503C\u89C1 ${
|
|
18319
|
+
env2("ZQAGENT_LOG_ADMIN_KEY") === "0" ? `\u7BA1\u7406\u5458\u5BC6\u94A5: ${maskKey(info.adminKey)}\uFF08\u5DF2\u8131\u654F\uFF0C\u5B8C\u6574\u503C\u89C1 ${path33.join(info.dataDir, "admin.json")}\uFF09` : `\u7BA1\u7406\u5458\u5BC6\u94A5: ${info.adminKey}\uFF08\u63A7\u5236\u53F0\u591A\u9879\u76EE\u5165\u53E3\uFF1A/api/v1/projects\uFF1B\u9879\u76EE\u7EA7\u63A5\u53E3\u52A0 X-Project-Id \u5934\uFF09`
|
|
17763
18320
|
];
|
|
17764
18321
|
if (info.fleetNodeId) {
|
|
17765
18322
|
lines.push(
|
|
@@ -17780,7 +18337,7 @@ function printReady(info, qq) {
|
|
|
17780
18337
|
|
|
17781
18338
|
// src/cli/account-enroll.ts
|
|
17782
18339
|
init_cjs_shims();
|
|
17783
|
-
var
|
|
18340
|
+
var path34 = __toESM(require("path"), 1);
|
|
17784
18341
|
var readline = __toESM(require("readline/promises"), 1);
|
|
17785
18342
|
var SKIP_ANSWERS = /* @__PURE__ */ new Set(["-", "skip", "none", "no"]);
|
|
17786
18343
|
function resolveDefaultHub(hubUrl2, env3 = process.env) {
|
|
@@ -17792,7 +18349,7 @@ function parseHubAnswer(answer, fallback) {
|
|
|
17792
18349
|
return (a || fallback).replace(/\/$/, "");
|
|
17793
18350
|
}
|
|
17794
18351
|
function resolveDataDir(dataDir) {
|
|
17795
|
-
return
|
|
18352
|
+
return path34.resolve(dataDir ?? process.env.DATA_DIR ?? DEFAULT_DATA_DIR);
|
|
17796
18353
|
}
|
|
17797
18354
|
async function maybeAccountEnroll(opts = {}) {
|
|
17798
18355
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|