@zqagent/core 0.2.4 → 0.2.6
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 +125 -43
- 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 +1135 -599
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +1141 -605
- 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 +974 -442
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +980 -448
- 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.js
CHANGED
|
@@ -555,8 +555,8 @@ import "dotenv/config";
|
|
|
555
555
|
|
|
556
556
|
// src/server/runtime.ts
|
|
557
557
|
init_esm_shims();
|
|
558
|
-
import * as
|
|
559
|
-
import * as
|
|
558
|
+
import * as fs28 from "fs";
|
|
559
|
+
import * as path34 from "path";
|
|
560
560
|
|
|
561
561
|
// src/server/store.ts
|
|
562
562
|
init_esm_shims();
|
|
@@ -1045,6 +1045,10 @@ function configDir() {
|
|
|
1045
1045
|
const base = process.env.XDG_CONFIG_HOME || (IS_WINDOWS ? process.env.APPDATA : void 0) || path3.join(homeDir(), ".config");
|
|
1046
1046
|
return path3.join(base, APP_DIR_NAME);
|
|
1047
1047
|
}
|
|
1048
|
+
function stateDir() {
|
|
1049
|
+
const base = process.env.XDG_STATE_HOME || (IS_WINDOWS ? process.env.LOCALAPPDATA : void 0) || path3.join(homeDir(), ".local", "state");
|
|
1050
|
+
return path3.join(base, APP_DIR_NAME);
|
|
1051
|
+
}
|
|
1048
1052
|
|
|
1049
1053
|
// ../shared/src/llm-presets.ts
|
|
1050
1054
|
init_esm_shims();
|
|
@@ -1608,6 +1612,7 @@ var Store = class {
|
|
|
1608
1612
|
|
|
1609
1613
|
// src/server/session.ts
|
|
1610
1614
|
init_esm_shims();
|
|
1615
|
+
import * as path21 from "path";
|
|
1611
1616
|
import { AsyncLocalStorage } from "async_hooks";
|
|
1612
1617
|
|
|
1613
1618
|
// src/engine/TurnEngine.ts
|
|
@@ -1735,8 +1740,11 @@ function lookupContextWindow(modelId) {
|
|
|
1735
1740
|
return DEFAULT_CONTEXT_WINDOW;
|
|
1736
1741
|
}
|
|
1737
1742
|
var KEEP_RECENT_FRACTION = 0.25;
|
|
1738
|
-
var
|
|
1739
|
-
var
|
|
1743
|
+
var SOFT_THRESHOLD_RATIO = 0.75;
|
|
1744
|
+
var ELIDE_KEEP_FRACTION = 0.15;
|
|
1745
|
+
var SUMMARY_MAX_TOKENS = 3e3;
|
|
1746
|
+
var SPAN_TOOL_RESULT_CLIP = 200;
|
|
1747
|
+
var SPAN_BUDGET_CHARS = 8e4;
|
|
1740
1748
|
var USER_MESSAGE_CLIP = 600;
|
|
1741
1749
|
var USER_MESSAGES_MAX = 40;
|
|
1742
1750
|
var TRIM_FRACTION = 0.1;
|
|
@@ -1759,9 +1767,22 @@ function triggerTokens(contextWindow, thresholdPct = DEFAULT_THRESHOLD_PCT, capT
|
|
|
1759
1767
|
}
|
|
1760
1768
|
return base;
|
|
1761
1769
|
}
|
|
1770
|
+
function softTriggerTokens(contextWindow, thresholdPct = DEFAULT_THRESHOLD_PCT, capTokens, softThresholdPct) {
|
|
1771
|
+
const hard = triggerTokens(contextWindow, thresholdPct, capTokens);
|
|
1772
|
+
if (softThresholdPct != null && Number.isFinite(softThresholdPct) && softThresholdPct > 0) {
|
|
1773
|
+
return Math.min(
|
|
1774
|
+
triggerTokens(contextWindow, softThresholdPct, capTokens),
|
|
1775
|
+
Math.floor(hard * 0.95)
|
|
1776
|
+
);
|
|
1777
|
+
}
|
|
1778
|
+
return Math.floor(hard * SOFT_THRESHOLD_RATIO);
|
|
1779
|
+
}
|
|
1762
1780
|
function shouldCompact(signal, contextWindow, thresholdPct, capTokens) {
|
|
1763
1781
|
return signal >= triggerTokens(contextWindow, thresholdPct, capTokens);
|
|
1764
1782
|
}
|
|
1783
|
+
function elisionKeepChars(hardTriggerTokens, fraction = ELIDE_KEEP_FRACTION) {
|
|
1784
|
+
return Math.max(4e3, Math.floor(hardTriggerTokens * fraction * 4));
|
|
1785
|
+
}
|
|
1765
1786
|
function turnStarts(messages, start) {
|
|
1766
1787
|
const users = [];
|
|
1767
1788
|
const assistants = [];
|
|
@@ -1911,10 +1932,11 @@ var SUMMARY_SYSTEM_PROMPT = `\u4F60\u6B63\u5728\u538B\u7F29\u4E00\u4E2A AI \u534
|
|
|
1911
1932
|
|
|
1912
1933
|
\u89C4\u5219\uFF1A
|
|
1913
1934
|
- \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
|
|
1935
|
+
- \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
|
|
1914
1936
|
- \u8981\u5177\u4F53\uFF1A\u8DEF\u5F84\u3001\u540D\u79F0\u3001\u547D\u4EE4\u3001id\u2014\u2014\u800C\u4E0D\u662F\u6A21\u7CCA\u7684\u5F15\u7528\u3002
|
|
1915
1937
|
- \u4EC5\u8F93\u51FA\u6458\u8981\u7AE0\u8282\uFF0C\u4E0D\u9644\u52A0\u524D\u8A00\u3002`;
|
|
1916
1938
|
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";
|
|
1917
|
-
function renderSpan(span,
|
|
1939
|
+
function renderSpan(span, budgetChars2 = SPAN_BUDGET_CHARS) {
|
|
1918
1940
|
const lines = [];
|
|
1919
1941
|
for (const m of span) {
|
|
1920
1942
|
const role = m.role;
|
|
@@ -1938,8 +1960,8 @@ function renderSpan(span, budgetChars = SPAN_BUDGET_CHARS) {
|
|
|
1938
1960
|
if (text) lines.push(`[${role}] ${text}`);
|
|
1939
1961
|
}
|
|
1940
1962
|
let rendered = lines.join("\n");
|
|
1941
|
-
if (rendered.length >
|
|
1942
|
-
rendered = "(\u2026oldest turns elided\u2026)\n" + rendered.slice(-
|
|
1963
|
+
if (rendered.length > budgetChars2) {
|
|
1964
|
+
rendered = "(\u2026oldest turns elided\u2026)\n" + rendered.slice(-budgetChars2);
|
|
1943
1965
|
}
|
|
1944
1966
|
return rendered;
|
|
1945
1967
|
}
|
|
@@ -1953,9 +1975,17 @@ function summarizerMessages(span, priorSummary = "") {
|
|
|
1953
1975
|
{ role: "user", content: body }
|
|
1954
1976
|
];
|
|
1955
1977
|
}
|
|
1978
|
+
function summarizerReasoningEffort() {
|
|
1979
|
+
const v = process.env.COMPACTION_REASONING_EFFORT?.toLowerCase();
|
|
1980
|
+
if (v === "low" || v === "medium" || v === "high") return v;
|
|
1981
|
+
return "low";
|
|
1982
|
+
}
|
|
1956
1983
|
async function summarizeSpan(provider, model, span, priorSummary = "", opts) {
|
|
1957
1984
|
const msgs = summarizerMessages(span, priorSummary);
|
|
1958
|
-
const chunks = await provider.stream(model, msgs, [],
|
|
1985
|
+
const chunks = await provider.stream(model, msgs, [], {
|
|
1986
|
+
...opts,
|
|
1987
|
+
maxTokens: SUMMARY_MAX_TOKENS
|
|
1988
|
+
});
|
|
1959
1989
|
let text = "";
|
|
1960
1990
|
for await (const chunk of chunks) {
|
|
1961
1991
|
if (chunk.content) text += chunk.content;
|
|
@@ -2066,6 +2096,166 @@ function applyToOutbound(messages, state) {
|
|
|
2066
2096
|
head.push({ role: "user", content: compactedBlock(state) });
|
|
2067
2097
|
return [...head, ...messages.slice(boundary)];
|
|
2068
2098
|
}
|
|
2099
|
+
var ELIDE_EXEMPT_TOOLS = /* @__PURE__ */ new Set([
|
|
2100
|
+
"explore",
|
|
2101
|
+
"ask_user",
|
|
2102
|
+
"propose_plan",
|
|
2103
|
+
"request_directory",
|
|
2104
|
+
"todo_write"
|
|
2105
|
+
]);
|
|
2106
|
+
function elidedToolContent(m) {
|
|
2107
|
+
const name = m.tool_call_name || "tool";
|
|
2108
|
+
const size = m.content.length;
|
|
2109
|
+
const saved = /full output saved to ([^\]]+)/.exec(m.content)?.[1]?.trim();
|
|
2110
|
+
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]`;
|
|
2111
|
+
}
|
|
2112
|
+
function isPlaceholder(content) {
|
|
2113
|
+
return content.startsWith("[old tool result elided") || content.startsWith("[superseded by a later read_file");
|
|
2114
|
+
}
|
|
2115
|
+
var ONE_SHOT_TOOLS = /* @__PURE__ */ new Set(["git_diff"]);
|
|
2116
|
+
var ONE_SHOT_KEEP_RECENT = 1;
|
|
2117
|
+
function applyOneShotElision(messages, keepRecent = ONE_SHOT_KEEP_RECENT, tools = ONE_SHOT_TOOLS) {
|
|
2118
|
+
const out = [...messages];
|
|
2119
|
+
let seen = 0;
|
|
2120
|
+
let elided = 0;
|
|
2121
|
+
let freed = 0;
|
|
2122
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2123
|
+
const m = messages[i];
|
|
2124
|
+
if (m.role !== "tool" || !tools.has(m.tool_call_name ?? "")) continue;
|
|
2125
|
+
if (isPlaceholder(m.content)) continue;
|
|
2126
|
+
seen++;
|
|
2127
|
+
if (seen <= keepRecent) continue;
|
|
2128
|
+
const placeholder = elidedToolContent(m);
|
|
2129
|
+
freed += m.content.length - placeholder.length;
|
|
2130
|
+
out[i] = { ...m, content: placeholder };
|
|
2131
|
+
elided++;
|
|
2132
|
+
}
|
|
2133
|
+
return elided > 0 ? { messages: out, elided, freedChars: freed } : { messages, elided: 0, freedChars: 0 };
|
|
2134
|
+
}
|
|
2135
|
+
function applyToolResultElision(messages, keepChars, exempt = ELIDE_EXEMPT_TOOLS) {
|
|
2136
|
+
let total = 0;
|
|
2137
|
+
for (const m of messages) {
|
|
2138
|
+
if (m.role === "tool" && !exempt.has(m.tool_call_name ?? "")) {
|
|
2139
|
+
total += m.content.length;
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
if (total <= keepChars) return { messages, elided: 0, freedChars: 0 };
|
|
2143
|
+
const out = [...messages];
|
|
2144
|
+
let budget = 0;
|
|
2145
|
+
let elided = 0;
|
|
2146
|
+
let freed = 0;
|
|
2147
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2148
|
+
const m = messages[i];
|
|
2149
|
+
if (m.role !== "tool") continue;
|
|
2150
|
+
if (exempt.has(m.tool_call_name ?? "")) continue;
|
|
2151
|
+
if (elided === 0 && budget === 0 && m.content.length > keepChars) {
|
|
2152
|
+
const kept = m.content.slice(0, keepChars) + "\n\u2026(truncated)";
|
|
2153
|
+
freed += m.content.length - kept.length;
|
|
2154
|
+
out[i] = { ...m, content: kept };
|
|
2155
|
+
budget = keepChars;
|
|
2156
|
+
continue;
|
|
2157
|
+
}
|
|
2158
|
+
if (budget + m.content.length <= keepChars) {
|
|
2159
|
+
budget += m.content.length;
|
|
2160
|
+
continue;
|
|
2161
|
+
}
|
|
2162
|
+
const placeholder = elidedToolContent(m);
|
|
2163
|
+
freed += m.content.length - placeholder.length;
|
|
2164
|
+
out[i] = { ...m, content: placeholder };
|
|
2165
|
+
elided++;
|
|
2166
|
+
}
|
|
2167
|
+
return { messages: out, elided, freedChars: freed };
|
|
2168
|
+
}
|
|
2169
|
+
function normalizeToolPath(p) {
|
|
2170
|
+
let s = p.trim().replace(/\\/g, "/");
|
|
2171
|
+
while (s.startsWith("./")) s = s.slice(2);
|
|
2172
|
+
return s.replace(/\/{2,}/g, "/");
|
|
2173
|
+
}
|
|
2174
|
+
var MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
2175
|
+
"write_file",
|
|
2176
|
+
"replace_in_file",
|
|
2177
|
+
"apply_patch",
|
|
2178
|
+
"apply_unified_diff",
|
|
2179
|
+
"run_shell"
|
|
2180
|
+
]);
|
|
2181
|
+
function parseReadFileSpan(content) {
|
|
2182
|
+
const body = content.replace(/^\[tool output truncated:[^\n]*\]\n\n/, "");
|
|
2183
|
+
let path36;
|
|
2184
|
+
let start;
|
|
2185
|
+
let end;
|
|
2186
|
+
try {
|
|
2187
|
+
const obj = JSON.parse(body);
|
|
2188
|
+
path36 = obj.path;
|
|
2189
|
+
start = obj.start_line;
|
|
2190
|
+
end = obj.end_line;
|
|
2191
|
+
} catch {
|
|
2192
|
+
const str2 = (key) => new RegExp(`"${key}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`).exec(body)?.[1];
|
|
2193
|
+
const num2 = (key) => {
|
|
2194
|
+
const m = new RegExp(`"${key}"\\s*:\\s*(\\d+)`).exec(body);
|
|
2195
|
+
return m ? Number(m[1]) : void 0;
|
|
2196
|
+
};
|
|
2197
|
+
path36 = str2("path");
|
|
2198
|
+
start = num2("start_line");
|
|
2199
|
+
end = num2("end_line");
|
|
2200
|
+
}
|
|
2201
|
+
if (typeof path36 !== "string" || !path36) return null;
|
|
2202
|
+
if (typeof start !== "number" || typeof end !== "number") return null;
|
|
2203
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return null;
|
|
2204
|
+
return { path: path36, start, end };
|
|
2205
|
+
}
|
|
2206
|
+
function addSpan(spans, start, end) {
|
|
2207
|
+
spans.push([start, end]);
|
|
2208
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
2209
|
+
const merged = [];
|
|
2210
|
+
for (const cur of spans) {
|
|
2211
|
+
const last = merged[merged.length - 1];
|
|
2212
|
+
if (last && cur[0] <= last[1] + 1) {
|
|
2213
|
+
last[1] = Math.max(last[1], cur[1]);
|
|
2214
|
+
} else {
|
|
2215
|
+
merged.push([...cur]);
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
spans.length = 0;
|
|
2219
|
+
spans.push(...merged);
|
|
2220
|
+
}
|
|
2221
|
+
function coversSpans(spans, start, end) {
|
|
2222
|
+
return spans.some(([a, b]) => a <= start && b >= end);
|
|
2223
|
+
}
|
|
2224
|
+
function applyReadFileDedup(messages) {
|
|
2225
|
+
let candidates = 0;
|
|
2226
|
+
for (const m of messages) {
|
|
2227
|
+
if (m.role === "tool" && m.tool_call_name === "read_file") candidates++;
|
|
2228
|
+
}
|
|
2229
|
+
if (candidates < 2) return { messages, elided: 0 };
|
|
2230
|
+
const out = [...messages];
|
|
2231
|
+
const covered = /* @__PURE__ */ new Map();
|
|
2232
|
+
let elided = 0;
|
|
2233
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2234
|
+
const m = messages[i];
|
|
2235
|
+
if (m.role === "assistant") {
|
|
2236
|
+
if (m.tool_calls?.some((tc) => MUTATING_TOOLS.has(tc.function.name))) {
|
|
2237
|
+
covered.clear();
|
|
2238
|
+
}
|
|
2239
|
+
continue;
|
|
2240
|
+
}
|
|
2241
|
+
if (m.role !== "tool" || m.tool_call_name !== "read_file") continue;
|
|
2242
|
+
const span = parseReadFileSpan(m.content);
|
|
2243
|
+
if (!span) continue;
|
|
2244
|
+
const key = normalizeToolPath(span.path);
|
|
2245
|
+
const seen = covered.get(key) ?? [];
|
|
2246
|
+
if (coversSpans(seen, span.start, span.end)) {
|
|
2247
|
+
out[i] = {
|
|
2248
|
+
...m,
|
|
2249
|
+
content: `[superseded by a later read_file of ${span.path} (lines ${span.start}-${span.end})]`
|
|
2250
|
+
};
|
|
2251
|
+
elided++;
|
|
2252
|
+
continue;
|
|
2253
|
+
}
|
|
2254
|
+
addSpan(seen, span.start, span.end);
|
|
2255
|
+
covered.set(key, seen);
|
|
2256
|
+
}
|
|
2257
|
+
return { messages: elided > 0 ? out : messages, elided };
|
|
2258
|
+
}
|
|
2069
2259
|
var OVERFLOW_MARKERS = [
|
|
2070
2260
|
"context_length_exceeded",
|
|
2071
2261
|
"maximum context length",
|
|
@@ -2081,9 +2271,94 @@ function isContextOverflow(exc) {
|
|
|
2081
2271
|
return OVERFLOW_MARKERS.some((m) => text.includes(m));
|
|
2082
2272
|
}
|
|
2083
2273
|
|
|
2274
|
+
// src/engine/toolResultBudget.ts
|
|
2275
|
+
init_esm_shims();
|
|
2276
|
+
import * as fs4 from "fs";
|
|
2277
|
+
import * as path6 from "path";
|
|
2278
|
+
var DEFAULT_TOOL_RESULT_MAX_CHARS = 24e3;
|
|
2279
|
+
var HEAD_RATIO = 0.6;
|
|
2280
|
+
function budgetChars() {
|
|
2281
|
+
const n = Number(process.env.TOOL_RESULT_MAX_CHARS);
|
|
2282
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_TOOL_RESULT_MAX_CHARS;
|
|
2283
|
+
}
|
|
2284
|
+
function toolResultDir() {
|
|
2285
|
+
const env3 = process.env.TOOL_RESULT_DIR?.trim();
|
|
2286
|
+
return env3 || path6.join(stateDir(), "tool-results");
|
|
2287
|
+
}
|
|
2288
|
+
function clipText(text, budget) {
|
|
2289
|
+
if (text.length <= budget) return text;
|
|
2290
|
+
const room = Math.max(
|
|
2291
|
+
0,
|
|
2292
|
+
budget - `
|
|
2293
|
+
|
|
2294
|
+
\u2026 [${text.length} characters omitted] \u2026
|
|
2295
|
+
|
|
2296
|
+
`.length
|
|
2297
|
+
);
|
|
2298
|
+
const headLen = Math.floor(room * HEAD_RATIO);
|
|
2299
|
+
const tailLen = room - headLen;
|
|
2300
|
+
const omitted = text.length - headLen - tailLen;
|
|
2301
|
+
return text.slice(0, headLen) + `
|
|
2302
|
+
|
|
2303
|
+
\u2026 [${omitted} characters omitted] \u2026
|
|
2304
|
+
|
|
2305
|
+
` + text.slice(-tailLen);
|
|
2306
|
+
}
|
|
2307
|
+
function clipDeep(value, perField) {
|
|
2308
|
+
if (typeof value === "string") {
|
|
2309
|
+
return value.length <= perField ? value : clipText(value, perField);
|
|
2310
|
+
}
|
|
2311
|
+
if (Array.isArray(value)) {
|
|
2312
|
+
return value.map((v) => clipDeep(v, perField));
|
|
2313
|
+
}
|
|
2314
|
+
if (value && typeof value === "object") {
|
|
2315
|
+
const out = {};
|
|
2316
|
+
for (const [k, v] of Object.entries(value)) out[k] = clipDeep(v, perField);
|
|
2317
|
+
return out;
|
|
2318
|
+
}
|
|
2319
|
+
return value;
|
|
2320
|
+
}
|
|
2321
|
+
function saveRaw(tool, raw, dir) {
|
|
2322
|
+
try {
|
|
2323
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
2324
|
+
const safe = tool.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40) || "tool";
|
|
2325
|
+
const name = `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}-${safe}.txt`;
|
|
2326
|
+
const file = path6.join(dir, name);
|
|
2327
|
+
fs4.writeFileSync(file, raw, "utf-8");
|
|
2328
|
+
return file;
|
|
2329
|
+
} catch {
|
|
2330
|
+
return void 0;
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
function applyToolResultBudget(tool, result, opts = {}) {
|
|
2334
|
+
const budget = opts.maxChars ?? budgetChars();
|
|
2335
|
+
const raw = typeof result === "string" ? result : JSON.stringify(result, null, 2) ?? String(result);
|
|
2336
|
+
if (raw.length <= budget) {
|
|
2337
|
+
return { content: raw, truncated: false };
|
|
2338
|
+
}
|
|
2339
|
+
let content = raw;
|
|
2340
|
+
if (typeof result === "object" && result !== null) {
|
|
2341
|
+
let perField = Math.floor(budget * 0.7);
|
|
2342
|
+
let clipped = JSON.stringify(clipDeep(result, perField), null, 2);
|
|
2343
|
+
for (let i = 0; i < 6 && clipped && clipped.length > budget && perField > 128; i++) {
|
|
2344
|
+
perField = Math.floor(perField / 2);
|
|
2345
|
+
clipped = JSON.stringify(clipDeep(result, perField), null, 2);
|
|
2346
|
+
}
|
|
2347
|
+
if (clipped && clipped.length <= budget) content = clipped;
|
|
2348
|
+
}
|
|
2349
|
+
if (content.length > budget) content = clipText(content, budget);
|
|
2350
|
+
const savedPath = saveRaw(tool, raw, opts.dir ?? toolResultDir());
|
|
2351
|
+
if (savedPath) {
|
|
2352
|
+
content = `[tool output truncated: ${raw.length} chars, full output saved to ${savedPath}]
|
|
2353
|
+
|
|
2354
|
+
` + content;
|
|
2355
|
+
}
|
|
2356
|
+
return { content, truncated: true, savedPath };
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2084
2359
|
// src/engine/TurnEngine.ts
|
|
2085
2360
|
import * as crypto3 from "crypto";
|
|
2086
|
-
import * as
|
|
2361
|
+
import * as fs5 from "fs";
|
|
2087
2362
|
import * as _path from "path";
|
|
2088
2363
|
|
|
2089
2364
|
// src/prompts/index.ts
|
|
@@ -2209,13 +2484,17 @@ var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
2209
2484
|
"propose_plan",
|
|
2210
2485
|
"request_directory"
|
|
2211
2486
|
]);
|
|
2212
|
-
|
|
2213
|
-
|
|
2487
|
+
var MAX_COMPACT_ROUNDS = 4;
|
|
2488
|
+
var OVERFLOW_TARGET_RATIO = 0.5;
|
|
2489
|
+
var OVERFLOW_TRIM_FRACTION = 0.5;
|
|
2490
|
+
var COMPACT_STALL_RETRY_DELTA = 20;
|
|
2491
|
+
function toolResultMessage(tc, result, taskId, toolResultDir2) {
|
|
2492
|
+
const budgeted = applyToolResultBudget(tc.name, result, { dir: toolResultDir2 });
|
|
2214
2493
|
return {
|
|
2215
2494
|
role: "tool",
|
|
2216
2495
|
tool_call_id: tc.id,
|
|
2217
2496
|
tool_call_name: tc.name,
|
|
2218
|
-
content,
|
|
2497
|
+
content: budgeted.content,
|
|
2219
2498
|
taskId
|
|
2220
2499
|
};
|
|
2221
2500
|
}
|
|
@@ -2326,12 +2605,22 @@ var TurnEngine = class _TurnEngine {
|
|
|
2326
2605
|
// 上下文压缩
|
|
2327
2606
|
compactionState = null;
|
|
2328
2607
|
_lastContextTokens = null;
|
|
2608
|
+
/** 压缩无进展时的消息条数快照;-1 表示无冷却 */
|
|
2609
|
+
_compactStalledAt = -1;
|
|
2329
2610
|
/** 显式配置的上下文窗口大小(token 数),未设置时为 undefined(走自动检测) */
|
|
2330
2611
|
_contextWindow;
|
|
2331
2612
|
/** 压缩触发阈值比例(默认 0.8) */
|
|
2332
2613
|
_thresholdPct;
|
|
2614
|
+
/** 软阈值比例(undefined = 取硬阈值的 75%) */
|
|
2615
|
+
_softThresholdPct;
|
|
2616
|
+
/** 是否启用软阈值淘汰(COMPACTION_ELIDE=0 可关闭) */
|
|
2617
|
+
_elisionEnabled;
|
|
2618
|
+
/** 是否启用重复读取去重(COMPACTION_DEDUP=0 可关闭) */
|
|
2619
|
+
_dedupEnabled;
|
|
2333
2620
|
/** 压缩触发阈值绝对上限(null = 不限制) */
|
|
2334
2621
|
_capTokens;
|
|
2622
|
+
/** 超预算工具结果的落盘目录(undefined = 使用默认状态目录) */
|
|
2623
|
+
_toolResultDir;
|
|
2335
2624
|
// 产出文件追踪(本轮 Turn 中成功写入的文件)
|
|
2336
2625
|
_producedFiles = [];
|
|
2337
2626
|
_turnStartTime = 0;
|
|
@@ -2383,7 +2672,15 @@ var TurnEngine = class _TurnEngine {
|
|
|
2383
2672
|
this._hook = opts.hook;
|
|
2384
2673
|
this._contextWindow = opts.contextWindow;
|
|
2385
2674
|
this._thresholdPct = opts.thresholdPct ?? DEFAULT_THRESHOLD_PCT;
|
|
2675
|
+
this._softThresholdPct = opts.softThresholdPct;
|
|
2676
|
+
this._elisionEnabled = !/^(0|false|off|no)$/i.test(
|
|
2677
|
+
(process.env.COMPACTION_ELIDE ?? "").trim()
|
|
2678
|
+
);
|
|
2679
|
+
this._dedupEnabled = !/^(0|false|off|no)$/i.test(
|
|
2680
|
+
(process.env.COMPACTION_DEDUP ?? "").trim()
|
|
2681
|
+
);
|
|
2386
2682
|
this._capTokens = opts.capTokens ?? null;
|
|
2683
|
+
this._toolResultDir = opts.toolResultDir;
|
|
2387
2684
|
this._reasoningEffort = opts.reasoningEffort;
|
|
2388
2685
|
if (this.messages.length === 0 || this.messages[0].role !== "system") {
|
|
2389
2686
|
this.messages.unshift({ role: "system", content: this.instructions });
|
|
@@ -2439,6 +2736,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
2439
2736
|
this._cancel = false;
|
|
2440
2737
|
this._producedFiles = [];
|
|
2441
2738
|
this._turnStartTime = Date.now();
|
|
2739
|
+
this._compactStalledAt = -1;
|
|
2442
2740
|
this._emitHook((h) => h.onTurnStart?.(userInput));
|
|
2443
2741
|
yield {
|
|
2444
2742
|
type: "turn_start" /* TURN_START */,
|
|
@@ -3155,7 +3453,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
3155
3453
|
for (let i = 0; i < concurrent.length; i++) {
|
|
3156
3454
|
const tc = concurrent[i];
|
|
3157
3455
|
const { result, status } = results[i];
|
|
3158
|
-
this.messages.push(
|
|
3456
|
+
this.messages.push(
|
|
3457
|
+
toolResultMessage(tc, result, this.taskId, this._toolResultDir)
|
|
3458
|
+
);
|
|
3159
3459
|
this._trackProducedFile(
|
|
3160
3460
|
tc,
|
|
3161
3461
|
result,
|
|
@@ -3218,7 +3518,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
3218
3518
|
};
|
|
3219
3519
|
status = "error";
|
|
3220
3520
|
}
|
|
3221
|
-
this.messages.push(
|
|
3521
|
+
this.messages.push(
|
|
3522
|
+
toolResultMessage(tc, result, this.taskId, this._toolResultDir)
|
|
3523
|
+
);
|
|
3222
3524
|
this._trackProducedFile(
|
|
3223
3525
|
tc,
|
|
3224
3526
|
result,
|
|
@@ -3253,13 +3555,41 @@ var TurnEngine = class _TurnEngine {
|
|
|
3253
3555
|
* 过滤 notice 消息;tool 消息只保留必要字段;
|
|
3254
3556
|
* 其余消息剥离 display-only sidecar 字段(source/_display/ts/
|
|
3255
3557
|
* reasoning/usage),避免污染模型上下文。
|
|
3558
|
+
*
|
|
3559
|
+
* 达到软阈值时先淘汰旧工具结果(零 LLM 成本),再产出最终视图。
|
|
3256
3560
|
*/
|
|
3257
3561
|
_outboundMessages() {
|
|
3258
|
-
const
|
|
3259
|
-
const
|
|
3260
|
-
|
|
3261
|
-
|
|
3562
|
+
const base = applyToOutbound(this.messages, this.compactionState);
|
|
3563
|
+
const deduped = this._dedupEnabled ? applyReadFileDedup(base).messages : base;
|
|
3564
|
+
let view = this._finalizeOutbound(deduped);
|
|
3565
|
+
if (!this._elisionEnabled) return view;
|
|
3566
|
+
if (estimateTokens(view) < this._softTriggerTokens()) {
|
|
3567
|
+
return view;
|
|
3568
|
+
}
|
|
3569
|
+
const oneShot = applyOneShotElision(deduped);
|
|
3570
|
+
if (oneShot.freedChars > 0) {
|
|
3571
|
+
view = this._finalizeOutbound(oneShot.messages);
|
|
3572
|
+
if (estimateTokens(view) < this._softTriggerTokens()) {
|
|
3573
|
+
return view;
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3576
|
+
const keepChars = elisionKeepChars(
|
|
3577
|
+
triggerTokens(
|
|
3578
|
+
this._getContextWindow(),
|
|
3579
|
+
this._thresholdPct,
|
|
3580
|
+
this._capTokens
|
|
3581
|
+
)
|
|
3262
3582
|
);
|
|
3583
|
+
const elided = applyToolResultElision(
|
|
3584
|
+
oneShot.messages,
|
|
3585
|
+
keepChars
|
|
3586
|
+
);
|
|
3587
|
+
if (elided.freedChars === 0) return view;
|
|
3588
|
+
return this._finalizeOutbound(elided.messages);
|
|
3589
|
+
}
|
|
3590
|
+
/** 过滤 notice + 剥离 display-only 字段,得到最终发送视图 */
|
|
3591
|
+
_finalizeOutbound(sourceMessages) {
|
|
3592
|
+
const _STRIP = /* @__PURE__ */ new Set(["source", "_display", "ts", "reasoning", "usage"]);
|
|
3263
3593
|
return sourceMessages.filter((m) => m.role !== "notice").map((m) => {
|
|
3264
3594
|
if (m.role === "tool") {
|
|
3265
3595
|
return {
|
|
@@ -3277,6 +3607,15 @@ var TurnEngine = class _TurnEngine {
|
|
|
3277
3607
|
return stripped;
|
|
3278
3608
|
});
|
|
3279
3609
|
}
|
|
3610
|
+
/** 软阈值(token 数):达到它只淘汰旧工具结果 */
|
|
3611
|
+
_softTriggerTokens() {
|
|
3612
|
+
return softTriggerTokens(
|
|
3613
|
+
this._getContextWindow(),
|
|
3614
|
+
this._thresholdPct,
|
|
3615
|
+
this._capTokens,
|
|
3616
|
+
this._softThresholdPct
|
|
3617
|
+
);
|
|
3618
|
+
}
|
|
3280
3619
|
/** 通知消息:仅用于日志/展示,不发给模型 */
|
|
3281
3620
|
_appendNotice(kind, text) {
|
|
3282
3621
|
this.messages.push({
|
|
@@ -3318,18 +3657,36 @@ var TurnEngine = class _TurnEngine {
|
|
|
3318
3657
|
return false;
|
|
3319
3658
|
}
|
|
3320
3659
|
// ===================== 自动压缩 =====================
|
|
3321
|
-
/**
|
|
3660
|
+
/**
|
|
3661
|
+
* 当前上下文 token 用量。
|
|
3662
|
+
*
|
|
3663
|
+
* API 返回的 `usage.input` 是精确值,但反映的是「上一次请求」——工具结果
|
|
3664
|
+
* 与淘汰/压缩都会让它过时。因此同时估算当前 outbound 视图,取较大者:
|
|
3665
|
+
* 宁可早一点压缩,也不要漏判导致溢出。
|
|
3666
|
+
*/
|
|
3322
3667
|
_compactionTokens() {
|
|
3323
|
-
|
|
3324
|
-
|
|
3668
|
+
const estimated = estimateTokens(this._outboundMessages());
|
|
3669
|
+
if (this._lastContextTokens == null) {
|
|
3670
|
+
return { tokens: estimated, estimated: true };
|
|
3325
3671
|
}
|
|
3326
|
-
|
|
3327
|
-
tokens:
|
|
3328
|
-
|
|
3329
|
-
};
|
|
3672
|
+
if (estimated > this._lastContextTokens) {
|
|
3673
|
+
return { tokens: estimated, estimated: true };
|
|
3674
|
+
}
|
|
3675
|
+
return { tokens: this._lastContextTokens, estimated: false };
|
|
3676
|
+
}
|
|
3677
|
+
/** 压缩后重置精确计数:后续按估算值判断 */
|
|
3678
|
+
_resetContextTokens() {
|
|
3679
|
+
this._lastContextTokens = null;
|
|
3680
|
+
}
|
|
3681
|
+
/** 当前 outbound 视图的估算 token 数 */
|
|
3682
|
+
_estimateContextTokens() {
|
|
3683
|
+
return estimateTokens(this._outboundMessages());
|
|
3330
3684
|
}
|
|
3331
3685
|
/** 检查是否需要压缩(每轮 LLM 调用前) */
|
|
3332
3686
|
_compactionDue() {
|
|
3687
|
+
if (this._compactStalledAt >= 0 && this.messages.length < this._compactStalledAt + COMPACT_STALL_RETRY_DELTA) {
|
|
3688
|
+
return false;
|
|
3689
|
+
}
|
|
3333
3690
|
const { tokens } = this._compactionTokens();
|
|
3334
3691
|
return shouldCompact(
|
|
3335
3692
|
tokens,
|
|
@@ -3376,63 +3733,82 @@ var TurnEngine = class _TurnEngine {
|
|
|
3376
3733
|
async _compactNow(reqId, force = false) {
|
|
3377
3734
|
const contextWindow = this._getContextWindow();
|
|
3378
3735
|
const messagesBefore = this._outboundMessages().length;
|
|
3379
|
-
const
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
this._capTokens
|
|
3384
|
-
)
|
|
3736
|
+
const trigger = triggerTokens(
|
|
3737
|
+
contextWindow,
|
|
3738
|
+
this._thresholdPct,
|
|
3739
|
+
this._capTokens
|
|
3385
3740
|
);
|
|
3386
|
-
const
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3741
|
+
const target = Math.floor(
|
|
3742
|
+
trigger * (force ? OVERFLOW_TARGET_RATIO : 0.9)
|
|
3743
|
+
);
|
|
3744
|
+
const model = process.env.COMPACTION_MODEL?.trim() || this.model;
|
|
3745
|
+
const reasoningEffort = summarizerReasoningEffort();
|
|
3746
|
+
let keep = Math.floor(KEEP_RECENT_FRACTION * trigger);
|
|
3747
|
+
let method = null;
|
|
3748
|
+
let lastState = null;
|
|
3749
|
+
let progressed = false;
|
|
3750
|
+
for (let round = 0; round < MAX_COMPACT_ROUNDS; round++) {
|
|
3751
|
+
if (round > 0 && this._estimateContextTokens() <= target) break;
|
|
3752
|
+
let state = null;
|
|
3753
|
+
for (let attempt = 0; attempt < 2 && !state; attempt++) {
|
|
3754
|
+
try {
|
|
3755
|
+
const logTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
3756
|
+
state = await buildState(
|
|
3757
|
+
this.messages,
|
|
3758
|
+
this.provider,
|
|
3759
|
+
model,
|
|
3760
|
+
keep,
|
|
3761
|
+
this.compactionState,
|
|
3762
|
+
{ reqId, logTime, reasoningEffort }
|
|
3763
|
+
);
|
|
3764
|
+
} catch {
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
if (state) {
|
|
3768
|
+
this.compactionState = state;
|
|
3769
|
+
lastState = state;
|
|
3770
|
+
method = "summarized";
|
|
3771
|
+
progressed = true;
|
|
3772
|
+
} else {
|
|
3773
|
+
const trimmed = trimState(
|
|
3393
3774
|
this.messages,
|
|
3394
|
-
this.provider,
|
|
3395
|
-
model,
|
|
3396
|
-
keep,
|
|
3397
3775
|
this.compactionState,
|
|
3398
|
-
|
|
3776
|
+
force ? OVERFLOW_TRIM_FRACTION : 0.25
|
|
3399
3777
|
);
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
this.
|
|
3408
|
-
|
|
3778
|
+
if (!trimmed) break;
|
|
3779
|
+
this.compactionState = trimmed;
|
|
3780
|
+
lastState = trimmed;
|
|
3781
|
+
method = "trimmed";
|
|
3782
|
+
progressed = true;
|
|
3783
|
+
}
|
|
3784
|
+
this._resetContextTokens();
|
|
3785
|
+
if (this._estimateContextTokens() <= trigger) break;
|
|
3786
|
+
keep = Math.max(Math.floor(trigger * 0.05), Math.floor(keep / 2));
|
|
3787
|
+
}
|
|
3788
|
+
if (!progressed || !lastState) return { notice: null, method: null };
|
|
3789
|
+
this._compactStalledAt = this._estimateContextTokens() >= trigger ? this.messages.length : -1;
|
|
3790
|
+
const messagesAfter = this._outboundMessages().length;
|
|
3791
|
+
const common = {
|
|
3792
|
+
boundaryIndex: lastState.boundaryIndex,
|
|
3793
|
+
messagesBefore,
|
|
3794
|
+
messagesAfter
|
|
3795
|
+
};
|
|
3796
|
+
if (method === "summarized") {
|
|
3409
3797
|
return {
|
|
3410
3798
|
notice: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29 \u2014 \u65E9\u671F\u8F6E\u6B21\u5DF2\u6458\u8981",
|
|
3411
3799
|
method: "summarized",
|
|
3412
|
-
modelUsed:
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
messagesBefore,
|
|
3418
|
-
messagesAfter: this._outboundMessages().length
|
|
3800
|
+
modelUsed: lastState.modelUsed,
|
|
3801
|
+
summaryPreview: lastState.summaryText.slice(0, 200),
|
|
3802
|
+
keptUserMessages: lastState.userMessages.length,
|
|
3803
|
+
droppedUserMessages: lastState.userMessagesDropped,
|
|
3804
|
+
...common
|
|
3419
3805
|
};
|
|
3420
3806
|
}
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
return {
|
|
3427
|
-
notice: "\u4E0A\u4E0B\u6587\u5DF2\u88C1\u526A \u2014 \u6700\u65E7\u7684\u8F6E\u6B21\u5DF2\u4E22\u5F03\uFF08\u6458\u8981\u4E0D\u53EF\u7528\uFF09",
|
|
3428
|
-
method: "trimmed",
|
|
3429
|
-
boundaryIndex: trimmed.boundaryIndex,
|
|
3430
|
-
messagesBefore,
|
|
3431
|
-
messagesAfter: this._outboundMessages().length
|
|
3432
|
-
};
|
|
3433
|
-
}
|
|
3434
|
-
}
|
|
3435
|
-
return { notice: null, method: null };
|
|
3807
|
+
return {
|
|
3808
|
+
notice: "\u4E0A\u4E0B\u6587\u5DF2\u88C1\u526A \u2014 \u6700\u65E7\u7684\u8F6E\u6B21\u5DF2\u4E22\u5F03\uFF08\u6458\u8981\u4E0D\u53EF\u7528\uFF09",
|
|
3809
|
+
method: "trimmed",
|
|
3810
|
+
...common
|
|
3811
|
+
};
|
|
3436
3812
|
}
|
|
3437
3813
|
/** 追踪文件写入工具成功产生的文件 */
|
|
3438
3814
|
_trackProducedFile(tc, result, status) {
|
|
@@ -3442,9 +3818,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
3442
3818
|
return;
|
|
3443
3819
|
}
|
|
3444
3820
|
if (result && typeof result === "object") {
|
|
3445
|
-
const
|
|
3446
|
-
if (
|
|
3447
|
-
this._producedFiles.push(
|
|
3821
|
+
const path36 = result.path;
|
|
3822
|
+
if (path36) {
|
|
3823
|
+
this._producedFiles.push(path36);
|
|
3448
3824
|
return;
|
|
3449
3825
|
}
|
|
3450
3826
|
const changed = result.changed_files;
|
|
@@ -3468,7 +3844,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
3468
3844
|
_fileExists(f, ws) {
|
|
3469
3845
|
if (!ws && !_path.isAbsolute(f)) return true;
|
|
3470
3846
|
try {
|
|
3471
|
-
return
|
|
3847
|
+
return fs5.existsSync(_path.isAbsolute(f) ? f : _path.join(ws, f));
|
|
3472
3848
|
} catch {
|
|
3473
3849
|
return false;
|
|
3474
3850
|
}
|
|
@@ -3491,7 +3867,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
3491
3867
|
if (depth > 4 || results.length >= 50) return;
|
|
3492
3868
|
let entries;
|
|
3493
3869
|
try {
|
|
3494
|
-
entries =
|
|
3870
|
+
entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
3495
3871
|
} catch {
|
|
3496
3872
|
return;
|
|
3497
3873
|
}
|
|
@@ -3506,7 +3882,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
3506
3882
|
const ext = dotIdx !== -1 ? e.name.slice(dotIdx).toLowerCase() : "";
|
|
3507
3883
|
if (!_TurnEngine.SCAN_SUFFIXES.has(ext)) continue;
|
|
3508
3884
|
try {
|
|
3509
|
-
const st =
|
|
3885
|
+
const st = fs5.statSync(full);
|
|
3510
3886
|
if (st.mtimeMs >= since) {
|
|
3511
3887
|
results.push(
|
|
3512
3888
|
_path.relative(_path.resolve(ws), _path.resolve(full))
|
|
@@ -3963,16 +4339,16 @@ var McpManager = class {
|
|
|
3963
4339
|
|
|
3964
4340
|
// src/server/mcpConfig.ts
|
|
3965
4341
|
init_esm_shims();
|
|
3966
|
-
import
|
|
3967
|
-
import
|
|
4342
|
+
import fs6 from "fs";
|
|
4343
|
+
import path7 from "path";
|
|
3968
4344
|
function isValidMcpConfig(v) {
|
|
3969
4345
|
return !!v && typeof v === "object" && typeof v.name === "string" && (v.type === "stdio" || v.type === "http");
|
|
3970
4346
|
}
|
|
3971
4347
|
function readConfigFile(file) {
|
|
3972
4348
|
if (!file) return [];
|
|
3973
4349
|
try {
|
|
3974
|
-
if (!
|
|
3975
|
-
const raw = JSON.parse(
|
|
4350
|
+
if (!fs6.existsSync(file)) return [];
|
|
4351
|
+
const raw = JSON.parse(fs6.readFileSync(file, "utf8"));
|
|
3976
4352
|
const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.servers) ? raw.servers : [];
|
|
3977
4353
|
return list.filter(isValidMcpConfig);
|
|
3978
4354
|
} catch (err) {
|
|
@@ -3984,7 +4360,7 @@ function hasServerMcpConfig() {
|
|
|
3984
4360
|
if (readConfigFile(process.env.MCP_CONFIG_PATH).length > 0) return true;
|
|
3985
4361
|
const workspace = process.env.WORKSPACE;
|
|
3986
4362
|
if (!workspace) return false;
|
|
3987
|
-
return readConfigFile(
|
|
4363
|
+
return readConfigFile(path7.join(workspace, ".agent", "mcp.json")).length > 0;
|
|
3988
4364
|
}
|
|
3989
4365
|
function loadServerMcpConfigs(tenant) {
|
|
3990
4366
|
const fromEnv = readConfigFile(process.env.MCP_CONFIG_PATH);
|
|
@@ -3994,7 +4370,7 @@ function loadServerMcpConfigs(tenant) {
|
|
|
3994
4370
|
}
|
|
3995
4371
|
const workspace = tenant.workspaceRoot ?? process.env.WORKSPACE;
|
|
3996
4372
|
if (workspace)
|
|
3997
|
-
return readConfigFile(
|
|
4373
|
+
return readConfigFile(path7.join(workspace, ".agent", "mcp.json"));
|
|
3998
4374
|
return [];
|
|
3999
4375
|
}
|
|
4000
4376
|
|
|
@@ -4003,62 +4379,62 @@ init_esm_shims();
|
|
|
4003
4379
|
|
|
4004
4380
|
// src/tools/pathSafety.ts
|
|
4005
4381
|
init_esm_shims();
|
|
4006
|
-
import * as
|
|
4007
|
-
import * as
|
|
4382
|
+
import * as fs7 from "fs";
|
|
4383
|
+
import * as path8 from "path";
|
|
4008
4384
|
function isInsideRelative(rel) {
|
|
4009
|
-
return !
|
|
4385
|
+
return !path8.isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${path8.sep}`);
|
|
4010
4386
|
}
|
|
4011
4387
|
function isFilesystemRoot(p) {
|
|
4012
|
-
return
|
|
4388
|
+
return path8.parse(p).root === p;
|
|
4013
4389
|
}
|
|
4014
4390
|
function resolveWithinRoot(workspace, target) {
|
|
4015
|
-
const abs =
|
|
4391
|
+
const abs = path8.resolve(workspace, target);
|
|
4016
4392
|
let rootReal;
|
|
4017
4393
|
try {
|
|
4018
|
-
rootReal =
|
|
4394
|
+
rootReal = fs7.realpathSync(workspace);
|
|
4019
4395
|
} catch {
|
|
4020
|
-
const rootAbs =
|
|
4021
|
-
if (!isInsideRelative(
|
|
4396
|
+
const rootAbs = path8.resolve(workspace);
|
|
4397
|
+
if (!isInsideRelative(path8.relative(rootAbs, abs))) return null;
|
|
4022
4398
|
return { root: rootAbs, real: abs };
|
|
4023
4399
|
}
|
|
4024
4400
|
if (isFilesystemRoot(rootReal)) return null;
|
|
4025
4401
|
let real;
|
|
4026
4402
|
try {
|
|
4027
|
-
real =
|
|
4403
|
+
real = fs7.realpathSync(abs);
|
|
4028
4404
|
} catch {
|
|
4029
4405
|
let probe = abs;
|
|
4030
4406
|
const tail = [];
|
|
4031
4407
|
for (; ; ) {
|
|
4032
|
-
const exists =
|
|
4408
|
+
const exists = fs7.existsSync(probe) || fs7.lstatSync(probe, { throwIfNoEntry: false }) !== void 0;
|
|
4033
4409
|
if (exists) {
|
|
4034
4410
|
try {
|
|
4035
|
-
real =
|
|
4411
|
+
real = path8.join(fs7.realpathSync(probe), ...tail);
|
|
4036
4412
|
break;
|
|
4037
4413
|
} catch {
|
|
4038
4414
|
return null;
|
|
4039
4415
|
}
|
|
4040
4416
|
}
|
|
4041
|
-
tail.unshift(
|
|
4042
|
-
const parent =
|
|
4417
|
+
tail.unshift(path8.basename(probe));
|
|
4418
|
+
const parent = path8.dirname(probe);
|
|
4043
4419
|
if (parent === probe) return null;
|
|
4044
4420
|
probe = parent;
|
|
4045
4421
|
}
|
|
4046
4422
|
}
|
|
4047
|
-
if (!isInsideRelative(
|
|
4423
|
+
if (!isInsideRelative(path8.relative(rootReal, real))) return null;
|
|
4048
4424
|
return { root: rootReal, real };
|
|
4049
4425
|
}
|
|
4050
4426
|
function isWithinRoot(workspace, target) {
|
|
4051
4427
|
return resolveWithinRoot(workspace, target) !== null;
|
|
4052
4428
|
}
|
|
4053
4429
|
function resolveEntryWithinRoot(workspace, target) {
|
|
4054
|
-
const abs =
|
|
4055
|
-
const parent = resolveWithinRoot(workspace,
|
|
4430
|
+
const abs = path8.resolve(workspace, target);
|
|
4431
|
+
const parent = resolveWithinRoot(workspace, path8.dirname(abs));
|
|
4056
4432
|
if (!parent) return null;
|
|
4057
|
-
return { root: parent.root, real:
|
|
4433
|
+
return { root: parent.root, real: path8.join(parent.real, path8.basename(abs)) };
|
|
4058
4434
|
}
|
|
4059
4435
|
|
|
4060
4436
|
// src/permissions/PermissionEngine.ts
|
|
4061
|
-
import * as
|
|
4437
|
+
import * as path9 from "path";
|
|
4062
4438
|
var READ_ONLY_MODES = /* @__PURE__ */ new Set(["discuss" /* DISCUSS */, "plan" /* PLAN */]);
|
|
4063
4439
|
var WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
4064
4440
|
"write_file",
|
|
@@ -4108,7 +4484,7 @@ var PermissionEngine = class {
|
|
|
4108
4484
|
/** 会话级「总是允许此命令」集合 */
|
|
4109
4485
|
sessionAllowCommands;
|
|
4110
4486
|
constructor(opts) {
|
|
4111
|
-
this.workspaceRoot =
|
|
4487
|
+
this.workspaceRoot = path9.resolve(opts.workspaceRoot);
|
|
4112
4488
|
this.mode = opts.mode;
|
|
4113
4489
|
this.allowedCommands = Array.isArray(opts.allowedCommands) ? opts.allowedCommands : [];
|
|
4114
4490
|
this.sessionAllowTools = /* @__PURE__ */ new Set();
|
|
@@ -4201,8 +4577,8 @@ var PermissionEngine = class {
|
|
|
4201
4577
|
|
|
4202
4578
|
// src/providers/OpenAIProvider.ts
|
|
4203
4579
|
init_esm_shims();
|
|
4204
|
-
import * as
|
|
4205
|
-
import * as
|
|
4580
|
+
import * as fs8 from "fs";
|
|
4581
|
+
import * as path10 from "path";
|
|
4206
4582
|
import OpenAI from "openai";
|
|
4207
4583
|
|
|
4208
4584
|
// src/terminal.ts
|
|
@@ -4340,7 +4716,7 @@ var OpenAIProvider = class {
|
|
|
4340
4716
|
});
|
|
4341
4717
|
if (opts.logDir) {
|
|
4342
4718
|
this._logDir = opts.logDir;
|
|
4343
|
-
|
|
4719
|
+
fs8.mkdirSync(opts.logDir, { recursive: true });
|
|
4344
4720
|
}
|
|
4345
4721
|
}
|
|
4346
4722
|
// =====================================================================
|
|
@@ -4493,6 +4869,8 @@ var OpenAIProvider = class {
|
|
|
4493
4869
|
tools: this._toResponsesTools(tools, model),
|
|
4494
4870
|
// 推理强度:DeepSeek 不支持,跳过
|
|
4495
4871
|
...opts.reasoningEffort && !this._isDeepSeek(model) ? { reasoning: { effort: opts.reasoningEffort } } : {},
|
|
4872
|
+
// 限制输出长度(摘要器等轻量请求)
|
|
4873
|
+
...opts.maxTokens ? { max_output_tokens: opts.maxTokens } : {},
|
|
4496
4874
|
stream: true
|
|
4497
4875
|
}, requestOptions);
|
|
4498
4876
|
return this._responsesStream(reqId, startTime, rawStream, {
|
|
@@ -4591,6 +4969,8 @@ var OpenAIProvider = class {
|
|
|
4591
4969
|
})),
|
|
4592
4970
|
// 推理强度:DeepSeek 不支持,跳过
|
|
4593
4971
|
...opts.reasoningEffort && !this._isDeepSeek(model) ? { reasoning_effort: opts.reasoningEffort } : {},
|
|
4972
|
+
// 限制输出长度:o 系列 / gpt-5 推理模型仅支持 max_completion_tokens
|
|
4973
|
+
...opts.maxTokens ? isReasoningModel(model) ? { max_completion_tokens: opts.maxTokens } : { max_tokens: opts.maxTokens } : {},
|
|
4594
4974
|
stream: true,
|
|
4595
4975
|
// 要求返回 Token 用量统计(在流结束前的一个 chunk 中携带)
|
|
4596
4976
|
stream_options: { include_usage: true }
|
|
@@ -4704,8 +5084,8 @@ var OpenAIProvider = class {
|
|
|
4704
5084
|
if (!this._logDir) return;
|
|
4705
5085
|
try {
|
|
4706
5086
|
const today = logTime?.split("T")[0];
|
|
4707
|
-
const dir =
|
|
4708
|
-
|
|
5087
|
+
const dir = path10.join(this._logDir, today);
|
|
5088
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
4709
5089
|
const entry = {
|
|
4710
5090
|
reqId,
|
|
4711
5091
|
ts: logTime,
|
|
@@ -4717,8 +5097,8 @@ var OpenAIProvider = class {
|
|
|
4717
5097
|
toolNames: tools?.map((t) => t.function.name) ?? []
|
|
4718
5098
|
}
|
|
4719
5099
|
};
|
|
4720
|
-
|
|
4721
|
-
|
|
5100
|
+
fs8.writeFileSync(
|
|
5101
|
+
path10.join(dir, `${reqId}.json`),
|
|
4722
5102
|
JSON.stringify(entry, null, 2),
|
|
4723
5103
|
"utf-8"
|
|
4724
5104
|
);
|
|
@@ -4737,9 +5117,9 @@ var OpenAIProvider = class {
|
|
|
4737
5117
|
if (!this._logDir) return;
|
|
4738
5118
|
try {
|
|
4739
5119
|
const today = logTime?.split("T")[0];
|
|
4740
|
-
const filePath =
|
|
4741
|
-
if (!
|
|
4742
|
-
const entry = JSON.parse(
|
|
5120
|
+
const filePath = path10.join(this._logDir, today, `${reqId}.json`);
|
|
5121
|
+
if (!fs8.existsSync(filePath)) return;
|
|
5122
|
+
const entry = JSON.parse(fs8.readFileSync(filePath, "utf-8"));
|
|
4743
5123
|
entry.elapsedMs = Date.now() - startTime;
|
|
4744
5124
|
if (data.response) {
|
|
4745
5125
|
entry.response = data.response;
|
|
@@ -4747,7 +5127,7 @@ var OpenAIProvider = class {
|
|
|
4747
5127
|
if (data.error) {
|
|
4748
5128
|
entry.error = data.error;
|
|
4749
5129
|
}
|
|
4750
|
-
|
|
5130
|
+
fs8.writeFileSync(filePath, JSON.stringify(entry, null, 2), "utf-8");
|
|
4751
5131
|
} catch (e) {
|
|
4752
5132
|
console.error(e);
|
|
4753
5133
|
}
|
|
@@ -5105,12 +5485,12 @@ var OpenAIProvider = class {
|
|
|
5105
5485
|
|
|
5106
5486
|
// src/tools/files.ts
|
|
5107
5487
|
init_esm_shims();
|
|
5108
|
-
import * as
|
|
5109
|
-
import * as
|
|
5488
|
+
import * as fs9 from "fs";
|
|
5489
|
+
import * as path11 from "path";
|
|
5110
5490
|
var DEFAULT_MAX_LINES = 2e3;
|
|
5111
5491
|
var MAX_LINE_CHARS = 500;
|
|
5112
5492
|
function makeReadFile(workspace) {
|
|
5113
|
-
const root =
|
|
5493
|
+
const root = path11.resolve(workspace);
|
|
5114
5494
|
return function read_file(args) {
|
|
5115
5495
|
const filePath = String(args.path ?? "");
|
|
5116
5496
|
const startLine = Math.max(1, Number(args.start_line ?? 1));
|
|
@@ -5120,13 +5500,13 @@ function makeReadFile(workspace) {
|
|
|
5120
5500
|
return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5121
5501
|
}
|
|
5122
5502
|
const target = resolved.real;
|
|
5123
|
-
if (!
|
|
5503
|
+
if (!fs9.existsSync(target) || !fs9.statSync(target).isFile()) {
|
|
5124
5504
|
return { error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
|
|
5125
5505
|
}
|
|
5126
5506
|
const selected = [];
|
|
5127
5507
|
let total = 0;
|
|
5128
5508
|
try {
|
|
5129
|
-
const content =
|
|
5509
|
+
const content = fs9.readFileSync(target, "utf-8");
|
|
5130
5510
|
const lines = content.split("\n");
|
|
5131
5511
|
total = lines.length;
|
|
5132
5512
|
for (let i = startLine - 1; i < Math.min(startLine - 1 + maxLines, lines.length); i++) {
|
|
@@ -5141,7 +5521,7 @@ function makeReadFile(workspace) {
|
|
|
5141
5521
|
}
|
|
5142
5522
|
const end = startLine + selected.length - 1;
|
|
5143
5523
|
const result = {
|
|
5144
|
-
path:
|
|
5524
|
+
path: path11.relative(resolved.root, target),
|
|
5145
5525
|
start_line: startLine,
|
|
5146
5526
|
end_line: end,
|
|
5147
5527
|
total_lines: total,
|
|
@@ -5154,7 +5534,7 @@ function makeReadFile(workspace) {
|
|
|
5154
5534
|
};
|
|
5155
5535
|
}
|
|
5156
5536
|
function makeWriteFile(workspace) {
|
|
5157
|
-
const root =
|
|
5537
|
+
const root = path11.resolve(workspace);
|
|
5158
5538
|
return function write_file(args) {
|
|
5159
5539
|
const filePath = String(args.path ?? "");
|
|
5160
5540
|
const content = String(args.content ?? "");
|
|
@@ -5164,34 +5544,34 @@ function makeWriteFile(workspace) {
|
|
|
5164
5544
|
return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5165
5545
|
}
|
|
5166
5546
|
const target = resolved.real;
|
|
5167
|
-
if (
|
|
5547
|
+
if (fs9.existsSync(target) && fs9.statSync(target).isDirectory()) {
|
|
5168
5548
|
return { error: "\u8DEF\u5F84\u662F\u76EE\u5F55\uFF0C\u4E0D\u662F\u6587\u4EF6" };
|
|
5169
5549
|
}
|
|
5170
|
-
if (
|
|
5550
|
+
if (fs9.existsSync(target) && !overwrite) {
|
|
5171
5551
|
return { error: "\u6587\u4EF6\u5DF2\u5B58\u5728 (overwrite=false)" };
|
|
5172
5552
|
}
|
|
5173
5553
|
try {
|
|
5174
|
-
|
|
5175
|
-
|
|
5554
|
+
fs9.mkdirSync(path11.dirname(target), { recursive: true });
|
|
5555
|
+
fs9.writeFileSync(target, content, "utf-8");
|
|
5176
5556
|
} catch (exc) {
|
|
5177
5557
|
return { error: `\u5199\u5165\u5931\u8D25: ${String(exc)}` };
|
|
5178
5558
|
}
|
|
5179
|
-
return
|
|
5559
|
+
return path11.relative(resolved.root, target);
|
|
5180
5560
|
};
|
|
5181
5561
|
}
|
|
5182
5562
|
function makeListFiles(workspace) {
|
|
5183
|
-
const root =
|
|
5563
|
+
const root = path11.resolve(workspace);
|
|
5184
5564
|
return function list_files(args) {
|
|
5185
5565
|
const dirPath = String(args.path ?? ".");
|
|
5186
5566
|
const maxResults = Math.min(Number(args.max_results ?? 100), 100);
|
|
5187
5567
|
const resolved = resolveWithinRoot(root, dirPath);
|
|
5188
5568
|
if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5189
5569
|
const target = resolved.real;
|
|
5190
|
-
if (!
|
|
5570
|
+
if (!fs9.existsSync(target) || !fs9.statSync(target).isDirectory()) {
|
|
5191
5571
|
return { error: `\u4E0D\u662F\u76EE\u5F55: ${dirPath}` };
|
|
5192
5572
|
}
|
|
5193
5573
|
try {
|
|
5194
|
-
const entries =
|
|
5574
|
+
const entries = fs9.readdirSync(target, { withFileTypes: true });
|
|
5195
5575
|
const results = [];
|
|
5196
5576
|
for (const e of entries) {
|
|
5197
5577
|
if (results.length >= maxResults) break;
|
|
@@ -5200,14 +5580,14 @@ function makeListFiles(workspace) {
|
|
|
5200
5580
|
results.push(e.isDirectory() ? `${e.name}/` : e.name);
|
|
5201
5581
|
}
|
|
5202
5582
|
results.sort();
|
|
5203
|
-
return { path:
|
|
5583
|
+
return { path: path11.relative(resolved.root, target), entries: results.slice(0, maxResults) };
|
|
5204
5584
|
} catch (exc) {
|
|
5205
5585
|
return { error: `\u5217\u51FA\u6587\u4EF6\u5931\u8D25: ${String(exc)}` };
|
|
5206
5586
|
}
|
|
5207
5587
|
};
|
|
5208
5588
|
}
|
|
5209
5589
|
function makeReplaceInFile(workspace) {
|
|
5210
|
-
const root =
|
|
5590
|
+
const root = path11.resolve(workspace);
|
|
5211
5591
|
return function replace_in_file(args) {
|
|
5212
5592
|
const filePath = String(args.path ?? "");
|
|
5213
5593
|
const oldStr = String(args.old ?? "");
|
|
@@ -5217,19 +5597,19 @@ function makeReplaceInFile(workspace) {
|
|
|
5217
5597
|
const resolved = resolveWithinRoot(root, filePath);
|
|
5218
5598
|
if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5219
5599
|
const target = resolved.real;
|
|
5220
|
-
if (!
|
|
5600
|
+
if (!fs9.existsSync(target) || !fs9.statSync(target).isFile()) {
|
|
5221
5601
|
return { error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
|
|
5222
5602
|
}
|
|
5223
5603
|
try {
|
|
5224
|
-
const content =
|
|
5604
|
+
const content = fs9.readFileSync(target, "utf-8");
|
|
5225
5605
|
const count = content.split(oldStr).length - 1;
|
|
5226
5606
|
if (count !== expectedReplacements) {
|
|
5227
5607
|
return { error: `\u671F\u671B ${expectedReplacements} \u5904\u66FF\u6362\uFF0C\u5B9E\u9645\u627E\u5230 ${count} \u5904: ${filePath}` };
|
|
5228
5608
|
}
|
|
5229
5609
|
const updated = content.replaceAll(oldStr, newStr);
|
|
5230
|
-
|
|
5610
|
+
fs9.writeFileSync(target, updated, "utf-8");
|
|
5231
5611
|
return {
|
|
5232
|
-
path:
|
|
5612
|
+
path: path11.relative(resolved.root, target),
|
|
5233
5613
|
replacements: count,
|
|
5234
5614
|
chars_before: content.length,
|
|
5235
5615
|
chars_after: updated.length
|
|
@@ -5330,8 +5710,8 @@ function registerFileTools(registry, workspace) {
|
|
|
5330
5710
|
|
|
5331
5711
|
// src/tools/shell.ts
|
|
5332
5712
|
init_esm_shims();
|
|
5333
|
-
import * as
|
|
5334
|
-
import * as
|
|
5713
|
+
import * as fs11 from "fs";
|
|
5714
|
+
import * as path13 from "path";
|
|
5335
5715
|
|
|
5336
5716
|
// src/tools/execAsync.ts
|
|
5337
5717
|
init_esm_shims();
|
|
@@ -5496,15 +5876,15 @@ function execAsync(file, argv, opts = {}) {
|
|
|
5496
5876
|
|
|
5497
5877
|
// src/tools/shellRuntime.ts
|
|
5498
5878
|
init_esm_shims();
|
|
5499
|
-
import * as
|
|
5500
|
-
import * as
|
|
5879
|
+
import * as fs10 from "fs";
|
|
5880
|
+
import * as path12 from "path";
|
|
5501
5881
|
var WINDOWS_SUFFIXES = [".exe", ".cmd", ".bat", ".com", ""];
|
|
5502
5882
|
function pathApi(platform) {
|
|
5503
|
-
return platform === "win32" ?
|
|
5883
|
+
return platform === "win32" ? path12.win32 : path12.posix;
|
|
5504
5884
|
}
|
|
5505
5885
|
function defaultFileExists(p) {
|
|
5506
5886
|
try {
|
|
5507
|
-
return
|
|
5887
|
+
return fs10.statSync(p).isFile();
|
|
5508
5888
|
} catch {
|
|
5509
5889
|
return false;
|
|
5510
5890
|
}
|
|
@@ -5535,7 +5915,7 @@ function kindFromBasename(base) {
|
|
|
5535
5915
|
return "sh";
|
|
5536
5916
|
}
|
|
5537
5917
|
function busyboxArgs(file, command) {
|
|
5538
|
-
const base = (file ?
|
|
5918
|
+
const base = (file ? path12.win32.basename(file) : "busybox").toLowerCase().replace(/\.(exe|cmd|bat|com)$/, "");
|
|
5539
5919
|
return base === "busybox" ? ["sh", "-c", command] : ["-c", command];
|
|
5540
5920
|
}
|
|
5541
5921
|
function argsFor(kind, command) {
|
|
@@ -5802,7 +6182,7 @@ var MAX_STDERR = 5e3;
|
|
|
5802
6182
|
var MAX_BUFFER = 1024 * 1024 * 5;
|
|
5803
6183
|
var shellSessions = /* @__PURE__ */ new Map();
|
|
5804
6184
|
function sessionFor(workspace) {
|
|
5805
|
-
const key =
|
|
6185
|
+
const key = path13.resolve(workspace);
|
|
5806
6186
|
let s = shellSessions.get(key);
|
|
5807
6187
|
if (!s) {
|
|
5808
6188
|
s = { cwd: key };
|
|
@@ -5859,8 +6239,8 @@ function makeRunShell(workspace, spec, tasks, scope) {
|
|
|
5859
6239
|
let toRun = command;
|
|
5860
6240
|
if (target) {
|
|
5861
6241
|
try {
|
|
5862
|
-
const resolved =
|
|
5863
|
-
if (
|
|
6242
|
+
const resolved = path13.resolve(session.cwd, target);
|
|
6243
|
+
if (fs11.existsSync(resolved) && fs11.statSync(resolved).isDirectory()) {
|
|
5864
6244
|
session.cwd = resolved;
|
|
5865
6245
|
}
|
|
5866
6246
|
} catch {
|
|
@@ -5995,8 +6375,8 @@ init_esm_shims();
|
|
|
5995
6375
|
|
|
5996
6376
|
// src/tools/nodeGrep.ts
|
|
5997
6377
|
init_esm_shims();
|
|
5998
|
-
import * as
|
|
5999
|
-
import * as
|
|
6378
|
+
import * as fs12 from "fs";
|
|
6379
|
+
import * as path14 from "path";
|
|
6000
6380
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
6001
6381
|
".git",
|
|
6002
6382
|
"node_modules",
|
|
@@ -6021,27 +6401,27 @@ function nodeGrep(opts) {
|
|
|
6021
6401
|
} catch (err) {
|
|
6022
6402
|
throw new Error(`\u65E0\u6548\u7684\u6B63\u5219\u8868\u8FBE\u5F0F: ${opts.pattern}\uFF08${String(err)}\uFF09`);
|
|
6023
6403
|
}
|
|
6024
|
-
const target =
|
|
6025
|
-
if (!
|
|
6404
|
+
const target = path14.resolve(opts.workspace, opts.target);
|
|
6405
|
+
if (!fs12.existsSync(target)) {
|
|
6026
6406
|
throw new Error(`\u641C\u7D22\u8DEF\u5F84\u4E0D\u5B58\u5728: ${opts.target}`);
|
|
6027
6407
|
}
|
|
6028
6408
|
const matches = [];
|
|
6029
6409
|
const scanFile = (file) => {
|
|
6030
6410
|
let stat2;
|
|
6031
6411
|
try {
|
|
6032
|
-
stat2 =
|
|
6412
|
+
stat2 = fs12.statSync(file);
|
|
6033
6413
|
} catch {
|
|
6034
6414
|
return false;
|
|
6035
6415
|
}
|
|
6036
6416
|
if (!stat2.isFile() || stat2.size > maxFileBytes) return false;
|
|
6037
6417
|
let buf;
|
|
6038
6418
|
try {
|
|
6039
|
-
buf =
|
|
6419
|
+
buf = fs12.readFileSync(file);
|
|
6040
6420
|
} catch {
|
|
6041
6421
|
return false;
|
|
6042
6422
|
}
|
|
6043
6423
|
if (looksBinary(buf)) return false;
|
|
6044
|
-
const rel =
|
|
6424
|
+
const rel = path14.relative(opts.workspace, file) || path14.basename(file);
|
|
6045
6425
|
const lines = buf.toString("utf-8").split("\n");
|
|
6046
6426
|
for (let i = 0; i < lines.length; i++) {
|
|
6047
6427
|
const raw = lines[i].replace(/\r$/, "");
|
|
@@ -6052,7 +6432,7 @@ function nodeGrep(opts) {
|
|
|
6052
6432
|
}
|
|
6053
6433
|
return false;
|
|
6054
6434
|
};
|
|
6055
|
-
const stat =
|
|
6435
|
+
const stat = fs12.statSync(target);
|
|
6056
6436
|
if (stat.isFile()) {
|
|
6057
6437
|
scanFile(target);
|
|
6058
6438
|
return matches;
|
|
@@ -6062,12 +6442,12 @@ function nodeGrep(opts) {
|
|
|
6062
6442
|
const dir = stack.pop();
|
|
6063
6443
|
let entries;
|
|
6064
6444
|
try {
|
|
6065
|
-
entries =
|
|
6445
|
+
entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
6066
6446
|
} catch {
|
|
6067
6447
|
continue;
|
|
6068
6448
|
}
|
|
6069
6449
|
for (const entry of entries) {
|
|
6070
|
-
const full =
|
|
6450
|
+
const full = path14.join(dir, entry.name);
|
|
6071
6451
|
if (entry.isDirectory()) {
|
|
6072
6452
|
if (!SKIP_DIRS.has(entry.name)) stack.push(full);
|
|
6073
6453
|
} else if (entry.isFile()) {
|
|
@@ -6080,22 +6460,22 @@ function nodeGrep(opts) {
|
|
|
6080
6460
|
|
|
6081
6461
|
// src/tools/ripgrep.ts
|
|
6082
6462
|
init_esm_shims();
|
|
6083
|
-
import * as
|
|
6463
|
+
import * as fs13 from "fs";
|
|
6084
6464
|
import * as os2 from "os";
|
|
6085
|
-
import * as
|
|
6465
|
+
import * as path15 from "path";
|
|
6086
6466
|
import { createRequire } from "module";
|
|
6087
6467
|
var cached2;
|
|
6088
6468
|
function extractBundledRipgrep() {
|
|
6089
6469
|
try {
|
|
6090
|
-
const req2 = typeof __require === "function" ? __require : createRequire(
|
|
6470
|
+
const req2 = typeof __require === "function" ? __require : createRequire(path15.join(process.cwd(), "__zqagent__.js"));
|
|
6091
6471
|
const sea = req2("node:sea");
|
|
6092
6472
|
if (!sea?.isSea?.() || typeof sea.getAsset !== "function") return null;
|
|
6093
6473
|
const asset = sea.getAsset("rg");
|
|
6094
6474
|
if (!asset) return null;
|
|
6095
|
-
const dir =
|
|
6096
|
-
const file =
|
|
6097
|
-
|
|
6098
|
-
|
|
6475
|
+
const dir = fs13.mkdtempSync(path15.join(os2.tmpdir(), "zqagent-rg-"));
|
|
6476
|
+
const file = path15.join(dir, process.platform === "win32" ? "rg.exe" : "rg");
|
|
6477
|
+
fs13.writeFileSync(file, Buffer.from(asset));
|
|
6478
|
+
fs13.chmodSync(file, 493);
|
|
6099
6479
|
return file;
|
|
6100
6480
|
} catch {
|
|
6101
6481
|
return null;
|
|
@@ -6104,7 +6484,7 @@ function extractBundledRipgrep() {
|
|
|
6104
6484
|
function resolveRipgrep() {
|
|
6105
6485
|
if (cached2 !== void 0) return cached2;
|
|
6106
6486
|
const explicit = (process.env.ZQAGENT_RG ?? "").trim();
|
|
6107
|
-
if (explicit &&
|
|
6487
|
+
if (explicit && fs13.existsSync(explicit)) {
|
|
6108
6488
|
cached2 = explicit;
|
|
6109
6489
|
return cached2;
|
|
6110
6490
|
}
|
|
@@ -6118,7 +6498,7 @@ function resolveRipgrep() {
|
|
|
6118
6498
|
env: process.env,
|
|
6119
6499
|
fileExists: (p) => {
|
|
6120
6500
|
try {
|
|
6121
|
-
return
|
|
6501
|
+
return fs13.statSync(p).isFile();
|
|
6122
6502
|
} catch {
|
|
6123
6503
|
return false;
|
|
6124
6504
|
}
|
|
@@ -6667,8 +7047,8 @@ function registerWebTools(registry) {
|
|
|
6667
7047
|
|
|
6668
7048
|
// src/tools/git.ts
|
|
6669
7049
|
init_esm_shims();
|
|
6670
|
-
import * as
|
|
6671
|
-
import * as
|
|
7050
|
+
import * as fs14 from "fs";
|
|
7051
|
+
import * as path16 from "path";
|
|
6672
7052
|
async function runGit(repoDir, args, timeout = 15e3) {
|
|
6673
7053
|
const res = await execAsync("git", ["-c", "core.quotepath=false", ...args], {
|
|
6674
7054
|
cwd: repoDir,
|
|
@@ -6685,9 +7065,9 @@ async function runGit(repoDir, args, timeout = 15e3) {
|
|
|
6685
7065
|
async function resolveRepoRoot(workspace, pathArg) {
|
|
6686
7066
|
let dir = workspace;
|
|
6687
7067
|
if (pathArg) {
|
|
6688
|
-
const abs =
|
|
7068
|
+
const abs = path16.resolve(workspace, pathArg);
|
|
6689
7069
|
try {
|
|
6690
|
-
dir =
|
|
7070
|
+
dir = fs14.statSync(abs).isDirectory() ? abs : path16.dirname(abs);
|
|
6691
7071
|
} catch {
|
|
6692
7072
|
dir = abs;
|
|
6693
7073
|
}
|
|
@@ -6700,24 +7080,24 @@ function repoDirArg(workspace, raw) {
|
|
|
6700
7080
|
if (typeof raw !== "string") return void 0;
|
|
6701
7081
|
const v = raw.trim();
|
|
6702
7082
|
if (!v) return void 0;
|
|
6703
|
-
const base =
|
|
6704
|
-
const abs =
|
|
6705
|
-
const rel =
|
|
6706
|
-
if (rel.startsWith("..") ||
|
|
7083
|
+
const base = path16.resolve(workspace);
|
|
7084
|
+
const abs = path16.resolve(base, v);
|
|
7085
|
+
const rel = path16.relative(base, abs);
|
|
7086
|
+
if (rel.startsWith("..") || path16.isAbsolute(rel)) return void 0;
|
|
6707
7087
|
return abs;
|
|
6708
7088
|
}
|
|
6709
7089
|
function toPathspec(repoRoot, workspace, pathArg) {
|
|
6710
7090
|
if (!pathArg) return null;
|
|
6711
|
-
const abs =
|
|
6712
|
-
let rel =
|
|
7091
|
+
const abs = path16.resolve(workspace, pathArg);
|
|
7092
|
+
let rel = path16.relative(repoRoot, abs);
|
|
6713
7093
|
if (rel === "") return ".";
|
|
6714
|
-
if (rel.startsWith("..") ||
|
|
7094
|
+
if (rel.startsWith("..") || path16.isAbsolute(rel)) {
|
|
6715
7095
|
try {
|
|
6716
|
-
const realRepo =
|
|
6717
|
-
const realAbs =
|
|
6718
|
-
const rel2 =
|
|
7096
|
+
const realRepo = fs14.realpathSync(repoRoot);
|
|
7097
|
+
const realAbs = fs14.realpathSync(abs);
|
|
7098
|
+
const rel2 = path16.relative(realRepo, realAbs);
|
|
6719
7099
|
if (rel2 === "") return ".";
|
|
6720
|
-
if (!rel2.startsWith("..") && !
|
|
7100
|
+
if (!rel2.startsWith("..") && !path16.isAbsolute(rel2)) return rel2;
|
|
6721
7101
|
} catch {
|
|
6722
7102
|
}
|
|
6723
7103
|
return null;
|
|
@@ -7143,18 +7523,18 @@ var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".s
|
|
|
7143
7523
|
var SCAN_DEFAULT_DEPTH = 3;
|
|
7144
7524
|
var SCAN_MAX_DIRS = 2e3;
|
|
7145
7525
|
function readRepoHead(repoDir) {
|
|
7146
|
-
const dotGit =
|
|
7526
|
+
const dotGit = path16.join(repoDir, ".git");
|
|
7147
7527
|
try {
|
|
7148
7528
|
let headFile;
|
|
7149
|
-
if (
|
|
7150
|
-
headFile =
|
|
7529
|
+
if (fs14.statSync(dotGit).isDirectory()) {
|
|
7530
|
+
headFile = path16.join(dotGit, "HEAD");
|
|
7151
7531
|
} else {
|
|
7152
|
-
const raw =
|
|
7532
|
+
const raw = fs14.readFileSync(dotGit, "utf-8");
|
|
7153
7533
|
const m = /^gitdir:\s*(.+)$/m.exec(raw);
|
|
7154
7534
|
if (!m) return {};
|
|
7155
|
-
headFile =
|
|
7535
|
+
headFile = path16.join(path16.resolve(repoDir, m[1].trim()), "HEAD");
|
|
7156
7536
|
}
|
|
7157
|
-
const head =
|
|
7537
|
+
const head = fs14.readFileSync(headFile, "utf-8").trim();
|
|
7158
7538
|
const prefix = "ref: refs/heads/";
|
|
7159
7539
|
if (head.startsWith(prefix)) return { branch: head.slice(prefix.length), detached: false };
|
|
7160
7540
|
if (/^[0-9a-f]{7,40}$/i.test(head)) return { branch: head.slice(0, 7), detached: true };
|
|
@@ -7163,7 +7543,7 @@ function readRepoHead(repoDir) {
|
|
|
7163
7543
|
return {};
|
|
7164
7544
|
}
|
|
7165
7545
|
function scanGitRepos(workspace, depth = SCAN_DEFAULT_DEPTH) {
|
|
7166
|
-
const root =
|
|
7546
|
+
const root = path16.resolve(workspace);
|
|
7167
7547
|
const maxDepth = Number.isFinite(depth) ? Math.min(Math.max(Math.floor(depth), 0), 6) : SCAN_DEFAULT_DEPTH;
|
|
7168
7548
|
const repos = [];
|
|
7169
7549
|
const stack = [{ dir: root, depth: 0 }];
|
|
@@ -7176,24 +7556,24 @@ function scanGitRepos(workspace, depth = SCAN_DEFAULT_DEPTH) {
|
|
|
7176
7556
|
truncated = true;
|
|
7177
7557
|
break;
|
|
7178
7558
|
}
|
|
7179
|
-
const rel =
|
|
7180
|
-
if (
|
|
7559
|
+
const rel = path16.relative(root, dir).split(path16.sep).join("/");
|
|
7560
|
+
if (fs14.existsSync(path16.join(dir, ".git"))) {
|
|
7181
7561
|
repos.push({
|
|
7182
7562
|
path: rel,
|
|
7183
|
-
name: rel ?
|
|
7563
|
+
name: rel ? path16.basename(dir) : path16.basename(root),
|
|
7184
7564
|
...readRepoHead(dir)
|
|
7185
7565
|
});
|
|
7186
7566
|
}
|
|
7187
7567
|
if (d >= maxDepth) continue;
|
|
7188
7568
|
let entries;
|
|
7189
7569
|
try {
|
|
7190
|
-
entries =
|
|
7570
|
+
entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
7191
7571
|
} catch {
|
|
7192
7572
|
continue;
|
|
7193
7573
|
}
|
|
7194
7574
|
for (const e of entries) {
|
|
7195
7575
|
if (!e.isDirectory() || SCAN_SKIP_DIRS.has(e.name)) continue;
|
|
7196
|
-
stack.push({ dir:
|
|
7576
|
+
stack.push({ dir: path16.join(dir, e.name), depth: d + 1 });
|
|
7197
7577
|
}
|
|
7198
7578
|
}
|
|
7199
7579
|
repos.sort(
|
|
@@ -7203,9 +7583,9 @@ function scanGitRepos(workspace, depth = SCAN_DEFAULT_DEPTH) {
|
|
|
7203
7583
|
}
|
|
7204
7584
|
function makeGitRepos(workspace) {
|
|
7205
7585
|
return async function git_repos(args) {
|
|
7206
|
-
const root =
|
|
7586
|
+
const root = path16.resolve(workspace);
|
|
7207
7587
|
try {
|
|
7208
|
-
if (!
|
|
7588
|
+
if (!fs14.statSync(root).isDirectory()) return { root, repos: [], error: "\u5DE5\u4F5C\u533A\u4E0D\u662F\u76EE\u5F55" };
|
|
7209
7589
|
} catch {
|
|
7210
7590
|
return { root, repos: [], error: "\u5DE5\u4F5C\u533A\u76EE\u5F55\u4E0D\u5B58\u5728" };
|
|
7211
7591
|
}
|
|
@@ -7217,10 +7597,10 @@ function makeGitRepos(workspace) {
|
|
|
7217
7597
|
|
|
7218
7598
|
// src/tools/diff.ts
|
|
7219
7599
|
init_esm_shims();
|
|
7220
|
-
import * as
|
|
7221
|
-
import * as
|
|
7600
|
+
import * as fs15 from "fs";
|
|
7601
|
+
import * as path17 from "path";
|
|
7222
7602
|
function makeApplyPatch(workspace) {
|
|
7223
|
-
const root =
|
|
7603
|
+
const root = path17.resolve(workspace);
|
|
7224
7604
|
return function apply_patch(args) {
|
|
7225
7605
|
const patch = String(args.patch ?? "");
|
|
7226
7606
|
if (!patch.trim()) return { error: "\u8865\u4E01\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
@@ -7259,12 +7639,12 @@ function makeApplyPatch(workspace) {
|
|
|
7259
7639
|
contentLines.push(current.slice(1));
|
|
7260
7640
|
i++;
|
|
7261
7641
|
}
|
|
7262
|
-
if (
|
|
7642
|
+
if (fs15.existsSync(target)) {
|
|
7263
7643
|
return { error: `\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}` };
|
|
7264
7644
|
}
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
const rel =
|
|
7645
|
+
fs15.mkdirSync(path17.dirname(target), { recursive: true });
|
|
7646
|
+
fs15.writeFileSync(target, contentLines.join("\n") + "\n", "utf-8");
|
|
7647
|
+
const rel = path17.relative(resolved.root, target);
|
|
7268
7648
|
changedFiles.push(rel);
|
|
7269
7649
|
addedFiles.push(rel);
|
|
7270
7650
|
fileCount++;
|
|
@@ -7277,11 +7657,11 @@ function makeApplyPatch(workspace) {
|
|
|
7277
7657
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${filePath}` };
|
|
7278
7658
|
}
|
|
7279
7659
|
const target = entry.real;
|
|
7280
|
-
if (!
|
|
7660
|
+
if (!fs15.existsSync(target)) {
|
|
7281
7661
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
7282
7662
|
}
|
|
7283
|
-
|
|
7284
|
-
deletedFiles.push(
|
|
7663
|
+
fs15.unlinkSync(target);
|
|
7664
|
+
deletedFiles.push(path17.relative(entry.root, target));
|
|
7285
7665
|
fileCount++;
|
|
7286
7666
|
i++;
|
|
7287
7667
|
continue;
|
|
@@ -7317,10 +7697,10 @@ function makeApplyPatch(workspace) {
|
|
|
7317
7697
|
if (!chunkLines.length) {
|
|
7318
7698
|
return { error: `\u8865\u4E01\u66F4\u65B0\u6CA1\u6709 hunks: ${filePath}` };
|
|
7319
7699
|
}
|
|
7320
|
-
if (!
|
|
7700
|
+
if (!fs15.existsSync(target)) {
|
|
7321
7701
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
7322
7702
|
}
|
|
7323
|
-
const oldLines =
|
|
7703
|
+
const oldLines = fs15.readFileSync(target, "utf-8").split("\n");
|
|
7324
7704
|
const newLines = applyPatchHunks(oldLines, chunkLines, filePath);
|
|
7325
7705
|
let finalResolved = resolved;
|
|
7326
7706
|
if (moveTo) {
|
|
@@ -7331,20 +7711,20 @@ function makeApplyPatch(workspace) {
|
|
|
7331
7711
|
finalResolved = moved;
|
|
7332
7712
|
}
|
|
7333
7713
|
const finalTarget = finalResolved.real;
|
|
7334
|
-
if (moveTo &&
|
|
7714
|
+
if (moveTo && fs15.existsSync(finalTarget) && finalTarget !== target) {
|
|
7335
7715
|
return { error: `\u6587\u4EF6\u5DF2\u5B58\u5728: ${moveTo}` };
|
|
7336
7716
|
}
|
|
7337
|
-
|
|
7338
|
-
|
|
7717
|
+
fs15.mkdirSync(path17.dirname(finalTarget), { recursive: true });
|
|
7718
|
+
fs15.writeFileSync(finalTarget, newLines.join("\n"), "utf-8");
|
|
7339
7719
|
if (moveTo && finalTarget !== target) {
|
|
7340
7720
|
const entry = resolveEntryWithinRoot(root, filePath);
|
|
7341
7721
|
if (!entry) {
|
|
7342
7722
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${filePath}` };
|
|
7343
7723
|
}
|
|
7344
|
-
|
|
7345
|
-
deletedFiles.push(
|
|
7724
|
+
fs15.unlinkSync(entry.real);
|
|
7725
|
+
deletedFiles.push(path17.relative(resolved.root, target));
|
|
7346
7726
|
}
|
|
7347
|
-
changedFiles.push(
|
|
7727
|
+
changedFiles.push(path17.relative(finalResolved.root, finalTarget));
|
|
7348
7728
|
fileCount++;
|
|
7349
7729
|
continue;
|
|
7350
7730
|
}
|
|
@@ -7356,18 +7736,18 @@ function makeApplyPatch(workspace) {
|
|
|
7356
7736
|
return { changed_files: changedFiles, added_files: addedFiles, deleted_files: deletedFiles, file_count: fileCount };
|
|
7357
7737
|
};
|
|
7358
7738
|
}
|
|
7359
|
-
function applyPatchHunks(oldLines, chunks,
|
|
7739
|
+
function applyPatchHunks(oldLines, chunks, path36) {
|
|
7360
7740
|
let newLines = [...oldLines];
|
|
7361
7741
|
let cursor = 0;
|
|
7362
7742
|
for (const chunk of chunks) {
|
|
7363
7743
|
const oldBlock = chunk.filter((l) => l[0] === " " || l[0] === "-").map((l) => l.slice(1));
|
|
7364
7744
|
const newBlock = chunk.filter((l) => l[0] === " " || l[0] === "+").map((l) => l.slice(1));
|
|
7365
7745
|
if (!oldBlock.length) {
|
|
7366
|
-
throw new Error(`\u8865\u4E01 hunk \u5FC5\u987B\u5305\u542B\u4E0A\u4E0B\u6587\u6216\u5220\u9664\u884C: ${
|
|
7746
|
+
throw new Error(`\u8865\u4E01 hunk \u5FC5\u987B\u5305\u542B\u4E0A\u4E0B\u6587\u6216\u5220\u9664\u884C: ${path36}`);
|
|
7367
7747
|
}
|
|
7368
7748
|
const matchStart = findBlockMatch(newLines, oldBlock, cursor);
|
|
7369
7749
|
if (matchStart === -1) {
|
|
7370
|
-
throw new Error(`\u8865\u4E01\u4E0A\u4E0B\u6587\u4E0D\u5339\u914D: ${
|
|
7750
|
+
throw new Error(`\u8865\u4E01\u4E0A\u4E0B\u6587\u4E0D\u5339\u914D: ${path36}`);
|
|
7371
7751
|
}
|
|
7372
7752
|
const matchEnd = matchStart + oldBlock.length;
|
|
7373
7753
|
newLines.splice(matchStart, oldBlock.length, ...newBlock);
|
|
@@ -7384,7 +7764,7 @@ function findBlockMatch(lines, block, start) {
|
|
|
7384
7764
|
return -1;
|
|
7385
7765
|
}
|
|
7386
7766
|
function makeApplyUnifiedDiff(workspace) {
|
|
7387
|
-
const root =
|
|
7767
|
+
const root = path17.resolve(workspace);
|
|
7388
7768
|
return function apply_unified_diff(args) {
|
|
7389
7769
|
const diff = String(args.diff ?? "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
7390
7770
|
if (!diff.trim()) return { error: "diff \u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
@@ -7403,26 +7783,26 @@ function makeApplyUnifiedDiff(workspace) {
|
|
|
7403
7783
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${targetPath}` };
|
|
7404
7784
|
}
|
|
7405
7785
|
const target = resolved.real;
|
|
7406
|
-
const oldLines = oldIsDevNull ? [] :
|
|
7786
|
+
const oldLines = oldIsDevNull ? [] : fs15.existsSync(target) ? fs15.readFileSync(target, "utf-8").split("\n") : [];
|
|
7407
7787
|
const newLines = applyDiffHunks(oldLines, patch.hunks, targetPath);
|
|
7408
7788
|
hunkCount += patch.hunks.length;
|
|
7409
7789
|
if (newIsDevNull) {
|
|
7410
|
-
if (
|
|
7411
|
-
if (!
|
|
7790
|
+
if (fs15.existsSync(target)) {
|
|
7791
|
+
if (!fs15.statSync(target).isFile()) {
|
|
7412
7792
|
return { error: `\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${targetPath}` };
|
|
7413
7793
|
}
|
|
7414
7794
|
const entry = resolveEntryWithinRoot(root, targetPath);
|
|
7415
7795
|
if (!entry) {
|
|
7416
7796
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${targetPath}` };
|
|
7417
7797
|
}
|
|
7418
|
-
|
|
7798
|
+
fs15.unlinkSync(entry.real);
|
|
7419
7799
|
}
|
|
7420
|
-
deletedFiles.push(
|
|
7800
|
+
deletedFiles.push(path17.relative(resolved.root, target));
|
|
7421
7801
|
continue;
|
|
7422
7802
|
}
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
const rel =
|
|
7803
|
+
fs15.mkdirSync(path17.dirname(target), { recursive: true });
|
|
7804
|
+
fs15.writeFileSync(target, newLines.join("\n"), "utf-8");
|
|
7805
|
+
const rel = path17.relative(resolved.root, target);
|
|
7426
7806
|
changedFiles.push(rel);
|
|
7427
7807
|
if (oldIsDevNull) addedFiles.push(rel);
|
|
7428
7808
|
}
|
|
@@ -7823,7 +8203,8 @@ function buildExplorerEngine(opts) {
|
|
|
7823
8203
|
model: opts.model,
|
|
7824
8204
|
instructions: EXPLORER_INSTRUCTIONS,
|
|
7825
8205
|
maxIterations: parseMaxIterations(process.env.EXPLORE_MAX_ITERATIONS, 30),
|
|
7826
|
-
hook: opts.hook
|
|
8206
|
+
hook: opts.hook,
|
|
8207
|
+
toolResultDir: opts.toolResultDir
|
|
7827
8208
|
});
|
|
7828
8209
|
}
|
|
7829
8210
|
function makeExploreTool(opts) {
|
|
@@ -7835,7 +8216,8 @@ function makeExploreTool(opts) {
|
|
|
7835
8216
|
provider: opts.provider,
|
|
7836
8217
|
model: opts.model,
|
|
7837
8218
|
readonlyRegistrars: opts.readonlyRegistrars,
|
|
7838
|
-
hook: opts.hookFactory?.()
|
|
8219
|
+
hook: opts.hookFactory?.(),
|
|
8220
|
+
toolResultDir: opts.toolResultDir
|
|
7839
8221
|
});
|
|
7840
8222
|
let report = "";
|
|
7841
8223
|
let status = "unknown";
|
|
@@ -7896,14 +8278,14 @@ function registerExploreTool(registry, opts) {
|
|
|
7896
8278
|
|
|
7897
8279
|
// src/tools/media.ts
|
|
7898
8280
|
init_esm_shims();
|
|
7899
|
-
import * as
|
|
7900
|
-
import * as
|
|
8281
|
+
import * as fs17 from "fs";
|
|
8282
|
+
import * as path19 from "path";
|
|
7901
8283
|
|
|
7902
8284
|
// src/tools/ffmpegRuntime.ts
|
|
7903
8285
|
init_esm_shims();
|
|
7904
|
-
import * as
|
|
8286
|
+
import * as fs16 from "fs";
|
|
7905
8287
|
import * as os3 from "os";
|
|
7906
|
-
import * as
|
|
8288
|
+
import * as path18 from "path";
|
|
7907
8289
|
import { createRequire as createRequire2 } from "module";
|
|
7908
8290
|
import { createHash } from "crypto";
|
|
7909
8291
|
import { pipeline } from "stream/promises";
|
|
@@ -7926,29 +8308,29 @@ if (PROXY_ENV2) {
|
|
|
7926
8308
|
}
|
|
7927
8309
|
var DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
7928
8310
|
function agentBinDir(workspace) {
|
|
7929
|
-
return
|
|
8311
|
+
return path18.join(path18.resolve(workspace), ".agent", "bin");
|
|
7930
8312
|
}
|
|
7931
8313
|
function exeName(name, platform) {
|
|
7932
8314
|
return platform === "win32" ? `${name}.exe` : name;
|
|
7933
8315
|
}
|
|
7934
8316
|
function defaultFileExists2(p) {
|
|
7935
8317
|
try {
|
|
7936
|
-
return
|
|
8318
|
+
return fs16.statSync(p).isFile();
|
|
7937
8319
|
} catch {
|
|
7938
8320
|
return false;
|
|
7939
8321
|
}
|
|
7940
8322
|
}
|
|
7941
8323
|
function extractBundled(name, platform) {
|
|
7942
8324
|
try {
|
|
7943
|
-
const req2 = typeof __require === "function" ? __require : createRequire2(
|
|
8325
|
+
const req2 = typeof __require === "function" ? __require : createRequire2(path18.join(process.cwd(), "__zqagent__.js"));
|
|
7944
8326
|
const sea = req2("node:sea");
|
|
7945
8327
|
if (!sea?.isSea?.() || typeof sea.getAsset !== "function") return null;
|
|
7946
8328
|
const asset = sea.getAsset(name);
|
|
7947
8329
|
if (!asset) return null;
|
|
7948
|
-
const dir =
|
|
7949
|
-
const file =
|
|
7950
|
-
|
|
7951
|
-
|
|
8330
|
+
const dir = fs16.mkdtempSync(path18.join(os3.tmpdir(), `zqagent-${name}-`));
|
|
8331
|
+
const file = path18.join(dir, exeName(name, platform));
|
|
8332
|
+
fs16.writeFileSync(file, Buffer.from(asset));
|
|
8333
|
+
fs16.chmodSync(file, 493);
|
|
7952
8334
|
return file;
|
|
7953
8335
|
} catch {
|
|
7954
8336
|
return null;
|
|
@@ -7958,7 +8340,7 @@ function resolveOne(name, workspace, o) {
|
|
|
7958
8340
|
const envKey = name === "ffmpeg" ? ENV_FFMPEG : ENV_FFPROBE;
|
|
7959
8341
|
const explicit = (o.env[envKey] ?? "").trim();
|
|
7960
8342
|
if (explicit && o.fileExists(explicit)) return { path: explicit, source: "env" };
|
|
7961
|
-
const local =
|
|
8343
|
+
const local = path18.join(agentBinDir(workspace), exeName(name, o.platform));
|
|
7962
8344
|
if (o.fileExists(local)) return { path: local, source: "agent-bin" };
|
|
7963
8345
|
const bundled = extractBundled(name, o.platform);
|
|
7964
8346
|
if (bundled) return { path: bundled, source: "bundled" };
|
|
@@ -7972,7 +8354,7 @@ function resolveOne(name, workspace, o) {
|
|
|
7972
8354
|
var cache = /* @__PURE__ */ new Map();
|
|
7973
8355
|
function resolveMedia(workspace, opts = {}) {
|
|
7974
8356
|
const platform = opts.platform ?? process.platform;
|
|
7975
|
-
const key = `${platform}:${
|
|
8357
|
+
const key = `${platform}:${path18.resolve(workspace)}`;
|
|
7976
8358
|
const hit = cache.get(key);
|
|
7977
8359
|
if (hit) return hit;
|
|
7978
8360
|
const ctx = {
|
|
@@ -8078,7 +8460,7 @@ function parseChecksumFile(text, filename) {
|
|
|
8078
8460
|
if (!m) continue;
|
|
8079
8461
|
const hash = m[1].toLowerCase();
|
|
8080
8462
|
const name = m[2]?.trim() ?? "";
|
|
8081
|
-
if (!name || name === filename ||
|
|
8463
|
+
if (!name || name === filename || path18.basename(name) === filename) return hash;
|
|
8082
8464
|
}
|
|
8083
8465
|
const single = lines[0]?.trim();
|
|
8084
8466
|
if (lines.length === 1 && single && /^[0-9a-f]{32,128}$/i.test(single)) {
|
|
@@ -8088,7 +8470,7 @@ function parseChecksumFile(text, filename) {
|
|
|
8088
8470
|
}
|
|
8089
8471
|
async function computeChecksum(file, algo) {
|
|
8090
8472
|
const hash = createHash(algo);
|
|
8091
|
-
await pipeline(
|
|
8473
|
+
await pipeline(fs16.createReadStream(file), hash);
|
|
8092
8474
|
return hash.digest("hex");
|
|
8093
8475
|
}
|
|
8094
8476
|
function fetchInit() {
|
|
@@ -8103,7 +8485,7 @@ async function downloadToFile(url, dest) {
|
|
|
8103
8485
|
const resp = await nodeFetch3(url, fetchInit());
|
|
8104
8486
|
if (!resp.ok) throw new Error(`HTTP ${resp.status} ${resp.statusText}`);
|
|
8105
8487
|
if (!resp.body) throw new Error("\u54CD\u5E94\u6CA1\u6709\u5185\u5BB9");
|
|
8106
|
-
await pipeline(resp.body,
|
|
8488
|
+
await pipeline(resp.body, fs16.createWriteStream(dest));
|
|
8107
8489
|
}
|
|
8108
8490
|
async function resolveComponentUrl(c) {
|
|
8109
8491
|
if (c.url) return c.url;
|
|
@@ -8120,12 +8502,12 @@ function findBinaryInTree(dir, names) {
|
|
|
8120
8502
|
const cur = stack.pop();
|
|
8121
8503
|
let entries;
|
|
8122
8504
|
try {
|
|
8123
|
-
entries =
|
|
8505
|
+
entries = fs16.readdirSync(cur, { withFileTypes: true });
|
|
8124
8506
|
} catch {
|
|
8125
8507
|
continue;
|
|
8126
8508
|
}
|
|
8127
8509
|
for (const e of entries) {
|
|
8128
|
-
const full =
|
|
8510
|
+
const full = path18.join(cur, e.name);
|
|
8129
8511
|
if (e.isDirectory()) {
|
|
8130
8512
|
stack.push(full);
|
|
8131
8513
|
} else if (names.includes(e.name)) {
|
|
@@ -8136,7 +8518,7 @@ function findBinaryInTree(dir, names) {
|
|
|
8136
8518
|
return null;
|
|
8137
8519
|
}
|
|
8138
8520
|
async function extractArchive(archive, dest, kind, platform) {
|
|
8139
|
-
|
|
8521
|
+
fs16.mkdirSync(dest, { recursive: true });
|
|
8140
8522
|
let res;
|
|
8141
8523
|
if (kind === "tar.xz") {
|
|
8142
8524
|
res = await execAsync("tar", ["-xJf", archive, "-C", dest]);
|
|
@@ -8172,7 +8554,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8172
8554
|
error: `\u6682\u4E0D\u652F\u6301\u81EA\u52A8\u4E0B\u8F7D\u7684\u5E73\u53F0: ${platform}/${arch}`
|
|
8173
8555
|
};
|
|
8174
8556
|
}
|
|
8175
|
-
const tmp =
|
|
8557
|
+
const tmp = fs16.mkdtempSync(path18.join(os3.tmpdir(), "zqagent-ffmpeg-"));
|
|
8176
8558
|
const binDir = agentBinDir(workspace);
|
|
8177
8559
|
const installed = {};
|
|
8178
8560
|
try {
|
|
@@ -8186,7 +8568,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8186
8568
|
}
|
|
8187
8569
|
let archive = fetched.get(url);
|
|
8188
8570
|
if (!archive) {
|
|
8189
|
-
const archivePath =
|
|
8571
|
+
const archivePath = path18.join(tmp, path18.basename(new URL(url).pathname) || `${c.name}.${c.archive}`);
|
|
8190
8572
|
log(`\u4E0B\u8F7D ${url}`);
|
|
8191
8573
|
try {
|
|
8192
8574
|
await downloadToFile(url, archivePath);
|
|
@@ -8196,7 +8578,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8196
8578
|
if (c.checksumUrl) {
|
|
8197
8579
|
const algo = c.checksumAlgo ?? "sha256";
|
|
8198
8580
|
try {
|
|
8199
|
-
const expected = parseChecksumFile(await fetchText(c.checksumUrl),
|
|
8581
|
+
const expected = parseChecksumFile(await fetchText(c.checksumUrl), path18.basename(archivePath));
|
|
8200
8582
|
if (!expected) {
|
|
8201
8583
|
notes.push(`\u672A\u80FD\u4ECE ${c.checksumUrl} \u89E3\u6790\u51FA\u6821\u9A8C\u548C\uFF0C\u5DF2\u8DF3\u8FC7\u6821\u9A8C`);
|
|
8202
8584
|
} else {
|
|
@@ -8213,24 +8595,24 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8213
8595
|
notes.push(`\u6821\u9A8C\u548C\u83B7\u53D6\u5931\u8D25 (${c.checksumUrl}): ${String(e instanceof Error ? e.message : e)}`);
|
|
8214
8596
|
}
|
|
8215
8597
|
} else {
|
|
8216
|
-
notes.push(`${
|
|
8598
|
+
notes.push(`${path18.basename(archivePath)} \u672A\u63D0\u4F9B\u6821\u9A8C\u548C\uFF08\u4EC5 HTTPS \u4F20\u8F93\u4FDD\u62A4\uFF09`);
|
|
8217
8599
|
}
|
|
8218
8600
|
archive = { file: archivePath, kind: c.archive };
|
|
8219
8601
|
fetched.set(url, archive);
|
|
8220
8602
|
}
|
|
8221
|
-
const extractDir =
|
|
8603
|
+
const extractDir = fs16.mkdtempSync(path18.join(tmp, "x-"));
|
|
8222
8604
|
const err = await extractArchive(archive.file, extractDir, archive.kind, platform);
|
|
8223
8605
|
if (err) return { ok: false, error: err };
|
|
8224
8606
|
const found = findBinaryInTree(extractDir, [exeName(c.name, platform), c.name]);
|
|
8225
8607
|
if (!found) {
|
|
8226
|
-
return { ok: false, error: `\u5F52\u6863\u5185\u672A\u627E\u5230 ${c.name}: ${
|
|
8608
|
+
return { ok: false, error: `\u5F52\u6863\u5185\u672A\u627E\u5230 ${c.name}: ${path18.basename(archive.file)}` };
|
|
8227
8609
|
}
|
|
8228
|
-
|
|
8229
|
-
const target =
|
|
8610
|
+
fs16.mkdirSync(binDir, { recursive: true });
|
|
8611
|
+
const target = path18.join(binDir, exeName(c.name, platform));
|
|
8230
8612
|
const staging = `${target}.tmp-${process.pid}`;
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8613
|
+
fs16.copyFileSync(found, staging);
|
|
8614
|
+
fs16.chmodSync(staging, 493);
|
|
8615
|
+
fs16.renameSync(staging, target);
|
|
8234
8616
|
installed[c.name] = target;
|
|
8235
8617
|
}
|
|
8236
8618
|
resetFfmpegCache();
|
|
@@ -8239,7 +8621,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8239
8621
|
} catch (e) {
|
|
8240
8622
|
return { ok: false, error: `\u5B89\u88C5\u5931\u8D25: ${String(e instanceof Error ? e.message : e)}` };
|
|
8241
8623
|
} finally {
|
|
8242
|
-
|
|
8624
|
+
fs16.rmSync(tmp, { recursive: true, force: true });
|
|
8243
8625
|
}
|
|
8244
8626
|
}
|
|
8245
8627
|
function canAutoDownload(platform, arch) {
|
|
@@ -8504,13 +8886,13 @@ function resolveTarget(workspace, value) {
|
|
|
8504
8886
|
return { abs: resolved.real, root: resolved.root };
|
|
8505
8887
|
}
|
|
8506
8888
|
function relOut(workspace, abs) {
|
|
8507
|
-
return
|
|
8889
|
+
return path19.relative(workspace, abs) || abs;
|
|
8508
8890
|
}
|
|
8509
8891
|
function registerMediaTool(registry, workspace, opts = {}) {
|
|
8510
8892
|
const platform = opts.platform ?? process.platform;
|
|
8511
8893
|
const arch = process.arch;
|
|
8512
8894
|
const media = async (args) => {
|
|
8513
|
-
const root =
|
|
8895
|
+
const root = path19.resolve(workspace);
|
|
8514
8896
|
const op = str(args["op"]);
|
|
8515
8897
|
if (!op) {
|
|
8516
8898
|
return { error: `\u7F3A\u5C11\u53C2\u6570 op\u3002\u53EF\u9009\u503C\uFF1A${OPS.join(" / ")}` };
|
|
@@ -8555,7 +8937,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
8555
8937
|
if (!input) return { error: `\u64CD\u4F5C ${op} \u9700\u8981\u53C2\u6570 input\uFF08\u5DE5\u4F5C\u533A\u5185\u76F8\u5BF9\u8DEF\u5F84\uFF09` };
|
|
8556
8938
|
const r = resolveTarget(root, input);
|
|
8557
8939
|
if ("error" in r) return { error: r.error };
|
|
8558
|
-
if (!
|
|
8940
|
+
if (!fs17.existsSync(r.abs) || !fs17.statSync(r.abs).isFile()) {
|
|
8559
8941
|
return { error: `\u8F93\u5165\u6587\u4EF6\u4E0D\u5B58\u5728: ${input}` };
|
|
8560
8942
|
}
|
|
8561
8943
|
inputAbs = r.abs;
|
|
@@ -8567,7 +8949,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
8567
8949
|
if (!output) return { error: `\u64CD\u4F5C ${op} \u9700\u8981\u53C2\u6570 output\uFF08\u5DE5\u4F5C\u533A\u5185\u76F8\u5BF9\u8DEF\u5F84\uFF09` };
|
|
8568
8950
|
const r = resolveTarget(root, output);
|
|
8569
8951
|
if ("error" in r) return { error: r.error };
|
|
8570
|
-
|
|
8952
|
+
fs17.mkdirSync(path19.dirname(r.abs), { recursive: true });
|
|
8571
8953
|
outputAbs = r.abs;
|
|
8572
8954
|
relRoot = r.root;
|
|
8573
8955
|
}
|
|
@@ -8617,7 +8999,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
8617
8999
|
};
|
|
8618
9000
|
if (outputAbs) {
|
|
8619
9001
|
try {
|
|
8620
|
-
result["size_bytes"] =
|
|
9002
|
+
result["size_bytes"] = fs17.statSync(outputAbs).size;
|
|
8621
9003
|
} catch {
|
|
8622
9004
|
}
|
|
8623
9005
|
}
|
|
@@ -8808,8 +9190,8 @@ function resolveSessionProvider(store, tenant, providerId) {
|
|
|
8808
9190
|
|
|
8809
9191
|
// src/server/usage.ts
|
|
8810
9192
|
init_esm_shims();
|
|
8811
|
-
import * as
|
|
8812
|
-
import * as
|
|
9193
|
+
import * as fs18 from "fs";
|
|
9194
|
+
import * as path20 from "path";
|
|
8813
9195
|
var LEDGER_RETENTION_MONTHS = 12;
|
|
8814
9196
|
var USAGE_SCHEMA = 1;
|
|
8815
9197
|
var DEFAULT_PRICING = {
|
|
@@ -8890,15 +9272,15 @@ function retainedMonths(now = /* @__PURE__ */ new Date()) {
|
|
|
8890
9272
|
return out;
|
|
8891
9273
|
}
|
|
8892
9274
|
function atomicWrite(file, content) {
|
|
8893
|
-
|
|
9275
|
+
fs18.mkdirSync(path20.dirname(file), { recursive: true });
|
|
8894
9276
|
const tmp = `${file}.tmp`;
|
|
8895
|
-
|
|
8896
|
-
|
|
9277
|
+
fs18.writeFileSync(tmp, content, "utf-8");
|
|
9278
|
+
fs18.renameSync(tmp, file);
|
|
8897
9279
|
}
|
|
8898
9280
|
var UsageLedger = class {
|
|
8899
9281
|
constructor(root) {
|
|
8900
9282
|
this.root = root;
|
|
8901
|
-
this.dir =
|
|
9283
|
+
this.dir = path20.join(root, "usage");
|
|
8902
9284
|
}
|
|
8903
9285
|
root;
|
|
8904
9286
|
dir;
|
|
@@ -8911,18 +9293,18 @@ var UsageLedger = class {
|
|
|
8911
9293
|
return this.dir;
|
|
8912
9294
|
}
|
|
8913
9295
|
ledgerFile(month) {
|
|
8914
|
-
return
|
|
9296
|
+
return path20.join(this.ledgerDir, `ledger-${month}.jsonl`);
|
|
8915
9297
|
}
|
|
8916
9298
|
get rollupFile() {
|
|
8917
|
-
return
|
|
9299
|
+
return path20.join(this.ledgerDir, "rollup.json");
|
|
8918
9300
|
}
|
|
8919
9301
|
get pricingFile() {
|
|
8920
|
-
return
|
|
9302
|
+
return path20.join(this.ledgerDir, "pricing.json");
|
|
8921
9303
|
}
|
|
8922
9304
|
// ===================== 初始化 =====================
|
|
8923
9305
|
init(now = /* @__PURE__ */ new Date()) {
|
|
8924
9306
|
if (this.inited) return;
|
|
8925
|
-
|
|
9307
|
+
fs18.mkdirSync(this.ledgerDir, { recursive: true });
|
|
8926
9308
|
this.pricing = this.loadPricing();
|
|
8927
9309
|
this.rollup = this.loadRollup(now);
|
|
8928
9310
|
this.pruneLedgers(now);
|
|
@@ -8930,7 +9312,7 @@ var UsageLedger = class {
|
|
|
8930
9312
|
}
|
|
8931
9313
|
loadPricing() {
|
|
8932
9314
|
try {
|
|
8933
|
-
const raw =
|
|
9315
|
+
const raw = fs18.readFileSync(this.pricingFile, "utf-8");
|
|
8934
9316
|
const parsed = JSON.parse(raw);
|
|
8935
9317
|
const vendors = {};
|
|
8936
9318
|
for (const v of /* @__PURE__ */ new Set([
|
|
@@ -8959,7 +9341,7 @@ var UsageLedger = class {
|
|
|
8959
9341
|
}
|
|
8960
9342
|
loadRollup(now) {
|
|
8961
9343
|
try {
|
|
8962
|
-
const raw =
|
|
9344
|
+
const raw = fs18.readFileSync(this.rollupFile, "utf-8");
|
|
8963
9345
|
const parsed = JSON.parse(raw);
|
|
8964
9346
|
return {
|
|
8965
9347
|
schema: USAGE_SCHEMA,
|
|
@@ -8986,10 +9368,10 @@ var UsageLedger = class {
|
|
|
8986
9368
|
const keep = retainedMonths(now);
|
|
8987
9369
|
let changed = false;
|
|
8988
9370
|
try {
|
|
8989
|
-
for (const f of
|
|
9371
|
+
for (const f of fs18.readdirSync(this.ledgerDir)) {
|
|
8990
9372
|
const m = /^ledger-(\d{4}-\d{2})\.jsonl$/.exec(f);
|
|
8991
9373
|
if (m && !keep.has(m[1])) {
|
|
8992
|
-
|
|
9374
|
+
fs18.unlinkSync(path20.join(this.ledgerDir, f));
|
|
8993
9375
|
}
|
|
8994
9376
|
}
|
|
8995
9377
|
} catch {
|
|
@@ -9066,7 +9448,7 @@ var UsageLedger = class {
|
|
|
9066
9448
|
};
|
|
9067
9449
|
const line = JSON.stringify(rec) + "\n";
|
|
9068
9450
|
try {
|
|
9069
|
-
|
|
9451
|
+
fs18.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
|
|
9070
9452
|
} catch (e) {
|
|
9071
9453
|
console.error(`[usage] \u5199\u8D26\u672C\u5931\u8D25: ${String(e)}`);
|
|
9072
9454
|
}
|
|
@@ -9238,10 +9620,10 @@ var UsageLedger = class {
|
|
|
9238
9620
|
const rows = [];
|
|
9239
9621
|
for (const month of months) {
|
|
9240
9622
|
const file = this.ledgerFile(month);
|
|
9241
|
-
if (!
|
|
9623
|
+
if (!fs18.existsSync(file)) continue;
|
|
9242
9624
|
let content = "";
|
|
9243
9625
|
try {
|
|
9244
|
-
content =
|
|
9626
|
+
content = fs18.readFileSync(file, "utf-8");
|
|
9245
9627
|
} catch {
|
|
9246
9628
|
continue;
|
|
9247
9629
|
}
|
|
@@ -9350,10 +9732,10 @@ var UsageLedger = class {
|
|
|
9350
9732
|
const seen = /* @__PURE__ */ new Set();
|
|
9351
9733
|
for (const month of this.monthsInRange("", "")) {
|
|
9352
9734
|
const file = this.ledgerFile(month);
|
|
9353
|
-
if (!
|
|
9735
|
+
if (!fs18.existsSync(file)) continue;
|
|
9354
9736
|
let content = "";
|
|
9355
9737
|
try {
|
|
9356
|
-
content =
|
|
9738
|
+
content = fs18.readFileSync(file, "utf-8");
|
|
9357
9739
|
} catch {
|
|
9358
9740
|
continue;
|
|
9359
9741
|
}
|
|
@@ -10661,7 +11043,10 @@ data: ${JSON.stringify(entry.data)}
|
|
|
10661
11043
|
// 上下文窗口:环境变量显式覆盖 > 供应商 /models 提供的真实长度 > 引擎内置表
|
|
10662
11044
|
contextWindow: parseOptionalPositiveNumber(process.env.CONTEXT_WINDOW) ?? modelContextWindow(eff.modelMeta, activeModel),
|
|
10663
11045
|
thresholdPct: parseRatio(process.env.COMPACTION_THRESHOLD_PCT),
|
|
10664
|
-
|
|
11046
|
+
softThresholdPct: parseRatio(process.env.COMPACTION_SOFT_THRESHOLD_PCT),
|
|
11047
|
+
capTokens: parseOptionalPositiveNumber(process.env.COMPACTION_CAP_TOKENS) ?? null,
|
|
11048
|
+
// 超预算工具结果按租户/工作区隔离落盘(多租户共用一个状态目录会混内容)
|
|
11049
|
+
toolResultDir: path21.join(workspace, ".agent", "tool-results")
|
|
10665
11050
|
});
|
|
10666
11051
|
engine.setApprover(this._makeApprover(rt));
|
|
10667
11052
|
const mcpConfigs = loadServerMcpConfigs(tenant);
|
|
@@ -10842,11 +11227,11 @@ var ApiError = class extends Error {
|
|
|
10842
11227
|
// src/server/routes.ts
|
|
10843
11228
|
init_esm_shims();
|
|
10844
11229
|
import { createServer } from "http";
|
|
10845
|
-
import * as
|
|
11230
|
+
import * as path26 from "path";
|
|
10846
11231
|
|
|
10847
11232
|
// src/server/projects.ts
|
|
10848
11233
|
init_esm_shims();
|
|
10849
|
-
import * as
|
|
11234
|
+
import * as path22 from "path";
|
|
10850
11235
|
var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
|
|
10851
11236
|
function toProjectSummary(store, tenant) {
|
|
10852
11237
|
return {
|
|
@@ -10979,9 +11364,9 @@ function createProject(store, input) {
|
|
|
10979
11364
|
if (store.loadTenant(id)) {
|
|
10980
11365
|
throw new ApiError(409, "project_exists", `\u9879\u76EE ${id} \u5DF2\u5B58\u5728`);
|
|
10981
11366
|
}
|
|
10982
|
-
const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ?
|
|
11367
|
+
const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ? path22.resolve(raw.workspaceRoot.trim()) : void 0;
|
|
10983
11368
|
const explicitName = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : "";
|
|
10984
|
-
const defaultName = workspaceRoot ?
|
|
11369
|
+
const defaultName = workspaceRoot ? path22.basename(workspaceRoot) : id;
|
|
10985
11370
|
const tenant = {
|
|
10986
11371
|
id,
|
|
10987
11372
|
name: explicitName || defaultName,
|
|
@@ -11011,7 +11396,7 @@ function buildNodeInfo(store, opts) {
|
|
|
11011
11396
|
nodeId: opts.nodeId ?? ensureNodeId(store),
|
|
11012
11397
|
platform: process.platform,
|
|
11013
11398
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
11014
|
-
version: "0.2.
|
|
11399
|
+
version: "0.2.6",
|
|
11015
11400
|
admin: opts.admin,
|
|
11016
11401
|
projects: opts.projects
|
|
11017
11402
|
};
|
|
@@ -11095,8 +11480,8 @@ function hubProxyEnabled(host) {
|
|
|
11095
11480
|
const h = (host ?? "127.0.0.1").trim();
|
|
11096
11481
|
return h === "127.0.0.1" || h === "localhost" || h === "::1";
|
|
11097
11482
|
}
|
|
11098
|
-
function isHubProxyPath(
|
|
11099
|
-
return
|
|
11483
|
+
function isHubProxyPath(path36) {
|
|
11484
|
+
return path36.startsWith(HUB_PROXY_PREFIX);
|
|
11100
11485
|
}
|
|
11101
11486
|
function decodeTarget(enc) {
|
|
11102
11487
|
try {
|
|
@@ -11190,34 +11575,34 @@ async function handleHubProxy(req2, res) {
|
|
|
11190
11575
|
|
|
11191
11576
|
// src/server/fsBrowser.ts
|
|
11192
11577
|
init_esm_shims();
|
|
11193
|
-
import * as
|
|
11578
|
+
import * as fs19 from "fs";
|
|
11194
11579
|
import * as os4 from "os";
|
|
11195
|
-
import * as
|
|
11580
|
+
import * as path23 from "path";
|
|
11196
11581
|
var MAX_ENTRIES = 2e3;
|
|
11197
11582
|
var DEFAULT_SEARCH_LIMIT = 200;
|
|
11198
11583
|
var MAX_SEARCH_LIMIT = 1e3;
|
|
11199
11584
|
function listDirs(input) {
|
|
11200
11585
|
const base = (input ?? "").trim() || os4.homedir();
|
|
11201
|
-
const target =
|
|
11586
|
+
const target = path23.resolve(base);
|
|
11202
11587
|
let st;
|
|
11203
11588
|
try {
|
|
11204
|
-
st =
|
|
11589
|
+
st = fs19.statSync(target);
|
|
11205
11590
|
} catch {
|
|
11206
11591
|
throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
|
|
11207
11592
|
}
|
|
11208
11593
|
if (!st.isDirectory()) throw new Error(`\u4E0D\u662F\u76EE\u5F55: ${target}`);
|
|
11209
11594
|
let dirents;
|
|
11210
11595
|
try {
|
|
11211
|
-
dirents =
|
|
11596
|
+
dirents = fs19.readdirSync(target, { withFileTypes: true });
|
|
11212
11597
|
} catch {
|
|
11213
11598
|
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
|
|
11214
11599
|
}
|
|
11215
11600
|
const entries = dirents.map((d) => {
|
|
11216
|
-
const full =
|
|
11601
|
+
const full = path23.join(target, d.name);
|
|
11217
11602
|
let isDir = d.isDirectory();
|
|
11218
11603
|
if (d.isSymbolicLink()) {
|
|
11219
11604
|
try {
|
|
11220
|
-
isDir =
|
|
11605
|
+
isDir = fs19.statSync(full).isDirectory();
|
|
11221
11606
|
} catch {
|
|
11222
11607
|
}
|
|
11223
11608
|
}
|
|
@@ -11226,7 +11611,7 @@ function listDirs(input) {
|
|
|
11226
11611
|
entries.sort(
|
|
11227
11612
|
(a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
|
|
11228
11613
|
);
|
|
11229
|
-
const parentDir =
|
|
11614
|
+
const parentDir = path23.dirname(target);
|
|
11230
11615
|
return {
|
|
11231
11616
|
path: target,
|
|
11232
11617
|
parent: parentDir !== target ? parentDir : null,
|
|
@@ -11257,7 +11642,7 @@ var NAME_CACHE_MAX_DIRS = 4;
|
|
|
11257
11642
|
var NAME_CACHE_MAX_ENTRIES = 5e4;
|
|
11258
11643
|
var nameCache = /* @__PURE__ */ new Map();
|
|
11259
11644
|
function invalidateDirNames(target) {
|
|
11260
|
-
nameCache.delete(
|
|
11645
|
+
nameCache.delete(path23.resolve(target));
|
|
11261
11646
|
}
|
|
11262
11647
|
async function loadDirNames(target) {
|
|
11263
11648
|
const now = Date.now();
|
|
@@ -11269,7 +11654,7 @@ async function loadDirNames(target) {
|
|
|
11269
11654
|
}
|
|
11270
11655
|
let dirents;
|
|
11271
11656
|
try {
|
|
11272
|
-
dirents = await
|
|
11657
|
+
dirents = await fs19.promises.readdir(target, { withFileTypes: true });
|
|
11273
11658
|
} catch {
|
|
11274
11659
|
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
|
|
11275
11660
|
}
|
|
@@ -11292,11 +11677,11 @@ async function loadDirNames(target) {
|
|
|
11292
11677
|
return entries;
|
|
11293
11678
|
}
|
|
11294
11679
|
async function toDirEntry(dir, e) {
|
|
11295
|
-
const full =
|
|
11680
|
+
const full = path23.join(dir, e.name);
|
|
11296
11681
|
let isDir = e.isDir;
|
|
11297
11682
|
if (e.isLink) {
|
|
11298
11683
|
try {
|
|
11299
|
-
isDir = (await
|
|
11684
|
+
isDir = (await fs19.promises.stat(full)).isDirectory();
|
|
11300
11685
|
} catch {
|
|
11301
11686
|
}
|
|
11302
11687
|
}
|
|
@@ -11304,10 +11689,10 @@ async function toDirEntry(dir, e) {
|
|
|
11304
11689
|
}
|
|
11305
11690
|
async function searchDirs(input, query, limit) {
|
|
11306
11691
|
const base = (input ?? "").trim() || os4.homedir();
|
|
11307
|
-
const target =
|
|
11692
|
+
const target = path23.resolve(base);
|
|
11308
11693
|
let st;
|
|
11309
11694
|
try {
|
|
11310
|
-
st = await
|
|
11695
|
+
st = await fs19.promises.stat(target);
|
|
11311
11696
|
} catch {
|
|
11312
11697
|
throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
|
|
11313
11698
|
}
|
|
@@ -11337,7 +11722,7 @@ async function searchDirs(input, query, limit) {
|
|
|
11337
11722
|
);
|
|
11338
11723
|
const hit = scored.slice(0, max);
|
|
11339
11724
|
const entries = await Promise.all(hit.map(({ entry }) => toDirEntry(target, entry)));
|
|
11340
|
-
const parentDir =
|
|
11725
|
+
const parentDir = path23.dirname(target);
|
|
11341
11726
|
return {
|
|
11342
11727
|
path: target,
|
|
11343
11728
|
parent: parentDir !== target ? parentDir : null,
|
|
@@ -11348,52 +11733,52 @@ async function searchDirs(input, query, limit) {
|
|
|
11348
11733
|
};
|
|
11349
11734
|
}
|
|
11350
11735
|
function makeDirIn(parentInput, name) {
|
|
11351
|
-
const parent =
|
|
11736
|
+
const parent = path23.resolve((parentInput ?? "").trim() || os4.homedir());
|
|
11352
11737
|
const clean = (name ?? "").trim();
|
|
11353
11738
|
if (!clean) throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u4E3A\u7A7A");
|
|
11354
11739
|
if (clean === "." || clean === "..") throw new Error("\u76EE\u5F55\u540D\u975E\u6CD5");
|
|
11355
|
-
const target =
|
|
11356
|
-
const rel =
|
|
11357
|
-
if (!rel || rel.startsWith("..") ||
|
|
11740
|
+
const target = path23.resolve(parent, clean);
|
|
11741
|
+
const rel = path23.relative(parent, target);
|
|
11742
|
+
if (!rel || rel.startsWith("..") || path23.isAbsolute(rel)) {
|
|
11358
11743
|
throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u8D8A\u51FA\u5F53\u524D\u76EE\u5F55");
|
|
11359
11744
|
}
|
|
11360
11745
|
let st = null;
|
|
11361
11746
|
try {
|
|
11362
|
-
st =
|
|
11747
|
+
st = fs19.statSync(target);
|
|
11363
11748
|
} catch {
|
|
11364
11749
|
st = null;
|
|
11365
11750
|
}
|
|
11366
11751
|
if (st && !st.isDirectory()) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u6587\u4EF6: ${target}`);
|
|
11367
11752
|
try {
|
|
11368
|
-
|
|
11753
|
+
fs19.mkdirSync(target, { recursive: true });
|
|
11369
11754
|
} catch (e) {
|
|
11370
11755
|
throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
|
|
11371
11756
|
}
|
|
11372
11757
|
invalidateDirNames(parent);
|
|
11373
|
-
return { name:
|
|
11758
|
+
return { name: path23.basename(target), path: target, type: "dir" };
|
|
11374
11759
|
}
|
|
11375
11760
|
|
|
11376
11761
|
// src/server/projectFiles.ts
|
|
11377
11762
|
init_esm_shims();
|
|
11378
|
-
import * as
|
|
11379
|
-
import * as
|
|
11763
|
+
import * as fs20 from "fs";
|
|
11764
|
+
import * as path24 from "path";
|
|
11380
11765
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
11381
11766
|
var MAX_DOWNLOAD_BYTES = 16 * 1024 * 1024;
|
|
11382
11767
|
var MAX_ENTRIES2 = 2e3;
|
|
11383
11768
|
function toPosix(p) {
|
|
11384
|
-
return p.split(
|
|
11769
|
+
return p.split(path24.sep).join("/");
|
|
11385
11770
|
}
|
|
11386
11771
|
function projectRoot(workspaceRoot) {
|
|
11387
11772
|
const r = (workspaceRoot ?? "").trim();
|
|
11388
11773
|
if (!r) {
|
|
11389
11774
|
throw new Error("\u9879\u76EE\u672A\u8BBE\u7F6E\u5DE5\u4F5C\u533A\u76EE\u5F55\uFF08workspaceRoot\uFF09\uFF0C\u65E0\u6CD5\u6D4F\u89C8\u6587\u4EF6");
|
|
11390
11775
|
}
|
|
11391
|
-
const abs =
|
|
11392
|
-
if (!
|
|
11776
|
+
const abs = path24.resolve(r);
|
|
11777
|
+
if (!fs20.existsSync(abs)) {
|
|
11393
11778
|
throw new Error(`\u5DE5\u4F5C\u533A\u76EE\u5F55\u4E0D\u5B58\u5728: ${abs}`);
|
|
11394
11779
|
}
|
|
11395
11780
|
try {
|
|
11396
|
-
return
|
|
11781
|
+
return fs20.realpathSync(abs);
|
|
11397
11782
|
} catch {
|
|
11398
11783
|
return abs;
|
|
11399
11784
|
}
|
|
@@ -11411,37 +11796,37 @@ function listProjectFiles(workspaceRoot, relPath) {
|
|
|
11411
11796
|
const dir = resolveWithin(base, relPath ?? "");
|
|
11412
11797
|
let st;
|
|
11413
11798
|
try {
|
|
11414
|
-
st =
|
|
11799
|
+
st = fs20.statSync(dir);
|
|
11415
11800
|
} catch {
|
|
11416
|
-
throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(
|
|
11801
|
+
throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(path24.relative(base, dir)) || "/"}`);
|
|
11417
11802
|
}
|
|
11418
11803
|
if (!st.isDirectory()) throw new Error("\u76EE\u6807\u4E0D\u662F\u76EE\u5F55");
|
|
11419
11804
|
let dirents;
|
|
11420
11805
|
try {
|
|
11421
|
-
dirents =
|
|
11806
|
+
dirents = fs20.readdirSync(dir, { withFileTypes: true });
|
|
11422
11807
|
} catch {
|
|
11423
11808
|
throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09");
|
|
11424
11809
|
}
|
|
11425
11810
|
const entries = dirents.map((d) => {
|
|
11426
|
-
const full =
|
|
11811
|
+
const full = path24.join(dir, d.name);
|
|
11427
11812
|
let isDir = d.isDirectory();
|
|
11428
11813
|
if (d.isSymbolicLink()) {
|
|
11429
11814
|
try {
|
|
11430
|
-
isDir =
|
|
11815
|
+
isDir = fs20.statSync(full).isDirectory();
|
|
11431
11816
|
} catch {
|
|
11432
11817
|
}
|
|
11433
11818
|
}
|
|
11434
11819
|
let size = 0;
|
|
11435
11820
|
let mtime = "";
|
|
11436
11821
|
try {
|
|
11437
|
-
const s =
|
|
11822
|
+
const s = fs20.statSync(full);
|
|
11438
11823
|
size = s.size;
|
|
11439
11824
|
mtime = s.mtime.toISOString();
|
|
11440
11825
|
} catch {
|
|
11441
11826
|
}
|
|
11442
11827
|
return {
|
|
11443
11828
|
name: d.name,
|
|
11444
|
-
path: toPosix(
|
|
11829
|
+
path: toPosix(path24.relative(base, full)),
|
|
11445
11830
|
type: isDir ? "dir" : "file",
|
|
11446
11831
|
size,
|
|
11447
11832
|
mtime
|
|
@@ -11450,12 +11835,12 @@ function listProjectFiles(workspaceRoot, relPath) {
|
|
|
11450
11835
|
entries.sort(
|
|
11451
11836
|
(a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
|
|
11452
11837
|
);
|
|
11453
|
-
const relDir = toPosix(
|
|
11454
|
-
const parentAbs =
|
|
11838
|
+
const relDir = toPosix(path24.relative(base, dir));
|
|
11839
|
+
const parentAbs = path24.dirname(dir);
|
|
11455
11840
|
return {
|
|
11456
11841
|
root: base,
|
|
11457
11842
|
path: relDir,
|
|
11458
|
-
parent: isWithinRoot(base, parentAbs) ? toPosix(
|
|
11843
|
+
parent: isWithinRoot(base, parentAbs) ? toPosix(path24.relative(base, parentAbs)) : null,
|
|
11459
11844
|
entries: entries.slice(0, MAX_ENTRIES2)
|
|
11460
11845
|
};
|
|
11461
11846
|
}
|
|
@@ -11464,17 +11849,17 @@ function readProjectFile(workspaceRoot, relPath) {
|
|
|
11464
11849
|
const file = resolveWithin(base, relPath ?? "");
|
|
11465
11850
|
let st;
|
|
11466
11851
|
try {
|
|
11467
|
-
st =
|
|
11852
|
+
st = fs20.statSync(file);
|
|
11468
11853
|
} catch {
|
|
11469
11854
|
throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
11470
11855
|
}
|
|
11471
11856
|
if (!st.isFile()) throw new Error("\u76EE\u6807\u4E0D\u662F\u6587\u4EF6");
|
|
11472
|
-
const buf =
|
|
11857
|
+
const buf = fs20.readFileSync(file);
|
|
11473
11858
|
const binary = isBinary(buf);
|
|
11474
11859
|
const truncated = buf.length > MAX_FILE_BYTES;
|
|
11475
11860
|
const slice = truncated ? buf.subarray(0, MAX_FILE_BYTES) : buf;
|
|
11476
11861
|
return {
|
|
11477
|
-
path: toPosix(
|
|
11862
|
+
path: toPosix(path24.relative(base, file)),
|
|
11478
11863
|
size: st.size,
|
|
11479
11864
|
binary,
|
|
11480
11865
|
truncated,
|
|
@@ -11486,7 +11871,7 @@ function downloadProjectFile(workspaceRoot, relPath) {
|
|
|
11486
11871
|
const file = resolveWithin(base, relPath ?? "");
|
|
11487
11872
|
let st;
|
|
11488
11873
|
try {
|
|
11489
|
-
st =
|
|
11874
|
+
st = fs20.statSync(file);
|
|
11490
11875
|
} catch {
|
|
11491
11876
|
throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
11492
11877
|
}
|
|
@@ -11494,10 +11879,10 @@ function downloadProjectFile(workspaceRoot, relPath) {
|
|
|
11494
11879
|
if (st.size > MAX_DOWNLOAD_BYTES) {
|
|
11495
11880
|
throw new Error(`\u6587\u4EF6\u8D85\u8FC7\u4E0B\u8F7D\u4E0A\u9650\uFF08${MAX_DOWNLOAD_BYTES / (1024 * 1024)}MB\uFF09`);
|
|
11496
11881
|
}
|
|
11497
|
-
const buf =
|
|
11882
|
+
const buf = fs20.readFileSync(file);
|
|
11498
11883
|
return {
|
|
11499
|
-
path: toPosix(
|
|
11500
|
-
name:
|
|
11884
|
+
path: toPosix(path24.relative(base, file)),
|
|
11885
|
+
name: path24.basename(file),
|
|
11501
11886
|
size: buf.length,
|
|
11502
11887
|
base64: buf.toString("base64")
|
|
11503
11888
|
};
|
|
@@ -11519,12 +11904,12 @@ function writeProjectFile(workspaceRoot, relPath, content) {
|
|
|
11519
11904
|
throw new Error("\u5185\u5BB9\u8D85\u8FC7 2MB \u4E0A\u9650\uFF0C\u65E0\u6CD5\u4FDD\u5B58");
|
|
11520
11905
|
}
|
|
11521
11906
|
try {
|
|
11522
|
-
|
|
11523
|
-
|
|
11907
|
+
fs20.mkdirSync(path24.dirname(file), { recursive: true });
|
|
11908
|
+
fs20.writeFileSync(file, buf);
|
|
11524
11909
|
} catch (e) {
|
|
11525
11910
|
throw new Error(`\u5199\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
11526
11911
|
}
|
|
11527
|
-
return { path: toPosix(
|
|
11912
|
+
return { path: toPosix(path24.relative(base, file)), size: buf.length };
|
|
11528
11913
|
}
|
|
11529
11914
|
function createProjectDir(workspaceRoot, relPath) {
|
|
11530
11915
|
const base = projectRoot(workspaceRoot);
|
|
@@ -11532,26 +11917,26 @@ function createProjectDir(workspaceRoot, relPath) {
|
|
|
11532
11917
|
if (!rel) throw new Error("\u76EE\u5F55\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
|
|
11533
11918
|
const dir = resolveWithin(base, rel);
|
|
11534
11919
|
try {
|
|
11535
|
-
|
|
11920
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
11536
11921
|
} catch (e) {
|
|
11537
11922
|
throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
11538
11923
|
}
|
|
11539
|
-
return { path: toPosix(
|
|
11924
|
+
return { path: toPosix(path24.relative(base, dir)) };
|
|
11540
11925
|
}
|
|
11541
11926
|
function createProjectFile(workspaceRoot, relPath, content = "") {
|
|
11542
11927
|
const base = projectRoot(workspaceRoot);
|
|
11543
11928
|
const rel = (relPath ?? "").trim();
|
|
11544
11929
|
if (!rel) throw new Error("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
|
|
11545
11930
|
const file = resolveWithin(base, rel);
|
|
11546
|
-
if (
|
|
11931
|
+
if (fs20.existsSync(file)) throw new Error("\u6587\u4EF6\u5DF2\u5B58\u5728");
|
|
11547
11932
|
const buf = Buffer.from(String(content ?? ""), "utf-8");
|
|
11548
11933
|
try {
|
|
11549
|
-
|
|
11550
|
-
|
|
11934
|
+
fs20.mkdirSync(path24.dirname(file), { recursive: true });
|
|
11935
|
+
fs20.writeFileSync(file, buf);
|
|
11551
11936
|
} catch (e) {
|
|
11552
11937
|
throw new Error(`\u521B\u5EFA\u6587\u4EF6\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
11553
11938
|
}
|
|
11554
|
-
return { path: toPosix(
|
|
11939
|
+
return { path: toPosix(path24.relative(base, file)), size: buf.length };
|
|
11555
11940
|
}
|
|
11556
11941
|
function searchProjectFiles(workspaceRoot, query) {
|
|
11557
11942
|
const base = projectRoot(workspaceRoot);
|
|
@@ -11574,7 +11959,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
11574
11959
|
if (out.length >= LIMIT) return;
|
|
11575
11960
|
let dirents;
|
|
11576
11961
|
try {
|
|
11577
|
-
dirents =
|
|
11962
|
+
dirents = fs20.readdirSync(dir, { withFileTypes: true });
|
|
11578
11963
|
} catch {
|
|
11579
11964
|
return;
|
|
11580
11965
|
}
|
|
@@ -11585,7 +11970,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
11585
11970
|
let isDir = d.isDirectory();
|
|
11586
11971
|
if (d.isSymbolicLink()) {
|
|
11587
11972
|
try {
|
|
11588
|
-
isDir =
|
|
11973
|
+
isDir = fs20.statSync(path24.join(dir, d.name)).isDirectory();
|
|
11589
11974
|
} catch {
|
|
11590
11975
|
isDir = false;
|
|
11591
11976
|
}
|
|
@@ -11593,7 +11978,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
11593
11978
|
if (d.name.toLowerCase().includes(q)) {
|
|
11594
11979
|
out.push({ path: rel, type: isDir ? "dir" : "file" });
|
|
11595
11980
|
}
|
|
11596
|
-
if (isDir) walk(
|
|
11981
|
+
if (isDir) walk(path24.join(dir, d.name), rel);
|
|
11597
11982
|
}
|
|
11598
11983
|
};
|
|
11599
11984
|
walk(base, "");
|
|
@@ -11603,7 +11988,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
11603
11988
|
// src/server/projectOps.ts
|
|
11604
11989
|
init_esm_shims();
|
|
11605
11990
|
import * as os5 from "os";
|
|
11606
|
-
import * as
|
|
11991
|
+
import * as path25 from "path";
|
|
11607
11992
|
var MAX_STDOUT2 = 2e4;
|
|
11608
11993
|
var MAX_STDERR2 = 1e4;
|
|
11609
11994
|
var MAX_BUFFER3 = 1024 * 1024 * 5;
|
|
@@ -11617,7 +12002,7 @@ function resolveInvocation2(spec, prepared) {
|
|
|
11617
12002
|
}
|
|
11618
12003
|
function workRoot(workspaceRoot) {
|
|
11619
12004
|
const r = (workspaceRoot ?? "").trim();
|
|
11620
|
-
return r ?
|
|
12005
|
+
return r ? path25.resolve(r) : os5.homedir();
|
|
11621
12006
|
}
|
|
11622
12007
|
async function runProjectCommand(workspaceRoot, command, timeoutSeconds) {
|
|
11623
12008
|
const cmd = String(command ?? "");
|
|
@@ -11737,6 +12122,92 @@ async function projectGitPush(workspaceRoot, repoPath) {
|
|
|
11737
12122
|
});
|
|
11738
12123
|
}
|
|
11739
12124
|
|
|
12125
|
+
// src/server/gitCommitMessage.ts
|
|
12126
|
+
init_esm_shims();
|
|
12127
|
+
import * as crypto5 from "crypto";
|
|
12128
|
+
var MAX_DIFF_CHARS = 12e3;
|
|
12129
|
+
var MAX_MESSAGE_CHARS = 200;
|
|
12130
|
+
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
|
|
12131
|
+
|
|
12132
|
+
\u8981\u6C42\uFF1A
|
|
12133
|
+
1. \u4F7F\u7528 Conventional Commits \u98CE\u683C\uFF1Atype(scope): \u63CF\u8FF0\uFF08type \u5982 feat / fix / refactor / docs / test / chore / perf / build / ci\uFF09\uFF1B
|
|
12134
|
+
2. scope \u53D6\u53D8\u66F4\u6700\u96C6\u4E2D\u7684\u6A21\u5757\u6216\u76EE\u5F55\uFF0C\u65E0\u6CD5\u5224\u65AD\u65F6\u7701\u7565\uFF1B
|
|
12135
|
+
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
|
|
12136
|
+
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
|
|
12137
|
+
5. \u4E0D\u8D85\u8FC7 72 \u4E2A\u5B57\u7B26\u3002`;
|
|
12138
|
+
function genReqId2() {
|
|
12139
|
+
return crypto5.randomBytes(6).toString("base64url");
|
|
12140
|
+
}
|
|
12141
|
+
function cleanCommitMessage(raw) {
|
|
12142
|
+
let text = String(raw ?? "").trim();
|
|
12143
|
+
if (!text) return "";
|
|
12144
|
+
text = text.replace(/^```[^\n]*\n?/, "").replace(/\n?```$/, "").trim();
|
|
12145
|
+
const first = text.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0) ?? "";
|
|
12146
|
+
return first.replace(/^(commit message|提交说明|提交信息|message)\s*[::]\s*/i, "").replace(/^["'“”`]+|["'“”`]+$/g, "").replace(/\s+/g, " ").trim().slice(0, MAX_MESSAGE_CHARS);
|
|
12147
|
+
}
|
|
12148
|
+
async function collectDiff(workspaceRoot, repo) {
|
|
12149
|
+
const staged = await projectGitDiff(workspaceRoot, void 0, true, repo);
|
|
12150
|
+
if (!staged.error && String(staged.diff ?? "").trim()) {
|
|
12151
|
+
return { text: String(staged.diff), source: "staged" };
|
|
12152
|
+
}
|
|
12153
|
+
const worktree = await projectGitDiff(workspaceRoot, void 0, false, repo);
|
|
12154
|
+
if (!worktree.error && String(worktree.diff ?? "").trim()) {
|
|
12155
|
+
return { text: String(worktree.diff), source: "worktree" };
|
|
12156
|
+
}
|
|
12157
|
+
const status = await projectGitStatus(workspaceRoot, void 0, repo);
|
|
12158
|
+
if (status.error) throw new ApiError(400, "git_error", String(status.error));
|
|
12159
|
+
const entries = status.entries ?? [];
|
|
12160
|
+
if (entries.length === 0) throw new ApiError(400, "no_changes", "\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4");
|
|
12161
|
+
return {
|
|
12162
|
+
text: entries.map((e) => `${e.status ?? ""} ${e.file ?? ""}`.trim()).join("\n"),
|
|
12163
|
+
source: "worktree"
|
|
12164
|
+
};
|
|
12165
|
+
}
|
|
12166
|
+
async function generateGitCommitMessage(opts) {
|
|
12167
|
+
const { store, tenant } = opts;
|
|
12168
|
+
const { text, source } = await collectDiff(opts.workspaceRoot, opts.repo);
|
|
12169
|
+
const truncated = text.length > MAX_DIFF_CHARS;
|
|
12170
|
+
const clipped = text.slice(0, MAX_DIFF_CHARS);
|
|
12171
|
+
const eff = resolveSessionProvider(store, tenant);
|
|
12172
|
+
const apiKey = eff.apiKey || process.env.OPENAI_API_KEY || "";
|
|
12173
|
+
const model = (eff.model || tenant.model || process.env.OPENAI_MODEL || process.env.MODEL || "").trim();
|
|
12174
|
+
if (!apiKey) {
|
|
12175
|
+
throw new ApiError(
|
|
12176
|
+
400,
|
|
12177
|
+
"llm_not_configured",
|
|
12178
|
+
"\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"
|
|
12179
|
+
);
|
|
12180
|
+
}
|
|
12181
|
+
if (!model) {
|
|
12182
|
+
throw new ApiError(
|
|
12183
|
+
400,
|
|
12184
|
+
"llm_not_configured",
|
|
12185
|
+
"\u5F53\u524D\u8282\u70B9\u672A\u914D\u7F6E\u6A21\u578B\uFF0C\u8BF7\u5148\u5728\u8282\u70B9\u914D\u7F6E\u4E2D\u9009\u62E9\u6A21\u578B"
|
|
12186
|
+
);
|
|
12187
|
+
}
|
|
12188
|
+
const provider = new OpenAIProvider({ apiKey, baseURL: eff.baseURL });
|
|
12189
|
+
const raw = await provider.chat(
|
|
12190
|
+
model,
|
|
12191
|
+
[
|
|
12192
|
+
{ role: "system", content: GEN_COMMIT_MESSAGE_INSTRUCTIONS },
|
|
12193
|
+
{
|
|
12194
|
+
role: "user",
|
|
12195
|
+
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
|
|
12196
|
+
|
|
12197
|
+
${clipped}`
|
|
12198
|
+
}
|
|
12199
|
+
],
|
|
12200
|
+
{},
|
|
12201
|
+
{ reqId: `llm_commit_${Date.now()}_${genReqId2()}`, logTime: (/* @__PURE__ */ new Date()).toISOString() },
|
|
12202
|
+
// 不设 maxTokens:推理模型的思考会占用输出配额,过小会导致 content 为空;
|
|
12203
|
+
// 提示词已约束只输出一行短说明。
|
|
12204
|
+
{}
|
|
12205
|
+
);
|
|
12206
|
+
const message = cleanCommitMessage(raw ?? "");
|
|
12207
|
+
if (!message) throw new ApiError(502, "llm_empty", "AI \u672A\u8FD4\u56DE\u6709\u6548\u7684\u63D0\u4EA4\u8BF4\u660E\uFF0C\u8BF7\u91CD\u8BD5");
|
|
12208
|
+
return message;
|
|
12209
|
+
}
|
|
12210
|
+
|
|
11740
12211
|
// src/server/toolsCatalog.ts
|
|
11741
12212
|
init_esm_shims();
|
|
11742
12213
|
function buildToolCatalog(workspace) {
|
|
@@ -11842,11 +12313,11 @@ function projectStats(manager, tenantId) {
|
|
|
11842
12313
|
|
|
11843
12314
|
// src/server/nodeHost.ts
|
|
11844
12315
|
init_esm_shims();
|
|
11845
|
-
import * as
|
|
12316
|
+
import * as fs21 from "fs";
|
|
11846
12317
|
import * as os6 from "os";
|
|
11847
12318
|
function diskInfo(pathStr) {
|
|
11848
12319
|
try {
|
|
11849
|
-
const st =
|
|
12320
|
+
const st = fs21.statfsSync(pathStr);
|
|
11850
12321
|
return {
|
|
11851
12322
|
path: pathStr,
|
|
11852
12323
|
total: Number(st.blocks) * Number(st.bsize),
|
|
@@ -11918,7 +12389,7 @@ function buildNodeHost(store, manager, diskPath) {
|
|
|
11918
12389
|
arch: process.arch,
|
|
11919
12390
|
release: os6.release(),
|
|
11920
12391
|
uptimeSec: Math.round(os6.uptime()),
|
|
11921
|
-
version: "0.2.
|
|
12392
|
+
version: "0.2.6",
|
|
11922
12393
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
11923
12394
|
cpu: {
|
|
11924
12395
|
count: cpus2.length,
|
|
@@ -12493,7 +12964,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
12493
12964
|
applyProvidersPatch(tenant, raw);
|
|
12494
12965
|
if (typeof raw.workspaceRoot === "string") {
|
|
12495
12966
|
const ws = raw.workspaceRoot.trim();
|
|
12496
|
-
if (ws) tenant.workspaceRoot =
|
|
12967
|
+
if (ws) tenant.workspaceRoot = path26.resolve(ws);
|
|
12497
12968
|
else delete tenant.workspaceRoot;
|
|
12498
12969
|
}
|
|
12499
12970
|
if (typeof raw.name === "string" && raw.name.trim()) {
|
|
@@ -12902,6 +13373,25 @@ function createApp(store, manager, opts = {}) {
|
|
|
12902
13373
|
}
|
|
12903
13374
|
}
|
|
12904
13375
|
},
|
|
13376
|
+
{
|
|
13377
|
+
method: "POST",
|
|
13378
|
+
pattern: ["git", "commit-message"],
|
|
13379
|
+
fn: async (_req, res, { tenant, body }) => {
|
|
13380
|
+
const raw = body ?? {};
|
|
13381
|
+
try {
|
|
13382
|
+
const message = await generateGitCommitMessage({
|
|
13383
|
+
store,
|
|
13384
|
+
tenant,
|
|
13385
|
+
workspaceRoot: tenant.workspaceRoot ?? null,
|
|
13386
|
+
repo: String(raw.repo ?? "") || void 0
|
|
13387
|
+
});
|
|
13388
|
+
ok(res, { message });
|
|
13389
|
+
} catch (e) {
|
|
13390
|
+
if (e instanceof ApiError) throw e;
|
|
13391
|
+
throw new ApiError(400, "bad_request", e instanceof Error ? e.message : String(e));
|
|
13392
|
+
}
|
|
13393
|
+
}
|
|
13394
|
+
},
|
|
12905
13395
|
{
|
|
12906
13396
|
method: "POST",
|
|
12907
13397
|
pattern: ["git", "checkout"],
|
|
@@ -13425,10 +13915,10 @@ function createApp(store, manager, opts = {}) {
|
|
|
13425
13915
|
function handle(req2, res) {
|
|
13426
13916
|
const startedAt = Date.now();
|
|
13427
13917
|
const url = new URL(req2.url ?? "/", "http://localhost");
|
|
13428
|
-
const
|
|
13918
|
+
const path36 = url.pathname;
|
|
13429
13919
|
const method = req2.method ?? "GET";
|
|
13430
13920
|
console.log(
|
|
13431
|
-
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2192 ${method} ${
|
|
13921
|
+
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2192 ${method} ${path36}${url.search}`
|
|
13432
13922
|
);
|
|
13433
13923
|
let tenantId = null;
|
|
13434
13924
|
let logged = false;
|
|
@@ -13436,7 +13926,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
13436
13926
|
if (logged) return;
|
|
13437
13927
|
logged = true;
|
|
13438
13928
|
console.log(
|
|
13439
|
-
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2190 ${method} ${
|
|
13929
|
+
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2190 ${method} ${path36} ${extra} (${Date.now() - startedAt}ms) tenant=${tenantId ?? "-"}`
|
|
13440
13930
|
);
|
|
13441
13931
|
};
|
|
13442
13932
|
res.on("finish", () => logDone(`HTTP ${res.statusCode}`));
|
|
@@ -13447,16 +13937,16 @@ function createApp(store, manager, opts = {}) {
|
|
|
13447
13937
|
res.end();
|
|
13448
13938
|
return;
|
|
13449
13939
|
}
|
|
13450
|
-
if (req2.method === "GET" &&
|
|
13940
|
+
if (req2.method === "GET" && path36 === "/healthz") {
|
|
13451
13941
|
json(res, 200, {
|
|
13452
13942
|
status: "ok",
|
|
13453
13943
|
// 版本让运维/探针能确认线上跑的是哪个构建(更新与回滚据此判断)
|
|
13454
|
-
version: "0.2.
|
|
13944
|
+
version: "0.2.6",
|
|
13455
13945
|
features: { mcp: hasServerMcpConfig() }
|
|
13456
13946
|
});
|
|
13457
13947
|
return;
|
|
13458
13948
|
}
|
|
13459
|
-
if (hubProxyEnabled(opts.host) && isHubProxyPath(
|
|
13949
|
+
if (hubProxyEnabled(opts.host) && isHubProxyPath(path36)) {
|
|
13460
13950
|
void handleHubProxy(req2, res).catch(() => {
|
|
13461
13951
|
if (!res.headersSent) {
|
|
13462
13952
|
res.writeHead(502, { "Content-Type": "application/json; charset=utf-8" });
|
|
@@ -13467,11 +13957,11 @@ function createApp(store, manager, opts = {}) {
|
|
|
13467
13957
|
});
|
|
13468
13958
|
return;
|
|
13469
13959
|
}
|
|
13470
|
-
if (!
|
|
13960
|
+
if (!path36.startsWith(API_PREFIX)) {
|
|
13471
13961
|
json(res, 404, { error: { code: "not_found", message: "\u672A\u77E5\u8DEF\u5F84" } });
|
|
13472
13962
|
return;
|
|
13473
13963
|
}
|
|
13474
|
-
const segs =
|
|
13964
|
+
const segs = path36.slice(API_PREFIX.length).split("/").filter(Boolean);
|
|
13475
13965
|
const apiKey = extractApiKey(req2);
|
|
13476
13966
|
if (!apiKey) {
|
|
13477
13967
|
json(res, 401, {
|
|
@@ -13536,7 +14026,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
13536
14026
|
).catch((err) => {
|
|
13537
14027
|
const msg = err instanceof Error ? err.message : String(err);
|
|
13538
14028
|
console.log(
|
|
13539
|
-
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} ! ${method} ${
|
|
14029
|
+
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} ! ${method} ${path36} ERROR: ${msg}`
|
|
13540
14030
|
);
|
|
13541
14031
|
error(res, err);
|
|
13542
14032
|
});
|
|
@@ -14051,6 +14541,17 @@ async function dispatchFleetCommand(deps, env3) {
|
|
|
14051
14541
|
)
|
|
14052
14542
|
);
|
|
14053
14543
|
}
|
|
14544
|
+
case "git_commit_message": {
|
|
14545
|
+
const t = project();
|
|
14546
|
+
return ok2({
|
|
14547
|
+
message: await generateGitCommitMessage({
|
|
14548
|
+
store,
|
|
14549
|
+
tenant: t,
|
|
14550
|
+
workspaceRoot: t.workspaceRoot ?? null,
|
|
14551
|
+
repo: gitRepo(payload.repo)
|
|
14552
|
+
})
|
|
14553
|
+
});
|
|
14554
|
+
}
|
|
14054
14555
|
case "git_checkout": {
|
|
14055
14556
|
const t = project();
|
|
14056
14557
|
return ok2(
|
|
@@ -14756,6 +15257,18 @@ var READONLY_CMD_TYPES = /* @__PURE__ */ new Set([
|
|
|
14756
15257
|
"git_show"
|
|
14757
15258
|
]);
|
|
14758
15259
|
var CMD_CACHE_TTL_MS = 1500;
|
|
15260
|
+
var STATE_CHANGING_EVENTS = /* @__PURE__ */ new Set([
|
|
15261
|
+
"turn_start",
|
|
15262
|
+
"turn_end",
|
|
15263
|
+
"permission_required",
|
|
15264
|
+
"ask_user_question",
|
|
15265
|
+
"plan_proposed",
|
|
15266
|
+
"directory_requested",
|
|
15267
|
+
"error",
|
|
15268
|
+
"interrupted",
|
|
15269
|
+
"queue_steering",
|
|
15270
|
+
"session_named"
|
|
15271
|
+
]);
|
|
14759
15272
|
var READONLY_CONCURRENCY = 4;
|
|
14760
15273
|
var BATCH_MAX_SUBS = 64;
|
|
14761
15274
|
var BATCH_SUB_TIMEOUT_MS = 7e3;
|
|
@@ -14991,6 +15504,20 @@ var FleetNodeBridge = class {
|
|
|
14991
15504
|
this.writeEpoch += 1;
|
|
14992
15505
|
this.cmdResultCache.clear();
|
|
14993
15506
|
}
|
|
15507
|
+
/**
|
|
15508
|
+
* 会话快照缓存失效(事件/通知驱动的状态变化)。
|
|
15509
|
+
*
|
|
15510
|
+
* 与写操作不同,这些变化只影响会话相关查询;文件树 / Git 等重查询的缓存
|
|
15511
|
+
* 保持有效,避免多会话并行运行时把整个只读缓存反复清空。
|
|
15512
|
+
*/
|
|
15513
|
+
_invalidateSessionCache() {
|
|
15514
|
+
this.writeEpoch += 1;
|
|
15515
|
+
for (const key of [...this.cmdResultCache.keys()]) {
|
|
15516
|
+
if (/^(get_state|get_events|list_sessions|get_session_states)\|/.test(key)) {
|
|
15517
|
+
this.cmdResultCache.delete(key);
|
|
15518
|
+
}
|
|
15519
|
+
}
|
|
15520
|
+
}
|
|
14994
15521
|
close() {
|
|
14995
15522
|
this.closing = true;
|
|
14996
15523
|
if (this.usageTimer) {
|
|
@@ -15019,6 +15546,7 @@ var FleetNodeBridge = class {
|
|
|
15019
15546
|
}
|
|
15020
15547
|
/** 通知广播(SessionManager 注入的 notify 回调) */
|
|
15021
15548
|
notify(tenantId, ev) {
|
|
15549
|
+
this._invalidateSessionCache();
|
|
15022
15550
|
const payload = ev.kind === "approval_request" ? {
|
|
15023
15551
|
...ev,
|
|
15024
15552
|
projectId: tenantId,
|
|
@@ -15031,6 +15559,7 @@ var FleetNodeBridge = class {
|
|
|
15031
15559
|
}
|
|
15032
15560
|
/** 事件桥接(SessionManager 注入的 onEvent 回调);发布按窗口批量合并 */
|
|
15033
15561
|
onEvent(tenantId, sessionId, entry) {
|
|
15562
|
+
if (STATE_CHANGING_EVENTS.has(entry.type)) this._invalidateSessionCache();
|
|
15034
15563
|
if (entry.type === "turn_end") this.stats.turns += 1;
|
|
15035
15564
|
else if (entry.type === "tool_finished") this.stats.toolCalls += 1;
|
|
15036
15565
|
else if (entry.type === "error") this.stats.errors += 1;
|
|
@@ -15115,7 +15644,7 @@ var FleetNodeBridge = class {
|
|
|
15115
15644
|
label: this.opts.deps.store.loadNodeLabel() ?? void 0,
|
|
15116
15645
|
platform: process.platform,
|
|
15117
15646
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
15118
|
-
version: "0.2.
|
|
15647
|
+
version: "0.2.6",
|
|
15119
15648
|
ip: primaryIp()
|
|
15120
15649
|
};
|
|
15121
15650
|
}
|
|
@@ -15149,7 +15678,10 @@ var FleetNodeBridge = class {
|
|
|
15149
15678
|
sessionId: s.sessionId,
|
|
15150
15679
|
name: s.name ?? null,
|
|
15151
15680
|
status: s.status,
|
|
15152
|
-
lastUserMessage: lastUserMessage(s)
|
|
15681
|
+
lastUserMessage: lastUserMessage(s),
|
|
15682
|
+
// 控制面的聚合邮件靠它判断「这台机器最近有没有在干活」;
|
|
15683
|
+
// 状态跃迁可能被 info 节流跳过,完成时间不会。
|
|
15684
|
+
lastCompletedAt: s.lastCompletedAt ?? null
|
|
15153
15685
|
});
|
|
15154
15686
|
}
|
|
15155
15687
|
} catch {
|
|
@@ -15167,7 +15699,7 @@ var FleetNodeBridge = class {
|
|
|
15167
15699
|
label: this.opts.deps.store.loadNodeLabel() ?? void 0,
|
|
15168
15700
|
platform: process.platform,
|
|
15169
15701
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
15170
|
-
version: "0.2.
|
|
15702
|
+
version: "0.2.6",
|
|
15171
15703
|
ip: primaryIp(),
|
|
15172
15704
|
projects,
|
|
15173
15705
|
totals,
|
|
@@ -15936,8 +16468,8 @@ init_esm_shims();
|
|
|
15936
16468
|
|
|
15937
16469
|
// src/server/qq/bindings.ts
|
|
15938
16470
|
init_esm_shims();
|
|
15939
|
-
import * as
|
|
15940
|
-
import * as
|
|
16471
|
+
import * as fs22 from "fs";
|
|
16472
|
+
import * as path27 from "path";
|
|
15941
16473
|
function conversationKey(target) {
|
|
15942
16474
|
switch (target.kind) {
|
|
15943
16475
|
case "c2c":
|
|
@@ -15976,12 +16508,12 @@ var QqBindingsStore = class _QqBindingsStore {
|
|
|
15976
16508
|
cache = null;
|
|
15977
16509
|
/** 基于 DATA_DIR 构造 */
|
|
15978
16510
|
static in(dataDir) {
|
|
15979
|
-
return new _QqBindingsStore(
|
|
16511
|
+
return new _QqBindingsStore(path27.join(dataDir, "qq", "bindings.json"));
|
|
15980
16512
|
}
|
|
15981
16513
|
load() {
|
|
15982
16514
|
if (this.cache) return this.cache;
|
|
15983
16515
|
try {
|
|
15984
|
-
const raw = JSON.parse(
|
|
16516
|
+
const raw = JSON.parse(fs22.readFileSync(this.file, "utf-8"));
|
|
15985
16517
|
this.cache = {
|
|
15986
16518
|
conversations: raw.conversations ?? {}
|
|
15987
16519
|
};
|
|
@@ -16013,8 +16545,8 @@ var QqBindingsStore = class _QqBindingsStore {
|
|
|
16013
16545
|
return null;
|
|
16014
16546
|
}
|
|
16015
16547
|
persist() {
|
|
16016
|
-
|
|
16017
|
-
|
|
16548
|
+
fs22.mkdirSync(path27.dirname(this.file), { recursive: true });
|
|
16549
|
+
fs22.writeFileSync(this.file, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
16018
16550
|
}
|
|
16019
16551
|
};
|
|
16020
16552
|
|
|
@@ -16426,8 +16958,8 @@ ${lines.join("\n")}`, msgId);
|
|
|
16426
16958
|
|
|
16427
16959
|
// src/server/qq/QqManager.ts
|
|
16428
16960
|
init_esm_shims();
|
|
16429
|
-
import * as
|
|
16430
|
-
import * as
|
|
16961
|
+
import * as fs23 from "fs";
|
|
16962
|
+
import * as path28 from "path";
|
|
16431
16963
|
var QqManager = class {
|
|
16432
16964
|
constructor(opts) {
|
|
16433
16965
|
this.opts = opts;
|
|
@@ -16436,7 +16968,7 @@ var QqManager = class {
|
|
|
16436
16968
|
adapter = null;
|
|
16437
16969
|
config = null;
|
|
16438
16970
|
get configFile() {
|
|
16439
|
-
return
|
|
16971
|
+
return path28.join(this.opts.dataDir, "qq", "config.json");
|
|
16440
16972
|
}
|
|
16441
16973
|
/** 启动:读配置(env 优先,其次文件),有则建连接 */
|
|
16442
16974
|
start() {
|
|
@@ -16474,7 +17006,7 @@ var QqManager = class {
|
|
|
16474
17006
|
/** 清空配置:删除文件 + 断开 */
|
|
16475
17007
|
clearConfig() {
|
|
16476
17008
|
try {
|
|
16477
|
-
|
|
17009
|
+
fs23.rmSync(this.configFile, { force: true });
|
|
16478
17010
|
} catch {
|
|
16479
17011
|
}
|
|
16480
17012
|
this.stop();
|
|
@@ -16516,7 +17048,7 @@ var QqManager = class {
|
|
|
16516
17048
|
}
|
|
16517
17049
|
_readConfigFile() {
|
|
16518
17050
|
try {
|
|
16519
|
-
const raw = JSON.parse(
|
|
17051
|
+
const raw = JSON.parse(fs23.readFileSync(this.configFile, "utf-8"));
|
|
16520
17052
|
if (raw.appId && raw.appSecret) {
|
|
16521
17053
|
return {
|
|
16522
17054
|
appId: raw.appId,
|
|
@@ -16529,18 +17061,18 @@ var QqManager = class {
|
|
|
16529
17061
|
return null;
|
|
16530
17062
|
}
|
|
16531
17063
|
_persistConfig(cfg) {
|
|
16532
|
-
|
|
16533
|
-
|
|
17064
|
+
fs23.mkdirSync(path28.dirname(this.configFile), { recursive: true });
|
|
17065
|
+
fs23.writeFileSync(this.configFile, JSON.stringify(cfg, null, 2), "utf-8");
|
|
16534
17066
|
}
|
|
16535
17067
|
};
|
|
16536
17068
|
|
|
16537
17069
|
// src/server/qq/pairingCodes.ts
|
|
16538
17070
|
init_esm_shims();
|
|
16539
|
-
import * as
|
|
17071
|
+
import * as crypto6 from "crypto";
|
|
16540
17072
|
var PAIRING_CODE_TTL_MS = 5 * 60 * 1e3;
|
|
16541
17073
|
function randomCode(len = 8) {
|
|
16542
17074
|
const alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
16543
|
-
const bytes =
|
|
17075
|
+
const bytes = crypto6.randomBytes(len);
|
|
16544
17076
|
let out = "";
|
|
16545
17077
|
for (let i = 0; i < len; i++) {
|
|
16546
17078
|
out += alphabet[bytes[i] % alphabet.length];
|
|
@@ -16582,13 +17114,13 @@ var baseUrlResolver = null;
|
|
|
16582
17114
|
function resolveBase(baseUrl) {
|
|
16583
17115
|
return baseUrlResolver ? baseUrlResolver(baseUrl) : baseUrl;
|
|
16584
17116
|
}
|
|
16585
|
-
async function req(baseUrl, token,
|
|
17117
|
+
async function req(baseUrl, token, path36, init = {}) {
|
|
16586
17118
|
const headers = {
|
|
16587
17119
|
...init.body ? { "Content-Type": "application/json" } : {},
|
|
16588
17120
|
...init.headers ?? {}
|
|
16589
17121
|
};
|
|
16590
17122
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
16591
|
-
const res = await fetch(`${resolveBase(baseUrl).replace(/\/$/, "")}${
|
|
17123
|
+
const res = await fetch(`${resolveBase(baseUrl).replace(/\/$/, "")}${path36}`, {
|
|
16592
17124
|
...init,
|
|
16593
17125
|
headers
|
|
16594
17126
|
});
|
|
@@ -16630,17 +17162,17 @@ async function hubEnrollByAccount(baseUrl, input) {
|
|
|
16630
17162
|
|
|
16631
17163
|
// src/update/controller.ts
|
|
16632
17164
|
init_esm_shims();
|
|
16633
|
-
import * as
|
|
16634
|
-
import * as
|
|
17165
|
+
import * as fs27 from "fs";
|
|
17166
|
+
import * as path33 from "path";
|
|
16635
17167
|
|
|
16636
17168
|
// src/update/downloader.ts
|
|
16637
17169
|
init_esm_shims();
|
|
16638
17170
|
import { createHash as createHash3 } from "crypto";
|
|
16639
|
-
import * as
|
|
17171
|
+
import * as fs24 from "fs";
|
|
16640
17172
|
import { Readable as Readable2 } from "stream";
|
|
16641
17173
|
import { pipeline as pipeline2 } from "stream/promises";
|
|
16642
17174
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
16643
|
-
import * as
|
|
17175
|
+
import * as path29 from "path";
|
|
16644
17176
|
|
|
16645
17177
|
// src/update/manifest.ts
|
|
16646
17178
|
init_esm_shims();
|
|
@@ -16815,8 +17347,8 @@ async function fetchManifest(url, publicKey, opts = {}) {
|
|
|
16815
17347
|
async function downloadBinary(bin, destPath, log = () => {
|
|
16816
17348
|
}) {
|
|
16817
17349
|
const partPath = `${destPath}.part`;
|
|
16818
|
-
|
|
16819
|
-
|
|
17350
|
+
fs24.mkdirSync(path29.dirname(destPath), { recursive: true });
|
|
17351
|
+
fs24.rmSync(partPath, { force: true });
|
|
16820
17352
|
let res;
|
|
16821
17353
|
try {
|
|
16822
17354
|
res = await fetch(bin.url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS2) });
|
|
@@ -16841,25 +17373,25 @@ async function downloadBinary(bin, destPath, log = () => {
|
|
|
16841
17373
|
yield chunk;
|
|
16842
17374
|
}
|
|
16843
17375
|
},
|
|
16844
|
-
|
|
17376
|
+
fs24.createWriteStream(partPath, { mode: 493 })
|
|
16845
17377
|
);
|
|
16846
17378
|
} catch (err) {
|
|
16847
|
-
|
|
17379
|
+
fs24.rmSync(partPath, { force: true });
|
|
16848
17380
|
return { error: `\u4E0B\u8F7D\u4E2D\u65AD\uFF1A${err.message}` };
|
|
16849
17381
|
}
|
|
16850
17382
|
const actual = hash.digest("hex");
|
|
16851
17383
|
if (actual !== bin.sha256) {
|
|
16852
|
-
|
|
17384
|
+
fs24.rmSync(partPath, { force: true });
|
|
16853
17385
|
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` };
|
|
16854
17386
|
}
|
|
16855
17387
|
if (expected && bytes !== expected) {
|
|
16856
|
-
|
|
17388
|
+
fs24.rmSync(partPath, { force: true });
|
|
16857
17389
|
return { error: `\u4EA7\u7269\u5927\u5C0F\u4E0D\u5339\u914D\uFF08\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${bytes}\uFF09` };
|
|
16858
17390
|
}
|
|
16859
|
-
|
|
16860
|
-
|
|
16861
|
-
|
|
16862
|
-
log(`\u5DF2\u4E0B\u8F7D ${
|
|
17391
|
+
fs24.rmSync(destPath, { force: true });
|
|
17392
|
+
fs24.renameSync(partPath, destPath);
|
|
17393
|
+
fs24.chmodSync(destPath, 493);
|
|
17394
|
+
log(`\u5DF2\u4E0B\u8F7D ${path29.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
|
|
16863
17395
|
return { bytes };
|
|
16864
17396
|
}
|
|
16865
17397
|
function selfCheck(binPath, expectedVersion) {
|
|
@@ -16883,10 +17415,10 @@ function selfCheck(binPath, expectedVersion) {
|
|
|
16883
17415
|
|
|
16884
17416
|
// src/update/platform.ts
|
|
16885
17417
|
init_esm_shims();
|
|
16886
|
-
import * as
|
|
17418
|
+
import * as path30 from "path";
|
|
16887
17419
|
import { createRequire as createRequire3 } from "module";
|
|
16888
17420
|
function currentVersion() {
|
|
16889
|
-
const injected = "0.2.
|
|
17421
|
+
const injected = "0.2.6"?.trim();
|
|
16890
17422
|
if (injected) return injected;
|
|
16891
17423
|
try {
|
|
16892
17424
|
const req2 = createRequire3(import.meta.url);
|
|
@@ -16898,7 +17430,7 @@ function currentVersion() {
|
|
|
16898
17430
|
}
|
|
16899
17431
|
function isSeaRuntime() {
|
|
16900
17432
|
if (process.env.ZQAGENT_SEA === "1") return true;
|
|
16901
|
-
const base =
|
|
17433
|
+
const base = path30.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
|
|
16902
17434
|
return base !== "node" && base !== "nodejs";
|
|
16903
17435
|
}
|
|
16904
17436
|
function serviceManager() {
|
|
@@ -16926,22 +17458,22 @@ function selfUpdateCapability(hasPublicKey) {
|
|
|
16926
17458
|
|
|
16927
17459
|
// src/update/installer.ts
|
|
16928
17460
|
init_esm_shims();
|
|
16929
|
-
import * as
|
|
16930
|
-
import * as
|
|
17461
|
+
import * as fs26 from "fs";
|
|
17462
|
+
import * as path32 from "path";
|
|
16931
17463
|
|
|
16932
17464
|
// src/update/state.ts
|
|
16933
17465
|
init_esm_shims();
|
|
16934
|
-
import * as
|
|
16935
|
-
import * as
|
|
17466
|
+
import * as fs25 from "fs";
|
|
17467
|
+
import * as path31 from "path";
|
|
16936
17468
|
function updatesDir(dataDir) {
|
|
16937
|
-
return
|
|
17469
|
+
return path31.join(dataDir, "updates");
|
|
16938
17470
|
}
|
|
16939
17471
|
function statePath(dataDir) {
|
|
16940
|
-
return
|
|
17472
|
+
return path31.join(updatesDir(dataDir), "state.json");
|
|
16941
17473
|
}
|
|
16942
17474
|
function loadState(dataDir) {
|
|
16943
17475
|
try {
|
|
16944
|
-
const raw =
|
|
17476
|
+
const raw = fs25.readFileSync(statePath(dataDir), "utf-8");
|
|
16945
17477
|
const parsed = JSON.parse(raw);
|
|
16946
17478
|
if (typeof parsed === "object" && parsed !== null) return parsed;
|
|
16947
17479
|
} catch {
|
|
@@ -16950,11 +17482,11 @@ function loadState(dataDir) {
|
|
|
16950
17482
|
}
|
|
16951
17483
|
function saveState(dataDir, state) {
|
|
16952
17484
|
const dir = updatesDir(dataDir);
|
|
16953
|
-
|
|
17485
|
+
fs25.mkdirSync(dir, { recursive: true });
|
|
16954
17486
|
const file = statePath(dataDir);
|
|
16955
17487
|
const tmp = `${file}.tmp`;
|
|
16956
|
-
|
|
16957
|
-
|
|
17488
|
+
fs25.writeFileSync(tmp, JSON.stringify(state, null, 2), "utf-8");
|
|
17489
|
+
fs25.renameSync(tmp, file);
|
|
16958
17490
|
}
|
|
16959
17491
|
function patchState(dataDir, patch) {
|
|
16960
17492
|
const next = { ...loadState(dataDir), ...patch };
|
|
@@ -16964,36 +17496,36 @@ function patchState(dataDir, patch) {
|
|
|
16964
17496
|
|
|
16965
17497
|
// src/update/installer.ts
|
|
16966
17498
|
function backupDir(dataDir) {
|
|
16967
|
-
return
|
|
17499
|
+
return path32.join(updatesDir(dataDir), "backup");
|
|
16968
17500
|
}
|
|
16969
17501
|
function backupCurrent(dataDir, execPath, version) {
|
|
16970
17502
|
const dir = backupDir(dataDir);
|
|
16971
|
-
|
|
16972
|
-
const target =
|
|
16973
|
-
|
|
16974
|
-
|
|
17503
|
+
fs26.mkdirSync(dir, { recursive: true });
|
|
17504
|
+
const target = path32.join(dir, `zqagent-${version}${process.platform === "win32" ? ".exe" : ""}`);
|
|
17505
|
+
fs26.copyFileSync(execPath, target);
|
|
17506
|
+
fs26.chmodSync(target, 493);
|
|
16975
17507
|
return target;
|
|
16976
17508
|
}
|
|
16977
17509
|
function replaceExecutable(execPath, stagedPath) {
|
|
16978
17510
|
if (process.platform === "win32") {
|
|
16979
17511
|
const oldPath = `${execPath}.old`;
|
|
16980
|
-
|
|
16981
|
-
|
|
16982
|
-
|
|
16983
|
-
|
|
17512
|
+
fs26.rmSync(oldPath, { force: true });
|
|
17513
|
+
fs26.renameSync(execPath, oldPath);
|
|
17514
|
+
fs26.copyFileSync(stagedPath, execPath);
|
|
17515
|
+
fs26.chmodSync(execPath, 493);
|
|
16984
17516
|
return;
|
|
16985
17517
|
}
|
|
16986
17518
|
const tmp = `${execPath}.new`;
|
|
16987
|
-
|
|
17519
|
+
fs26.rmSync(tmp, { force: true });
|
|
16988
17520
|
try {
|
|
16989
|
-
|
|
16990
|
-
|
|
16991
|
-
|
|
17521
|
+
fs26.copyFileSync(stagedPath, tmp);
|
|
17522
|
+
fs26.chmodSync(tmp, 493);
|
|
17523
|
+
fs26.renameSync(tmp, execPath);
|
|
16992
17524
|
} catch (err) {
|
|
16993
|
-
|
|
17525
|
+
fs26.rmSync(tmp, { force: true });
|
|
16994
17526
|
throw err;
|
|
16995
17527
|
}
|
|
16996
|
-
|
|
17528
|
+
fs26.rmSync(stagedPath, { force: true });
|
|
16997
17529
|
}
|
|
16998
17530
|
function installStaged(opts) {
|
|
16999
17531
|
const log = opts.log ?? (() => {
|
|
@@ -17001,7 +17533,7 @@ function installStaged(opts) {
|
|
|
17001
17533
|
const from = currentVersion();
|
|
17002
17534
|
log(`\u6B63\u5728\u5B89\u88C5 v${opts.version}\uFF08\u5F53\u524D v${from}\uFF09`);
|
|
17003
17535
|
const backupPath = backupCurrent(opts.dataDir, opts.execPath, from);
|
|
17004
|
-
log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${
|
|
17536
|
+
log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${path32.basename(backupPath)}`);
|
|
17005
17537
|
replaceExecutable(opts.execPath, opts.stagedPath);
|
|
17006
17538
|
return patchState(opts.dataDir, {
|
|
17007
17539
|
stagedVersion: void 0,
|
|
@@ -17020,18 +17552,18 @@ function rollback(dataDir, execPath, log = () => {
|
|
|
17020
17552
|
}) {
|
|
17021
17553
|
const state = readRollbackTarget(dataDir);
|
|
17022
17554
|
if (!state?.backupPath || !state.backupVersion) return null;
|
|
17023
|
-
if (!
|
|
17555
|
+
if (!fs26.existsSync(state.backupPath)) return null;
|
|
17024
17556
|
log(`\u6B63\u5728\u56DE\u6EDA\u5230 v${state.backupVersion}`);
|
|
17025
17557
|
if (process.platform === "win32") {
|
|
17026
|
-
|
|
17027
|
-
|
|
17028
|
-
|
|
17029
|
-
|
|
17558
|
+
fs26.rmSync(`${execPath}.old`, { force: true });
|
|
17559
|
+
fs26.renameSync(execPath, `${execPath}.old`);
|
|
17560
|
+
fs26.copyFileSync(state.backupPath, execPath);
|
|
17561
|
+
fs26.chmodSync(execPath, 493);
|
|
17030
17562
|
} else {
|
|
17031
17563
|
const tmp = `${execPath}.rollback`;
|
|
17032
|
-
|
|
17033
|
-
|
|
17034
|
-
|
|
17564
|
+
fs26.copyFileSync(state.backupPath, tmp);
|
|
17565
|
+
fs26.chmodSync(tmp, 493);
|
|
17566
|
+
fs26.renameSync(tmp, execPath);
|
|
17035
17567
|
}
|
|
17036
17568
|
patchState(dataDir, {
|
|
17037
17569
|
pendingVersion: state.backupVersion,
|
|
@@ -17045,7 +17577,7 @@ function rollback(dataDir, execPath, log = () => {
|
|
|
17045
17577
|
}
|
|
17046
17578
|
function readRollbackTarget(dataDir) {
|
|
17047
17579
|
try {
|
|
17048
|
-
const raw =
|
|
17580
|
+
const raw = fs26.readFileSync(path32.join(updatesDir(dataDir), "state.json"), "utf-8");
|
|
17049
17581
|
const s = JSON.parse(raw);
|
|
17050
17582
|
if (s.backupPath && s.backupVersion) return s;
|
|
17051
17583
|
} catch {
|
|
@@ -17128,7 +17660,7 @@ var UpdateController = class {
|
|
|
17128
17660
|
reconcile() {
|
|
17129
17661
|
const state = loadState(this.opts.dataDir);
|
|
17130
17662
|
const current = this.currentVersion;
|
|
17131
|
-
if (state.stagedPath && !
|
|
17663
|
+
if (state.stagedPath && !fs27.existsSync(state.stagedPath)) {
|
|
17132
17664
|
patchState(this.opts.dataDir, { stagedVersion: void 0, stagedPath: void 0 });
|
|
17133
17665
|
}
|
|
17134
17666
|
const patch = { runningVersion: current };
|
|
@@ -17143,7 +17675,7 @@ var UpdateController = class {
|
|
|
17143
17675
|
}
|
|
17144
17676
|
patchState(this.opts.dataDir, patch);
|
|
17145
17677
|
if (process.platform === "win32") {
|
|
17146
|
-
|
|
17678
|
+
fs27.rmSync(`${process.execPath}.old`, { force: true });
|
|
17147
17679
|
}
|
|
17148
17680
|
}
|
|
17149
17681
|
schedule(delayMs) {
|
|
@@ -17219,8 +17751,8 @@ var UpdateController = class {
|
|
|
17219
17751
|
const bin = manifest.binaries[platformKey()];
|
|
17220
17752
|
if (!bin) return `\u6E05\u5355\u6CA1\u6709\u5F53\u524D\u5E73\u53F0\uFF08${platformKey()}\uFF09\u7684\u4EA7\u7269`;
|
|
17221
17753
|
let stagedPath = state.stagedPath;
|
|
17222
|
-
if (!(state.stagedVersion === manifest.version && stagedPath &&
|
|
17223
|
-
stagedPath =
|
|
17754
|
+
if (!(state.stagedVersion === manifest.version && stagedPath && fs27.existsSync(stagedPath))) {
|
|
17755
|
+
stagedPath = path33.join(
|
|
17224
17756
|
updatesDir(this.opts.dataDir),
|
|
17225
17757
|
"staged",
|
|
17226
17758
|
`zqagent-${manifest.version}${process.platform === "win32" ? ".exe" : ""}`
|
|
@@ -17232,7 +17764,7 @@ var UpdateController = class {
|
|
|
17232
17764
|
}
|
|
17233
17765
|
const selfTest = selfCheck(stagedPath, manifest.version);
|
|
17234
17766
|
if (!selfTest.ok) {
|
|
17235
|
-
|
|
17767
|
+
fs27.rmSync(stagedPath, { force: true });
|
|
17236
17768
|
this.recordFailure(manifest.version, selfTest.error ?? "\u81EA\u68C0\u5931\u8D25");
|
|
17237
17769
|
return `\u5B89\u88C5\u5305\u81EA\u68C0\u5931\u8D25\uFF1A${selfTest.error}`;
|
|
17238
17770
|
}
|
|
@@ -17373,7 +17905,7 @@ function pemFile(key) {
|
|
|
17373
17905
|
const p = env2(key);
|
|
17374
17906
|
if (!p) return void 0;
|
|
17375
17907
|
try {
|
|
17376
|
-
return
|
|
17908
|
+
return fs28.readFileSync(p);
|
|
17377
17909
|
} catch (e) {
|
|
17378
17910
|
console.warn(`[tls] \u8BFB\u53D6 ${key}=${p} \u5931\u8D25: ${e instanceof Error ? e.message : e}`);
|
|
17379
17911
|
return void 0;
|
|
@@ -17395,7 +17927,7 @@ function seedDemoTenant(store) {
|
|
|
17395
17927
|
apiKey: env2("OPENAI_API_KEY"),
|
|
17396
17928
|
baseURL: env2("OPENAI_BASE_URL")
|
|
17397
17929
|
},
|
|
17398
|
-
workspaceRoot: workspace ?
|
|
17930
|
+
workspaceRoot: workspace ? path34.resolve(workspace) : void 0,
|
|
17399
17931
|
allowedCommands: []
|
|
17400
17932
|
};
|
|
17401
17933
|
store.saveTenant(demo);
|
|
@@ -17449,9 +17981,9 @@ function syncTenantModels(store) {
|
|
|
17449
17981
|
tenant.model = model;
|
|
17450
17982
|
changed = true;
|
|
17451
17983
|
}
|
|
17452
|
-
if (workspace && tenant.workspaceRoot !==
|
|
17453
|
-
console.log(`[sync] \u79DF\u6237 ${id} workspaceRoot \u5DF2\u540C\u6B65: ${tenant.workspaceRoot ?? "(\u6C99\u7BB1)"} \u2192 ${
|
|
17454
|
-
tenant.workspaceRoot =
|
|
17984
|
+
if (workspace && tenant.workspaceRoot !== path34.resolve(workspace)) {
|
|
17985
|
+
console.log(`[sync] \u79DF\u6237 ${id} workspaceRoot \u5DF2\u540C\u6B65: ${tenant.workspaceRoot ?? "(\u6C99\u7BB1)"} \u2192 ${path34.resolve(workspace)}`);
|
|
17986
|
+
tenant.workspaceRoot = path34.resolve(workspace);
|
|
17455
17987
|
changed = true;
|
|
17456
17988
|
}
|
|
17457
17989
|
if (apiKey && tenant.provider.apiKey !== apiKey) {
|
|
@@ -17483,7 +18015,7 @@ function exitWhenOrphaned() {
|
|
|
17483
18015
|
timer.unref();
|
|
17484
18016
|
}
|
|
17485
18017
|
function startServer(opts = {}) {
|
|
17486
|
-
const dataDir =
|
|
18018
|
+
const dataDir = path34.resolve(opts.dataDir ?? env2("DATA_DIR") ?? DEFAULT_DATA_DIR);
|
|
17487
18019
|
const port = opts.port ?? Number(env2("PORT") ?? 8787);
|
|
17488
18020
|
const host = opts.host ?? env2("HOST") ?? "127.0.0.1";
|
|
17489
18021
|
exitWhenOrphaned();
|
|
@@ -17513,7 +18045,7 @@ function startServer(opts = {}) {
|
|
|
17513
18045
|
}
|
|
17514
18046
|
const adminKey = ensureAdminKey();
|
|
17515
18047
|
const secretKey = env2("SECRET_KEY");
|
|
17516
|
-
const secretBox = secretKey ? SecretBox.fromHex(secretKey) : SecretBox.fromFile(
|
|
18048
|
+
const secretBox = secretKey ? SecretBox.fromHex(secretKey) : SecretBox.fromFile(path34.join(dataDir, "secret.key"));
|
|
17517
18049
|
if (secretKey && !secretBox) {
|
|
17518
18050
|
console.warn("[crypto] SECRET_KEY \u975E\u6CD5\uFF08\u9700 64 \u4F4D hex\uFF09\uFF0C\u901A\u77E5\u51ED\u636E\u5C06\u9000\u5316\u4E3A\u660E\u6587");
|
|
17519
18051
|
}
|
|
@@ -17742,7 +18274,7 @@ function printReady(info, qq) {
|
|
|
17742
18274
|
`zqagent server \u76D1\u542C http://${info.host}:${info.port}`,
|
|
17743
18275
|
`\u6570\u636E\u76EE\u5F55: ${info.dataDir}`,
|
|
17744
18276
|
`\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`,
|
|
17745
|
-
env2("ZQAGENT_LOG_ADMIN_KEY") === "0" ? `\u7BA1\u7406\u5458\u5BC6\u94A5: ${maskKey(info.adminKey)}\uFF08\u5DF2\u8131\u654F\uFF0C\u5B8C\u6574\u503C\u89C1 ${
|
|
18277
|
+
env2("ZQAGENT_LOG_ADMIN_KEY") === "0" ? `\u7BA1\u7406\u5458\u5BC6\u94A5: ${maskKey(info.adminKey)}\uFF08\u5DF2\u8131\u654F\uFF0C\u5B8C\u6574\u503C\u89C1 ${path34.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`
|
|
17746
18278
|
];
|
|
17747
18279
|
if (info.fleetNodeId) {
|
|
17748
18280
|
lines.push(
|
|
@@ -17763,7 +18295,7 @@ function printReady(info, qq) {
|
|
|
17763
18295
|
|
|
17764
18296
|
// src/cli/account-enroll.ts
|
|
17765
18297
|
init_esm_shims();
|
|
17766
|
-
import * as
|
|
18298
|
+
import * as path35 from "path";
|
|
17767
18299
|
import * as readline from "readline/promises";
|
|
17768
18300
|
var SKIP_ANSWERS = /* @__PURE__ */ new Set(["-", "skip", "none", "no"]);
|
|
17769
18301
|
function resolveDefaultHub(hubUrl2, env3 = process.env) {
|
|
@@ -17775,7 +18307,7 @@ function parseHubAnswer(answer, fallback) {
|
|
|
17775
18307
|
return (a || fallback).replace(/\/$/, "");
|
|
17776
18308
|
}
|
|
17777
18309
|
function resolveDataDir(dataDir) {
|
|
17778
|
-
return
|
|
18310
|
+
return path35.resolve(dataDir ?? process.env.DATA_DIR ?? DEFAULT_DATA_DIR);
|
|
17779
18311
|
}
|
|
17780
18312
|
async function maybeAccountEnroll(opts = {}) {
|
|
17781
18313
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|