@zqagent/core 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/README.oss.en.md +5 -0
- package/README.oss.md +5 -0
- package/README.oss.zh-TW.md +5 -0
- package/dist/cli/index.cjs +1161 -600
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +1167 -606
- package/dist/cli/index.js.map +1 -1
- package/dist/{explore-D4lCKXbA.d.ts → explore-B_bIPzmB.d.ts} +49 -1
- package/dist/{explore-Dj1j_GyN.d.cts → explore-COqHKDoK.d.cts} +49 -1
- package/dist/index.cjs +654 -258
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +654 -258
- package/dist/index.js.map +1 -1
- package/dist/server/index.cjs +1000 -443
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +1006 -449
- package/dist/server/index.js.map +1 -1
- package/dist/tools/explore.cjs +528 -141
- package/dist/tools/explore.cjs.map +1 -1
- package/dist/tools/explore.d.cts +1 -1
- package/dist/tools/explore.d.ts +1 -1
- package/dist/tools/explore.js +528 -141
- package/dist/tools/explore.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -552,9 +552,9 @@ var init_wire = __esm({
|
|
|
552
552
|
// src/cli/index.ts
|
|
553
553
|
init_esm_shims();
|
|
554
554
|
import "dotenv/config";
|
|
555
|
-
import * as
|
|
555
|
+
import * as fs34 from "fs";
|
|
556
556
|
import * as readline2 from "readline";
|
|
557
|
-
import * as
|
|
557
|
+
import * as path43 from "path";
|
|
558
558
|
import { exec } from "child_process";
|
|
559
559
|
import { promisify } from "util";
|
|
560
560
|
|
|
@@ -920,8 +920,11 @@ function formatTokens(tokens) {
|
|
|
920
920
|
return `${tokens}`;
|
|
921
921
|
}
|
|
922
922
|
var KEEP_RECENT_FRACTION = 0.25;
|
|
923
|
-
var
|
|
924
|
-
var
|
|
923
|
+
var SOFT_THRESHOLD_RATIO = 0.75;
|
|
924
|
+
var ELIDE_KEEP_FRACTION = 0.15;
|
|
925
|
+
var SUMMARY_MAX_TOKENS = 3e3;
|
|
926
|
+
var SPAN_TOOL_RESULT_CLIP = 200;
|
|
927
|
+
var SPAN_BUDGET_CHARS = 8e4;
|
|
925
928
|
var USER_MESSAGE_CLIP = 600;
|
|
926
929
|
var USER_MESSAGES_MAX = 40;
|
|
927
930
|
var TRIM_FRACTION = 0.1;
|
|
@@ -944,9 +947,22 @@ function triggerTokens(contextWindow, thresholdPct = DEFAULT_THRESHOLD_PCT, capT
|
|
|
944
947
|
}
|
|
945
948
|
return base;
|
|
946
949
|
}
|
|
950
|
+
function softTriggerTokens(contextWindow, thresholdPct = DEFAULT_THRESHOLD_PCT, capTokens, softThresholdPct) {
|
|
951
|
+
const hard = triggerTokens(contextWindow, thresholdPct, capTokens);
|
|
952
|
+
if (softThresholdPct != null && Number.isFinite(softThresholdPct) && softThresholdPct > 0) {
|
|
953
|
+
return Math.min(
|
|
954
|
+
triggerTokens(contextWindow, softThresholdPct, capTokens),
|
|
955
|
+
Math.floor(hard * 0.95)
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
return Math.floor(hard * SOFT_THRESHOLD_RATIO);
|
|
959
|
+
}
|
|
947
960
|
function shouldCompact(signal, contextWindow, thresholdPct, capTokens) {
|
|
948
961
|
return signal >= triggerTokens(contextWindow, thresholdPct, capTokens);
|
|
949
962
|
}
|
|
963
|
+
function elisionKeepChars(hardTriggerTokens, fraction = ELIDE_KEEP_FRACTION) {
|
|
964
|
+
return Math.max(4e3, Math.floor(hardTriggerTokens * fraction * 4));
|
|
965
|
+
}
|
|
950
966
|
function turnStarts(messages, start) {
|
|
951
967
|
const users = [];
|
|
952
968
|
const assistants = [];
|
|
@@ -1096,10 +1112,11 @@ var SUMMARY_SYSTEM_PROMPT = `\u4F60\u6B63\u5728\u538B\u7F29\u4E00\u4E2A AI \u534
|
|
|
1096
1112
|
|
|
1097
1113
|
\u89C4\u5219\uFF1A
|
|
1098
1114
|
- \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
|
|
1115
|
+
- \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
|
|
1099
1116
|
- \u8981\u5177\u4F53\uFF1A\u8DEF\u5F84\u3001\u540D\u79F0\u3001\u547D\u4EE4\u3001id\u2014\u2014\u800C\u4E0D\u662F\u6A21\u7CCA\u7684\u5F15\u7528\u3002
|
|
1100
1117
|
- \u4EC5\u8F93\u51FA\u6458\u8981\u7AE0\u8282\uFF0C\u4E0D\u9644\u52A0\u524D\u8A00\u3002`;
|
|
1101
1118
|
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";
|
|
1102
|
-
function renderSpan(span,
|
|
1119
|
+
function renderSpan(span, budgetChars2 = SPAN_BUDGET_CHARS) {
|
|
1103
1120
|
const lines = [];
|
|
1104
1121
|
for (const m of span) {
|
|
1105
1122
|
const role = m.role;
|
|
@@ -1123,8 +1140,8 @@ function renderSpan(span, budgetChars = SPAN_BUDGET_CHARS) {
|
|
|
1123
1140
|
if (text) lines.push(`[${role}] ${text}`);
|
|
1124
1141
|
}
|
|
1125
1142
|
let rendered = lines.join("\n");
|
|
1126
|
-
if (rendered.length >
|
|
1127
|
-
rendered = "(\u2026oldest turns elided\u2026)\n" + rendered.slice(-
|
|
1143
|
+
if (rendered.length > budgetChars2) {
|
|
1144
|
+
rendered = "(\u2026oldest turns elided\u2026)\n" + rendered.slice(-budgetChars2);
|
|
1128
1145
|
}
|
|
1129
1146
|
return rendered;
|
|
1130
1147
|
}
|
|
@@ -1138,9 +1155,17 @@ function summarizerMessages(span, priorSummary = "") {
|
|
|
1138
1155
|
{ role: "user", content: body }
|
|
1139
1156
|
];
|
|
1140
1157
|
}
|
|
1158
|
+
function summarizerReasoningEffort() {
|
|
1159
|
+
const v = process.env.COMPACTION_REASONING_EFFORT?.toLowerCase();
|
|
1160
|
+
if (v === "low" || v === "medium" || v === "high") return v;
|
|
1161
|
+
return "low";
|
|
1162
|
+
}
|
|
1141
1163
|
async function summarizeSpan(provider, model, span, priorSummary = "", opts) {
|
|
1142
1164
|
const msgs = summarizerMessages(span, priorSummary);
|
|
1143
|
-
const chunks = await provider.stream(model, msgs, [],
|
|
1165
|
+
const chunks = await provider.stream(model, msgs, [], {
|
|
1166
|
+
...opts,
|
|
1167
|
+
maxTokens: SUMMARY_MAX_TOKENS
|
|
1168
|
+
});
|
|
1144
1169
|
let text = "";
|
|
1145
1170
|
for await (const chunk of chunks) {
|
|
1146
1171
|
if (chunk.content) text += chunk.content;
|
|
@@ -1251,6 +1276,166 @@ function applyToOutbound(messages, state) {
|
|
|
1251
1276
|
head.push({ role: "user", content: compactedBlock(state) });
|
|
1252
1277
|
return [...head, ...messages.slice(boundary)];
|
|
1253
1278
|
}
|
|
1279
|
+
var ELIDE_EXEMPT_TOOLS = /* @__PURE__ */ new Set([
|
|
1280
|
+
"explore",
|
|
1281
|
+
"ask_user",
|
|
1282
|
+
"propose_plan",
|
|
1283
|
+
"request_directory",
|
|
1284
|
+
"todo_write"
|
|
1285
|
+
]);
|
|
1286
|
+
function elidedToolContent(m) {
|
|
1287
|
+
const name = m.tool_call_name || "tool";
|
|
1288
|
+
const size = m.content.length;
|
|
1289
|
+
const saved = /full output saved to ([^\]]+)/.exec(m.content)?.[1]?.trim();
|
|
1290
|
+
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]`;
|
|
1291
|
+
}
|
|
1292
|
+
function isPlaceholder(content) {
|
|
1293
|
+
return content.startsWith("[old tool result elided") || content.startsWith("[superseded by a later read_file");
|
|
1294
|
+
}
|
|
1295
|
+
var ONE_SHOT_TOOLS = /* @__PURE__ */ new Set(["git_diff"]);
|
|
1296
|
+
var ONE_SHOT_KEEP_RECENT = 1;
|
|
1297
|
+
function applyOneShotElision(messages, keepRecent = ONE_SHOT_KEEP_RECENT, tools = ONE_SHOT_TOOLS) {
|
|
1298
|
+
const out = [...messages];
|
|
1299
|
+
let seen = 0;
|
|
1300
|
+
let elided = 0;
|
|
1301
|
+
let freed = 0;
|
|
1302
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1303
|
+
const m = messages[i];
|
|
1304
|
+
if (m.role !== "tool" || !tools.has(m.tool_call_name ?? "")) continue;
|
|
1305
|
+
if (isPlaceholder(m.content)) continue;
|
|
1306
|
+
seen++;
|
|
1307
|
+
if (seen <= keepRecent) continue;
|
|
1308
|
+
const placeholder = elidedToolContent(m);
|
|
1309
|
+
freed += m.content.length - placeholder.length;
|
|
1310
|
+
out[i] = { ...m, content: placeholder };
|
|
1311
|
+
elided++;
|
|
1312
|
+
}
|
|
1313
|
+
return elided > 0 ? { messages: out, elided, freedChars: freed } : { messages, elided: 0, freedChars: 0 };
|
|
1314
|
+
}
|
|
1315
|
+
function applyToolResultElision(messages, keepChars, exempt = ELIDE_EXEMPT_TOOLS) {
|
|
1316
|
+
let total = 0;
|
|
1317
|
+
for (const m of messages) {
|
|
1318
|
+
if (m.role === "tool" && !exempt.has(m.tool_call_name ?? "")) {
|
|
1319
|
+
total += m.content.length;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
if (total <= keepChars) return { messages, elided: 0, freedChars: 0 };
|
|
1323
|
+
const out = [...messages];
|
|
1324
|
+
let budget = 0;
|
|
1325
|
+
let elided = 0;
|
|
1326
|
+
let freed = 0;
|
|
1327
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1328
|
+
const m = messages[i];
|
|
1329
|
+
if (m.role !== "tool") continue;
|
|
1330
|
+
if (exempt.has(m.tool_call_name ?? "")) continue;
|
|
1331
|
+
if (elided === 0 && budget === 0 && m.content.length > keepChars) {
|
|
1332
|
+
const kept = m.content.slice(0, keepChars) + "\n\u2026(truncated)";
|
|
1333
|
+
freed += m.content.length - kept.length;
|
|
1334
|
+
out[i] = { ...m, content: kept };
|
|
1335
|
+
budget = keepChars;
|
|
1336
|
+
continue;
|
|
1337
|
+
}
|
|
1338
|
+
if (budget + m.content.length <= keepChars) {
|
|
1339
|
+
budget += m.content.length;
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
const placeholder = elidedToolContent(m);
|
|
1343
|
+
freed += m.content.length - placeholder.length;
|
|
1344
|
+
out[i] = { ...m, content: placeholder };
|
|
1345
|
+
elided++;
|
|
1346
|
+
}
|
|
1347
|
+
return { messages: out, elided, freedChars: freed };
|
|
1348
|
+
}
|
|
1349
|
+
function normalizeToolPath(p) {
|
|
1350
|
+
let s = p.trim().replace(/\\/g, "/");
|
|
1351
|
+
while (s.startsWith("./")) s = s.slice(2);
|
|
1352
|
+
return s.replace(/\/{2,}/g, "/");
|
|
1353
|
+
}
|
|
1354
|
+
var MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
1355
|
+
"write_file",
|
|
1356
|
+
"replace_in_file",
|
|
1357
|
+
"apply_patch",
|
|
1358
|
+
"apply_unified_diff",
|
|
1359
|
+
"run_shell"
|
|
1360
|
+
]);
|
|
1361
|
+
function parseReadFileSpan(content) {
|
|
1362
|
+
const body = content.replace(/^\[tool output truncated:[^\n]*\]\n\n/, "");
|
|
1363
|
+
let path44;
|
|
1364
|
+
let start;
|
|
1365
|
+
let end;
|
|
1366
|
+
try {
|
|
1367
|
+
const obj = JSON.parse(body);
|
|
1368
|
+
path44 = obj.path;
|
|
1369
|
+
start = obj.start_line;
|
|
1370
|
+
end = obj.end_line;
|
|
1371
|
+
} catch {
|
|
1372
|
+
const str2 = (key) => new RegExp(`"${key}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`).exec(body)?.[1];
|
|
1373
|
+
const num2 = (key) => {
|
|
1374
|
+
const m = new RegExp(`"${key}"\\s*:\\s*(\\d+)`).exec(body);
|
|
1375
|
+
return m ? Number(m[1]) : void 0;
|
|
1376
|
+
};
|
|
1377
|
+
path44 = str2("path");
|
|
1378
|
+
start = num2("start_line");
|
|
1379
|
+
end = num2("end_line");
|
|
1380
|
+
}
|
|
1381
|
+
if (typeof path44 !== "string" || !path44) return null;
|
|
1382
|
+
if (typeof start !== "number" || typeof end !== "number") return null;
|
|
1383
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return null;
|
|
1384
|
+
return { path: path44, start, end };
|
|
1385
|
+
}
|
|
1386
|
+
function addSpan(spans, start, end) {
|
|
1387
|
+
spans.push([start, end]);
|
|
1388
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
1389
|
+
const merged = [];
|
|
1390
|
+
for (const cur of spans) {
|
|
1391
|
+
const last = merged[merged.length - 1];
|
|
1392
|
+
if (last && cur[0] <= last[1] + 1) {
|
|
1393
|
+
last[1] = Math.max(last[1], cur[1]);
|
|
1394
|
+
} else {
|
|
1395
|
+
merged.push([...cur]);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
spans.length = 0;
|
|
1399
|
+
spans.push(...merged);
|
|
1400
|
+
}
|
|
1401
|
+
function coversSpans(spans, start, end) {
|
|
1402
|
+
return spans.some(([a, b]) => a <= start && b >= end);
|
|
1403
|
+
}
|
|
1404
|
+
function applyReadFileDedup(messages) {
|
|
1405
|
+
let candidates = 0;
|
|
1406
|
+
for (const m of messages) {
|
|
1407
|
+
if (m.role === "tool" && m.tool_call_name === "read_file") candidates++;
|
|
1408
|
+
}
|
|
1409
|
+
if (candidates < 2) return { messages, elided: 0 };
|
|
1410
|
+
const out = [...messages];
|
|
1411
|
+
const covered = /* @__PURE__ */ new Map();
|
|
1412
|
+
let elided = 0;
|
|
1413
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1414
|
+
const m = messages[i];
|
|
1415
|
+
if (m.role === "assistant") {
|
|
1416
|
+
if (m.tool_calls?.some((tc) => MUTATING_TOOLS.has(tc.function.name))) {
|
|
1417
|
+
covered.clear();
|
|
1418
|
+
}
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1421
|
+
if (m.role !== "tool" || m.tool_call_name !== "read_file") continue;
|
|
1422
|
+
const span = parseReadFileSpan(m.content);
|
|
1423
|
+
if (!span) continue;
|
|
1424
|
+
const key = normalizeToolPath(span.path);
|
|
1425
|
+
const seen = covered.get(key) ?? [];
|
|
1426
|
+
if (coversSpans(seen, span.start, span.end)) {
|
|
1427
|
+
out[i] = {
|
|
1428
|
+
...m,
|
|
1429
|
+
content: `[superseded by a later read_file of ${span.path} (lines ${span.start}-${span.end})]`
|
|
1430
|
+
};
|
|
1431
|
+
elided++;
|
|
1432
|
+
continue;
|
|
1433
|
+
}
|
|
1434
|
+
addSpan(seen, span.start, span.end);
|
|
1435
|
+
covered.set(key, seen);
|
|
1436
|
+
}
|
|
1437
|
+
return { messages: elided > 0 ? out : messages, elided };
|
|
1438
|
+
}
|
|
1254
1439
|
var OVERFLOW_MARKERS = [
|
|
1255
1440
|
"context_length_exceeded",
|
|
1256
1441
|
"maximum context length",
|
|
@@ -1266,9 +1451,116 @@ function isContextOverflow(exc) {
|
|
|
1266
1451
|
return OVERFLOW_MARKERS.some((m) => text.includes(m));
|
|
1267
1452
|
}
|
|
1268
1453
|
|
|
1454
|
+
// src/engine/toolResultBudget.ts
|
|
1455
|
+
init_esm_shims();
|
|
1456
|
+
import * as fs from "fs";
|
|
1457
|
+
import * as path3 from "path";
|
|
1458
|
+
|
|
1459
|
+
// src/paths.ts
|
|
1460
|
+
init_esm_shims();
|
|
1461
|
+
import * as os from "os";
|
|
1462
|
+
import * as path2 from "path";
|
|
1463
|
+
var APP_DIR_NAME = "zqagent";
|
|
1464
|
+
var DEFAULT_DATA_DIR = ".zqagent_data";
|
|
1465
|
+
var DEFAULT_HUB_URL = "https://hub.cd-zq.com";
|
|
1466
|
+
var IS_WINDOWS = process.platform === "win32";
|
|
1467
|
+
function homeDir() {
|
|
1468
|
+
return process.env.HOME || process.env.USERPROFILE || os.homedir() || os.tmpdir();
|
|
1469
|
+
}
|
|
1470
|
+
function configDir() {
|
|
1471
|
+
const base = process.env.XDG_CONFIG_HOME || (IS_WINDOWS ? process.env.APPDATA : void 0) || path2.join(homeDir(), ".config");
|
|
1472
|
+
return path2.join(base, APP_DIR_NAME);
|
|
1473
|
+
}
|
|
1474
|
+
function stateDir() {
|
|
1475
|
+
const base = process.env.XDG_STATE_HOME || (IS_WINDOWS ? process.env.LOCALAPPDATA : void 0) || path2.join(homeDir(), ".local", "state");
|
|
1476
|
+
return path2.join(base, APP_DIR_NAME);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// src/engine/toolResultBudget.ts
|
|
1480
|
+
var DEFAULT_TOOL_RESULT_MAX_CHARS = 24e3;
|
|
1481
|
+
var HEAD_RATIO = 0.6;
|
|
1482
|
+
function budgetChars() {
|
|
1483
|
+
const n = Number(process.env.TOOL_RESULT_MAX_CHARS);
|
|
1484
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_TOOL_RESULT_MAX_CHARS;
|
|
1485
|
+
}
|
|
1486
|
+
function toolResultDir() {
|
|
1487
|
+
const env3 = process.env.TOOL_RESULT_DIR?.trim();
|
|
1488
|
+
return env3 || path3.join(stateDir(), "tool-results");
|
|
1489
|
+
}
|
|
1490
|
+
function clipText(text, budget) {
|
|
1491
|
+
if (text.length <= budget) return text;
|
|
1492
|
+
const room = Math.max(
|
|
1493
|
+
0,
|
|
1494
|
+
budget - `
|
|
1495
|
+
|
|
1496
|
+
\u2026 [${text.length} characters omitted] \u2026
|
|
1497
|
+
|
|
1498
|
+
`.length
|
|
1499
|
+
);
|
|
1500
|
+
const headLen = Math.floor(room * HEAD_RATIO);
|
|
1501
|
+
const tailLen = room - headLen;
|
|
1502
|
+
const omitted = text.length - headLen - tailLen;
|
|
1503
|
+
return text.slice(0, headLen) + `
|
|
1504
|
+
|
|
1505
|
+
\u2026 [${omitted} characters omitted] \u2026
|
|
1506
|
+
|
|
1507
|
+
` + text.slice(-tailLen);
|
|
1508
|
+
}
|
|
1509
|
+
function clipDeep(value, perField) {
|
|
1510
|
+
if (typeof value === "string") {
|
|
1511
|
+
return value.length <= perField ? value : clipText(value, perField);
|
|
1512
|
+
}
|
|
1513
|
+
if (Array.isArray(value)) {
|
|
1514
|
+
return value.map((v) => clipDeep(v, perField));
|
|
1515
|
+
}
|
|
1516
|
+
if (value && typeof value === "object") {
|
|
1517
|
+
const out = {};
|
|
1518
|
+
for (const [k, v] of Object.entries(value)) out[k] = clipDeep(v, perField);
|
|
1519
|
+
return out;
|
|
1520
|
+
}
|
|
1521
|
+
return value;
|
|
1522
|
+
}
|
|
1523
|
+
function saveRaw(tool, raw, dir) {
|
|
1524
|
+
try {
|
|
1525
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1526
|
+
const safe2 = tool.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40) || "tool";
|
|
1527
|
+
const name = `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}-${safe2}.txt`;
|
|
1528
|
+
const file = path3.join(dir, name);
|
|
1529
|
+
fs.writeFileSync(file, raw, "utf-8");
|
|
1530
|
+
return file;
|
|
1531
|
+
} catch {
|
|
1532
|
+
return void 0;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
function applyToolResultBudget(tool, result, opts = {}) {
|
|
1536
|
+
const budget = opts.maxChars ?? budgetChars();
|
|
1537
|
+
const raw = typeof result === "string" ? result : JSON.stringify(result, null, 2) ?? String(result);
|
|
1538
|
+
if (raw.length <= budget) {
|
|
1539
|
+
return { content: raw, truncated: false };
|
|
1540
|
+
}
|
|
1541
|
+
let content = raw;
|
|
1542
|
+
if (typeof result === "object" && result !== null) {
|
|
1543
|
+
let perField = Math.floor(budget * 0.7);
|
|
1544
|
+
let clipped = JSON.stringify(clipDeep(result, perField), null, 2);
|
|
1545
|
+
for (let i = 0; i < 6 && clipped && clipped.length > budget && perField > 128; i++) {
|
|
1546
|
+
perField = Math.floor(perField / 2);
|
|
1547
|
+
clipped = JSON.stringify(clipDeep(result, perField), null, 2);
|
|
1548
|
+
}
|
|
1549
|
+
if (clipped && clipped.length <= budget) content = clipped;
|
|
1550
|
+
}
|
|
1551
|
+
if (content.length > budget) content = clipText(content, budget);
|
|
1552
|
+
const savedPath = saveRaw(tool, raw, opts.dir ?? toolResultDir());
|
|
1553
|
+
if (savedPath) {
|
|
1554
|
+
content = `[tool output truncated: ${raw.length} chars, full output saved to ${savedPath}]
|
|
1555
|
+
|
|
1556
|
+
` + content;
|
|
1557
|
+
}
|
|
1558
|
+
return { content, truncated: true, savedPath };
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1269
1561
|
// src/engine/TurnEngine.ts
|
|
1270
1562
|
import * as crypto from "crypto";
|
|
1271
|
-
import * as
|
|
1563
|
+
import * as fs2 from "fs";
|
|
1272
1564
|
import * as _path from "path";
|
|
1273
1565
|
|
|
1274
1566
|
// src/prompts/index.ts
|
|
@@ -1394,13 +1686,17 @@ var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1394
1686
|
"propose_plan",
|
|
1395
1687
|
"request_directory"
|
|
1396
1688
|
]);
|
|
1397
|
-
|
|
1398
|
-
|
|
1689
|
+
var MAX_COMPACT_ROUNDS = 4;
|
|
1690
|
+
var OVERFLOW_TARGET_RATIO = 0.5;
|
|
1691
|
+
var OVERFLOW_TRIM_FRACTION = 0.5;
|
|
1692
|
+
var COMPACT_STALL_RETRY_DELTA = 20;
|
|
1693
|
+
function toolResultMessage(tc, result, taskId, toolResultDir2) {
|
|
1694
|
+
const budgeted = applyToolResultBudget(tc.name, result, { dir: toolResultDir2 });
|
|
1399
1695
|
return {
|
|
1400
1696
|
role: "tool",
|
|
1401
1697
|
tool_call_id: tc.id,
|
|
1402
1698
|
tool_call_name: tc.name,
|
|
1403
|
-
content,
|
|
1699
|
+
content: budgeted.content,
|
|
1404
1700
|
taskId
|
|
1405
1701
|
};
|
|
1406
1702
|
}
|
|
@@ -1511,12 +1807,22 @@ var TurnEngine = class _TurnEngine {
|
|
|
1511
1807
|
// 上下文压缩
|
|
1512
1808
|
compactionState = null;
|
|
1513
1809
|
_lastContextTokens = null;
|
|
1810
|
+
/** 压缩无进展时的消息条数快照;-1 表示无冷却 */
|
|
1811
|
+
_compactStalledAt = -1;
|
|
1514
1812
|
/** 显式配置的上下文窗口大小(token 数),未设置时为 undefined(走自动检测) */
|
|
1515
1813
|
_contextWindow;
|
|
1516
1814
|
/** 压缩触发阈值比例(默认 0.8) */
|
|
1517
1815
|
_thresholdPct;
|
|
1816
|
+
/** 软阈值比例(undefined = 取硬阈值的 75%) */
|
|
1817
|
+
_softThresholdPct;
|
|
1818
|
+
/** 是否启用软阈值淘汰(COMPACTION_ELIDE=0 可关闭) */
|
|
1819
|
+
_elisionEnabled;
|
|
1820
|
+
/** 是否启用重复读取去重(COMPACTION_DEDUP=0 可关闭) */
|
|
1821
|
+
_dedupEnabled;
|
|
1518
1822
|
/** 压缩触发阈值绝对上限(null = 不限制) */
|
|
1519
1823
|
_capTokens;
|
|
1824
|
+
/** 超预算工具结果的落盘目录(undefined = 使用默认状态目录) */
|
|
1825
|
+
_toolResultDir;
|
|
1520
1826
|
// 产出文件追踪(本轮 Turn 中成功写入的文件)
|
|
1521
1827
|
_producedFiles = [];
|
|
1522
1828
|
_turnStartTime = 0;
|
|
@@ -1568,7 +1874,15 @@ var TurnEngine = class _TurnEngine {
|
|
|
1568
1874
|
this._hook = opts.hook;
|
|
1569
1875
|
this._contextWindow = opts.contextWindow;
|
|
1570
1876
|
this._thresholdPct = opts.thresholdPct ?? DEFAULT_THRESHOLD_PCT;
|
|
1877
|
+
this._softThresholdPct = opts.softThresholdPct;
|
|
1878
|
+
this._elisionEnabled = !/^(0|false|off|no)$/i.test(
|
|
1879
|
+
(process.env.COMPACTION_ELIDE ?? "").trim()
|
|
1880
|
+
);
|
|
1881
|
+
this._dedupEnabled = !/^(0|false|off|no)$/i.test(
|
|
1882
|
+
(process.env.COMPACTION_DEDUP ?? "").trim()
|
|
1883
|
+
);
|
|
1571
1884
|
this._capTokens = opts.capTokens ?? null;
|
|
1885
|
+
this._toolResultDir = opts.toolResultDir;
|
|
1572
1886
|
this._reasoningEffort = opts.reasoningEffort;
|
|
1573
1887
|
if (this.messages.length === 0 || this.messages[0].role !== "system") {
|
|
1574
1888
|
this.messages.unshift({ role: "system", content: this.instructions });
|
|
@@ -1624,6 +1938,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
1624
1938
|
this._cancel = false;
|
|
1625
1939
|
this._producedFiles = [];
|
|
1626
1940
|
this._turnStartTime = Date.now();
|
|
1941
|
+
this._compactStalledAt = -1;
|
|
1627
1942
|
this._emitHook((h) => h.onTurnStart?.(userInput));
|
|
1628
1943
|
yield {
|
|
1629
1944
|
type: "turn_start" /* TURN_START */,
|
|
@@ -2340,7 +2655,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
2340
2655
|
for (let i = 0; i < concurrent.length; i++) {
|
|
2341
2656
|
const tc = concurrent[i];
|
|
2342
2657
|
const { result, status } = results[i];
|
|
2343
|
-
this.messages.push(
|
|
2658
|
+
this.messages.push(
|
|
2659
|
+
toolResultMessage(tc, result, this.taskId, this._toolResultDir)
|
|
2660
|
+
);
|
|
2344
2661
|
this._trackProducedFile(
|
|
2345
2662
|
tc,
|
|
2346
2663
|
result,
|
|
@@ -2403,7 +2720,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
2403
2720
|
};
|
|
2404
2721
|
status = "error";
|
|
2405
2722
|
}
|
|
2406
|
-
this.messages.push(
|
|
2723
|
+
this.messages.push(
|
|
2724
|
+
toolResultMessage(tc, result, this.taskId, this._toolResultDir)
|
|
2725
|
+
);
|
|
2407
2726
|
this._trackProducedFile(
|
|
2408
2727
|
tc,
|
|
2409
2728
|
result,
|
|
@@ -2438,13 +2757,41 @@ var TurnEngine = class _TurnEngine {
|
|
|
2438
2757
|
* 过滤 notice 消息;tool 消息只保留必要字段;
|
|
2439
2758
|
* 其余消息剥离 display-only sidecar 字段(source/_display/ts/
|
|
2440
2759
|
* reasoning/usage),避免污染模型上下文。
|
|
2760
|
+
*
|
|
2761
|
+
* 达到软阈值时先淘汰旧工具结果(零 LLM 成本),再产出最终视图。
|
|
2441
2762
|
*/
|
|
2442
2763
|
_outboundMessages() {
|
|
2443
|
-
const
|
|
2444
|
-
const
|
|
2445
|
-
|
|
2446
|
-
|
|
2764
|
+
const base = applyToOutbound(this.messages, this.compactionState);
|
|
2765
|
+
const deduped = this._dedupEnabled ? applyReadFileDedup(base).messages : base;
|
|
2766
|
+
let view = this._finalizeOutbound(deduped);
|
|
2767
|
+
if (!this._elisionEnabled) return view;
|
|
2768
|
+
if (estimateTokens(view) < this._softTriggerTokens()) {
|
|
2769
|
+
return view;
|
|
2770
|
+
}
|
|
2771
|
+
const oneShot = applyOneShotElision(deduped);
|
|
2772
|
+
if (oneShot.freedChars > 0) {
|
|
2773
|
+
view = this._finalizeOutbound(oneShot.messages);
|
|
2774
|
+
if (estimateTokens(view) < this._softTriggerTokens()) {
|
|
2775
|
+
return view;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
const keepChars = elisionKeepChars(
|
|
2779
|
+
triggerTokens(
|
|
2780
|
+
this._getContextWindow(),
|
|
2781
|
+
this._thresholdPct,
|
|
2782
|
+
this._capTokens
|
|
2783
|
+
)
|
|
2784
|
+
);
|
|
2785
|
+
const elided = applyToolResultElision(
|
|
2786
|
+
oneShot.messages,
|
|
2787
|
+
keepChars
|
|
2447
2788
|
);
|
|
2789
|
+
if (elided.freedChars === 0) return view;
|
|
2790
|
+
return this._finalizeOutbound(elided.messages);
|
|
2791
|
+
}
|
|
2792
|
+
/** 过滤 notice + 剥离 display-only 字段,得到最终发送视图 */
|
|
2793
|
+
_finalizeOutbound(sourceMessages) {
|
|
2794
|
+
const _STRIP = /* @__PURE__ */ new Set(["source", "_display", "ts", "reasoning", "usage"]);
|
|
2448
2795
|
return sourceMessages.filter((m) => m.role !== "notice").map((m) => {
|
|
2449
2796
|
if (m.role === "tool") {
|
|
2450
2797
|
return {
|
|
@@ -2462,6 +2809,15 @@ var TurnEngine = class _TurnEngine {
|
|
|
2462
2809
|
return stripped;
|
|
2463
2810
|
});
|
|
2464
2811
|
}
|
|
2812
|
+
/** 软阈值(token 数):达到它只淘汰旧工具结果 */
|
|
2813
|
+
_softTriggerTokens() {
|
|
2814
|
+
return softTriggerTokens(
|
|
2815
|
+
this._getContextWindow(),
|
|
2816
|
+
this._thresholdPct,
|
|
2817
|
+
this._capTokens,
|
|
2818
|
+
this._softThresholdPct
|
|
2819
|
+
);
|
|
2820
|
+
}
|
|
2465
2821
|
/** 通知消息:仅用于日志/展示,不发给模型 */
|
|
2466
2822
|
_appendNotice(kind, text) {
|
|
2467
2823
|
this.messages.push({
|
|
@@ -2503,18 +2859,36 @@ var TurnEngine = class _TurnEngine {
|
|
|
2503
2859
|
return false;
|
|
2504
2860
|
}
|
|
2505
2861
|
// ===================== 自动压缩 =====================
|
|
2506
|
-
/**
|
|
2862
|
+
/**
|
|
2863
|
+
* 当前上下文 token 用量。
|
|
2864
|
+
*
|
|
2865
|
+
* API 返回的 `usage.input` 是精确值,但反映的是「上一次请求」——工具结果
|
|
2866
|
+
* 与淘汰/压缩都会让它过时。因此同时估算当前 outbound 视图,取较大者:
|
|
2867
|
+
* 宁可早一点压缩,也不要漏判导致溢出。
|
|
2868
|
+
*/
|
|
2507
2869
|
_compactionTokens() {
|
|
2508
|
-
|
|
2509
|
-
|
|
2870
|
+
const estimated = estimateTokens(this._outboundMessages());
|
|
2871
|
+
if (this._lastContextTokens == null) {
|
|
2872
|
+
return { tokens: estimated, estimated: true };
|
|
2510
2873
|
}
|
|
2511
|
-
|
|
2512
|
-
tokens:
|
|
2513
|
-
|
|
2514
|
-
};
|
|
2874
|
+
if (estimated > this._lastContextTokens) {
|
|
2875
|
+
return { tokens: estimated, estimated: true };
|
|
2876
|
+
}
|
|
2877
|
+
return { tokens: this._lastContextTokens, estimated: false };
|
|
2878
|
+
}
|
|
2879
|
+
/** 压缩后重置精确计数:后续按估算值判断 */
|
|
2880
|
+
_resetContextTokens() {
|
|
2881
|
+
this._lastContextTokens = null;
|
|
2882
|
+
}
|
|
2883
|
+
/** 当前 outbound 视图的估算 token 数 */
|
|
2884
|
+
_estimateContextTokens() {
|
|
2885
|
+
return estimateTokens(this._outboundMessages());
|
|
2515
2886
|
}
|
|
2516
2887
|
/** 检查是否需要压缩(每轮 LLM 调用前) */
|
|
2517
2888
|
_compactionDue() {
|
|
2889
|
+
if (this._compactStalledAt >= 0 && this.messages.length < this._compactStalledAt + COMPACT_STALL_RETRY_DELTA) {
|
|
2890
|
+
return false;
|
|
2891
|
+
}
|
|
2518
2892
|
const { tokens } = this._compactionTokens();
|
|
2519
2893
|
return shouldCompact(
|
|
2520
2894
|
tokens,
|
|
@@ -2561,63 +2935,82 @@ var TurnEngine = class _TurnEngine {
|
|
|
2561
2935
|
async _compactNow(reqId, force = false) {
|
|
2562
2936
|
const contextWindow = this._getContextWindow();
|
|
2563
2937
|
const messagesBefore = this._outboundMessages().length;
|
|
2564
|
-
const
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
this._capTokens
|
|
2569
|
-
)
|
|
2938
|
+
const trigger = triggerTokens(
|
|
2939
|
+
contextWindow,
|
|
2940
|
+
this._thresholdPct,
|
|
2941
|
+
this._capTokens
|
|
2570
2942
|
);
|
|
2571
|
-
const
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2943
|
+
const target = Math.floor(
|
|
2944
|
+
trigger * (force ? OVERFLOW_TARGET_RATIO : 0.9)
|
|
2945
|
+
);
|
|
2946
|
+
const model = process.env.COMPACTION_MODEL?.trim() || this.model;
|
|
2947
|
+
const reasoningEffort = summarizerReasoningEffort();
|
|
2948
|
+
let keep = Math.floor(KEEP_RECENT_FRACTION * trigger);
|
|
2949
|
+
let method = null;
|
|
2950
|
+
let lastState = null;
|
|
2951
|
+
let progressed = false;
|
|
2952
|
+
for (let round = 0; round < MAX_COMPACT_ROUNDS; round++) {
|
|
2953
|
+
if (round > 0 && this._estimateContextTokens() <= target) break;
|
|
2954
|
+
let state = null;
|
|
2955
|
+
for (let attempt = 0; attempt < 2 && !state; attempt++) {
|
|
2956
|
+
try {
|
|
2957
|
+
const logTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
2958
|
+
state = await buildState(
|
|
2959
|
+
this.messages,
|
|
2960
|
+
this.provider,
|
|
2961
|
+
model,
|
|
2962
|
+
keep,
|
|
2963
|
+
this.compactionState,
|
|
2964
|
+
{ reqId, logTime, reasoningEffort }
|
|
2965
|
+
);
|
|
2966
|
+
} catch {
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
if (state) {
|
|
2970
|
+
this.compactionState = state;
|
|
2971
|
+
lastState = state;
|
|
2972
|
+
method = "summarized";
|
|
2973
|
+
progressed = true;
|
|
2974
|
+
} else {
|
|
2975
|
+
const trimmed = trimState(
|
|
2578
2976
|
this.messages,
|
|
2579
|
-
this.provider,
|
|
2580
|
-
model,
|
|
2581
|
-
keep,
|
|
2582
2977
|
this.compactionState,
|
|
2583
|
-
|
|
2978
|
+
force ? OVERFLOW_TRIM_FRACTION : 0.25
|
|
2584
2979
|
);
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
this.
|
|
2593
|
-
|
|
2980
|
+
if (!trimmed) break;
|
|
2981
|
+
this.compactionState = trimmed;
|
|
2982
|
+
lastState = trimmed;
|
|
2983
|
+
method = "trimmed";
|
|
2984
|
+
progressed = true;
|
|
2985
|
+
}
|
|
2986
|
+
this._resetContextTokens();
|
|
2987
|
+
if (this._estimateContextTokens() <= trigger) break;
|
|
2988
|
+
keep = Math.max(Math.floor(trigger * 0.05), Math.floor(keep / 2));
|
|
2989
|
+
}
|
|
2990
|
+
if (!progressed || !lastState) return { notice: null, method: null };
|
|
2991
|
+
this._compactStalledAt = this._estimateContextTokens() >= trigger ? this.messages.length : -1;
|
|
2992
|
+
const messagesAfter = this._outboundMessages().length;
|
|
2993
|
+
const common = {
|
|
2994
|
+
boundaryIndex: lastState.boundaryIndex,
|
|
2995
|
+
messagesBefore,
|
|
2996
|
+
messagesAfter
|
|
2997
|
+
};
|
|
2998
|
+
if (method === "summarized") {
|
|
2594
2999
|
return {
|
|
2595
3000
|
notice: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29 \u2014 \u65E9\u671F\u8F6E\u6B21\u5DF2\u6458\u8981",
|
|
2596
3001
|
method: "summarized",
|
|
2597
|
-
modelUsed:
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
messagesBefore,
|
|
2603
|
-
messagesAfter: this._outboundMessages().length
|
|
3002
|
+
modelUsed: lastState.modelUsed,
|
|
3003
|
+
summaryPreview: lastState.summaryText.slice(0, 200),
|
|
3004
|
+
keptUserMessages: lastState.userMessages.length,
|
|
3005
|
+
droppedUserMessages: lastState.userMessagesDropped,
|
|
3006
|
+
...common
|
|
2604
3007
|
};
|
|
2605
3008
|
}
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
return {
|
|
2612
|
-
notice: "\u4E0A\u4E0B\u6587\u5DF2\u88C1\u526A \u2014 \u6700\u65E7\u7684\u8F6E\u6B21\u5DF2\u4E22\u5F03\uFF08\u6458\u8981\u4E0D\u53EF\u7528\uFF09",
|
|
2613
|
-
method: "trimmed",
|
|
2614
|
-
boundaryIndex: trimmed.boundaryIndex,
|
|
2615
|
-
messagesBefore,
|
|
2616
|
-
messagesAfter: this._outboundMessages().length
|
|
2617
|
-
};
|
|
2618
|
-
}
|
|
2619
|
-
}
|
|
2620
|
-
return { notice: null, method: null };
|
|
3009
|
+
return {
|
|
3010
|
+
notice: "\u4E0A\u4E0B\u6587\u5DF2\u88C1\u526A \u2014 \u6700\u65E7\u7684\u8F6E\u6B21\u5DF2\u4E22\u5F03\uFF08\u6458\u8981\u4E0D\u53EF\u7528\uFF09",
|
|
3011
|
+
method: "trimmed",
|
|
3012
|
+
...common
|
|
3013
|
+
};
|
|
2621
3014
|
}
|
|
2622
3015
|
/** 追踪文件写入工具成功产生的文件 */
|
|
2623
3016
|
_trackProducedFile(tc, result, status) {
|
|
@@ -2627,9 +3020,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
2627
3020
|
return;
|
|
2628
3021
|
}
|
|
2629
3022
|
if (result && typeof result === "object") {
|
|
2630
|
-
const
|
|
2631
|
-
if (
|
|
2632
|
-
this._producedFiles.push(
|
|
3023
|
+
const path44 = result.path;
|
|
3024
|
+
if (path44) {
|
|
3025
|
+
this._producedFiles.push(path44);
|
|
2633
3026
|
return;
|
|
2634
3027
|
}
|
|
2635
3028
|
const changed = result.changed_files;
|
|
@@ -2653,7 +3046,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
2653
3046
|
_fileExists(f, ws) {
|
|
2654
3047
|
if (!ws && !_path.isAbsolute(f)) return true;
|
|
2655
3048
|
try {
|
|
2656
|
-
return
|
|
3049
|
+
return fs2.existsSync(_path.isAbsolute(f) ? f : _path.join(ws, f));
|
|
2657
3050
|
} catch {
|
|
2658
3051
|
return false;
|
|
2659
3052
|
}
|
|
@@ -2676,7 +3069,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
2676
3069
|
if (depth > 4 || results.length >= 50) return;
|
|
2677
3070
|
let entries;
|
|
2678
3071
|
try {
|
|
2679
|
-
entries =
|
|
3072
|
+
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
2680
3073
|
} catch {
|
|
2681
3074
|
return;
|
|
2682
3075
|
}
|
|
@@ -2691,7 +3084,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
2691
3084
|
const ext = dotIdx !== -1 ? e.name.slice(dotIdx).toLowerCase() : "";
|
|
2692
3085
|
if (!_TurnEngine.SCAN_SUFFIXES.has(ext)) continue;
|
|
2693
3086
|
try {
|
|
2694
|
-
const st =
|
|
3087
|
+
const st = fs2.statSync(full);
|
|
2695
3088
|
if (st.mtimeMs >= since) {
|
|
2696
3089
|
results.push(
|
|
2697
3090
|
_path.relative(_path.resolve(ws), _path.resolve(full))
|
|
@@ -2713,8 +3106,8 @@ var TurnEngine = class _TurnEngine {
|
|
|
2713
3106
|
|
|
2714
3107
|
// src/providers/OpenAIProvider.ts
|
|
2715
3108
|
init_esm_shims();
|
|
2716
|
-
import * as
|
|
2717
|
-
import * as
|
|
3109
|
+
import * as fs3 from "fs";
|
|
3110
|
+
import * as path4 from "path";
|
|
2718
3111
|
import OpenAI from "openai";
|
|
2719
3112
|
|
|
2720
3113
|
// src/terminal.ts
|
|
@@ -2900,7 +3293,7 @@ var OpenAIProvider = class {
|
|
|
2900
3293
|
});
|
|
2901
3294
|
if (opts.logDir) {
|
|
2902
3295
|
this._logDir = opts.logDir;
|
|
2903
|
-
|
|
3296
|
+
fs3.mkdirSync(opts.logDir, { recursive: true });
|
|
2904
3297
|
}
|
|
2905
3298
|
}
|
|
2906
3299
|
// =====================================================================
|
|
@@ -3053,6 +3446,8 @@ var OpenAIProvider = class {
|
|
|
3053
3446
|
tools: this._toResponsesTools(tools, model),
|
|
3054
3447
|
// 推理强度:DeepSeek 不支持,跳过
|
|
3055
3448
|
...opts.reasoningEffort && !this._isDeepSeek(model) ? { reasoning: { effort: opts.reasoningEffort } } : {},
|
|
3449
|
+
// 限制输出长度(摘要器等轻量请求)
|
|
3450
|
+
...opts.maxTokens ? { max_output_tokens: opts.maxTokens } : {},
|
|
3056
3451
|
stream: true
|
|
3057
3452
|
}, requestOptions);
|
|
3058
3453
|
return this._responsesStream(reqId, startTime, rawStream, {
|
|
@@ -3151,6 +3546,8 @@ var OpenAIProvider = class {
|
|
|
3151
3546
|
})),
|
|
3152
3547
|
// 推理强度:DeepSeek 不支持,跳过
|
|
3153
3548
|
...opts.reasoningEffort && !this._isDeepSeek(model) ? { reasoning_effort: opts.reasoningEffort } : {},
|
|
3549
|
+
// 限制输出长度:o 系列 / gpt-5 推理模型仅支持 max_completion_tokens
|
|
3550
|
+
...opts.maxTokens ? isReasoningModel(model) ? { max_completion_tokens: opts.maxTokens } : { max_tokens: opts.maxTokens } : {},
|
|
3154
3551
|
stream: true,
|
|
3155
3552
|
// 要求返回 Token 用量统计(在流结束前的一个 chunk 中携带)
|
|
3156
3553
|
stream_options: { include_usage: true }
|
|
@@ -3264,8 +3661,8 @@ var OpenAIProvider = class {
|
|
|
3264
3661
|
if (!this._logDir) return;
|
|
3265
3662
|
try {
|
|
3266
3663
|
const today = logTime?.split("T")[0];
|
|
3267
|
-
const dir =
|
|
3268
|
-
|
|
3664
|
+
const dir = path4.join(this._logDir, today);
|
|
3665
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
3269
3666
|
const entry = {
|
|
3270
3667
|
reqId,
|
|
3271
3668
|
ts: logTime,
|
|
@@ -3277,8 +3674,8 @@ var OpenAIProvider = class {
|
|
|
3277
3674
|
toolNames: tools?.map((t) => t.function.name) ?? []
|
|
3278
3675
|
}
|
|
3279
3676
|
};
|
|
3280
|
-
|
|
3281
|
-
|
|
3677
|
+
fs3.writeFileSync(
|
|
3678
|
+
path4.join(dir, `${reqId}.json`),
|
|
3282
3679
|
JSON.stringify(entry, null, 2),
|
|
3283
3680
|
"utf-8"
|
|
3284
3681
|
);
|
|
@@ -3297,9 +3694,9 @@ var OpenAIProvider = class {
|
|
|
3297
3694
|
if (!this._logDir) return;
|
|
3298
3695
|
try {
|
|
3299
3696
|
const today = logTime?.split("T")[0];
|
|
3300
|
-
const filePath =
|
|
3301
|
-
if (!
|
|
3302
|
-
const entry = JSON.parse(
|
|
3697
|
+
const filePath = path4.join(this._logDir, today, `${reqId}.json`);
|
|
3698
|
+
if (!fs3.existsSync(filePath)) return;
|
|
3699
|
+
const entry = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
3303
3700
|
entry.elapsedMs = Date.now() - startTime;
|
|
3304
3701
|
if (data.response) {
|
|
3305
3702
|
entry.response = data.response;
|
|
@@ -3307,7 +3704,7 @@ var OpenAIProvider = class {
|
|
|
3307
3704
|
if (data.error) {
|
|
3308
3705
|
entry.error = data.error;
|
|
3309
3706
|
}
|
|
3310
|
-
|
|
3707
|
+
fs3.writeFileSync(filePath, JSON.stringify(entry, null, 2), "utf-8");
|
|
3311
3708
|
} catch (e) {
|
|
3312
3709
|
console.error(e);
|
|
3313
3710
|
}
|
|
@@ -3770,62 +4167,62 @@ init_esm_shims();
|
|
|
3770
4167
|
|
|
3771
4168
|
// src/tools/pathSafety.ts
|
|
3772
4169
|
init_esm_shims();
|
|
3773
|
-
import * as
|
|
3774
|
-
import * as
|
|
4170
|
+
import * as fs4 from "fs";
|
|
4171
|
+
import * as path5 from "path";
|
|
3775
4172
|
function isInsideRelative(rel) {
|
|
3776
|
-
return !
|
|
4173
|
+
return !path5.isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${path5.sep}`);
|
|
3777
4174
|
}
|
|
3778
4175
|
function isFilesystemRoot(p) {
|
|
3779
|
-
return
|
|
4176
|
+
return path5.parse(p).root === p;
|
|
3780
4177
|
}
|
|
3781
4178
|
function resolveWithinRoot(workspace, target) {
|
|
3782
|
-
const abs =
|
|
4179
|
+
const abs = path5.resolve(workspace, target);
|
|
3783
4180
|
let rootReal;
|
|
3784
4181
|
try {
|
|
3785
|
-
rootReal =
|
|
4182
|
+
rootReal = fs4.realpathSync(workspace);
|
|
3786
4183
|
} catch {
|
|
3787
|
-
const rootAbs =
|
|
3788
|
-
if (!isInsideRelative(
|
|
4184
|
+
const rootAbs = path5.resolve(workspace);
|
|
4185
|
+
if (!isInsideRelative(path5.relative(rootAbs, abs))) return null;
|
|
3789
4186
|
return { root: rootAbs, real: abs };
|
|
3790
4187
|
}
|
|
3791
4188
|
if (isFilesystemRoot(rootReal)) return null;
|
|
3792
4189
|
let real;
|
|
3793
4190
|
try {
|
|
3794
|
-
real =
|
|
4191
|
+
real = fs4.realpathSync(abs);
|
|
3795
4192
|
} catch {
|
|
3796
4193
|
let probe = abs;
|
|
3797
4194
|
const tail = [];
|
|
3798
4195
|
for (; ; ) {
|
|
3799
|
-
const exists =
|
|
4196
|
+
const exists = fs4.existsSync(probe) || fs4.lstatSync(probe, { throwIfNoEntry: false }) !== void 0;
|
|
3800
4197
|
if (exists) {
|
|
3801
4198
|
try {
|
|
3802
|
-
real =
|
|
4199
|
+
real = path5.join(fs4.realpathSync(probe), ...tail);
|
|
3803
4200
|
break;
|
|
3804
4201
|
} catch {
|
|
3805
4202
|
return null;
|
|
3806
4203
|
}
|
|
3807
4204
|
}
|
|
3808
|
-
tail.unshift(
|
|
3809
|
-
const parent =
|
|
4205
|
+
tail.unshift(path5.basename(probe));
|
|
4206
|
+
const parent = path5.dirname(probe);
|
|
3810
4207
|
if (parent === probe) return null;
|
|
3811
4208
|
probe = parent;
|
|
3812
4209
|
}
|
|
3813
4210
|
}
|
|
3814
|
-
if (!isInsideRelative(
|
|
4211
|
+
if (!isInsideRelative(path5.relative(rootReal, real))) return null;
|
|
3815
4212
|
return { root: rootReal, real };
|
|
3816
4213
|
}
|
|
3817
4214
|
function isWithinRoot(workspace, target) {
|
|
3818
4215
|
return resolveWithinRoot(workspace, target) !== null;
|
|
3819
4216
|
}
|
|
3820
4217
|
function resolveEntryWithinRoot(workspace, target) {
|
|
3821
|
-
const abs =
|
|
3822
|
-
const parent = resolveWithinRoot(workspace,
|
|
4218
|
+
const abs = path5.resolve(workspace, target);
|
|
4219
|
+
const parent = resolveWithinRoot(workspace, path5.dirname(abs));
|
|
3823
4220
|
if (!parent) return null;
|
|
3824
|
-
return { root: parent.root, real:
|
|
4221
|
+
return { root: parent.root, real: path5.join(parent.real, path5.basename(abs)) };
|
|
3825
4222
|
}
|
|
3826
4223
|
|
|
3827
4224
|
// src/permissions/PermissionEngine.ts
|
|
3828
|
-
import * as
|
|
4225
|
+
import * as path6 from "path";
|
|
3829
4226
|
var READ_ONLY_MODES = /* @__PURE__ */ new Set(["discuss" /* DISCUSS */, "plan" /* PLAN */]);
|
|
3830
4227
|
var WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
3831
4228
|
"write_file",
|
|
@@ -3875,7 +4272,7 @@ var PermissionEngine = class {
|
|
|
3875
4272
|
/** 会话级「总是允许此命令」集合 */
|
|
3876
4273
|
sessionAllowCommands;
|
|
3877
4274
|
constructor(opts) {
|
|
3878
|
-
this.workspaceRoot =
|
|
4275
|
+
this.workspaceRoot = path6.resolve(opts.workspaceRoot);
|
|
3879
4276
|
this.mode = opts.mode;
|
|
3880
4277
|
this.allowedCommands = Array.isArray(opts.allowedCommands) ? opts.allowedCommands : [];
|
|
3881
4278
|
this.sessionAllowTools = /* @__PURE__ */ new Set();
|
|
@@ -4801,14 +5198,14 @@ function approvalToHtmlTable(input, maxLen = APPROVAL_VALUE_MAX) {
|
|
|
4801
5198
|
|
|
4802
5199
|
// src/engine/FileLogHook.ts
|
|
4803
5200
|
init_esm_shims();
|
|
4804
|
-
import * as
|
|
4805
|
-
import * as
|
|
5201
|
+
import * as fs5 from "fs";
|
|
5202
|
+
import * as path7 from "path";
|
|
4806
5203
|
var FileLogHook = class {
|
|
4807
5204
|
constructor(sessionId, stateDir2) {
|
|
4808
5205
|
this.sessionId = sessionId;
|
|
4809
|
-
|
|
4810
|
-
this._logPath =
|
|
4811
|
-
this._statePath =
|
|
5206
|
+
fs5.mkdirSync(stateDir2, { recursive: true });
|
|
5207
|
+
this._logPath = path7.join(stateDir2, `${sessionId}.jsonl`);
|
|
5208
|
+
this._statePath = path7.join(stateDir2, `${sessionId}_state.json`);
|
|
4812
5209
|
}
|
|
4813
5210
|
sessionId;
|
|
4814
5211
|
_logPath;
|
|
@@ -4902,13 +5299,13 @@ var FileLogHook = class {
|
|
|
4902
5299
|
...meta?.nameSource !== void 0 ? { nameSource: meta.nameSource } : {},
|
|
4903
5300
|
...meta?.taskCount !== void 0 ? { taskCount: meta.taskCount } : {}
|
|
4904
5301
|
};
|
|
4905
|
-
|
|
5302
|
+
fs5.writeFileSync(this._statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
4906
5303
|
}
|
|
4907
5304
|
/** 读取保存的引擎状态 */
|
|
4908
5305
|
static loadState(sessionId, stateDir2) {
|
|
4909
|
-
const p =
|
|
5306
|
+
const p = path7.join(stateDir2, `${sessionId}_state.json`);
|
|
4910
5307
|
try {
|
|
4911
|
-
const raw =
|
|
5308
|
+
const raw = fs5.readFileSync(p, "utf-8");
|
|
4912
5309
|
return JSON.parse(raw);
|
|
4913
5310
|
} catch (e) {
|
|
4914
5311
|
console.error(e);
|
|
@@ -4917,9 +5314,9 @@ var FileLogHook = class {
|
|
|
4917
5314
|
}
|
|
4918
5315
|
/** 读取事件日志 */
|
|
4919
5316
|
static loadEvents(sessionId, stateDir2) {
|
|
4920
|
-
const p =
|
|
5317
|
+
const p = path7.join(stateDir2, `${sessionId}.jsonl`);
|
|
4921
5318
|
try {
|
|
4922
|
-
const raw =
|
|
5319
|
+
const raw = fs5.readFileSync(p, "utf-8");
|
|
4923
5320
|
return raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
4924
5321
|
} catch (e) {
|
|
4925
5322
|
console.error(e);
|
|
@@ -4930,7 +5327,7 @@ var FileLogHook = class {
|
|
|
4930
5327
|
_append(entry) {
|
|
4931
5328
|
entry["ts"] = (/* @__PURE__ */ new Date()).toISOString();
|
|
4932
5329
|
try {
|
|
4933
|
-
|
|
5330
|
+
fs5.appendFileSync(this._logPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
4934
5331
|
} catch (e) {
|
|
4935
5332
|
console.error(e);
|
|
4936
5333
|
}
|
|
@@ -5358,12 +5755,12 @@ var McpManager = class {
|
|
|
5358
5755
|
|
|
5359
5756
|
// src/tools/files.ts
|
|
5360
5757
|
init_esm_shims();
|
|
5361
|
-
import * as
|
|
5362
|
-
import * as
|
|
5758
|
+
import * as fs6 from "fs";
|
|
5759
|
+
import * as path8 from "path";
|
|
5363
5760
|
var DEFAULT_MAX_LINES = 2e3;
|
|
5364
5761
|
var MAX_LINE_CHARS = 500;
|
|
5365
5762
|
function makeReadFile(workspace) {
|
|
5366
|
-
const root =
|
|
5763
|
+
const root = path8.resolve(workspace);
|
|
5367
5764
|
return function read_file(args) {
|
|
5368
5765
|
const filePath = String(args.path ?? "");
|
|
5369
5766
|
const startLine = Math.max(1, Number(args.start_line ?? 1));
|
|
@@ -5373,13 +5770,13 @@ function makeReadFile(workspace) {
|
|
|
5373
5770
|
return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5374
5771
|
}
|
|
5375
5772
|
const target = resolved.real;
|
|
5376
|
-
if (!
|
|
5773
|
+
if (!fs6.existsSync(target) || !fs6.statSync(target).isFile()) {
|
|
5377
5774
|
return { error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
|
|
5378
5775
|
}
|
|
5379
5776
|
const selected = [];
|
|
5380
5777
|
let total = 0;
|
|
5381
5778
|
try {
|
|
5382
|
-
const content =
|
|
5779
|
+
const content = fs6.readFileSync(target, "utf-8");
|
|
5383
5780
|
const lines = content.split("\n");
|
|
5384
5781
|
total = lines.length;
|
|
5385
5782
|
for (let i = startLine - 1; i < Math.min(startLine - 1 + maxLines, lines.length); i++) {
|
|
@@ -5394,7 +5791,7 @@ function makeReadFile(workspace) {
|
|
|
5394
5791
|
}
|
|
5395
5792
|
const end = startLine + selected.length - 1;
|
|
5396
5793
|
const result = {
|
|
5397
|
-
path:
|
|
5794
|
+
path: path8.relative(resolved.root, target),
|
|
5398
5795
|
start_line: startLine,
|
|
5399
5796
|
end_line: end,
|
|
5400
5797
|
total_lines: total,
|
|
@@ -5407,7 +5804,7 @@ function makeReadFile(workspace) {
|
|
|
5407
5804
|
};
|
|
5408
5805
|
}
|
|
5409
5806
|
function makeWriteFile(workspace) {
|
|
5410
|
-
const root =
|
|
5807
|
+
const root = path8.resolve(workspace);
|
|
5411
5808
|
return function write_file(args) {
|
|
5412
5809
|
const filePath = String(args.path ?? "");
|
|
5413
5810
|
const content = String(args.content ?? "");
|
|
@@ -5417,34 +5814,34 @@ function makeWriteFile(workspace) {
|
|
|
5417
5814
|
return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5418
5815
|
}
|
|
5419
5816
|
const target = resolved.real;
|
|
5420
|
-
if (
|
|
5817
|
+
if (fs6.existsSync(target) && fs6.statSync(target).isDirectory()) {
|
|
5421
5818
|
return { error: "\u8DEF\u5F84\u662F\u76EE\u5F55\uFF0C\u4E0D\u662F\u6587\u4EF6" };
|
|
5422
5819
|
}
|
|
5423
|
-
if (
|
|
5820
|
+
if (fs6.existsSync(target) && !overwrite) {
|
|
5424
5821
|
return { error: "\u6587\u4EF6\u5DF2\u5B58\u5728 (overwrite=false)" };
|
|
5425
5822
|
}
|
|
5426
5823
|
try {
|
|
5427
|
-
|
|
5428
|
-
|
|
5824
|
+
fs6.mkdirSync(path8.dirname(target), { recursive: true });
|
|
5825
|
+
fs6.writeFileSync(target, content, "utf-8");
|
|
5429
5826
|
} catch (exc) {
|
|
5430
5827
|
return { error: `\u5199\u5165\u5931\u8D25: ${String(exc)}` };
|
|
5431
5828
|
}
|
|
5432
|
-
return
|
|
5829
|
+
return path8.relative(resolved.root, target);
|
|
5433
5830
|
};
|
|
5434
5831
|
}
|
|
5435
5832
|
function makeListFiles(workspace) {
|
|
5436
|
-
const root =
|
|
5833
|
+
const root = path8.resolve(workspace);
|
|
5437
5834
|
return function list_files(args) {
|
|
5438
5835
|
const dirPath = String(args.path ?? ".");
|
|
5439
5836
|
const maxResults = Math.min(Number(args.max_results ?? 100), 100);
|
|
5440
5837
|
const resolved = resolveWithinRoot(root, dirPath);
|
|
5441
5838
|
if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5442
5839
|
const target = resolved.real;
|
|
5443
|
-
if (!
|
|
5840
|
+
if (!fs6.existsSync(target) || !fs6.statSync(target).isDirectory()) {
|
|
5444
5841
|
return { error: `\u4E0D\u662F\u76EE\u5F55: ${dirPath}` };
|
|
5445
5842
|
}
|
|
5446
5843
|
try {
|
|
5447
|
-
const entries =
|
|
5844
|
+
const entries = fs6.readdirSync(target, { withFileTypes: true });
|
|
5448
5845
|
const results = [];
|
|
5449
5846
|
for (const e of entries) {
|
|
5450
5847
|
if (results.length >= maxResults) break;
|
|
@@ -5453,14 +5850,14 @@ function makeListFiles(workspace) {
|
|
|
5453
5850
|
results.push(e.isDirectory() ? `${e.name}/` : e.name);
|
|
5454
5851
|
}
|
|
5455
5852
|
results.sort();
|
|
5456
|
-
return { path:
|
|
5853
|
+
return { path: path8.relative(resolved.root, target), entries: results.slice(0, maxResults) };
|
|
5457
5854
|
} catch (exc) {
|
|
5458
5855
|
return { error: `\u5217\u51FA\u6587\u4EF6\u5931\u8D25: ${String(exc)}` };
|
|
5459
5856
|
}
|
|
5460
5857
|
};
|
|
5461
5858
|
}
|
|
5462
5859
|
function makeReplaceInFile(workspace) {
|
|
5463
|
-
const root =
|
|
5860
|
+
const root = path8.resolve(workspace);
|
|
5464
5861
|
return function replace_in_file(args) {
|
|
5465
5862
|
const filePath = String(args.path ?? "");
|
|
5466
5863
|
const oldStr = String(args.old ?? "");
|
|
@@ -5470,19 +5867,19 @@ function makeReplaceInFile(workspace) {
|
|
|
5470
5867
|
const resolved = resolveWithinRoot(root, filePath);
|
|
5471
5868
|
if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5472
5869
|
const target = resolved.real;
|
|
5473
|
-
if (!
|
|
5870
|
+
if (!fs6.existsSync(target) || !fs6.statSync(target).isFile()) {
|
|
5474
5871
|
return { error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
|
|
5475
5872
|
}
|
|
5476
5873
|
try {
|
|
5477
|
-
const content =
|
|
5874
|
+
const content = fs6.readFileSync(target, "utf-8");
|
|
5478
5875
|
const count = content.split(oldStr).length - 1;
|
|
5479
5876
|
if (count !== expectedReplacements) {
|
|
5480
5877
|
return { error: `\u671F\u671B ${expectedReplacements} \u5904\u66FF\u6362\uFF0C\u5B9E\u9645\u627E\u5230 ${count} \u5904: ${filePath}` };
|
|
5481
5878
|
}
|
|
5482
5879
|
const updated = content.replaceAll(oldStr, newStr);
|
|
5483
|
-
|
|
5880
|
+
fs6.writeFileSync(target, updated, "utf-8");
|
|
5484
5881
|
return {
|
|
5485
|
-
path:
|
|
5882
|
+
path: path8.relative(resolved.root, target),
|
|
5486
5883
|
replacements: count,
|
|
5487
5884
|
chars_before: content.length,
|
|
5488
5885
|
chars_after: updated.length
|
|
@@ -5583,8 +5980,8 @@ function registerFileTools(registry, workspace) {
|
|
|
5583
5980
|
|
|
5584
5981
|
// src/tools/shell.ts
|
|
5585
5982
|
init_esm_shims();
|
|
5586
|
-
import * as
|
|
5587
|
-
import * as
|
|
5983
|
+
import * as fs8 from "fs";
|
|
5984
|
+
import * as path10 from "path";
|
|
5588
5985
|
|
|
5589
5986
|
// src/tools/execAsync.ts
|
|
5590
5987
|
init_esm_shims();
|
|
@@ -5749,15 +6146,15 @@ function execAsync(file, argv, opts = {}) {
|
|
|
5749
6146
|
|
|
5750
6147
|
// src/tools/shellRuntime.ts
|
|
5751
6148
|
init_esm_shims();
|
|
5752
|
-
import * as
|
|
5753
|
-
import * as
|
|
6149
|
+
import * as fs7 from "fs";
|
|
6150
|
+
import * as path9 from "path";
|
|
5754
6151
|
var WINDOWS_SUFFIXES = [".exe", ".cmd", ".bat", ".com", ""];
|
|
5755
6152
|
function pathApi(platform) {
|
|
5756
|
-
return platform === "win32" ?
|
|
6153
|
+
return platform === "win32" ? path9.win32 : path9.posix;
|
|
5757
6154
|
}
|
|
5758
6155
|
function defaultFileExists(p) {
|
|
5759
6156
|
try {
|
|
5760
|
-
return
|
|
6157
|
+
return fs7.statSync(p).isFile();
|
|
5761
6158
|
} catch {
|
|
5762
6159
|
return false;
|
|
5763
6160
|
}
|
|
@@ -5788,7 +6185,7 @@ function kindFromBasename(base) {
|
|
|
5788
6185
|
return "sh";
|
|
5789
6186
|
}
|
|
5790
6187
|
function busyboxArgs(file, command) {
|
|
5791
|
-
const base = (file ?
|
|
6188
|
+
const base = (file ? path9.win32.basename(file) : "busybox").toLowerCase().replace(/\.(exe|cmd|bat|com)$/, "");
|
|
5792
6189
|
return base === "busybox" ? ["sh", "-c", command] : ["-c", command];
|
|
5793
6190
|
}
|
|
5794
6191
|
function argsFor(kind, command) {
|
|
@@ -6055,7 +6452,7 @@ var MAX_STDERR = 5e3;
|
|
|
6055
6452
|
var MAX_BUFFER = 1024 * 1024 * 5;
|
|
6056
6453
|
var shellSessions = /* @__PURE__ */ new Map();
|
|
6057
6454
|
function sessionFor(workspace) {
|
|
6058
|
-
const key =
|
|
6455
|
+
const key = path10.resolve(workspace);
|
|
6059
6456
|
let s = shellSessions.get(key);
|
|
6060
6457
|
if (!s) {
|
|
6061
6458
|
s = { cwd: key };
|
|
@@ -6112,8 +6509,8 @@ function makeRunShell(workspace, spec, tasks, scope) {
|
|
|
6112
6509
|
let toRun = command;
|
|
6113
6510
|
if (target) {
|
|
6114
6511
|
try {
|
|
6115
|
-
const resolved =
|
|
6116
|
-
if (
|
|
6512
|
+
const resolved = path10.resolve(session.cwd, target);
|
|
6513
|
+
if (fs8.existsSync(resolved) && fs8.statSync(resolved).isDirectory()) {
|
|
6117
6514
|
session.cwd = resolved;
|
|
6118
6515
|
}
|
|
6119
6516
|
} catch {
|
|
@@ -6248,8 +6645,8 @@ init_esm_shims();
|
|
|
6248
6645
|
|
|
6249
6646
|
// src/tools/nodeGrep.ts
|
|
6250
6647
|
init_esm_shims();
|
|
6251
|
-
import * as
|
|
6252
|
-
import * as
|
|
6648
|
+
import * as fs9 from "fs";
|
|
6649
|
+
import * as path11 from "path";
|
|
6253
6650
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
6254
6651
|
".git",
|
|
6255
6652
|
"node_modules",
|
|
@@ -6274,27 +6671,27 @@ function nodeGrep(opts) {
|
|
|
6274
6671
|
} catch (err) {
|
|
6275
6672
|
throw new Error(`\u65E0\u6548\u7684\u6B63\u5219\u8868\u8FBE\u5F0F: ${opts.pattern}\uFF08${String(err)}\uFF09`);
|
|
6276
6673
|
}
|
|
6277
|
-
const target =
|
|
6278
|
-
if (!
|
|
6674
|
+
const target = path11.resolve(opts.workspace, opts.target);
|
|
6675
|
+
if (!fs9.existsSync(target)) {
|
|
6279
6676
|
throw new Error(`\u641C\u7D22\u8DEF\u5F84\u4E0D\u5B58\u5728: ${opts.target}`);
|
|
6280
6677
|
}
|
|
6281
6678
|
const matches = [];
|
|
6282
6679
|
const scanFile = (file) => {
|
|
6283
6680
|
let stat2;
|
|
6284
6681
|
try {
|
|
6285
|
-
stat2 =
|
|
6682
|
+
stat2 = fs9.statSync(file);
|
|
6286
6683
|
} catch {
|
|
6287
6684
|
return false;
|
|
6288
6685
|
}
|
|
6289
6686
|
if (!stat2.isFile() || stat2.size > maxFileBytes) return false;
|
|
6290
6687
|
let buf;
|
|
6291
6688
|
try {
|
|
6292
|
-
buf =
|
|
6689
|
+
buf = fs9.readFileSync(file);
|
|
6293
6690
|
} catch {
|
|
6294
6691
|
return false;
|
|
6295
6692
|
}
|
|
6296
6693
|
if (looksBinary(buf)) return false;
|
|
6297
|
-
const rel =
|
|
6694
|
+
const rel = path11.relative(opts.workspace, file) || path11.basename(file);
|
|
6298
6695
|
const lines = buf.toString("utf-8").split("\n");
|
|
6299
6696
|
for (let i = 0; i < lines.length; i++) {
|
|
6300
6697
|
const raw = lines[i].replace(/\r$/, "");
|
|
@@ -6305,7 +6702,7 @@ function nodeGrep(opts) {
|
|
|
6305
6702
|
}
|
|
6306
6703
|
return false;
|
|
6307
6704
|
};
|
|
6308
|
-
const stat =
|
|
6705
|
+
const stat = fs9.statSync(target);
|
|
6309
6706
|
if (stat.isFile()) {
|
|
6310
6707
|
scanFile(target);
|
|
6311
6708
|
return matches;
|
|
@@ -6315,12 +6712,12 @@ function nodeGrep(opts) {
|
|
|
6315
6712
|
const dir = stack.pop();
|
|
6316
6713
|
let entries;
|
|
6317
6714
|
try {
|
|
6318
|
-
entries =
|
|
6715
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
6319
6716
|
} catch {
|
|
6320
6717
|
continue;
|
|
6321
6718
|
}
|
|
6322
6719
|
for (const entry of entries) {
|
|
6323
|
-
const full =
|
|
6720
|
+
const full = path11.join(dir, entry.name);
|
|
6324
6721
|
if (entry.isDirectory()) {
|
|
6325
6722
|
if (!SKIP_DIRS.has(entry.name)) stack.push(full);
|
|
6326
6723
|
} else if (entry.isFile()) {
|
|
@@ -6333,22 +6730,22 @@ function nodeGrep(opts) {
|
|
|
6333
6730
|
|
|
6334
6731
|
// src/tools/ripgrep.ts
|
|
6335
6732
|
init_esm_shims();
|
|
6336
|
-
import * as
|
|
6337
|
-
import * as
|
|
6338
|
-
import * as
|
|
6733
|
+
import * as fs10 from "fs";
|
|
6734
|
+
import * as os2 from "os";
|
|
6735
|
+
import * as path12 from "path";
|
|
6339
6736
|
import { createRequire } from "module";
|
|
6340
6737
|
var cached2;
|
|
6341
6738
|
function extractBundledRipgrep() {
|
|
6342
6739
|
try {
|
|
6343
|
-
const req2 = typeof __require === "function" ? __require : createRequire(
|
|
6740
|
+
const req2 = typeof __require === "function" ? __require : createRequire(path12.join(process.cwd(), "__zqagent__.js"));
|
|
6344
6741
|
const sea = req2("node:sea");
|
|
6345
6742
|
if (!sea?.isSea?.() || typeof sea.getAsset !== "function") return null;
|
|
6346
6743
|
const asset = sea.getAsset("rg");
|
|
6347
6744
|
if (!asset) return null;
|
|
6348
|
-
const dir =
|
|
6349
|
-
const file =
|
|
6350
|
-
|
|
6351
|
-
|
|
6745
|
+
const dir = fs10.mkdtempSync(path12.join(os2.tmpdir(), "zqagent-rg-"));
|
|
6746
|
+
const file = path12.join(dir, process.platform === "win32" ? "rg.exe" : "rg");
|
|
6747
|
+
fs10.writeFileSync(file, Buffer.from(asset));
|
|
6748
|
+
fs10.chmodSync(file, 493);
|
|
6352
6749
|
return file;
|
|
6353
6750
|
} catch {
|
|
6354
6751
|
return null;
|
|
@@ -6357,7 +6754,7 @@ function extractBundledRipgrep() {
|
|
|
6357
6754
|
function resolveRipgrep() {
|
|
6358
6755
|
if (cached2 !== void 0) return cached2;
|
|
6359
6756
|
const explicit = (process.env.ZQAGENT_RG ?? "").trim();
|
|
6360
|
-
if (explicit &&
|
|
6757
|
+
if (explicit && fs10.existsSync(explicit)) {
|
|
6361
6758
|
cached2 = explicit;
|
|
6362
6759
|
return cached2;
|
|
6363
6760
|
}
|
|
@@ -6371,7 +6768,7 @@ function resolveRipgrep() {
|
|
|
6371
6768
|
env: process.env,
|
|
6372
6769
|
fileExists: (p) => {
|
|
6373
6770
|
try {
|
|
6374
|
-
return
|
|
6771
|
+
return fs10.statSync(p).isFile();
|
|
6375
6772
|
} catch {
|
|
6376
6773
|
return false;
|
|
6377
6774
|
}
|
|
@@ -6920,8 +7317,8 @@ function registerWebTools(registry) {
|
|
|
6920
7317
|
|
|
6921
7318
|
// src/tools/git.ts
|
|
6922
7319
|
init_esm_shims();
|
|
6923
|
-
import * as
|
|
6924
|
-
import * as
|
|
7320
|
+
import * as fs11 from "fs";
|
|
7321
|
+
import * as path13 from "path";
|
|
6925
7322
|
async function runGit(repoDir, args, timeout = 15e3) {
|
|
6926
7323
|
const res = await execAsync("git", ["-c", "core.quotepath=false", ...args], {
|
|
6927
7324
|
cwd: repoDir,
|
|
@@ -6938,9 +7335,9 @@ async function runGit(repoDir, args, timeout = 15e3) {
|
|
|
6938
7335
|
async function resolveRepoRoot(workspace, pathArg) {
|
|
6939
7336
|
let dir = workspace;
|
|
6940
7337
|
if (pathArg) {
|
|
6941
|
-
const abs =
|
|
7338
|
+
const abs = path13.resolve(workspace, pathArg);
|
|
6942
7339
|
try {
|
|
6943
|
-
dir =
|
|
7340
|
+
dir = fs11.statSync(abs).isDirectory() ? abs : path13.dirname(abs);
|
|
6944
7341
|
} catch {
|
|
6945
7342
|
dir = abs;
|
|
6946
7343
|
}
|
|
@@ -6953,24 +7350,24 @@ function repoDirArg(workspace, raw) {
|
|
|
6953
7350
|
if (typeof raw !== "string") return void 0;
|
|
6954
7351
|
const v = raw.trim();
|
|
6955
7352
|
if (!v) return void 0;
|
|
6956
|
-
const base =
|
|
6957
|
-
const abs =
|
|
6958
|
-
const rel =
|
|
6959
|
-
if (rel.startsWith("..") ||
|
|
7353
|
+
const base = path13.resolve(workspace);
|
|
7354
|
+
const abs = path13.resolve(base, v);
|
|
7355
|
+
const rel = path13.relative(base, abs);
|
|
7356
|
+
if (rel.startsWith("..") || path13.isAbsolute(rel)) return void 0;
|
|
6960
7357
|
return abs;
|
|
6961
7358
|
}
|
|
6962
7359
|
function toPathspec(repoRoot, workspace, pathArg) {
|
|
6963
7360
|
if (!pathArg) return null;
|
|
6964
|
-
const abs =
|
|
6965
|
-
let rel =
|
|
7361
|
+
const abs = path13.resolve(workspace, pathArg);
|
|
7362
|
+
let rel = path13.relative(repoRoot, abs);
|
|
6966
7363
|
if (rel === "") return ".";
|
|
6967
|
-
if (rel.startsWith("..") ||
|
|
7364
|
+
if (rel.startsWith("..") || path13.isAbsolute(rel)) {
|
|
6968
7365
|
try {
|
|
6969
|
-
const realRepo =
|
|
6970
|
-
const realAbs =
|
|
6971
|
-
const rel2 =
|
|
7366
|
+
const realRepo = fs11.realpathSync(repoRoot);
|
|
7367
|
+
const realAbs = fs11.realpathSync(abs);
|
|
7368
|
+
const rel2 = path13.relative(realRepo, realAbs);
|
|
6972
7369
|
if (rel2 === "") return ".";
|
|
6973
|
-
if (!rel2.startsWith("..") && !
|
|
7370
|
+
if (!rel2.startsWith("..") && !path13.isAbsolute(rel2)) return rel2;
|
|
6974
7371
|
} catch {
|
|
6975
7372
|
}
|
|
6976
7373
|
return null;
|
|
@@ -7396,18 +7793,18 @@ var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".s
|
|
|
7396
7793
|
var SCAN_DEFAULT_DEPTH = 3;
|
|
7397
7794
|
var SCAN_MAX_DIRS = 2e3;
|
|
7398
7795
|
function readRepoHead(repoDir) {
|
|
7399
|
-
const dotGit =
|
|
7796
|
+
const dotGit = path13.join(repoDir, ".git");
|
|
7400
7797
|
try {
|
|
7401
7798
|
let headFile;
|
|
7402
|
-
if (
|
|
7403
|
-
headFile =
|
|
7799
|
+
if (fs11.statSync(dotGit).isDirectory()) {
|
|
7800
|
+
headFile = path13.join(dotGit, "HEAD");
|
|
7404
7801
|
} else {
|
|
7405
|
-
const raw =
|
|
7802
|
+
const raw = fs11.readFileSync(dotGit, "utf-8");
|
|
7406
7803
|
const m = /^gitdir:\s*(.+)$/m.exec(raw);
|
|
7407
7804
|
if (!m) return {};
|
|
7408
|
-
headFile =
|
|
7805
|
+
headFile = path13.join(path13.resolve(repoDir, m[1].trim()), "HEAD");
|
|
7409
7806
|
}
|
|
7410
|
-
const head =
|
|
7807
|
+
const head = fs11.readFileSync(headFile, "utf-8").trim();
|
|
7411
7808
|
const prefix = "ref: refs/heads/";
|
|
7412
7809
|
if (head.startsWith(prefix)) return { branch: head.slice(prefix.length), detached: false };
|
|
7413
7810
|
if (/^[0-9a-f]{7,40}$/i.test(head)) return { branch: head.slice(0, 7), detached: true };
|
|
@@ -7416,7 +7813,7 @@ function readRepoHead(repoDir) {
|
|
|
7416
7813
|
return {};
|
|
7417
7814
|
}
|
|
7418
7815
|
function scanGitRepos(workspace, depth = SCAN_DEFAULT_DEPTH) {
|
|
7419
|
-
const root =
|
|
7816
|
+
const root = path13.resolve(workspace);
|
|
7420
7817
|
const maxDepth = Number.isFinite(depth) ? Math.min(Math.max(Math.floor(depth), 0), 6) : SCAN_DEFAULT_DEPTH;
|
|
7421
7818
|
const repos = [];
|
|
7422
7819
|
const stack = [{ dir: root, depth: 0 }];
|
|
@@ -7429,24 +7826,24 @@ function scanGitRepos(workspace, depth = SCAN_DEFAULT_DEPTH) {
|
|
|
7429
7826
|
truncated = true;
|
|
7430
7827
|
break;
|
|
7431
7828
|
}
|
|
7432
|
-
const rel =
|
|
7433
|
-
if (
|
|
7829
|
+
const rel = path13.relative(root, dir).split(path13.sep).join("/");
|
|
7830
|
+
if (fs11.existsSync(path13.join(dir, ".git"))) {
|
|
7434
7831
|
repos.push({
|
|
7435
7832
|
path: rel,
|
|
7436
|
-
name: rel ?
|
|
7833
|
+
name: rel ? path13.basename(dir) : path13.basename(root),
|
|
7437
7834
|
...readRepoHead(dir)
|
|
7438
7835
|
});
|
|
7439
7836
|
}
|
|
7440
7837
|
if (d >= maxDepth) continue;
|
|
7441
7838
|
let entries;
|
|
7442
7839
|
try {
|
|
7443
|
-
entries =
|
|
7840
|
+
entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
7444
7841
|
} catch {
|
|
7445
7842
|
continue;
|
|
7446
7843
|
}
|
|
7447
7844
|
for (const e of entries) {
|
|
7448
7845
|
if (!e.isDirectory() || SCAN_SKIP_DIRS.has(e.name)) continue;
|
|
7449
|
-
stack.push({ dir:
|
|
7846
|
+
stack.push({ dir: path13.join(dir, e.name), depth: d + 1 });
|
|
7450
7847
|
}
|
|
7451
7848
|
}
|
|
7452
7849
|
repos.sort(
|
|
@@ -7456,9 +7853,9 @@ function scanGitRepos(workspace, depth = SCAN_DEFAULT_DEPTH) {
|
|
|
7456
7853
|
}
|
|
7457
7854
|
function makeGitRepos(workspace) {
|
|
7458
7855
|
return async function git_repos(args) {
|
|
7459
|
-
const root =
|
|
7856
|
+
const root = path13.resolve(workspace);
|
|
7460
7857
|
try {
|
|
7461
|
-
if (!
|
|
7858
|
+
if (!fs11.statSync(root).isDirectory()) return { root, repos: [], error: "\u5DE5\u4F5C\u533A\u4E0D\u662F\u76EE\u5F55" };
|
|
7462
7859
|
} catch {
|
|
7463
7860
|
return { root, repos: [], error: "\u5DE5\u4F5C\u533A\u76EE\u5F55\u4E0D\u5B58\u5728" };
|
|
7464
7861
|
}
|
|
@@ -7470,10 +7867,10 @@ function makeGitRepos(workspace) {
|
|
|
7470
7867
|
|
|
7471
7868
|
// src/tools/diff.ts
|
|
7472
7869
|
init_esm_shims();
|
|
7473
|
-
import * as
|
|
7474
|
-
import * as
|
|
7870
|
+
import * as fs12 from "fs";
|
|
7871
|
+
import * as path14 from "path";
|
|
7475
7872
|
function makeApplyPatch(workspace) {
|
|
7476
|
-
const root =
|
|
7873
|
+
const root = path14.resolve(workspace);
|
|
7477
7874
|
return function apply_patch(args) {
|
|
7478
7875
|
const patch = String(args.patch ?? "");
|
|
7479
7876
|
if (!patch.trim()) return { error: "\u8865\u4E01\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
@@ -7512,12 +7909,12 @@ function makeApplyPatch(workspace) {
|
|
|
7512
7909
|
contentLines.push(current.slice(1));
|
|
7513
7910
|
i++;
|
|
7514
7911
|
}
|
|
7515
|
-
if (
|
|
7912
|
+
if (fs12.existsSync(target)) {
|
|
7516
7913
|
return { error: `\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}` };
|
|
7517
7914
|
}
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
const rel =
|
|
7915
|
+
fs12.mkdirSync(path14.dirname(target), { recursive: true });
|
|
7916
|
+
fs12.writeFileSync(target, contentLines.join("\n") + "\n", "utf-8");
|
|
7917
|
+
const rel = path14.relative(resolved.root, target);
|
|
7521
7918
|
changedFiles.push(rel);
|
|
7522
7919
|
addedFiles.push(rel);
|
|
7523
7920
|
fileCount++;
|
|
@@ -7530,11 +7927,11 @@ function makeApplyPatch(workspace) {
|
|
|
7530
7927
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${filePath}` };
|
|
7531
7928
|
}
|
|
7532
7929
|
const target = entry.real;
|
|
7533
|
-
if (!
|
|
7930
|
+
if (!fs12.existsSync(target)) {
|
|
7534
7931
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
7535
7932
|
}
|
|
7536
|
-
|
|
7537
|
-
deletedFiles.push(
|
|
7933
|
+
fs12.unlinkSync(target);
|
|
7934
|
+
deletedFiles.push(path14.relative(entry.root, target));
|
|
7538
7935
|
fileCount++;
|
|
7539
7936
|
i++;
|
|
7540
7937
|
continue;
|
|
@@ -7570,10 +7967,10 @@ function makeApplyPatch(workspace) {
|
|
|
7570
7967
|
if (!chunkLines.length) {
|
|
7571
7968
|
return { error: `\u8865\u4E01\u66F4\u65B0\u6CA1\u6709 hunks: ${filePath}` };
|
|
7572
7969
|
}
|
|
7573
|
-
if (!
|
|
7970
|
+
if (!fs12.existsSync(target)) {
|
|
7574
7971
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
7575
7972
|
}
|
|
7576
|
-
const oldLines =
|
|
7973
|
+
const oldLines = fs12.readFileSync(target, "utf-8").split("\n");
|
|
7577
7974
|
const newLines = applyPatchHunks(oldLines, chunkLines, filePath);
|
|
7578
7975
|
let finalResolved = resolved;
|
|
7579
7976
|
if (moveTo) {
|
|
@@ -7584,20 +7981,20 @@ function makeApplyPatch(workspace) {
|
|
|
7584
7981
|
finalResolved = moved;
|
|
7585
7982
|
}
|
|
7586
7983
|
const finalTarget = finalResolved.real;
|
|
7587
|
-
if (moveTo &&
|
|
7984
|
+
if (moveTo && fs12.existsSync(finalTarget) && finalTarget !== target) {
|
|
7588
7985
|
return { error: `\u6587\u4EF6\u5DF2\u5B58\u5728: ${moveTo}` };
|
|
7589
7986
|
}
|
|
7590
|
-
|
|
7591
|
-
|
|
7987
|
+
fs12.mkdirSync(path14.dirname(finalTarget), { recursive: true });
|
|
7988
|
+
fs12.writeFileSync(finalTarget, newLines.join("\n"), "utf-8");
|
|
7592
7989
|
if (moveTo && finalTarget !== target) {
|
|
7593
7990
|
const entry = resolveEntryWithinRoot(root, filePath);
|
|
7594
7991
|
if (!entry) {
|
|
7595
7992
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${filePath}` };
|
|
7596
7993
|
}
|
|
7597
|
-
|
|
7598
|
-
deletedFiles.push(
|
|
7994
|
+
fs12.unlinkSync(entry.real);
|
|
7995
|
+
deletedFiles.push(path14.relative(resolved.root, target));
|
|
7599
7996
|
}
|
|
7600
|
-
changedFiles.push(
|
|
7997
|
+
changedFiles.push(path14.relative(finalResolved.root, finalTarget));
|
|
7601
7998
|
fileCount++;
|
|
7602
7999
|
continue;
|
|
7603
8000
|
}
|
|
@@ -7609,18 +8006,18 @@ function makeApplyPatch(workspace) {
|
|
|
7609
8006
|
return { changed_files: changedFiles, added_files: addedFiles, deleted_files: deletedFiles, file_count: fileCount };
|
|
7610
8007
|
};
|
|
7611
8008
|
}
|
|
7612
|
-
function applyPatchHunks(oldLines, chunks,
|
|
8009
|
+
function applyPatchHunks(oldLines, chunks, path44) {
|
|
7613
8010
|
let newLines = [...oldLines];
|
|
7614
8011
|
let cursor = 0;
|
|
7615
8012
|
for (const chunk of chunks) {
|
|
7616
8013
|
const oldBlock = chunk.filter((l) => l[0] === " " || l[0] === "-").map((l) => l.slice(1));
|
|
7617
8014
|
const newBlock = chunk.filter((l) => l[0] === " " || l[0] === "+").map((l) => l.slice(1));
|
|
7618
8015
|
if (!oldBlock.length) {
|
|
7619
|
-
throw new Error(`\u8865\u4E01 hunk \u5FC5\u987B\u5305\u542B\u4E0A\u4E0B\u6587\u6216\u5220\u9664\u884C: ${
|
|
8016
|
+
throw new Error(`\u8865\u4E01 hunk \u5FC5\u987B\u5305\u542B\u4E0A\u4E0B\u6587\u6216\u5220\u9664\u884C: ${path44}`);
|
|
7620
8017
|
}
|
|
7621
8018
|
const matchStart = findBlockMatch(newLines, oldBlock, cursor);
|
|
7622
8019
|
if (matchStart === -1) {
|
|
7623
|
-
throw new Error(`\u8865\u4E01\u4E0A\u4E0B\u6587\u4E0D\u5339\u914D: ${
|
|
8020
|
+
throw new Error(`\u8865\u4E01\u4E0A\u4E0B\u6587\u4E0D\u5339\u914D: ${path44}`);
|
|
7624
8021
|
}
|
|
7625
8022
|
const matchEnd = matchStart + oldBlock.length;
|
|
7626
8023
|
newLines.splice(matchStart, oldBlock.length, ...newBlock);
|
|
@@ -7637,7 +8034,7 @@ function findBlockMatch(lines, block, start) {
|
|
|
7637
8034
|
return -1;
|
|
7638
8035
|
}
|
|
7639
8036
|
function makeApplyUnifiedDiff(workspace) {
|
|
7640
|
-
const root =
|
|
8037
|
+
const root = path14.resolve(workspace);
|
|
7641
8038
|
return function apply_unified_diff(args) {
|
|
7642
8039
|
const diff = String(args.diff ?? "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
7643
8040
|
if (!diff.trim()) return { error: "diff \u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
@@ -7656,26 +8053,26 @@ function makeApplyUnifiedDiff(workspace) {
|
|
|
7656
8053
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${targetPath}` };
|
|
7657
8054
|
}
|
|
7658
8055
|
const target = resolved.real;
|
|
7659
|
-
const oldLines = oldIsDevNull ? [] :
|
|
8056
|
+
const oldLines = oldIsDevNull ? [] : fs12.existsSync(target) ? fs12.readFileSync(target, "utf-8").split("\n") : [];
|
|
7660
8057
|
const newLines = applyDiffHunks(oldLines, patch.hunks, targetPath);
|
|
7661
8058
|
hunkCount += patch.hunks.length;
|
|
7662
8059
|
if (newIsDevNull) {
|
|
7663
|
-
if (
|
|
7664
|
-
if (!
|
|
8060
|
+
if (fs12.existsSync(target)) {
|
|
8061
|
+
if (!fs12.statSync(target).isFile()) {
|
|
7665
8062
|
return { error: `\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${targetPath}` };
|
|
7666
8063
|
}
|
|
7667
8064
|
const entry = resolveEntryWithinRoot(root, targetPath);
|
|
7668
8065
|
if (!entry) {
|
|
7669
8066
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${targetPath}` };
|
|
7670
8067
|
}
|
|
7671
|
-
|
|
8068
|
+
fs12.unlinkSync(entry.real);
|
|
7672
8069
|
}
|
|
7673
|
-
deletedFiles.push(
|
|
8070
|
+
deletedFiles.push(path14.relative(resolved.root, target));
|
|
7674
8071
|
continue;
|
|
7675
8072
|
}
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
const rel =
|
|
8073
|
+
fs12.mkdirSync(path14.dirname(target), { recursive: true });
|
|
8074
|
+
fs12.writeFileSync(target, newLines.join("\n"), "utf-8");
|
|
8075
|
+
const rel = path14.relative(resolved.root, target);
|
|
7679
8076
|
changedFiles.push(rel);
|
|
7680
8077
|
if (oldIsDevNull) addedFiles.push(rel);
|
|
7681
8078
|
}
|
|
@@ -7939,7 +8336,8 @@ function buildExplorerEngine(opts) {
|
|
|
7939
8336
|
model: opts.model,
|
|
7940
8337
|
instructions: EXPLORER_INSTRUCTIONS,
|
|
7941
8338
|
maxIterations: parseMaxIterations(process.env.EXPLORE_MAX_ITERATIONS, 30),
|
|
7942
|
-
hook: opts.hook
|
|
8339
|
+
hook: opts.hook,
|
|
8340
|
+
toolResultDir: opts.toolResultDir
|
|
7943
8341
|
});
|
|
7944
8342
|
}
|
|
7945
8343
|
function makeExploreTool(opts) {
|
|
@@ -7951,7 +8349,8 @@ function makeExploreTool(opts) {
|
|
|
7951
8349
|
provider: opts.provider,
|
|
7952
8350
|
model: opts.model,
|
|
7953
8351
|
readonlyRegistrars: opts.readonlyRegistrars,
|
|
7954
|
-
hook: opts.hookFactory?.()
|
|
8352
|
+
hook: opts.hookFactory?.(),
|
|
8353
|
+
toolResultDir: opts.toolResultDir
|
|
7955
8354
|
});
|
|
7956
8355
|
let report = "";
|
|
7957
8356
|
let status = "unknown";
|
|
@@ -8012,14 +8411,14 @@ function registerExploreTool(registry, opts) {
|
|
|
8012
8411
|
|
|
8013
8412
|
// src/tools/media.ts
|
|
8014
8413
|
init_esm_shims();
|
|
8015
|
-
import * as
|
|
8016
|
-
import * as
|
|
8414
|
+
import * as fs14 from "fs";
|
|
8415
|
+
import * as path16 from "path";
|
|
8017
8416
|
|
|
8018
8417
|
// src/tools/ffmpegRuntime.ts
|
|
8019
8418
|
init_esm_shims();
|
|
8020
|
-
import * as
|
|
8021
|
-
import * as
|
|
8022
|
-
import * as
|
|
8419
|
+
import * as fs13 from "fs";
|
|
8420
|
+
import * as os3 from "os";
|
|
8421
|
+
import * as path15 from "path";
|
|
8023
8422
|
import { createRequire as createRequire2 } from "module";
|
|
8024
8423
|
import { createHash } from "crypto";
|
|
8025
8424
|
import { pipeline } from "stream/promises";
|
|
@@ -8042,29 +8441,29 @@ if (PROXY_ENV2) {
|
|
|
8042
8441
|
}
|
|
8043
8442
|
var DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
8044
8443
|
function agentBinDir(workspace) {
|
|
8045
|
-
return
|
|
8444
|
+
return path15.join(path15.resolve(workspace), ".agent", "bin");
|
|
8046
8445
|
}
|
|
8047
8446
|
function exeName(name, platform) {
|
|
8048
8447
|
return platform === "win32" ? `${name}.exe` : name;
|
|
8049
8448
|
}
|
|
8050
8449
|
function defaultFileExists2(p) {
|
|
8051
8450
|
try {
|
|
8052
|
-
return
|
|
8451
|
+
return fs13.statSync(p).isFile();
|
|
8053
8452
|
} catch {
|
|
8054
8453
|
return false;
|
|
8055
8454
|
}
|
|
8056
8455
|
}
|
|
8057
8456
|
function extractBundled(name, platform) {
|
|
8058
8457
|
try {
|
|
8059
|
-
const req2 = typeof __require === "function" ? __require : createRequire2(
|
|
8458
|
+
const req2 = typeof __require === "function" ? __require : createRequire2(path15.join(process.cwd(), "__zqagent__.js"));
|
|
8060
8459
|
const sea = req2("node:sea");
|
|
8061
8460
|
if (!sea?.isSea?.() || typeof sea.getAsset !== "function") return null;
|
|
8062
8461
|
const asset = sea.getAsset(name);
|
|
8063
8462
|
if (!asset) return null;
|
|
8064
|
-
const dir =
|
|
8065
|
-
const file =
|
|
8066
|
-
|
|
8067
|
-
|
|
8463
|
+
const dir = fs13.mkdtempSync(path15.join(os3.tmpdir(), `zqagent-${name}-`));
|
|
8464
|
+
const file = path15.join(dir, exeName(name, platform));
|
|
8465
|
+
fs13.writeFileSync(file, Buffer.from(asset));
|
|
8466
|
+
fs13.chmodSync(file, 493);
|
|
8068
8467
|
return file;
|
|
8069
8468
|
} catch {
|
|
8070
8469
|
return null;
|
|
@@ -8074,7 +8473,7 @@ function resolveOne(name, workspace, o) {
|
|
|
8074
8473
|
const envKey = name === "ffmpeg" ? ENV_FFMPEG : ENV_FFPROBE;
|
|
8075
8474
|
const explicit = (o.env[envKey] ?? "").trim();
|
|
8076
8475
|
if (explicit && o.fileExists(explicit)) return { path: explicit, source: "env" };
|
|
8077
|
-
const local =
|
|
8476
|
+
const local = path15.join(agentBinDir(workspace), exeName(name, o.platform));
|
|
8078
8477
|
if (o.fileExists(local)) return { path: local, source: "agent-bin" };
|
|
8079
8478
|
const bundled = extractBundled(name, o.platform);
|
|
8080
8479
|
if (bundled) return { path: bundled, source: "bundled" };
|
|
@@ -8088,7 +8487,7 @@ function resolveOne(name, workspace, o) {
|
|
|
8088
8487
|
var cache = /* @__PURE__ */ new Map();
|
|
8089
8488
|
function resolveMedia(workspace, opts = {}) {
|
|
8090
8489
|
const platform = opts.platform ?? process.platform;
|
|
8091
|
-
const key = `${platform}:${
|
|
8490
|
+
const key = `${platform}:${path15.resolve(workspace)}`;
|
|
8092
8491
|
const hit = cache.get(key);
|
|
8093
8492
|
if (hit) return hit;
|
|
8094
8493
|
const ctx = {
|
|
@@ -8194,7 +8593,7 @@ function parseChecksumFile(text, filename) {
|
|
|
8194
8593
|
if (!m) continue;
|
|
8195
8594
|
const hash = m[1].toLowerCase();
|
|
8196
8595
|
const name = m[2]?.trim() ?? "";
|
|
8197
|
-
if (!name || name === filename ||
|
|
8596
|
+
if (!name || name === filename || path15.basename(name) === filename) return hash;
|
|
8198
8597
|
}
|
|
8199
8598
|
const single = lines[0]?.trim();
|
|
8200
8599
|
if (lines.length === 1 && single && /^[0-9a-f]{32,128}$/i.test(single)) {
|
|
@@ -8204,7 +8603,7 @@ function parseChecksumFile(text, filename) {
|
|
|
8204
8603
|
}
|
|
8205
8604
|
async function computeChecksum(file, algo) {
|
|
8206
8605
|
const hash = createHash(algo);
|
|
8207
|
-
await pipeline(
|
|
8606
|
+
await pipeline(fs13.createReadStream(file), hash);
|
|
8208
8607
|
return hash.digest("hex");
|
|
8209
8608
|
}
|
|
8210
8609
|
function fetchInit() {
|
|
@@ -8219,7 +8618,7 @@ async function downloadToFile(url, dest) {
|
|
|
8219
8618
|
const resp = await nodeFetch3(url, fetchInit());
|
|
8220
8619
|
if (!resp.ok) throw new Error(`HTTP ${resp.status} ${resp.statusText}`);
|
|
8221
8620
|
if (!resp.body) throw new Error("\u54CD\u5E94\u6CA1\u6709\u5185\u5BB9");
|
|
8222
|
-
await pipeline(resp.body,
|
|
8621
|
+
await pipeline(resp.body, fs13.createWriteStream(dest));
|
|
8223
8622
|
}
|
|
8224
8623
|
async function resolveComponentUrl(c) {
|
|
8225
8624
|
if (c.url) return c.url;
|
|
@@ -8236,12 +8635,12 @@ function findBinaryInTree(dir, names) {
|
|
|
8236
8635
|
const cur = stack.pop();
|
|
8237
8636
|
let entries;
|
|
8238
8637
|
try {
|
|
8239
|
-
entries =
|
|
8638
|
+
entries = fs13.readdirSync(cur, { withFileTypes: true });
|
|
8240
8639
|
} catch {
|
|
8241
8640
|
continue;
|
|
8242
8641
|
}
|
|
8243
8642
|
for (const e of entries) {
|
|
8244
|
-
const full =
|
|
8643
|
+
const full = path15.join(cur, e.name);
|
|
8245
8644
|
if (e.isDirectory()) {
|
|
8246
8645
|
stack.push(full);
|
|
8247
8646
|
} else if (names.includes(e.name)) {
|
|
@@ -8252,7 +8651,7 @@ function findBinaryInTree(dir, names) {
|
|
|
8252
8651
|
return null;
|
|
8253
8652
|
}
|
|
8254
8653
|
async function extractArchive(archive, dest, kind, platform) {
|
|
8255
|
-
|
|
8654
|
+
fs13.mkdirSync(dest, { recursive: true });
|
|
8256
8655
|
let res;
|
|
8257
8656
|
if (kind === "tar.xz") {
|
|
8258
8657
|
res = await execAsync("tar", ["-xJf", archive, "-C", dest]);
|
|
@@ -8288,7 +8687,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8288
8687
|
error: `\u6682\u4E0D\u652F\u6301\u81EA\u52A8\u4E0B\u8F7D\u7684\u5E73\u53F0: ${platform}/${arch}`
|
|
8289
8688
|
};
|
|
8290
8689
|
}
|
|
8291
|
-
const tmp =
|
|
8690
|
+
const tmp = fs13.mkdtempSync(path15.join(os3.tmpdir(), "zqagent-ffmpeg-"));
|
|
8292
8691
|
const binDir = agentBinDir(workspace);
|
|
8293
8692
|
const installed = {};
|
|
8294
8693
|
try {
|
|
@@ -8302,7 +8701,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8302
8701
|
}
|
|
8303
8702
|
let archive = fetched.get(url);
|
|
8304
8703
|
if (!archive) {
|
|
8305
|
-
const archivePath =
|
|
8704
|
+
const archivePath = path15.join(tmp, path15.basename(new URL(url).pathname) || `${c.name}.${c.archive}`);
|
|
8306
8705
|
log(`\u4E0B\u8F7D ${url}`);
|
|
8307
8706
|
try {
|
|
8308
8707
|
await downloadToFile(url, archivePath);
|
|
@@ -8312,7 +8711,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8312
8711
|
if (c.checksumUrl) {
|
|
8313
8712
|
const algo = c.checksumAlgo ?? "sha256";
|
|
8314
8713
|
try {
|
|
8315
|
-
const expected = parseChecksumFile(await fetchText(c.checksumUrl),
|
|
8714
|
+
const expected = parseChecksumFile(await fetchText(c.checksumUrl), path15.basename(archivePath));
|
|
8316
8715
|
if (!expected) {
|
|
8317
8716
|
notes.push(`\u672A\u80FD\u4ECE ${c.checksumUrl} \u89E3\u6790\u51FA\u6821\u9A8C\u548C\uFF0C\u5DF2\u8DF3\u8FC7\u6821\u9A8C`);
|
|
8318
8717
|
} else {
|
|
@@ -8329,24 +8728,24 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8329
8728
|
notes.push(`\u6821\u9A8C\u548C\u83B7\u53D6\u5931\u8D25 (${c.checksumUrl}): ${String(e instanceof Error ? e.message : e)}`);
|
|
8330
8729
|
}
|
|
8331
8730
|
} else {
|
|
8332
|
-
notes.push(`${
|
|
8731
|
+
notes.push(`${path15.basename(archivePath)} \u672A\u63D0\u4F9B\u6821\u9A8C\u548C\uFF08\u4EC5 HTTPS \u4F20\u8F93\u4FDD\u62A4\uFF09`);
|
|
8333
8732
|
}
|
|
8334
8733
|
archive = { file: archivePath, kind: c.archive };
|
|
8335
8734
|
fetched.set(url, archive);
|
|
8336
8735
|
}
|
|
8337
|
-
const extractDir =
|
|
8736
|
+
const extractDir = fs13.mkdtempSync(path15.join(tmp, "x-"));
|
|
8338
8737
|
const err = await extractArchive(archive.file, extractDir, archive.kind, platform);
|
|
8339
8738
|
if (err) return { ok: false, error: err };
|
|
8340
8739
|
const found = findBinaryInTree(extractDir, [exeName(c.name, platform), c.name]);
|
|
8341
8740
|
if (!found) {
|
|
8342
|
-
return { ok: false, error: `\u5F52\u6863\u5185\u672A\u627E\u5230 ${c.name}: ${
|
|
8741
|
+
return { ok: false, error: `\u5F52\u6863\u5185\u672A\u627E\u5230 ${c.name}: ${path15.basename(archive.file)}` };
|
|
8343
8742
|
}
|
|
8344
|
-
|
|
8345
|
-
const target =
|
|
8743
|
+
fs13.mkdirSync(binDir, { recursive: true });
|
|
8744
|
+
const target = path15.join(binDir, exeName(c.name, platform));
|
|
8346
8745
|
const staging = `${target}.tmp-${process.pid}`;
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8746
|
+
fs13.copyFileSync(found, staging);
|
|
8747
|
+
fs13.chmodSync(staging, 493);
|
|
8748
|
+
fs13.renameSync(staging, target);
|
|
8350
8749
|
installed[c.name] = target;
|
|
8351
8750
|
}
|
|
8352
8751
|
resetFfmpegCache();
|
|
@@ -8355,7 +8754,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
8355
8754
|
} catch (e) {
|
|
8356
8755
|
return { ok: false, error: `\u5B89\u88C5\u5931\u8D25: ${String(e instanceof Error ? e.message : e)}` };
|
|
8357
8756
|
} finally {
|
|
8358
|
-
|
|
8757
|
+
fs13.rmSync(tmp, { recursive: true, force: true });
|
|
8359
8758
|
}
|
|
8360
8759
|
}
|
|
8361
8760
|
function canAutoDownload(platform, arch) {
|
|
@@ -8620,13 +9019,13 @@ function resolveTarget(workspace, value) {
|
|
|
8620
9019
|
return { abs: resolved.real, root: resolved.root };
|
|
8621
9020
|
}
|
|
8622
9021
|
function relOut(workspace, abs) {
|
|
8623
|
-
return
|
|
9022
|
+
return path16.relative(workspace, abs) || abs;
|
|
8624
9023
|
}
|
|
8625
9024
|
function registerMediaTool(registry, workspace, opts = {}) {
|
|
8626
9025
|
const platform = opts.platform ?? process.platform;
|
|
8627
9026
|
const arch = process.arch;
|
|
8628
9027
|
const media = async (args) => {
|
|
8629
|
-
const root =
|
|
9028
|
+
const root = path16.resolve(workspace);
|
|
8630
9029
|
const op = str(args["op"]);
|
|
8631
9030
|
if (!op) {
|
|
8632
9031
|
return { error: `\u7F3A\u5C11\u53C2\u6570 op\u3002\u53EF\u9009\u503C\uFF1A${OPS.join(" / ")}` };
|
|
@@ -8671,7 +9070,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
8671
9070
|
if (!input) return { error: `\u64CD\u4F5C ${op} \u9700\u8981\u53C2\u6570 input\uFF08\u5DE5\u4F5C\u533A\u5185\u76F8\u5BF9\u8DEF\u5F84\uFF09` };
|
|
8672
9071
|
const r = resolveTarget(root, input);
|
|
8673
9072
|
if ("error" in r) return { error: r.error };
|
|
8674
|
-
if (!
|
|
9073
|
+
if (!fs14.existsSync(r.abs) || !fs14.statSync(r.abs).isFile()) {
|
|
8675
9074
|
return { error: `\u8F93\u5165\u6587\u4EF6\u4E0D\u5B58\u5728: ${input}` };
|
|
8676
9075
|
}
|
|
8677
9076
|
inputAbs = r.abs;
|
|
@@ -8683,7 +9082,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
8683
9082
|
if (!output) return { error: `\u64CD\u4F5C ${op} \u9700\u8981\u53C2\u6570 output\uFF08\u5DE5\u4F5C\u533A\u5185\u76F8\u5BF9\u8DEF\u5F84\uFF09` };
|
|
8684
9083
|
const r = resolveTarget(root, output);
|
|
8685
9084
|
if ("error" in r) return { error: r.error };
|
|
8686
|
-
|
|
9085
|
+
fs14.mkdirSync(path16.dirname(r.abs), { recursive: true });
|
|
8687
9086
|
outputAbs = r.abs;
|
|
8688
9087
|
relRoot = r.root;
|
|
8689
9088
|
}
|
|
@@ -8733,7 +9132,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
8733
9132
|
};
|
|
8734
9133
|
if (outputAbs) {
|
|
8735
9134
|
try {
|
|
8736
|
-
result["size_bytes"] =
|
|
9135
|
+
result["size_bytes"] = fs14.statSync(outputAbs).size;
|
|
8737
9136
|
} catch {
|
|
8738
9137
|
}
|
|
8739
9138
|
}
|
|
@@ -8976,7 +9375,9 @@ function createDefaultEngine(opts) {
|
|
|
8976
9375
|
subagentHookFactory,
|
|
8977
9376
|
contextWindow,
|
|
8978
9377
|
thresholdPct,
|
|
9378
|
+
softThresholdPct,
|
|
8979
9379
|
capTokens,
|
|
9380
|
+
toolResultDir: toolResultDir2,
|
|
8980
9381
|
logDir
|
|
8981
9382
|
} = opts;
|
|
8982
9383
|
const envEffort = process.env.OPENAI_REASONING_EFFORT?.toLowerCase();
|
|
@@ -8999,7 +9400,8 @@ function createDefaultEngine(opts) {
|
|
|
8999
9400
|
workspace,
|
|
9000
9401
|
provider: effectiveProvider,
|
|
9001
9402
|
model,
|
|
9002
|
-
hookFactory: subagentHookFactory
|
|
9403
|
+
hookFactory: subagentHookFactory,
|
|
9404
|
+
toolResultDir: toolResultDir2
|
|
9003
9405
|
});
|
|
9004
9406
|
const permissions = new PermissionEngine({
|
|
9005
9407
|
workspaceRoot: workspace,
|
|
@@ -9021,7 +9423,9 @@ function createDefaultEngine(opts) {
|
|
|
9021
9423
|
hook,
|
|
9022
9424
|
contextWindow,
|
|
9023
9425
|
thresholdPct,
|
|
9024
|
-
|
|
9426
|
+
softThresholdPct,
|
|
9427
|
+
capTokens,
|
|
9428
|
+
toolResultDir: toolResultDir2
|
|
9025
9429
|
});
|
|
9026
9430
|
return engine;
|
|
9027
9431
|
}
|
|
@@ -9031,8 +9435,8 @@ init_esm_shims();
|
|
|
9031
9435
|
|
|
9032
9436
|
// src/llmConfig.ts
|
|
9033
9437
|
init_esm_shims();
|
|
9034
|
-
import * as
|
|
9035
|
-
import * as
|
|
9438
|
+
import * as fs16 from "fs";
|
|
9439
|
+
import * as path18 from "path";
|
|
9036
9440
|
import * as crypto3 from "crypto";
|
|
9037
9441
|
|
|
9038
9442
|
// src/crypto/secrets.ts
|
|
@@ -9041,8 +9445,8 @@ init_esm_shims();
|
|
|
9041
9445
|
// ../shared/src/secrets.ts
|
|
9042
9446
|
init_esm_shims();
|
|
9043
9447
|
import * as crypto2 from "crypto";
|
|
9044
|
-
import * as
|
|
9045
|
-
import * as
|
|
9448
|
+
import * as fs15 from "fs";
|
|
9449
|
+
import * as path17 from "path";
|
|
9046
9450
|
function encryptWithKey(key, plain) {
|
|
9047
9451
|
const iv = crypto2.randomBytes(12);
|
|
9048
9452
|
const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
|
|
@@ -9073,16 +9477,16 @@ function decryptWithKey(key, payload) {
|
|
|
9073
9477
|
}
|
|
9074
9478
|
}
|
|
9075
9479
|
function loadOrCreateMasterKey(masterKeyFile) {
|
|
9076
|
-
if (
|
|
9077
|
-
const hex =
|
|
9480
|
+
if (fs15.existsSync(masterKeyFile)) {
|
|
9481
|
+
const hex = fs15.readFileSync(masterKeyFile, "utf8").trim();
|
|
9078
9482
|
if (/^[0-9a-f]{64}$/i.test(hex)) {
|
|
9079
9483
|
return Buffer.from(hex, "hex");
|
|
9080
9484
|
}
|
|
9081
9485
|
console.warn(`\u26A0\uFE0F \u4E3B\u5BC6\u94A5\u6587\u4EF6\u635F\u574F\uFF0C\u5DF2\u91CD\u65B0\u751F\u6210\uFF08\u65E7\u5BC6\u6587\u5C06\u65E0\u6CD5\u89E3\u5BC6\uFF09`);
|
|
9082
9486
|
}
|
|
9083
9487
|
const key = crypto2.randomBytes(32);
|
|
9084
|
-
|
|
9085
|
-
|
|
9488
|
+
fs15.mkdirSync(path17.dirname(masterKeyFile), { recursive: true, mode: 448 });
|
|
9489
|
+
fs15.writeFileSync(masterKeyFile, key.toString("hex"), {
|
|
9086
9490
|
mode: 384,
|
|
9087
9491
|
encoding: "utf8"
|
|
9088
9492
|
});
|
|
@@ -9300,26 +9704,6 @@ async function probeModel(baseURL, apiKey, model) {
|
|
|
9300
9704
|
}
|
|
9301
9705
|
}
|
|
9302
9706
|
|
|
9303
|
-
// src/paths.ts
|
|
9304
|
-
init_esm_shims();
|
|
9305
|
-
import * as os3 from "os";
|
|
9306
|
-
import * as path16 from "path";
|
|
9307
|
-
var APP_DIR_NAME = "zqagent";
|
|
9308
|
-
var DEFAULT_DATA_DIR = ".zqagent_data";
|
|
9309
|
-
var DEFAULT_HUB_URL = "https://hub.cd-zq.com";
|
|
9310
|
-
var IS_WINDOWS = process.platform === "win32";
|
|
9311
|
-
function homeDir() {
|
|
9312
|
-
return process.env.HOME || process.env.USERPROFILE || os3.homedir() || os3.tmpdir();
|
|
9313
|
-
}
|
|
9314
|
-
function configDir() {
|
|
9315
|
-
const base = process.env.XDG_CONFIG_HOME || (IS_WINDOWS ? process.env.APPDATA : void 0) || path16.join(homeDir(), ".config");
|
|
9316
|
-
return path16.join(base, APP_DIR_NAME);
|
|
9317
|
-
}
|
|
9318
|
-
function stateDir() {
|
|
9319
|
-
const base = process.env.XDG_STATE_HOME || (IS_WINDOWS ? process.env.LOCALAPPDATA : void 0) || path16.join(homeDir(), ".local", "state");
|
|
9320
|
-
return path16.join(base, APP_DIR_NAME);
|
|
9321
|
-
}
|
|
9322
|
-
|
|
9323
9707
|
// ../shared/src/llm-presets.ts
|
|
9324
9708
|
init_esm_shims();
|
|
9325
9709
|
var LLM_PRESETS = [
|
|
@@ -9384,11 +9768,11 @@ function llmPreset(id) {
|
|
|
9384
9768
|
function llmConfigFile() {
|
|
9385
9769
|
const override = process.env.ZQAGENT_LLM_CONFIG_FILE?.trim();
|
|
9386
9770
|
if (override) return override;
|
|
9387
|
-
return
|
|
9771
|
+
return path18.join(configDir(), "connections.json");
|
|
9388
9772
|
}
|
|
9389
9773
|
var keyCache = /* @__PURE__ */ new Map();
|
|
9390
9774
|
function masterKeyFileFor(file) {
|
|
9391
|
-
return
|
|
9775
|
+
return path18.join(path18.dirname(file), "secret.key");
|
|
9392
9776
|
}
|
|
9393
9777
|
function getMasterKey(file) {
|
|
9394
9778
|
const keyFile = masterKeyFileFor(file);
|
|
@@ -9493,10 +9877,10 @@ function normalize(f) {
|
|
|
9493
9877
|
return { ...f, providers, activeId };
|
|
9494
9878
|
}
|
|
9495
9879
|
function readRaw(file, keyFile = file) {
|
|
9496
|
-
if (!
|
|
9880
|
+
if (!fs16.existsSync(file)) return null;
|
|
9497
9881
|
let raw;
|
|
9498
9882
|
try {
|
|
9499
|
-
raw = JSON.parse(
|
|
9883
|
+
raw = JSON.parse(fs16.readFileSync(file, "utf8"));
|
|
9500
9884
|
} catch {
|
|
9501
9885
|
return null;
|
|
9502
9886
|
}
|
|
@@ -9544,19 +9928,19 @@ function readLlmConfig(opts = {}) {
|
|
|
9544
9928
|
f = r.config;
|
|
9545
9929
|
changed = changed || r.changed;
|
|
9546
9930
|
}
|
|
9547
|
-
if (changed || !
|
|
9931
|
+
if (changed || !fs16.existsSync(file)) writeLlmConfig(f, file);
|
|
9548
9932
|
return f;
|
|
9549
9933
|
}
|
|
9550
9934
|
function writeLlmConfig(f, file = llmConfigFile()) {
|
|
9551
9935
|
const next = normalize(f);
|
|
9552
|
-
|
|
9553
|
-
|
|
9936
|
+
fs16.mkdirSync(path18.dirname(file), { recursive: true, mode: 448 });
|
|
9937
|
+
fs16.writeFileSync(file, JSON.stringify(next, null, 2) + "\n", {
|
|
9554
9938
|
mode: 384,
|
|
9555
9939
|
encoding: "utf8"
|
|
9556
9940
|
});
|
|
9557
9941
|
}
|
|
9558
9942
|
function commonLegacyFiles() {
|
|
9559
|
-
return [
|
|
9943
|
+
return [path18.join(stateDir(), "data", "node-llm.json")];
|
|
9560
9944
|
}
|
|
9561
9945
|
|
|
9562
9946
|
// src/cli/connection.ts
|
|
@@ -9989,17 +10373,17 @@ function renderBanner(opts = {}) {
|
|
|
9989
10373
|
// src/cli/terminalSetup.ts
|
|
9990
10374
|
init_esm_shims();
|
|
9991
10375
|
import { existsSync as existsSync13 } from "fs";
|
|
9992
|
-
import * as
|
|
10376
|
+
import * as path19 from "path";
|
|
9993
10377
|
var TERM_SETUP_ENV = "ZQAGENT_TERM_SETUP";
|
|
9994
10378
|
var FALSE_FLAGS = ["0", "false", "no", "off"];
|
|
9995
10379
|
var TRUE_FLAGS = ["1", "true", "yes", "on"];
|
|
9996
10380
|
function hasWindowsTerminal(env3 = process.env, fileExists = existsSync13) {
|
|
9997
10381
|
const raw = env3.PATH ?? env3.Path ?? "";
|
|
9998
|
-
const dirs = raw.split(
|
|
10382
|
+
const dirs = raw.split(path19.delimiter).filter(Boolean);
|
|
9999
10383
|
if (env3.LOCALAPPDATA) {
|
|
10000
|
-
dirs.push(
|
|
10384
|
+
dirs.push(path19.join(env3.LOCALAPPDATA, "Microsoft", "WindowsApps"));
|
|
10001
10385
|
}
|
|
10002
|
-
return dirs.some((dir) => fileExists(
|
|
10386
|
+
return dirs.some((dir) => fileExists(path19.join(dir, "wt.exe")));
|
|
10003
10387
|
}
|
|
10004
10388
|
function detectTerminalSetup(input = {}) {
|
|
10005
10389
|
const env3 = input.env ?? process.env;
|
|
@@ -10080,13 +10464,13 @@ function renderTerminalTutorial() {
|
|
|
10080
10464
|
|
|
10081
10465
|
// src/cli/link.ts
|
|
10082
10466
|
init_esm_shims();
|
|
10083
|
-
import * as
|
|
10467
|
+
import * as path22 from "path";
|
|
10084
10468
|
|
|
10085
10469
|
// src/server/store.ts
|
|
10086
10470
|
init_esm_shims();
|
|
10087
|
-
import * as
|
|
10471
|
+
import * as fs17 from "fs";
|
|
10088
10472
|
import * as fsp from "fs/promises";
|
|
10089
|
-
import * as
|
|
10473
|
+
import * as path20 from "path";
|
|
10090
10474
|
import { randomUUID } from "crypto";
|
|
10091
10475
|
|
|
10092
10476
|
// src/server/dbgTrace.ts
|
|
@@ -10140,57 +10524,57 @@ var Store = class {
|
|
|
10140
10524
|
sessionCache = /* @__PURE__ */ new Map();
|
|
10141
10525
|
// ===================== 租户 =====================
|
|
10142
10526
|
tenantDir(tenantId) {
|
|
10143
|
-
return
|
|
10527
|
+
return path20.join(this.root, "tenants", tenantId);
|
|
10144
10528
|
}
|
|
10145
10529
|
listTenants() {
|
|
10146
|
-
const dir =
|
|
10530
|
+
const dir = path20.join(this.root, "tenants");
|
|
10147
10531
|
try {
|
|
10148
|
-
return
|
|
10532
|
+
return fs17.readdirSync(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
10149
10533
|
} catch {
|
|
10150
10534
|
return [];
|
|
10151
10535
|
}
|
|
10152
10536
|
}
|
|
10153
10537
|
loadTenant(tenantId) {
|
|
10154
|
-
return this._readJson(
|
|
10538
|
+
return this._readJson(path20.join(this.tenantDir(tenantId), "config.json"));
|
|
10155
10539
|
}
|
|
10156
10540
|
saveTenant(config) {
|
|
10157
10541
|
const dir = this.tenantDir(config.id);
|
|
10158
|
-
|
|
10159
|
-
|
|
10542
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
10543
|
+
fs17.writeFileSync(path20.join(dir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
|
|
10160
10544
|
}
|
|
10161
10545
|
deleteTenant(tenantId) {
|
|
10162
|
-
|
|
10546
|
+
fs17.rmSync(this.tenantDir(tenantId), { recursive: true, force: true });
|
|
10163
10547
|
}
|
|
10164
10548
|
// ===================== 会话 =====================
|
|
10165
10549
|
workspaceDir(tenantId, sessionId) {
|
|
10166
|
-
return
|
|
10550
|
+
return path20.join(this.tenantDir(tenantId), "workspaces", sessionId);
|
|
10167
10551
|
}
|
|
10168
10552
|
/** 创建(或复用)会话工作区沙箱目录 */
|
|
10169
10553
|
ensureWorkspace(tenantId, sessionId) {
|
|
10170
10554
|
const dir = this.workspaceDir(tenantId, sessionId);
|
|
10171
|
-
|
|
10555
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
10172
10556
|
return dir;
|
|
10173
10557
|
}
|
|
10174
10558
|
sessionDir(tenantId, sessionId) {
|
|
10175
|
-
return
|
|
10559
|
+
return path20.join(this.tenantDir(tenantId), "sessions", sessionId);
|
|
10176
10560
|
}
|
|
10177
10561
|
listSessions(tenantId) {
|
|
10178
|
-
const dir =
|
|
10562
|
+
const dir = path20.join(this.tenantDir(tenantId), "sessions");
|
|
10179
10563
|
try {
|
|
10180
|
-
return
|
|
10564
|
+
return fs17.readdirSync(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
10181
10565
|
} catch {
|
|
10182
10566
|
return [];
|
|
10183
10567
|
}
|
|
10184
10568
|
}
|
|
10185
10569
|
loadSession(tenantId, sessionId) {
|
|
10186
|
-
const p =
|
|
10570
|
+
const p = path20.join(this.sessionDir(tenantId, sessionId), "session.json");
|
|
10187
10571
|
try {
|
|
10188
|
-
const st =
|
|
10572
|
+
const st = fs17.statSync(p);
|
|
10189
10573
|
const key = `${st.mtimeMs}:${st.size}`;
|
|
10190
10574
|
const hit = this.sessionCache.get(p);
|
|
10191
10575
|
if (hit && hit.key === key) return hit.rec;
|
|
10192
10576
|
const __t0 = dbgNow();
|
|
10193
|
-
const rec = JSON.parse(
|
|
10577
|
+
const rec = JSON.parse(fs17.readFileSync(p, "utf-8"));
|
|
10194
10578
|
dbgMs("store.loadSession(\u89E3\u6790)", __t0, `bytes=${st.size}`);
|
|
10195
10579
|
this.sessionCache.set(p, { key, rec });
|
|
10196
10580
|
if (this.sessionCache.size > 300) {
|
|
@@ -10206,8 +10590,8 @@ var Store = class {
|
|
|
10206
10590
|
saveSession(record) {
|
|
10207
10591
|
record.savedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10208
10592
|
const dir = this.sessionDir(record.tenantId, record.sessionId);
|
|
10209
|
-
|
|
10210
|
-
const file =
|
|
10593
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
10594
|
+
const file = path20.join(dir, "session.json");
|
|
10211
10595
|
const snapshot = JSON.stringify(record, null, 2);
|
|
10212
10596
|
const tmp = `${file}.${randomUUID()}.tmp`;
|
|
10213
10597
|
const prev = this.writeChains.get(file) ?? Promise.resolve();
|
|
@@ -10222,34 +10606,34 @@ var Store = class {
|
|
|
10222
10606
|
saveSessionSync(record) {
|
|
10223
10607
|
record.savedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10224
10608
|
const dir = this.sessionDir(record.tenantId, record.sessionId);
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10609
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
10610
|
+
fs17.writeFileSync(
|
|
10611
|
+
path20.join(dir, "session.json"),
|
|
10228
10612
|
JSON.stringify(record, null, 2),
|
|
10229
10613
|
"utf-8"
|
|
10230
10614
|
);
|
|
10231
10615
|
}
|
|
10232
10616
|
deleteSession(tenantId, sessionId) {
|
|
10233
|
-
this.sessionCache.delete(
|
|
10234
|
-
|
|
10235
|
-
|
|
10617
|
+
this.sessionCache.delete(path20.join(this.sessionDir(tenantId, sessionId), "session.json"));
|
|
10618
|
+
fs17.rmSync(this.sessionDir(tenantId, sessionId), { recursive: true, force: true });
|
|
10619
|
+
fs17.rmSync(this.workspaceDir(tenantId, sessionId), { recursive: true, force: true });
|
|
10236
10620
|
}
|
|
10237
10621
|
// ===================== 事件日志 =====================
|
|
10238
10622
|
appendEvent(tenantId, sessionId, entry) {
|
|
10239
10623
|
const dir = this.sessionDir(tenantId, sessionId);
|
|
10240
|
-
|
|
10624
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
10241
10625
|
return fsp.appendFile(
|
|
10242
|
-
|
|
10626
|
+
path20.join(dir, "events.jsonl"),
|
|
10243
10627
|
JSON.stringify(entry) + "\n",
|
|
10244
10628
|
"utf-8"
|
|
10245
10629
|
).catch(() => {
|
|
10246
10630
|
});
|
|
10247
10631
|
}
|
|
10248
10632
|
loadEvents(tenantId, sessionId) {
|
|
10249
|
-
const p =
|
|
10633
|
+
const p = path20.join(this.sessionDir(tenantId, sessionId), "events.jsonl");
|
|
10250
10634
|
const __t0 = dbgNow();
|
|
10251
10635
|
try {
|
|
10252
|
-
const raw =
|
|
10636
|
+
const raw = fs17.readFileSync(p, "utf-8");
|
|
10253
10637
|
const out = raw.trim().split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
10254
10638
|
dbgMs("store.loadEvents(\u6574\u6587\u4EF6)", __t0, `bytes=${raw.length} evts=${out.length}`);
|
|
10255
10639
|
return out;
|
|
@@ -10267,15 +10651,15 @@ var Store = class {
|
|
|
10267
10651
|
* 窗口内一条完整记录都没有时回退为整文件读取。
|
|
10268
10652
|
*/
|
|
10269
10653
|
loadEventsTail(tenantId, sessionId, maxBytes = 1024 * 1024) {
|
|
10270
|
-
const p =
|
|
10654
|
+
const p = path20.join(this.sessionDir(tenantId, sessionId), "events.jsonl");
|
|
10271
10655
|
const __t0 = dbgNow();
|
|
10272
10656
|
let fd = null;
|
|
10273
10657
|
try {
|
|
10274
|
-
const size =
|
|
10658
|
+
const size = fs17.statSync(p).size;
|
|
10275
10659
|
if (size <= maxBytes) return this.loadEvents(tenantId, sessionId);
|
|
10276
|
-
fd =
|
|
10660
|
+
fd = fs17.openSync(p, "r");
|
|
10277
10661
|
const buf = Buffer.allocUnsafe(maxBytes);
|
|
10278
|
-
const read2 =
|
|
10662
|
+
const read2 = fs17.readSync(fd, buf, 0, maxBytes, size - maxBytes);
|
|
10279
10663
|
let text = buf.subarray(0, read2).toString("utf-8");
|
|
10280
10664
|
const nl = text.indexOf("\n");
|
|
10281
10665
|
if (nl < 0) return this.loadEvents(tenantId, sessionId);
|
|
@@ -10300,7 +10684,7 @@ var Store = class {
|
|
|
10300
10684
|
} finally {
|
|
10301
10685
|
if (fd !== null) {
|
|
10302
10686
|
try {
|
|
10303
|
-
|
|
10687
|
+
fs17.closeSync(fd);
|
|
10304
10688
|
} catch {
|
|
10305
10689
|
}
|
|
10306
10690
|
}
|
|
@@ -10309,26 +10693,26 @@ var Store = class {
|
|
|
10309
10693
|
// ===================== 节点身份 & 管理员密钥 =====================
|
|
10310
10694
|
/** 节点稳定标识:{root}/node.json(首次启动生成后长期固定) */
|
|
10311
10695
|
loadNodeId() {
|
|
10312
|
-
const v = this._readJson(
|
|
10696
|
+
const v = this._readJson(path20.join(this.root, "node.json"));
|
|
10313
10697
|
return v?.id && typeof v.id === "string" ? v.id : null;
|
|
10314
10698
|
}
|
|
10315
10699
|
saveNodeId(id) {
|
|
10316
|
-
|
|
10317
|
-
const file =
|
|
10700
|
+
fs17.mkdirSync(this.root, { recursive: true });
|
|
10701
|
+
const file = path20.join(this.root, "node.json");
|
|
10318
10702
|
const cur = this._readJson(file) ?? {};
|
|
10319
|
-
|
|
10703
|
+
fs17.writeFileSync(file, JSON.stringify({ ...cur, id }, null, 2), "utf-8");
|
|
10320
10704
|
}
|
|
10321
10705
|
/** 节点展示名:{root}/node.json 的 label(Hub 下发或本地设置) */
|
|
10322
10706
|
loadNodeLabel() {
|
|
10323
|
-
const v = this._readJson(
|
|
10707
|
+
const v = this._readJson(path20.join(this.root, "node.json"));
|
|
10324
10708
|
return v?.label && typeof v.label === "string" ? v.label : null;
|
|
10325
10709
|
}
|
|
10326
10710
|
/** 写入/更新节点展示名(保留 id 等既有字段) */
|
|
10327
10711
|
saveNodeLabel(label) {
|
|
10328
|
-
|
|
10329
|
-
const file =
|
|
10712
|
+
fs17.mkdirSync(this.root, { recursive: true });
|
|
10713
|
+
const file = path20.join(this.root, "node.json");
|
|
10330
10714
|
const cur = this._readJson(file) ?? {};
|
|
10331
|
-
|
|
10715
|
+
fs17.writeFileSync(file, JSON.stringify({ ...cur, label }, null, 2), "utf-8");
|
|
10332
10716
|
}
|
|
10333
10717
|
/**
|
|
10334
10718
|
* 节点级 LLM 配置:统一存放于本机 `~/.config/zqagent/connections.json`
|
|
@@ -10339,7 +10723,7 @@ var Store = class {
|
|
|
10339
10723
|
const file = this.llmFile();
|
|
10340
10724
|
const cfg = readLlmConfig({
|
|
10341
10725
|
file,
|
|
10342
|
-
legacyFiles: [
|
|
10726
|
+
legacyFiles: [path20.join(this.root, "node-llm.json")]
|
|
10343
10727
|
});
|
|
10344
10728
|
return {
|
|
10345
10729
|
providers: cfg.providers.map((p) => ({
|
|
@@ -10361,7 +10745,7 @@ var Store = class {
|
|
|
10361
10745
|
const file = this.llmFile();
|
|
10362
10746
|
const cur = readLlmConfig({
|
|
10363
10747
|
file,
|
|
10364
|
-
legacyFiles: [
|
|
10748
|
+
legacyFiles: [path20.join(this.root, "node-llm.json")]
|
|
10365
10749
|
});
|
|
10366
10750
|
const old = new Map(cur.providers.map((p) => [p.id, p]));
|
|
10367
10751
|
const providers = cfg.providers.map((p) => {
|
|
@@ -10393,13 +10777,13 @@ var Store = class {
|
|
|
10393
10777
|
}
|
|
10394
10778
|
/** 管理员密钥:{root}/admin.json(可跨项目操作;桌面外壳注入) */
|
|
10395
10779
|
loadAdminKey() {
|
|
10396
|
-
const v = this._readJson(
|
|
10780
|
+
const v = this._readJson(path20.join(this.root, "admin.json"));
|
|
10397
10781
|
return v?.apiKey && typeof v.apiKey === "string" ? v.apiKey : null;
|
|
10398
10782
|
}
|
|
10399
10783
|
saveAdminKey(apiKey) {
|
|
10400
|
-
|
|
10401
|
-
|
|
10402
|
-
|
|
10784
|
+
fs17.mkdirSync(this.root, { recursive: true });
|
|
10785
|
+
fs17.writeFileSync(
|
|
10786
|
+
path20.join(this.root, "admin.json"),
|
|
10403
10787
|
JSON.stringify({ apiKey }, null, 2),
|
|
10404
10788
|
"utf-8"
|
|
10405
10789
|
);
|
|
@@ -10409,18 +10793,18 @@ var Store = class {
|
|
|
10409
10793
|
* 节点凭注册码接入后写入;启动时优先于环境变量读取。
|
|
10410
10794
|
*/
|
|
10411
10795
|
loadFleetCredential() {
|
|
10412
|
-
return this._readJson(
|
|
10796
|
+
return this._readJson(path20.join(this.root, "fleet.json"));
|
|
10413
10797
|
}
|
|
10414
10798
|
saveFleetCredential(cred) {
|
|
10415
|
-
|
|
10416
|
-
|
|
10417
|
-
|
|
10799
|
+
fs17.mkdirSync(this.root, { recursive: true });
|
|
10800
|
+
fs17.writeFileSync(
|
|
10801
|
+
path20.join(this.root, "fleet.json"),
|
|
10418
10802
|
JSON.stringify(cred, null, 2),
|
|
10419
10803
|
{ encoding: "utf-8", mode: 384 }
|
|
10420
10804
|
);
|
|
10421
10805
|
}
|
|
10422
10806
|
clearFleetCredential() {
|
|
10423
|
-
|
|
10807
|
+
fs17.rmSync(path20.join(this.root, "fleet.json"), { force: true });
|
|
10424
10808
|
}
|
|
10425
10809
|
/** 数据根目录(只读;节点管理配置等辅助文件定位用) */
|
|
10426
10810
|
get rootDir() {
|
|
@@ -10429,7 +10813,7 @@ var Store = class {
|
|
|
10429
10813
|
// ===================== 工具 =====================
|
|
10430
10814
|
_readJson(p) {
|
|
10431
10815
|
try {
|
|
10432
|
-
return JSON.parse(
|
|
10816
|
+
return JSON.parse(fs17.readFileSync(p, "utf-8"));
|
|
10433
10817
|
} catch {
|
|
10434
10818
|
return null;
|
|
10435
10819
|
}
|
|
@@ -10442,13 +10826,13 @@ var baseUrlResolver = null;
|
|
|
10442
10826
|
function resolveBase(baseUrl) {
|
|
10443
10827
|
return baseUrlResolver ? baseUrlResolver(baseUrl) : baseUrl;
|
|
10444
10828
|
}
|
|
10445
|
-
async function req(baseUrl, token,
|
|
10829
|
+
async function req(baseUrl, token, path44, init = {}) {
|
|
10446
10830
|
const headers = {
|
|
10447
10831
|
...init.body ? { "Content-Type": "application/json" } : {},
|
|
10448
10832
|
...init.headers ?? {}
|
|
10449
10833
|
};
|
|
10450
10834
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
10451
|
-
const res = await fetch(`${resolveBase(baseUrl).replace(/\/$/, "")}${
|
|
10835
|
+
const res = await fetch(`${resolveBase(baseUrl).replace(/\/$/, "")}${path44}`, {
|
|
10452
10836
|
...init,
|
|
10453
10837
|
headers
|
|
10454
10838
|
});
|
|
@@ -10491,8 +10875,8 @@ async function hubEnrollByAccount(baseUrl, input) {
|
|
|
10491
10875
|
// src/cli/serve-instances.ts
|
|
10492
10876
|
init_esm_shims();
|
|
10493
10877
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
10494
|
-
import * as
|
|
10495
|
-
import * as
|
|
10878
|
+
import * as fs18 from "fs";
|
|
10879
|
+
import * as path21 from "path";
|
|
10496
10880
|
var DEFAULT_SERVE_HOST = "127.0.0.1";
|
|
10497
10881
|
var DEFAULT_SERVE_PORT = 8787;
|
|
10498
10882
|
function serveSubcommandIndex(argv) {
|
|
@@ -10530,9 +10914,9 @@ function parseServeArgs(argv) {
|
|
|
10530
10914
|
}
|
|
10531
10915
|
function buildHotCandidates(explicitDataDir, discovered, fallback) {
|
|
10532
10916
|
if (explicitDataDir) {
|
|
10533
|
-
return [{ dataDir:
|
|
10917
|
+
return [{ dataDir: path21.resolve(explicitDataDir), ...fallback }];
|
|
10534
10918
|
}
|
|
10535
|
-
const out = [{ dataDir:
|
|
10919
|
+
const out = [{ dataDir: path21.resolve(DEFAULT_DATA_DIR), ...fallback }];
|
|
10536
10920
|
for (const inst of discovered) {
|
|
10537
10921
|
if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
|
|
10538
10922
|
}
|
|
@@ -10540,7 +10924,7 @@ function buildHotCandidates(explicitDataDir, discovered, fallback) {
|
|
|
10540
10924
|
}
|
|
10541
10925
|
function processCwd(pid) {
|
|
10542
10926
|
try {
|
|
10543
|
-
return
|
|
10927
|
+
return fs18.readlinkSync(`/proc/${pid}/cwd`);
|
|
10544
10928
|
} catch {
|
|
10545
10929
|
return null;
|
|
10546
10930
|
}
|
|
@@ -10548,7 +10932,7 @@ function processCwd(pid) {
|
|
|
10548
10932
|
function processEnv(pid) {
|
|
10549
10933
|
const out = {};
|
|
10550
10934
|
try {
|
|
10551
|
-
const raw =
|
|
10935
|
+
const raw = fs18.readFileSync(`/proc/${pid}/environ`, "utf-8");
|
|
10552
10936
|
for (const entry of raw.split("\0")) {
|
|
10553
10937
|
const eq = entry.indexOf("=");
|
|
10554
10938
|
if (eq > 0) out[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
@@ -10559,14 +10943,14 @@ function processEnv(pid) {
|
|
|
10559
10943
|
}
|
|
10560
10944
|
function resolveDataDir(rawDataDir, cwd, env3) {
|
|
10561
10945
|
const pick = rawDataDir ?? env3.DATA_DIR ?? null;
|
|
10562
|
-
if (pick) return
|
|
10563
|
-
return cwd ?
|
|
10946
|
+
if (pick) return path21.isAbsolute(pick) ? path21.normalize(pick) : cwd ? path21.resolve(cwd, pick) : null;
|
|
10947
|
+
return cwd ? path21.resolve(cwd, DEFAULT_DATA_DIR) : null;
|
|
10564
10948
|
}
|
|
10565
10949
|
function discoverFromProc() {
|
|
10566
10950
|
const out = [];
|
|
10567
10951
|
let pids;
|
|
10568
10952
|
try {
|
|
10569
|
-
pids =
|
|
10953
|
+
pids = fs18.readdirSync("/proc");
|
|
10570
10954
|
} catch {
|
|
10571
10955
|
return out;
|
|
10572
10956
|
}
|
|
@@ -10576,7 +10960,7 @@ function discoverFromProc() {
|
|
|
10576
10960
|
if (pid === process.pid) continue;
|
|
10577
10961
|
let argv;
|
|
10578
10962
|
try {
|
|
10579
|
-
argv =
|
|
10963
|
+
argv = fs18.readFileSync(`/proc/${pid}/cmdline`, "utf-8").split("\0").filter(Boolean);
|
|
10580
10964
|
} catch {
|
|
10581
10965
|
continue;
|
|
10582
10966
|
}
|
|
@@ -10597,11 +10981,11 @@ function discoverFromPs() {
|
|
|
10597
10981
|
const m = /--data-dir(?:=|\s+)(.+?)(?=\s+--|\s*$)/.exec(line);
|
|
10598
10982
|
if (!m) continue;
|
|
10599
10983
|
const raw = m[1].trim();
|
|
10600
|
-
if (!raw || !
|
|
10984
|
+
if (!raw || !path21.isAbsolute(raw)) continue;
|
|
10601
10985
|
const host = /--host(?:=|\s+)(\S+)/.exec(line)?.[1] ?? DEFAULT_SERVE_HOST;
|
|
10602
10986
|
const port = Number(/--port(?:=|\s+)(\d+)/.exec(line)?.[1] ?? DEFAULT_SERVE_PORT);
|
|
10603
10987
|
out.push({
|
|
10604
|
-
dataDir:
|
|
10988
|
+
dataDir: path21.normalize(raw),
|
|
10605
10989
|
host: host === "0.0.0.0" || host === "::" ? DEFAULT_SERVE_HOST : host,
|
|
10606
10990
|
port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT
|
|
10607
10991
|
});
|
|
@@ -10704,7 +11088,7 @@ async function linkNode(opts) {
|
|
|
10704
11088
|
const status = await enrollViaServer(inst.host, inst.port, adminKey, opts);
|
|
10705
11089
|
return { mode: "hot", ...status, dataDir: inst.dataDir };
|
|
10706
11090
|
}
|
|
10707
|
-
const dataDir =
|
|
11091
|
+
const dataDir = path22.resolve(explicitDataDir ?? DEFAULT_DATA_DIR);
|
|
10708
11092
|
const store = new Store(dataDir);
|
|
10709
11093
|
const nodeId = ensureNodeId(store);
|
|
10710
11094
|
const cred = await hubEnrollByCode(opts.hubUrl, {
|
|
@@ -10786,25 +11170,26 @@ init_esm_shims();
|
|
|
10786
11170
|
|
|
10787
11171
|
// src/server/runtime.ts
|
|
10788
11172
|
init_esm_shims();
|
|
10789
|
-
import * as
|
|
10790
|
-
import * as
|
|
11173
|
+
import * as fs31 from "fs";
|
|
11174
|
+
import * as path38 from "path";
|
|
10791
11175
|
|
|
10792
11176
|
// src/server/session.ts
|
|
10793
11177
|
init_esm_shims();
|
|
11178
|
+
import * as path25 from "path";
|
|
10794
11179
|
import { AsyncLocalStorage } from "async_hooks";
|
|
10795
11180
|
|
|
10796
11181
|
// src/server/mcpConfig.ts
|
|
10797
11182
|
init_esm_shims();
|
|
10798
|
-
import
|
|
10799
|
-
import
|
|
11183
|
+
import fs19 from "fs";
|
|
11184
|
+
import path23 from "path";
|
|
10800
11185
|
function isValidMcpConfig(v) {
|
|
10801
11186
|
return !!v && typeof v === "object" && typeof v.name === "string" && (v.type === "stdio" || v.type === "http");
|
|
10802
11187
|
}
|
|
10803
11188
|
function readConfigFile(file) {
|
|
10804
11189
|
if (!file) return [];
|
|
10805
11190
|
try {
|
|
10806
|
-
if (!
|
|
10807
|
-
const raw = JSON.parse(
|
|
11191
|
+
if (!fs19.existsSync(file)) return [];
|
|
11192
|
+
const raw = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
10808
11193
|
const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.servers) ? raw.servers : [];
|
|
10809
11194
|
return list.filter(isValidMcpConfig);
|
|
10810
11195
|
} catch (err) {
|
|
@@ -10816,7 +11201,7 @@ function hasServerMcpConfig() {
|
|
|
10816
11201
|
if (readConfigFile(process.env.MCP_CONFIG_PATH).length > 0) return true;
|
|
10817
11202
|
const workspace = process.env.WORKSPACE;
|
|
10818
11203
|
if (!workspace) return false;
|
|
10819
|
-
return readConfigFile(
|
|
11204
|
+
return readConfigFile(path23.join(workspace, ".agent", "mcp.json")).length > 0;
|
|
10820
11205
|
}
|
|
10821
11206
|
function loadServerMcpConfigs(tenant) {
|
|
10822
11207
|
const fromEnv = readConfigFile(process.env.MCP_CONFIG_PATH);
|
|
@@ -10826,7 +11211,7 @@ function loadServerMcpConfigs(tenant) {
|
|
|
10826
11211
|
}
|
|
10827
11212
|
const workspace = tenant.workspaceRoot ?? process.env.WORKSPACE;
|
|
10828
11213
|
if (workspace)
|
|
10829
|
-
return readConfigFile(
|
|
11214
|
+
return readConfigFile(path23.join(workspace, ".agent", "mcp.json"));
|
|
10830
11215
|
return [];
|
|
10831
11216
|
}
|
|
10832
11217
|
|
|
@@ -10971,8 +11356,8 @@ function resolveSessionProvider(store, tenant, providerId) {
|
|
|
10971
11356
|
|
|
10972
11357
|
// src/server/usage.ts
|
|
10973
11358
|
init_esm_shims();
|
|
10974
|
-
import * as
|
|
10975
|
-
import * as
|
|
11359
|
+
import * as fs20 from "fs";
|
|
11360
|
+
import * as path24 from "path";
|
|
10976
11361
|
var LEDGER_RETENTION_MONTHS = 12;
|
|
10977
11362
|
var USAGE_SCHEMA = 1;
|
|
10978
11363
|
var DEFAULT_PRICING = {
|
|
@@ -11053,15 +11438,15 @@ function retainedMonths(now = /* @__PURE__ */ new Date()) {
|
|
|
11053
11438
|
return out;
|
|
11054
11439
|
}
|
|
11055
11440
|
function atomicWrite(file, content) {
|
|
11056
|
-
|
|
11441
|
+
fs20.mkdirSync(path24.dirname(file), { recursive: true });
|
|
11057
11442
|
const tmp = `${file}.tmp`;
|
|
11058
|
-
|
|
11059
|
-
|
|
11443
|
+
fs20.writeFileSync(tmp, content, "utf-8");
|
|
11444
|
+
fs20.renameSync(tmp, file);
|
|
11060
11445
|
}
|
|
11061
11446
|
var UsageLedger = class {
|
|
11062
11447
|
constructor(root) {
|
|
11063
11448
|
this.root = root;
|
|
11064
|
-
this.dir =
|
|
11449
|
+
this.dir = path24.join(root, "usage");
|
|
11065
11450
|
}
|
|
11066
11451
|
root;
|
|
11067
11452
|
dir;
|
|
@@ -11074,18 +11459,18 @@ var UsageLedger = class {
|
|
|
11074
11459
|
return this.dir;
|
|
11075
11460
|
}
|
|
11076
11461
|
ledgerFile(month) {
|
|
11077
|
-
return
|
|
11462
|
+
return path24.join(this.ledgerDir, `ledger-${month}.jsonl`);
|
|
11078
11463
|
}
|
|
11079
11464
|
get rollupFile() {
|
|
11080
|
-
return
|
|
11465
|
+
return path24.join(this.ledgerDir, "rollup.json");
|
|
11081
11466
|
}
|
|
11082
11467
|
get pricingFile() {
|
|
11083
|
-
return
|
|
11468
|
+
return path24.join(this.ledgerDir, "pricing.json");
|
|
11084
11469
|
}
|
|
11085
11470
|
// ===================== 初始化 =====================
|
|
11086
11471
|
init(now = /* @__PURE__ */ new Date()) {
|
|
11087
11472
|
if (this.inited) return;
|
|
11088
|
-
|
|
11473
|
+
fs20.mkdirSync(this.ledgerDir, { recursive: true });
|
|
11089
11474
|
this.pricing = this.loadPricing();
|
|
11090
11475
|
this.rollup = this.loadRollup(now);
|
|
11091
11476
|
this.pruneLedgers(now);
|
|
@@ -11093,7 +11478,7 @@ var UsageLedger = class {
|
|
|
11093
11478
|
}
|
|
11094
11479
|
loadPricing() {
|
|
11095
11480
|
try {
|
|
11096
|
-
const raw =
|
|
11481
|
+
const raw = fs20.readFileSync(this.pricingFile, "utf-8");
|
|
11097
11482
|
const parsed = JSON.parse(raw);
|
|
11098
11483
|
const vendors = {};
|
|
11099
11484
|
for (const v of /* @__PURE__ */ new Set([
|
|
@@ -11122,7 +11507,7 @@ var UsageLedger = class {
|
|
|
11122
11507
|
}
|
|
11123
11508
|
loadRollup(now) {
|
|
11124
11509
|
try {
|
|
11125
|
-
const raw =
|
|
11510
|
+
const raw = fs20.readFileSync(this.rollupFile, "utf-8");
|
|
11126
11511
|
const parsed = JSON.parse(raw);
|
|
11127
11512
|
return {
|
|
11128
11513
|
schema: USAGE_SCHEMA,
|
|
@@ -11149,10 +11534,10 @@ var UsageLedger = class {
|
|
|
11149
11534
|
const keep = retainedMonths(now);
|
|
11150
11535
|
let changed = false;
|
|
11151
11536
|
try {
|
|
11152
|
-
for (const f of
|
|
11537
|
+
for (const f of fs20.readdirSync(this.ledgerDir)) {
|
|
11153
11538
|
const m = /^ledger-(\d{4}-\d{2})\.jsonl$/.exec(f);
|
|
11154
11539
|
if (m && !keep.has(m[1])) {
|
|
11155
|
-
|
|
11540
|
+
fs20.unlinkSync(path24.join(this.ledgerDir, f));
|
|
11156
11541
|
}
|
|
11157
11542
|
}
|
|
11158
11543
|
} catch {
|
|
@@ -11229,7 +11614,7 @@ var UsageLedger = class {
|
|
|
11229
11614
|
};
|
|
11230
11615
|
const line = JSON.stringify(rec) + "\n";
|
|
11231
11616
|
try {
|
|
11232
|
-
|
|
11617
|
+
fs20.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
|
|
11233
11618
|
} catch (e) {
|
|
11234
11619
|
console.error(`[usage] \u5199\u8D26\u672C\u5931\u8D25: ${String(e)}`);
|
|
11235
11620
|
}
|
|
@@ -11401,10 +11786,10 @@ var UsageLedger = class {
|
|
|
11401
11786
|
const rows = [];
|
|
11402
11787
|
for (const month of months) {
|
|
11403
11788
|
const file = this.ledgerFile(month);
|
|
11404
|
-
if (!
|
|
11789
|
+
if (!fs20.existsSync(file)) continue;
|
|
11405
11790
|
let content = "";
|
|
11406
11791
|
try {
|
|
11407
|
-
content =
|
|
11792
|
+
content = fs20.readFileSync(file, "utf-8");
|
|
11408
11793
|
} catch {
|
|
11409
11794
|
continue;
|
|
11410
11795
|
}
|
|
@@ -11513,10 +11898,10 @@ var UsageLedger = class {
|
|
|
11513
11898
|
const seen = /* @__PURE__ */ new Set();
|
|
11514
11899
|
for (const month of this.monthsInRange("", "")) {
|
|
11515
11900
|
const file = this.ledgerFile(month);
|
|
11516
|
-
if (!
|
|
11901
|
+
if (!fs20.existsSync(file)) continue;
|
|
11517
11902
|
let content = "";
|
|
11518
11903
|
try {
|
|
11519
|
-
content =
|
|
11904
|
+
content = fs20.readFileSync(file, "utf-8");
|
|
11520
11905
|
} catch {
|
|
11521
11906
|
continue;
|
|
11522
11907
|
}
|
|
@@ -12824,7 +13209,10 @@ data: ${JSON.stringify(entry.data)}
|
|
|
12824
13209
|
// 上下文窗口:环境变量显式覆盖 > 供应商 /models 提供的真实长度 > 引擎内置表
|
|
12825
13210
|
contextWindow: parseOptionalPositiveNumber(process.env.CONTEXT_WINDOW) ?? modelContextWindow(eff.modelMeta, activeModel),
|
|
12826
13211
|
thresholdPct: parseRatio(process.env.COMPACTION_THRESHOLD_PCT),
|
|
12827
|
-
|
|
13212
|
+
softThresholdPct: parseRatio(process.env.COMPACTION_SOFT_THRESHOLD_PCT),
|
|
13213
|
+
capTokens: parseOptionalPositiveNumber(process.env.COMPACTION_CAP_TOKENS) ?? null,
|
|
13214
|
+
// 超预算工具结果按租户/工作区隔离落盘(多租户共用一个状态目录会混内容)
|
|
13215
|
+
toolResultDir: path25.join(workspace, ".agent", "tool-results")
|
|
12828
13216
|
});
|
|
12829
13217
|
engine.setApprover(this._makeApprover(rt));
|
|
12830
13218
|
const mcpConfigs = loadServerMcpConfigs(tenant);
|
|
@@ -13005,11 +13393,11 @@ var ApiError = class extends Error {
|
|
|
13005
13393
|
// src/server/routes.ts
|
|
13006
13394
|
init_esm_shims();
|
|
13007
13395
|
import { createServer } from "http";
|
|
13008
|
-
import * as
|
|
13396
|
+
import * as path30 from "path";
|
|
13009
13397
|
|
|
13010
13398
|
// src/server/projects.ts
|
|
13011
13399
|
init_esm_shims();
|
|
13012
|
-
import * as
|
|
13400
|
+
import * as path26 from "path";
|
|
13013
13401
|
var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
|
|
13014
13402
|
function toProjectSummary(store, tenant) {
|
|
13015
13403
|
return {
|
|
@@ -13142,9 +13530,9 @@ function createProject(store, input) {
|
|
|
13142
13530
|
if (store.loadTenant(id)) {
|
|
13143
13531
|
throw new ApiError(409, "project_exists", `\u9879\u76EE ${id} \u5DF2\u5B58\u5728`);
|
|
13144
13532
|
}
|
|
13145
|
-
const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ?
|
|
13533
|
+
const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ? path26.resolve(raw.workspaceRoot.trim()) : void 0;
|
|
13146
13534
|
const explicitName = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : "";
|
|
13147
|
-
const defaultName = workspaceRoot ?
|
|
13535
|
+
const defaultName = workspaceRoot ? path26.basename(workspaceRoot) : id;
|
|
13148
13536
|
const tenant = {
|
|
13149
13537
|
id,
|
|
13150
13538
|
name: explicitName || defaultName,
|
|
@@ -13174,7 +13562,7 @@ function buildNodeInfo(store, opts) {
|
|
|
13174
13562
|
nodeId: opts.nodeId ?? ensureNodeId2(store),
|
|
13175
13563
|
platform: process.platform,
|
|
13176
13564
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
13177
|
-
version: "0.2.
|
|
13565
|
+
version: "0.2.7",
|
|
13178
13566
|
admin: opts.admin,
|
|
13179
13567
|
projects: opts.projects
|
|
13180
13568
|
};
|
|
@@ -13258,8 +13646,8 @@ function hubProxyEnabled(host) {
|
|
|
13258
13646
|
const h = (host ?? "127.0.0.1").trim();
|
|
13259
13647
|
return h === "127.0.0.1" || h === "localhost" || h === "::1";
|
|
13260
13648
|
}
|
|
13261
|
-
function isHubProxyPath(
|
|
13262
|
-
return
|
|
13649
|
+
function isHubProxyPath(path44) {
|
|
13650
|
+
return path44.startsWith(HUB_PROXY_PREFIX);
|
|
13263
13651
|
}
|
|
13264
13652
|
function decodeTarget(enc) {
|
|
13265
13653
|
try {
|
|
@@ -13353,34 +13741,34 @@ async function handleHubProxy(req2, res) {
|
|
|
13353
13741
|
|
|
13354
13742
|
// src/server/fsBrowser.ts
|
|
13355
13743
|
init_esm_shims();
|
|
13356
|
-
import * as
|
|
13744
|
+
import * as fs21 from "fs";
|
|
13357
13745
|
import * as os4 from "os";
|
|
13358
|
-
import * as
|
|
13746
|
+
import * as path27 from "path";
|
|
13359
13747
|
var MAX_ENTRIES = 2e3;
|
|
13360
13748
|
var DEFAULT_SEARCH_LIMIT = 200;
|
|
13361
13749
|
var MAX_SEARCH_LIMIT = 1e3;
|
|
13362
13750
|
function listDirs(input) {
|
|
13363
13751
|
const base = (input ?? "").trim() || os4.homedir();
|
|
13364
|
-
const target =
|
|
13752
|
+
const target = path27.resolve(base);
|
|
13365
13753
|
let st;
|
|
13366
13754
|
try {
|
|
13367
|
-
st =
|
|
13755
|
+
st = fs21.statSync(target);
|
|
13368
13756
|
} catch {
|
|
13369
13757
|
throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
|
|
13370
13758
|
}
|
|
13371
13759
|
if (!st.isDirectory()) throw new Error(`\u4E0D\u662F\u76EE\u5F55: ${target}`);
|
|
13372
13760
|
let dirents;
|
|
13373
13761
|
try {
|
|
13374
|
-
dirents =
|
|
13762
|
+
dirents = fs21.readdirSync(target, { withFileTypes: true });
|
|
13375
13763
|
} catch {
|
|
13376
13764
|
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
|
|
13377
13765
|
}
|
|
13378
13766
|
const entries = dirents.map((d) => {
|
|
13379
|
-
const full =
|
|
13767
|
+
const full = path27.join(target, d.name);
|
|
13380
13768
|
let isDir = d.isDirectory();
|
|
13381
13769
|
if (d.isSymbolicLink()) {
|
|
13382
13770
|
try {
|
|
13383
|
-
isDir =
|
|
13771
|
+
isDir = fs21.statSync(full).isDirectory();
|
|
13384
13772
|
} catch {
|
|
13385
13773
|
}
|
|
13386
13774
|
}
|
|
@@ -13389,7 +13777,7 @@ function listDirs(input) {
|
|
|
13389
13777
|
entries.sort(
|
|
13390
13778
|
(a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
|
|
13391
13779
|
);
|
|
13392
|
-
const parentDir =
|
|
13780
|
+
const parentDir = path27.dirname(target);
|
|
13393
13781
|
return {
|
|
13394
13782
|
path: target,
|
|
13395
13783
|
parent: parentDir !== target ? parentDir : null,
|
|
@@ -13420,7 +13808,7 @@ var NAME_CACHE_MAX_DIRS = 4;
|
|
|
13420
13808
|
var NAME_CACHE_MAX_ENTRIES = 5e4;
|
|
13421
13809
|
var nameCache = /* @__PURE__ */ new Map();
|
|
13422
13810
|
function invalidateDirNames(target) {
|
|
13423
|
-
nameCache.delete(
|
|
13811
|
+
nameCache.delete(path27.resolve(target));
|
|
13424
13812
|
}
|
|
13425
13813
|
async function loadDirNames(target) {
|
|
13426
13814
|
const now = Date.now();
|
|
@@ -13432,7 +13820,7 @@ async function loadDirNames(target) {
|
|
|
13432
13820
|
}
|
|
13433
13821
|
let dirents;
|
|
13434
13822
|
try {
|
|
13435
|
-
dirents = await
|
|
13823
|
+
dirents = await fs21.promises.readdir(target, { withFileTypes: true });
|
|
13436
13824
|
} catch {
|
|
13437
13825
|
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
|
|
13438
13826
|
}
|
|
@@ -13455,11 +13843,11 @@ async function loadDirNames(target) {
|
|
|
13455
13843
|
return entries;
|
|
13456
13844
|
}
|
|
13457
13845
|
async function toDirEntry(dir, e) {
|
|
13458
|
-
const full =
|
|
13846
|
+
const full = path27.join(dir, e.name);
|
|
13459
13847
|
let isDir = e.isDir;
|
|
13460
13848
|
if (e.isLink) {
|
|
13461
13849
|
try {
|
|
13462
|
-
isDir = (await
|
|
13850
|
+
isDir = (await fs21.promises.stat(full)).isDirectory();
|
|
13463
13851
|
} catch {
|
|
13464
13852
|
}
|
|
13465
13853
|
}
|
|
@@ -13467,10 +13855,10 @@ async function toDirEntry(dir, e) {
|
|
|
13467
13855
|
}
|
|
13468
13856
|
async function searchDirs(input, query, limit) {
|
|
13469
13857
|
const base = (input ?? "").trim() || os4.homedir();
|
|
13470
|
-
const target =
|
|
13858
|
+
const target = path27.resolve(base);
|
|
13471
13859
|
let st;
|
|
13472
13860
|
try {
|
|
13473
|
-
st = await
|
|
13861
|
+
st = await fs21.promises.stat(target);
|
|
13474
13862
|
} catch {
|
|
13475
13863
|
throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
|
|
13476
13864
|
}
|
|
@@ -13500,7 +13888,7 @@ async function searchDirs(input, query, limit) {
|
|
|
13500
13888
|
);
|
|
13501
13889
|
const hit = scored.slice(0, max);
|
|
13502
13890
|
const entries = await Promise.all(hit.map(({ entry }) => toDirEntry(target, entry)));
|
|
13503
|
-
const parentDir =
|
|
13891
|
+
const parentDir = path27.dirname(target);
|
|
13504
13892
|
return {
|
|
13505
13893
|
path: target,
|
|
13506
13894
|
parent: parentDir !== target ? parentDir : null,
|
|
@@ -13511,52 +13899,52 @@ async function searchDirs(input, query, limit) {
|
|
|
13511
13899
|
};
|
|
13512
13900
|
}
|
|
13513
13901
|
function makeDirIn(parentInput, name) {
|
|
13514
|
-
const parent =
|
|
13902
|
+
const parent = path27.resolve((parentInput ?? "").trim() || os4.homedir());
|
|
13515
13903
|
const clean = (name ?? "").trim();
|
|
13516
13904
|
if (!clean) throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u4E3A\u7A7A");
|
|
13517
13905
|
if (clean === "." || clean === "..") throw new Error("\u76EE\u5F55\u540D\u975E\u6CD5");
|
|
13518
|
-
const target =
|
|
13519
|
-
const rel =
|
|
13520
|
-
if (!rel || rel.startsWith("..") ||
|
|
13906
|
+
const target = path27.resolve(parent, clean);
|
|
13907
|
+
const rel = path27.relative(parent, target);
|
|
13908
|
+
if (!rel || rel.startsWith("..") || path27.isAbsolute(rel)) {
|
|
13521
13909
|
throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u8D8A\u51FA\u5F53\u524D\u76EE\u5F55");
|
|
13522
13910
|
}
|
|
13523
13911
|
let st = null;
|
|
13524
13912
|
try {
|
|
13525
|
-
st =
|
|
13913
|
+
st = fs21.statSync(target);
|
|
13526
13914
|
} catch {
|
|
13527
13915
|
st = null;
|
|
13528
13916
|
}
|
|
13529
13917
|
if (st && !st.isDirectory()) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u6587\u4EF6: ${target}`);
|
|
13530
13918
|
try {
|
|
13531
|
-
|
|
13919
|
+
fs21.mkdirSync(target, { recursive: true });
|
|
13532
13920
|
} catch (e) {
|
|
13533
13921
|
throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
|
|
13534
13922
|
}
|
|
13535
13923
|
invalidateDirNames(parent);
|
|
13536
|
-
return { name:
|
|
13924
|
+
return { name: path27.basename(target), path: target, type: "dir" };
|
|
13537
13925
|
}
|
|
13538
13926
|
|
|
13539
13927
|
// src/server/projectFiles.ts
|
|
13540
13928
|
init_esm_shims();
|
|
13541
|
-
import * as
|
|
13542
|
-
import * as
|
|
13929
|
+
import * as fs22 from "fs";
|
|
13930
|
+
import * as path28 from "path";
|
|
13543
13931
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
13544
13932
|
var MAX_DOWNLOAD_BYTES = 16 * 1024 * 1024;
|
|
13545
13933
|
var MAX_ENTRIES2 = 2e3;
|
|
13546
13934
|
function toPosix(p) {
|
|
13547
|
-
return p.split(
|
|
13935
|
+
return p.split(path28.sep).join("/");
|
|
13548
13936
|
}
|
|
13549
13937
|
function projectRoot(workspaceRoot) {
|
|
13550
13938
|
const r = (workspaceRoot ?? "").trim();
|
|
13551
13939
|
if (!r) {
|
|
13552
13940
|
throw new Error("\u9879\u76EE\u672A\u8BBE\u7F6E\u5DE5\u4F5C\u533A\u76EE\u5F55\uFF08workspaceRoot\uFF09\uFF0C\u65E0\u6CD5\u6D4F\u89C8\u6587\u4EF6");
|
|
13553
13941
|
}
|
|
13554
|
-
const abs =
|
|
13555
|
-
if (!
|
|
13942
|
+
const abs = path28.resolve(r);
|
|
13943
|
+
if (!fs22.existsSync(abs)) {
|
|
13556
13944
|
throw new Error(`\u5DE5\u4F5C\u533A\u76EE\u5F55\u4E0D\u5B58\u5728: ${abs}`);
|
|
13557
13945
|
}
|
|
13558
13946
|
try {
|
|
13559
|
-
return
|
|
13947
|
+
return fs22.realpathSync(abs);
|
|
13560
13948
|
} catch {
|
|
13561
13949
|
return abs;
|
|
13562
13950
|
}
|
|
@@ -13574,37 +13962,37 @@ function listProjectFiles(workspaceRoot, relPath) {
|
|
|
13574
13962
|
const dir = resolveWithin(base, relPath ?? "");
|
|
13575
13963
|
let st;
|
|
13576
13964
|
try {
|
|
13577
|
-
st =
|
|
13965
|
+
st = fs22.statSync(dir);
|
|
13578
13966
|
} catch {
|
|
13579
|
-
throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(
|
|
13967
|
+
throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(path28.relative(base, dir)) || "/"}`);
|
|
13580
13968
|
}
|
|
13581
13969
|
if (!st.isDirectory()) throw new Error("\u76EE\u6807\u4E0D\u662F\u76EE\u5F55");
|
|
13582
13970
|
let dirents;
|
|
13583
13971
|
try {
|
|
13584
|
-
dirents =
|
|
13972
|
+
dirents = fs22.readdirSync(dir, { withFileTypes: true });
|
|
13585
13973
|
} catch {
|
|
13586
13974
|
throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09");
|
|
13587
13975
|
}
|
|
13588
13976
|
const entries = dirents.map((d) => {
|
|
13589
|
-
const full =
|
|
13977
|
+
const full = path28.join(dir, d.name);
|
|
13590
13978
|
let isDir = d.isDirectory();
|
|
13591
13979
|
if (d.isSymbolicLink()) {
|
|
13592
13980
|
try {
|
|
13593
|
-
isDir =
|
|
13981
|
+
isDir = fs22.statSync(full).isDirectory();
|
|
13594
13982
|
} catch {
|
|
13595
13983
|
}
|
|
13596
13984
|
}
|
|
13597
13985
|
let size = 0;
|
|
13598
13986
|
let mtime = "";
|
|
13599
13987
|
try {
|
|
13600
|
-
const s =
|
|
13988
|
+
const s = fs22.statSync(full);
|
|
13601
13989
|
size = s.size;
|
|
13602
13990
|
mtime = s.mtime.toISOString();
|
|
13603
13991
|
} catch {
|
|
13604
13992
|
}
|
|
13605
13993
|
return {
|
|
13606
13994
|
name: d.name,
|
|
13607
|
-
path: toPosix(
|
|
13995
|
+
path: toPosix(path28.relative(base, full)),
|
|
13608
13996
|
type: isDir ? "dir" : "file",
|
|
13609
13997
|
size,
|
|
13610
13998
|
mtime
|
|
@@ -13613,12 +14001,12 @@ function listProjectFiles(workspaceRoot, relPath) {
|
|
|
13613
14001
|
entries.sort(
|
|
13614
14002
|
(a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
|
|
13615
14003
|
);
|
|
13616
|
-
const relDir = toPosix(
|
|
13617
|
-
const parentAbs =
|
|
14004
|
+
const relDir = toPosix(path28.relative(base, dir));
|
|
14005
|
+
const parentAbs = path28.dirname(dir);
|
|
13618
14006
|
return {
|
|
13619
14007
|
root: base,
|
|
13620
14008
|
path: relDir,
|
|
13621
|
-
parent: isWithinRoot(base, parentAbs) ? toPosix(
|
|
14009
|
+
parent: isWithinRoot(base, parentAbs) ? toPosix(path28.relative(base, parentAbs)) : null,
|
|
13622
14010
|
entries: entries.slice(0, MAX_ENTRIES2)
|
|
13623
14011
|
};
|
|
13624
14012
|
}
|
|
@@ -13627,17 +14015,17 @@ function readProjectFile(workspaceRoot, relPath) {
|
|
|
13627
14015
|
const file = resolveWithin(base, relPath ?? "");
|
|
13628
14016
|
let st;
|
|
13629
14017
|
try {
|
|
13630
|
-
st =
|
|
14018
|
+
st = fs22.statSync(file);
|
|
13631
14019
|
} catch {
|
|
13632
14020
|
throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
13633
14021
|
}
|
|
13634
14022
|
if (!st.isFile()) throw new Error("\u76EE\u6807\u4E0D\u662F\u6587\u4EF6");
|
|
13635
|
-
const buf =
|
|
14023
|
+
const buf = fs22.readFileSync(file);
|
|
13636
14024
|
const binary = isBinary(buf);
|
|
13637
14025
|
const truncated = buf.length > MAX_FILE_BYTES;
|
|
13638
14026
|
const slice = truncated ? buf.subarray(0, MAX_FILE_BYTES) : buf;
|
|
13639
14027
|
return {
|
|
13640
|
-
path: toPosix(
|
|
14028
|
+
path: toPosix(path28.relative(base, file)),
|
|
13641
14029
|
size: st.size,
|
|
13642
14030
|
binary,
|
|
13643
14031
|
truncated,
|
|
@@ -13649,7 +14037,7 @@ function downloadProjectFile(workspaceRoot, relPath) {
|
|
|
13649
14037
|
const file = resolveWithin(base, relPath ?? "");
|
|
13650
14038
|
let st;
|
|
13651
14039
|
try {
|
|
13652
|
-
st =
|
|
14040
|
+
st = fs22.statSync(file);
|
|
13653
14041
|
} catch {
|
|
13654
14042
|
throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
13655
14043
|
}
|
|
@@ -13657,10 +14045,10 @@ function downloadProjectFile(workspaceRoot, relPath) {
|
|
|
13657
14045
|
if (st.size > MAX_DOWNLOAD_BYTES) {
|
|
13658
14046
|
throw new Error(`\u6587\u4EF6\u8D85\u8FC7\u4E0B\u8F7D\u4E0A\u9650\uFF08${MAX_DOWNLOAD_BYTES / (1024 * 1024)}MB\uFF09`);
|
|
13659
14047
|
}
|
|
13660
|
-
const buf =
|
|
14048
|
+
const buf = fs22.readFileSync(file);
|
|
13661
14049
|
return {
|
|
13662
|
-
path: toPosix(
|
|
13663
|
-
name:
|
|
14050
|
+
path: toPosix(path28.relative(base, file)),
|
|
14051
|
+
name: path28.basename(file),
|
|
13664
14052
|
size: buf.length,
|
|
13665
14053
|
base64: buf.toString("base64")
|
|
13666
14054
|
};
|
|
@@ -13682,12 +14070,12 @@ function writeProjectFile(workspaceRoot, relPath, content) {
|
|
|
13682
14070
|
throw new Error("\u5185\u5BB9\u8D85\u8FC7 2MB \u4E0A\u9650\uFF0C\u65E0\u6CD5\u4FDD\u5B58");
|
|
13683
14071
|
}
|
|
13684
14072
|
try {
|
|
13685
|
-
|
|
13686
|
-
|
|
14073
|
+
fs22.mkdirSync(path28.dirname(file), { recursive: true });
|
|
14074
|
+
fs22.writeFileSync(file, buf);
|
|
13687
14075
|
} catch (e) {
|
|
13688
14076
|
throw new Error(`\u5199\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
13689
14077
|
}
|
|
13690
|
-
return { path: toPosix(
|
|
14078
|
+
return { path: toPosix(path28.relative(base, file)), size: buf.length };
|
|
13691
14079
|
}
|
|
13692
14080
|
function createProjectDir(workspaceRoot, relPath) {
|
|
13693
14081
|
const base = projectRoot(workspaceRoot);
|
|
@@ -13695,26 +14083,26 @@ function createProjectDir(workspaceRoot, relPath) {
|
|
|
13695
14083
|
if (!rel) throw new Error("\u76EE\u5F55\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
|
|
13696
14084
|
const dir = resolveWithin(base, rel);
|
|
13697
14085
|
try {
|
|
13698
|
-
|
|
14086
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
13699
14087
|
} catch (e) {
|
|
13700
14088
|
throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
13701
14089
|
}
|
|
13702
|
-
return { path: toPosix(
|
|
14090
|
+
return { path: toPosix(path28.relative(base, dir)) };
|
|
13703
14091
|
}
|
|
13704
14092
|
function createProjectFile(workspaceRoot, relPath, content = "") {
|
|
13705
14093
|
const base = projectRoot(workspaceRoot);
|
|
13706
14094
|
const rel = (relPath ?? "").trim();
|
|
13707
14095
|
if (!rel) throw new Error("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
|
|
13708
14096
|
const file = resolveWithin(base, rel);
|
|
13709
|
-
if (
|
|
14097
|
+
if (fs22.existsSync(file)) throw new Error("\u6587\u4EF6\u5DF2\u5B58\u5728");
|
|
13710
14098
|
const buf = Buffer.from(String(content ?? ""), "utf-8");
|
|
13711
14099
|
try {
|
|
13712
|
-
|
|
13713
|
-
|
|
14100
|
+
fs22.mkdirSync(path28.dirname(file), { recursive: true });
|
|
14101
|
+
fs22.writeFileSync(file, buf);
|
|
13714
14102
|
} catch (e) {
|
|
13715
14103
|
throw new Error(`\u521B\u5EFA\u6587\u4EF6\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
13716
14104
|
}
|
|
13717
|
-
return { path: toPosix(
|
|
14105
|
+
return { path: toPosix(path28.relative(base, file)), size: buf.length };
|
|
13718
14106
|
}
|
|
13719
14107
|
function searchProjectFiles(workspaceRoot, query) {
|
|
13720
14108
|
const base = projectRoot(workspaceRoot);
|
|
@@ -13737,7 +14125,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
13737
14125
|
if (out.length >= LIMIT) return;
|
|
13738
14126
|
let dirents;
|
|
13739
14127
|
try {
|
|
13740
|
-
dirents =
|
|
14128
|
+
dirents = fs22.readdirSync(dir, { withFileTypes: true });
|
|
13741
14129
|
} catch {
|
|
13742
14130
|
return;
|
|
13743
14131
|
}
|
|
@@ -13748,7 +14136,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
13748
14136
|
let isDir = d.isDirectory();
|
|
13749
14137
|
if (d.isSymbolicLink()) {
|
|
13750
14138
|
try {
|
|
13751
|
-
isDir =
|
|
14139
|
+
isDir = fs22.statSync(path28.join(dir, d.name)).isDirectory();
|
|
13752
14140
|
} catch {
|
|
13753
14141
|
isDir = false;
|
|
13754
14142
|
}
|
|
@@ -13756,7 +14144,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
13756
14144
|
if (d.name.toLowerCase().includes(q)) {
|
|
13757
14145
|
out.push({ path: rel, type: isDir ? "dir" : "file" });
|
|
13758
14146
|
}
|
|
13759
|
-
if (isDir) walk(
|
|
14147
|
+
if (isDir) walk(path28.join(dir, d.name), rel);
|
|
13760
14148
|
}
|
|
13761
14149
|
};
|
|
13762
14150
|
walk(base, "");
|
|
@@ -13766,7 +14154,7 @@ function searchProjectFiles(workspaceRoot, query) {
|
|
|
13766
14154
|
// src/server/projectOps.ts
|
|
13767
14155
|
init_esm_shims();
|
|
13768
14156
|
import * as os5 from "os";
|
|
13769
|
-
import * as
|
|
14157
|
+
import * as path29 from "path";
|
|
13770
14158
|
var MAX_STDOUT2 = 2e4;
|
|
13771
14159
|
var MAX_STDERR2 = 1e4;
|
|
13772
14160
|
var MAX_BUFFER3 = 1024 * 1024 * 5;
|
|
@@ -13780,7 +14168,7 @@ function resolveInvocation2(spec, prepared) {
|
|
|
13780
14168
|
}
|
|
13781
14169
|
function workRoot(workspaceRoot) {
|
|
13782
14170
|
const r = (workspaceRoot ?? "").trim();
|
|
13783
|
-
return r ?
|
|
14171
|
+
return r ? path29.resolve(r) : os5.homedir();
|
|
13784
14172
|
}
|
|
13785
14173
|
async function runProjectCommand(workspaceRoot, command, timeoutSeconds) {
|
|
13786
14174
|
const cmd = String(command ?? "");
|
|
@@ -13900,6 +14288,92 @@ async function projectGitPush(workspaceRoot, repoPath) {
|
|
|
13900
14288
|
});
|
|
13901
14289
|
}
|
|
13902
14290
|
|
|
14291
|
+
// src/server/gitCommitMessage.ts
|
|
14292
|
+
init_esm_shims();
|
|
14293
|
+
import * as crypto5 from "crypto";
|
|
14294
|
+
var MAX_DIFF_CHARS = 12e3;
|
|
14295
|
+
var MAX_MESSAGE_CHARS = 200;
|
|
14296
|
+
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
|
|
14297
|
+
|
|
14298
|
+
\u8981\u6C42\uFF1A
|
|
14299
|
+
1. \u4F7F\u7528 Conventional Commits \u98CE\u683C\uFF1Atype(scope): \u63CF\u8FF0\uFF08type \u5982 feat / fix / refactor / docs / test / chore / perf / build / ci\uFF09\uFF1B
|
|
14300
|
+
2. scope \u53D6\u53D8\u66F4\u6700\u96C6\u4E2D\u7684\u6A21\u5757\u6216\u76EE\u5F55\uFF0C\u65E0\u6CD5\u5224\u65AD\u65F6\u7701\u7565\uFF1B
|
|
14301
|
+
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
|
|
14302
|
+
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
|
|
14303
|
+
5. \u4E0D\u8D85\u8FC7 72 \u4E2A\u5B57\u7B26\u3002`;
|
|
14304
|
+
function genReqId2() {
|
|
14305
|
+
return crypto5.randomBytes(6).toString("base64url");
|
|
14306
|
+
}
|
|
14307
|
+
function cleanCommitMessage(raw) {
|
|
14308
|
+
let text = String(raw ?? "").trim();
|
|
14309
|
+
if (!text) return "";
|
|
14310
|
+
text = text.replace(/^```[^\n]*\n?/, "").replace(/\n?```$/, "").trim();
|
|
14311
|
+
const first = text.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0) ?? "";
|
|
14312
|
+
return first.replace(/^(commit message|提交说明|提交信息|message)\s*[::]\s*/i, "").replace(/^["'“”`]+|["'“”`]+$/g, "").replace(/\s+/g, " ").trim().slice(0, MAX_MESSAGE_CHARS);
|
|
14313
|
+
}
|
|
14314
|
+
async function collectDiff(workspaceRoot, repo) {
|
|
14315
|
+
const staged = await projectGitDiff(workspaceRoot, void 0, true, repo);
|
|
14316
|
+
if (!staged.error && String(staged.diff ?? "").trim()) {
|
|
14317
|
+
return { text: String(staged.diff), source: "staged" };
|
|
14318
|
+
}
|
|
14319
|
+
const worktree = await projectGitDiff(workspaceRoot, void 0, false, repo);
|
|
14320
|
+
if (!worktree.error && String(worktree.diff ?? "").trim()) {
|
|
14321
|
+
return { text: String(worktree.diff), source: "worktree" };
|
|
14322
|
+
}
|
|
14323
|
+
const status = await projectGitStatus(workspaceRoot, void 0, repo);
|
|
14324
|
+
if (status.error) throw new ApiError(400, "git_error", String(status.error));
|
|
14325
|
+
const entries = status.entries ?? [];
|
|
14326
|
+
if (entries.length === 0) throw new ApiError(400, "no_changes", "\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4");
|
|
14327
|
+
return {
|
|
14328
|
+
text: entries.map((e) => `${e.status ?? ""} ${e.file ?? ""}`.trim()).join("\n"),
|
|
14329
|
+
source: "worktree"
|
|
14330
|
+
};
|
|
14331
|
+
}
|
|
14332
|
+
async function generateGitCommitMessage(opts) {
|
|
14333
|
+
const { store, tenant } = opts;
|
|
14334
|
+
const { text, source } = await collectDiff(opts.workspaceRoot, opts.repo);
|
|
14335
|
+
const truncated = text.length > MAX_DIFF_CHARS;
|
|
14336
|
+
const clipped = text.slice(0, MAX_DIFF_CHARS);
|
|
14337
|
+
const eff = resolveSessionProvider(store, tenant);
|
|
14338
|
+
const apiKey = eff.apiKey || process.env.OPENAI_API_KEY || "";
|
|
14339
|
+
const model = (eff.model || tenant.model || process.env.OPENAI_MODEL || process.env.MODEL || "").trim();
|
|
14340
|
+
if (!apiKey) {
|
|
14341
|
+
throw new ApiError(
|
|
14342
|
+
400,
|
|
14343
|
+
"llm_not_configured",
|
|
14344
|
+
"\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"
|
|
14345
|
+
);
|
|
14346
|
+
}
|
|
14347
|
+
if (!model) {
|
|
14348
|
+
throw new ApiError(
|
|
14349
|
+
400,
|
|
14350
|
+
"llm_not_configured",
|
|
14351
|
+
"\u5F53\u524D\u8282\u70B9\u672A\u914D\u7F6E\u6A21\u578B\uFF0C\u8BF7\u5148\u5728\u8282\u70B9\u914D\u7F6E\u4E2D\u9009\u62E9\u6A21\u578B"
|
|
14352
|
+
);
|
|
14353
|
+
}
|
|
14354
|
+
const provider = new OpenAIProvider({ apiKey, baseURL: eff.baseURL });
|
|
14355
|
+
const raw = await provider.chat(
|
|
14356
|
+
model,
|
|
14357
|
+
[
|
|
14358
|
+
{ role: "system", content: GEN_COMMIT_MESSAGE_INSTRUCTIONS },
|
|
14359
|
+
{
|
|
14360
|
+
role: "user",
|
|
14361
|
+
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
|
|
14362
|
+
|
|
14363
|
+
${clipped}`
|
|
14364
|
+
}
|
|
14365
|
+
],
|
|
14366
|
+
{},
|
|
14367
|
+
{ reqId: `llm_commit_${Date.now()}_${genReqId2()}`, logTime: (/* @__PURE__ */ new Date()).toISOString() },
|
|
14368
|
+
// 不设 maxTokens:推理模型的思考会占用输出配额,过小会导致 content 为空;
|
|
14369
|
+
// 提示词已约束只输出一行短说明。
|
|
14370
|
+
{}
|
|
14371
|
+
);
|
|
14372
|
+
const message = cleanCommitMessage(raw ?? "");
|
|
14373
|
+
if (!message) throw new ApiError(502, "llm_empty", "AI \u672A\u8FD4\u56DE\u6709\u6548\u7684\u63D0\u4EA4\u8BF4\u660E\uFF0C\u8BF7\u91CD\u8BD5");
|
|
14374
|
+
return message;
|
|
14375
|
+
}
|
|
14376
|
+
|
|
13903
14377
|
// src/server/toolsCatalog.ts
|
|
13904
14378
|
init_esm_shims();
|
|
13905
14379
|
function buildToolCatalog(workspace) {
|
|
@@ -14005,11 +14479,11 @@ function projectStats(manager, tenantId) {
|
|
|
14005
14479
|
|
|
14006
14480
|
// src/server/nodeHost.ts
|
|
14007
14481
|
init_esm_shims();
|
|
14008
|
-
import * as
|
|
14482
|
+
import * as fs23 from "fs";
|
|
14009
14483
|
import * as os6 from "os";
|
|
14010
14484
|
function diskInfo(pathStr) {
|
|
14011
14485
|
try {
|
|
14012
|
-
const st =
|
|
14486
|
+
const st = fs23.statfsSync(pathStr);
|
|
14013
14487
|
return {
|
|
14014
14488
|
path: pathStr,
|
|
14015
14489
|
total: Number(st.blocks) * Number(st.bsize),
|
|
@@ -14081,7 +14555,7 @@ function buildNodeHost(store, manager, diskPath) {
|
|
|
14081
14555
|
arch: process.arch,
|
|
14082
14556
|
release: os6.release(),
|
|
14083
14557
|
uptimeSec: Math.round(os6.uptime()),
|
|
14084
|
-
version: "0.2.
|
|
14558
|
+
version: "0.2.7",
|
|
14085
14559
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
14086
14560
|
cpu: {
|
|
14087
14561
|
count: cpus2.length,
|
|
@@ -14656,7 +15130,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
14656
15130
|
applyProvidersPatch(tenant, raw);
|
|
14657
15131
|
if (typeof raw.workspaceRoot === "string") {
|
|
14658
15132
|
const ws = raw.workspaceRoot.trim();
|
|
14659
|
-
if (ws) tenant.workspaceRoot =
|
|
15133
|
+
if (ws) tenant.workspaceRoot = path30.resolve(ws);
|
|
14660
15134
|
else delete tenant.workspaceRoot;
|
|
14661
15135
|
}
|
|
14662
15136
|
if (typeof raw.name === "string" && raw.name.trim()) {
|
|
@@ -15065,6 +15539,25 @@ function createApp(store, manager, opts = {}) {
|
|
|
15065
15539
|
}
|
|
15066
15540
|
}
|
|
15067
15541
|
},
|
|
15542
|
+
{
|
|
15543
|
+
method: "POST",
|
|
15544
|
+
pattern: ["git", "commit-message"],
|
|
15545
|
+
fn: async (_req, res, { tenant, body }) => {
|
|
15546
|
+
const raw = body ?? {};
|
|
15547
|
+
try {
|
|
15548
|
+
const message = await generateGitCommitMessage({
|
|
15549
|
+
store,
|
|
15550
|
+
tenant,
|
|
15551
|
+
workspaceRoot: tenant.workspaceRoot ?? null,
|
|
15552
|
+
repo: String(raw.repo ?? "") || void 0
|
|
15553
|
+
});
|
|
15554
|
+
ok(res, { message });
|
|
15555
|
+
} catch (e) {
|
|
15556
|
+
if (e instanceof ApiError) throw e;
|
|
15557
|
+
throw new ApiError(400, "bad_request", e instanceof Error ? e.message : String(e));
|
|
15558
|
+
}
|
|
15559
|
+
}
|
|
15560
|
+
},
|
|
15068
15561
|
{
|
|
15069
15562
|
method: "POST",
|
|
15070
15563
|
pattern: ["git", "checkout"],
|
|
@@ -15588,10 +16081,10 @@ function createApp(store, manager, opts = {}) {
|
|
|
15588
16081
|
function handle(req2, res) {
|
|
15589
16082
|
const startedAt2 = Date.now();
|
|
15590
16083
|
const url = new URL(req2.url ?? "/", "http://localhost");
|
|
15591
|
-
const
|
|
16084
|
+
const path44 = url.pathname;
|
|
15592
16085
|
const method = req2.method ?? "GET";
|
|
15593
16086
|
console.log(
|
|
15594
|
-
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2192 ${method} ${
|
|
16087
|
+
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2192 ${method} ${path44}${url.search}`
|
|
15595
16088
|
);
|
|
15596
16089
|
let tenantId = null;
|
|
15597
16090
|
let logged = false;
|
|
@@ -15599,7 +16092,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
15599
16092
|
if (logged) return;
|
|
15600
16093
|
logged = true;
|
|
15601
16094
|
console.log(
|
|
15602
|
-
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2190 ${method} ${
|
|
16095
|
+
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} \u2190 ${method} ${path44} ${extra} (${Date.now() - startedAt2}ms) tenant=${tenantId ?? "-"}`
|
|
15603
16096
|
);
|
|
15604
16097
|
};
|
|
15605
16098
|
res.on("finish", () => logDone(`HTTP ${res.statusCode}`));
|
|
@@ -15610,16 +16103,16 @@ function createApp(store, manager, opts = {}) {
|
|
|
15610
16103
|
res.end();
|
|
15611
16104
|
return;
|
|
15612
16105
|
}
|
|
15613
|
-
if (req2.method === "GET" &&
|
|
16106
|
+
if (req2.method === "GET" && path44 === "/healthz") {
|
|
15614
16107
|
json(res, 200, {
|
|
15615
16108
|
status: "ok",
|
|
15616
16109
|
// 版本让运维/探针能确认线上跑的是哪个构建(更新与回滚据此判断)
|
|
15617
|
-
version: "0.2.
|
|
16110
|
+
version: "0.2.7",
|
|
15618
16111
|
features: { mcp: hasServerMcpConfig() }
|
|
15619
16112
|
});
|
|
15620
16113
|
return;
|
|
15621
16114
|
}
|
|
15622
|
-
if (hubProxyEnabled(opts.host) && isHubProxyPath(
|
|
16115
|
+
if (hubProxyEnabled(opts.host) && isHubProxyPath(path44)) {
|
|
15623
16116
|
void handleHubProxy(req2, res).catch(() => {
|
|
15624
16117
|
if (!res.headersSent) {
|
|
15625
16118
|
res.writeHead(502, { "Content-Type": "application/json; charset=utf-8" });
|
|
@@ -15630,11 +16123,11 @@ function createApp(store, manager, opts = {}) {
|
|
|
15630
16123
|
});
|
|
15631
16124
|
return;
|
|
15632
16125
|
}
|
|
15633
|
-
if (!
|
|
16126
|
+
if (!path44.startsWith(API_PREFIX)) {
|
|
15634
16127
|
json(res, 404, { error: { code: "not_found", message: "\u672A\u77E5\u8DEF\u5F84" } });
|
|
15635
16128
|
return;
|
|
15636
16129
|
}
|
|
15637
|
-
const segs =
|
|
16130
|
+
const segs = path44.slice(API_PREFIX.length).split("/").filter(Boolean);
|
|
15638
16131
|
const apiKey = extractApiKey(req2);
|
|
15639
16132
|
if (!apiKey) {
|
|
15640
16133
|
json(res, 401, {
|
|
@@ -15699,7 +16192,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
15699
16192
|
).catch((err) => {
|
|
15700
16193
|
const msg = err instanceof Error ? err.message : String(err);
|
|
15701
16194
|
console.log(
|
|
15702
|
-
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} ! ${method} ${
|
|
16195
|
+
`[api] ${(/* @__PURE__ */ new Date()).toISOString()} ! ${method} ${path44} ERROR: ${msg}`
|
|
15703
16196
|
);
|
|
15704
16197
|
error(res, err);
|
|
15705
16198
|
});
|
|
@@ -16214,6 +16707,17 @@ async function dispatchFleetCommand(deps, env3) {
|
|
|
16214
16707
|
)
|
|
16215
16708
|
);
|
|
16216
16709
|
}
|
|
16710
|
+
case "git_commit_message": {
|
|
16711
|
+
const t = project();
|
|
16712
|
+
return ok2({
|
|
16713
|
+
message: await generateGitCommitMessage({
|
|
16714
|
+
store,
|
|
16715
|
+
tenant: t,
|
|
16716
|
+
workspaceRoot: t.workspaceRoot ?? null,
|
|
16717
|
+
repo: gitRepo(payload.repo)
|
|
16718
|
+
})
|
|
16719
|
+
});
|
|
16720
|
+
}
|
|
16217
16721
|
case "git_checkout": {
|
|
16218
16722
|
const t = project();
|
|
16219
16723
|
return ok2(
|
|
@@ -16854,6 +17358,18 @@ var READONLY_CMD_TYPES = /* @__PURE__ */ new Set([
|
|
|
16854
17358
|
"git_show"
|
|
16855
17359
|
]);
|
|
16856
17360
|
var CMD_CACHE_TTL_MS = 1500;
|
|
17361
|
+
var STATE_CHANGING_EVENTS = /* @__PURE__ */ new Set([
|
|
17362
|
+
"turn_start",
|
|
17363
|
+
"turn_end",
|
|
17364
|
+
"permission_required",
|
|
17365
|
+
"ask_user_question",
|
|
17366
|
+
"plan_proposed",
|
|
17367
|
+
"directory_requested",
|
|
17368
|
+
"error",
|
|
17369
|
+
"interrupted",
|
|
17370
|
+
"queue_steering",
|
|
17371
|
+
"session_named"
|
|
17372
|
+
]);
|
|
16857
17373
|
var READONLY_CONCURRENCY = 4;
|
|
16858
17374
|
var BATCH_MAX_SUBS = 64;
|
|
16859
17375
|
var BATCH_SUB_TIMEOUT_MS = 7e3;
|
|
@@ -17089,6 +17605,20 @@ var FleetNodeBridge = class {
|
|
|
17089
17605
|
this.writeEpoch += 1;
|
|
17090
17606
|
this.cmdResultCache.clear();
|
|
17091
17607
|
}
|
|
17608
|
+
/**
|
|
17609
|
+
* 会话快照缓存失效(事件/通知驱动的状态变化)。
|
|
17610
|
+
*
|
|
17611
|
+
* 与写操作不同,这些变化只影响会话相关查询;文件树 / Git 等重查询的缓存
|
|
17612
|
+
* 保持有效,避免多会话并行运行时把整个只读缓存反复清空。
|
|
17613
|
+
*/
|
|
17614
|
+
_invalidateSessionCache() {
|
|
17615
|
+
this.writeEpoch += 1;
|
|
17616
|
+
for (const key of [...this.cmdResultCache.keys()]) {
|
|
17617
|
+
if (/^(get_state|get_events|list_sessions|get_session_states)\|/.test(key)) {
|
|
17618
|
+
this.cmdResultCache.delete(key);
|
|
17619
|
+
}
|
|
17620
|
+
}
|
|
17621
|
+
}
|
|
17092
17622
|
close() {
|
|
17093
17623
|
this.closing = true;
|
|
17094
17624
|
if (this.usageTimer) {
|
|
@@ -17117,6 +17647,7 @@ var FleetNodeBridge = class {
|
|
|
17117
17647
|
}
|
|
17118
17648
|
/** 通知广播(SessionManager 注入的 notify 回调) */
|
|
17119
17649
|
notify(tenantId, ev) {
|
|
17650
|
+
this._invalidateSessionCache();
|
|
17120
17651
|
const payload = ev.kind === "approval_request" ? {
|
|
17121
17652
|
...ev,
|
|
17122
17653
|
projectId: tenantId,
|
|
@@ -17129,6 +17660,7 @@ var FleetNodeBridge = class {
|
|
|
17129
17660
|
}
|
|
17130
17661
|
/** 事件桥接(SessionManager 注入的 onEvent 回调);发布按窗口批量合并 */
|
|
17131
17662
|
onEvent(tenantId, sessionId, entry) {
|
|
17663
|
+
if (STATE_CHANGING_EVENTS.has(entry.type)) this._invalidateSessionCache();
|
|
17132
17664
|
if (entry.type === "turn_end") this.stats.turns += 1;
|
|
17133
17665
|
else if (entry.type === "tool_finished") this.stats.toolCalls += 1;
|
|
17134
17666
|
else if (entry.type === "error") this.stats.errors += 1;
|
|
@@ -17213,7 +17745,7 @@ var FleetNodeBridge = class {
|
|
|
17213
17745
|
label: this.opts.deps.store.loadNodeLabel() ?? void 0,
|
|
17214
17746
|
platform: process.platform,
|
|
17215
17747
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
17216
|
-
version: "0.2.
|
|
17748
|
+
version: "0.2.7",
|
|
17217
17749
|
ip: primaryIp()
|
|
17218
17750
|
};
|
|
17219
17751
|
}
|
|
@@ -17247,7 +17779,10 @@ var FleetNodeBridge = class {
|
|
|
17247
17779
|
sessionId: s.sessionId,
|
|
17248
17780
|
name: s.name ?? null,
|
|
17249
17781
|
status: s.status,
|
|
17250
|
-
lastUserMessage: lastUserMessage(s)
|
|
17782
|
+
lastUserMessage: lastUserMessage(s),
|
|
17783
|
+
// 控制面的聚合邮件靠它判断「这台机器最近有没有在干活」;
|
|
17784
|
+
// 状态跃迁可能被 info 节流跳过,完成时间不会。
|
|
17785
|
+
lastCompletedAt: s.lastCompletedAt ?? null
|
|
17251
17786
|
});
|
|
17252
17787
|
}
|
|
17253
17788
|
} catch {
|
|
@@ -17265,7 +17800,7 @@ var FleetNodeBridge = class {
|
|
|
17265
17800
|
label: this.opts.deps.store.loadNodeLabel() ?? void 0,
|
|
17266
17801
|
platform: process.platform,
|
|
17267
17802
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
17268
|
-
version: "0.2.
|
|
17803
|
+
version: "0.2.7",
|
|
17269
17804
|
ip: primaryIp(),
|
|
17270
17805
|
projects,
|
|
17271
17806
|
totals,
|
|
@@ -18034,8 +18569,8 @@ init_esm_shims();
|
|
|
18034
18569
|
|
|
18035
18570
|
// src/server/qq/bindings.ts
|
|
18036
18571
|
init_esm_shims();
|
|
18037
|
-
import * as
|
|
18038
|
-
import * as
|
|
18572
|
+
import * as fs24 from "fs";
|
|
18573
|
+
import * as path31 from "path";
|
|
18039
18574
|
function conversationKey(target) {
|
|
18040
18575
|
switch (target.kind) {
|
|
18041
18576
|
case "c2c":
|
|
@@ -18074,12 +18609,12 @@ var QqBindingsStore = class _QqBindingsStore {
|
|
|
18074
18609
|
cache = null;
|
|
18075
18610
|
/** 基于 DATA_DIR 构造 */
|
|
18076
18611
|
static in(dataDir) {
|
|
18077
|
-
return new _QqBindingsStore(
|
|
18612
|
+
return new _QqBindingsStore(path31.join(dataDir, "qq", "bindings.json"));
|
|
18078
18613
|
}
|
|
18079
18614
|
load() {
|
|
18080
18615
|
if (this.cache) return this.cache;
|
|
18081
18616
|
try {
|
|
18082
|
-
const raw = JSON.parse(
|
|
18617
|
+
const raw = JSON.parse(fs24.readFileSync(this.file, "utf-8"));
|
|
18083
18618
|
this.cache = {
|
|
18084
18619
|
conversations: raw.conversations ?? {}
|
|
18085
18620
|
};
|
|
@@ -18111,8 +18646,8 @@ var QqBindingsStore = class _QqBindingsStore {
|
|
|
18111
18646
|
return null;
|
|
18112
18647
|
}
|
|
18113
18648
|
persist() {
|
|
18114
|
-
|
|
18115
|
-
|
|
18649
|
+
fs24.mkdirSync(path31.dirname(this.file), { recursive: true });
|
|
18650
|
+
fs24.writeFileSync(this.file, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
18116
18651
|
}
|
|
18117
18652
|
};
|
|
18118
18653
|
|
|
@@ -18524,8 +19059,8 @@ ${lines.join("\n")}`, msgId);
|
|
|
18524
19059
|
|
|
18525
19060
|
// src/server/qq/QqManager.ts
|
|
18526
19061
|
init_esm_shims();
|
|
18527
|
-
import * as
|
|
18528
|
-
import * as
|
|
19062
|
+
import * as fs25 from "fs";
|
|
19063
|
+
import * as path32 from "path";
|
|
18529
19064
|
var QqManager = class {
|
|
18530
19065
|
constructor(opts) {
|
|
18531
19066
|
this.opts = opts;
|
|
@@ -18534,7 +19069,7 @@ var QqManager = class {
|
|
|
18534
19069
|
adapter = null;
|
|
18535
19070
|
config = null;
|
|
18536
19071
|
get configFile() {
|
|
18537
|
-
return
|
|
19072
|
+
return path32.join(this.opts.dataDir, "qq", "config.json");
|
|
18538
19073
|
}
|
|
18539
19074
|
/** 启动:读配置(env 优先,其次文件),有则建连接 */
|
|
18540
19075
|
start() {
|
|
@@ -18572,7 +19107,7 @@ var QqManager = class {
|
|
|
18572
19107
|
/** 清空配置:删除文件 + 断开 */
|
|
18573
19108
|
clearConfig() {
|
|
18574
19109
|
try {
|
|
18575
|
-
|
|
19110
|
+
fs25.rmSync(this.configFile, { force: true });
|
|
18576
19111
|
} catch {
|
|
18577
19112
|
}
|
|
18578
19113
|
this.stop();
|
|
@@ -18614,7 +19149,7 @@ var QqManager = class {
|
|
|
18614
19149
|
}
|
|
18615
19150
|
_readConfigFile() {
|
|
18616
19151
|
try {
|
|
18617
|
-
const raw = JSON.parse(
|
|
19152
|
+
const raw = JSON.parse(fs25.readFileSync(this.configFile, "utf-8"));
|
|
18618
19153
|
if (raw.appId && raw.appSecret) {
|
|
18619
19154
|
return {
|
|
18620
19155
|
appId: raw.appId,
|
|
@@ -18627,18 +19162,18 @@ var QqManager = class {
|
|
|
18627
19162
|
return null;
|
|
18628
19163
|
}
|
|
18629
19164
|
_persistConfig(cfg) {
|
|
18630
|
-
|
|
18631
|
-
|
|
19165
|
+
fs25.mkdirSync(path32.dirname(this.configFile), { recursive: true });
|
|
19166
|
+
fs25.writeFileSync(this.configFile, JSON.stringify(cfg, null, 2), "utf-8");
|
|
18632
19167
|
}
|
|
18633
19168
|
};
|
|
18634
19169
|
|
|
18635
19170
|
// src/server/qq/pairingCodes.ts
|
|
18636
19171
|
init_esm_shims();
|
|
18637
|
-
import * as
|
|
19172
|
+
import * as crypto6 from "crypto";
|
|
18638
19173
|
var PAIRING_CODE_TTL_MS = 5 * 60 * 1e3;
|
|
18639
19174
|
function randomCode(len = 8) {
|
|
18640
19175
|
const alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
18641
|
-
const bytes =
|
|
19176
|
+
const bytes = crypto6.randomBytes(len);
|
|
18642
19177
|
let out = "";
|
|
18643
19178
|
for (let i = 0; i < len; i++) {
|
|
18644
19179
|
out += alphabet[bytes[i] % alphabet.length];
|
|
@@ -18676,17 +19211,17 @@ var PairingCodeStore = class {
|
|
|
18676
19211
|
|
|
18677
19212
|
// src/update/controller.ts
|
|
18678
19213
|
init_esm_shims();
|
|
18679
|
-
import * as
|
|
18680
|
-
import * as
|
|
19214
|
+
import * as fs30 from "fs";
|
|
19215
|
+
import * as path37 from "path";
|
|
18681
19216
|
|
|
18682
19217
|
// src/update/downloader.ts
|
|
18683
19218
|
init_esm_shims();
|
|
18684
19219
|
import { createHash as createHash3 } from "crypto";
|
|
18685
|
-
import * as
|
|
19220
|
+
import * as fs26 from "fs";
|
|
18686
19221
|
import { Readable as Readable2 } from "stream";
|
|
18687
19222
|
import { pipeline as pipeline2 } from "stream/promises";
|
|
18688
19223
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
18689
|
-
import * as
|
|
19224
|
+
import * as path33 from "path";
|
|
18690
19225
|
|
|
18691
19226
|
// src/update/manifest.ts
|
|
18692
19227
|
init_esm_shims();
|
|
@@ -18861,8 +19396,8 @@ async function fetchManifest(url, publicKey, opts = {}) {
|
|
|
18861
19396
|
async function downloadBinary(bin, destPath, log = () => {
|
|
18862
19397
|
}) {
|
|
18863
19398
|
const partPath = `${destPath}.part`;
|
|
18864
|
-
|
|
18865
|
-
|
|
19399
|
+
fs26.mkdirSync(path33.dirname(destPath), { recursive: true });
|
|
19400
|
+
fs26.rmSync(partPath, { force: true });
|
|
18866
19401
|
let res;
|
|
18867
19402
|
try {
|
|
18868
19403
|
res = await fetch(bin.url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS2) });
|
|
@@ -18887,25 +19422,25 @@ async function downloadBinary(bin, destPath, log = () => {
|
|
|
18887
19422
|
yield chunk;
|
|
18888
19423
|
}
|
|
18889
19424
|
},
|
|
18890
|
-
|
|
19425
|
+
fs26.createWriteStream(partPath, { mode: 493 })
|
|
18891
19426
|
);
|
|
18892
19427
|
} catch (err) {
|
|
18893
|
-
|
|
19428
|
+
fs26.rmSync(partPath, { force: true });
|
|
18894
19429
|
return { error: `\u4E0B\u8F7D\u4E2D\u65AD\uFF1A${err.message}` };
|
|
18895
19430
|
}
|
|
18896
19431
|
const actual = hash.digest("hex");
|
|
18897
19432
|
if (actual !== bin.sha256) {
|
|
18898
|
-
|
|
19433
|
+
fs26.rmSync(partPath, { force: true });
|
|
18899
19434
|
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` };
|
|
18900
19435
|
}
|
|
18901
19436
|
if (expected && bytes !== expected) {
|
|
18902
|
-
|
|
19437
|
+
fs26.rmSync(partPath, { force: true });
|
|
18903
19438
|
return { error: `\u4EA7\u7269\u5927\u5C0F\u4E0D\u5339\u914D\uFF08\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${bytes}\uFF09` };
|
|
18904
19439
|
}
|
|
18905
|
-
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
log(`\u5DF2\u4E0B\u8F7D ${
|
|
19440
|
+
fs26.rmSync(destPath, { force: true });
|
|
19441
|
+
fs26.renameSync(partPath, destPath);
|
|
19442
|
+
fs26.chmodSync(destPath, 493);
|
|
19443
|
+
log(`\u5DF2\u4E0B\u8F7D ${path33.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
|
|
18909
19444
|
return { bytes };
|
|
18910
19445
|
}
|
|
18911
19446
|
function selfCheck(binPath, expectedVersion) {
|
|
@@ -18929,10 +19464,11 @@ function selfCheck(binPath, expectedVersion) {
|
|
|
18929
19464
|
|
|
18930
19465
|
// src/update/platform.ts
|
|
18931
19466
|
init_esm_shims();
|
|
18932
|
-
import * as
|
|
19467
|
+
import * as fs27 from "fs";
|
|
19468
|
+
import * as path34 from "path";
|
|
18933
19469
|
import { createRequire as createRequire3 } from "module";
|
|
18934
19470
|
function currentVersion() {
|
|
18935
|
-
const injected = "0.2.
|
|
19471
|
+
const injected = "0.2.7"?.trim();
|
|
18936
19472
|
if (injected) return injected;
|
|
18937
19473
|
try {
|
|
18938
19474
|
const req2 = createRequire3(import.meta.url);
|
|
@@ -18944,12 +19480,36 @@ function currentVersion() {
|
|
|
18944
19480
|
}
|
|
18945
19481
|
function isSeaRuntime() {
|
|
18946
19482
|
if (process.env.ZQAGENT_SEA === "1") return true;
|
|
18947
|
-
const base =
|
|
19483
|
+
const base = path34.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
|
|
18948
19484
|
return base !== "node" && base !== "nodejs";
|
|
18949
19485
|
}
|
|
19486
|
+
var DEFAULT_SERVICE_NAME = "zqagent";
|
|
19487
|
+
function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
|
|
19488
|
+
const unitName = `${serviceName}.service`;
|
|
19489
|
+
for (const line of cgroup.split("\n")) {
|
|
19490
|
+
const idx = line.lastIndexOf(":");
|
|
19491
|
+
if (idx < 0) continue;
|
|
19492
|
+
const unitPath = line.slice(idx + 1).trim();
|
|
19493
|
+
if (unitPath.split("/").includes(unitName)) return "systemd";
|
|
19494
|
+
}
|
|
19495
|
+
return null;
|
|
19496
|
+
}
|
|
19497
|
+
function detectServiceManager() {
|
|
19498
|
+
if (process.platform !== "linux") return null;
|
|
19499
|
+
const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
|
|
19500
|
+
try {
|
|
19501
|
+
return serviceManagerFromCgroup(
|
|
19502
|
+
fs27.readFileSync("/proc/self/cgroup", "utf8"),
|
|
19503
|
+
serviceName
|
|
19504
|
+
);
|
|
19505
|
+
} catch {
|
|
19506
|
+
return null;
|
|
19507
|
+
}
|
|
19508
|
+
}
|
|
18950
19509
|
function serviceManager() {
|
|
18951
19510
|
const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
|
|
18952
|
-
|
|
19511
|
+
if (v === "systemd" || v === "launchd") return v;
|
|
19512
|
+
return detectServiceManager();
|
|
18953
19513
|
}
|
|
18954
19514
|
function selfUpdateCapability(hasPublicKey) {
|
|
18955
19515
|
if (!isSeaRuntime()) {
|
|
@@ -18972,22 +19532,22 @@ function selfUpdateCapability(hasPublicKey) {
|
|
|
18972
19532
|
|
|
18973
19533
|
// src/update/installer.ts
|
|
18974
19534
|
init_esm_shims();
|
|
18975
|
-
import * as
|
|
18976
|
-
import * as
|
|
19535
|
+
import * as fs29 from "fs";
|
|
19536
|
+
import * as path36 from "path";
|
|
18977
19537
|
|
|
18978
19538
|
// src/update/state.ts
|
|
18979
19539
|
init_esm_shims();
|
|
18980
|
-
import * as
|
|
18981
|
-
import * as
|
|
19540
|
+
import * as fs28 from "fs";
|
|
19541
|
+
import * as path35 from "path";
|
|
18982
19542
|
function updatesDir(dataDir) {
|
|
18983
|
-
return
|
|
19543
|
+
return path35.join(dataDir, "updates");
|
|
18984
19544
|
}
|
|
18985
19545
|
function statePath(dataDir) {
|
|
18986
|
-
return
|
|
19546
|
+
return path35.join(updatesDir(dataDir), "state.json");
|
|
18987
19547
|
}
|
|
18988
19548
|
function loadState(dataDir) {
|
|
18989
19549
|
try {
|
|
18990
|
-
const raw =
|
|
19550
|
+
const raw = fs28.readFileSync(statePath(dataDir), "utf-8");
|
|
18991
19551
|
const parsed = JSON.parse(raw);
|
|
18992
19552
|
if (typeof parsed === "object" && parsed !== null) return parsed;
|
|
18993
19553
|
} catch {
|
|
@@ -18996,11 +19556,11 @@ function loadState(dataDir) {
|
|
|
18996
19556
|
}
|
|
18997
19557
|
function saveState(dataDir, state) {
|
|
18998
19558
|
const dir = updatesDir(dataDir);
|
|
18999
|
-
|
|
19559
|
+
fs28.mkdirSync(dir, { recursive: true });
|
|
19000
19560
|
const file = statePath(dataDir);
|
|
19001
19561
|
const tmp = `${file}.tmp`;
|
|
19002
|
-
|
|
19003
|
-
|
|
19562
|
+
fs28.writeFileSync(tmp, JSON.stringify(state, null, 2), "utf-8");
|
|
19563
|
+
fs28.renameSync(tmp, file);
|
|
19004
19564
|
}
|
|
19005
19565
|
function patchState(dataDir, patch) {
|
|
19006
19566
|
const next = { ...loadState(dataDir), ...patch };
|
|
@@ -19010,36 +19570,36 @@ function patchState(dataDir, patch) {
|
|
|
19010
19570
|
|
|
19011
19571
|
// src/update/installer.ts
|
|
19012
19572
|
function backupDir(dataDir) {
|
|
19013
|
-
return
|
|
19573
|
+
return path36.join(updatesDir(dataDir), "backup");
|
|
19014
19574
|
}
|
|
19015
19575
|
function backupCurrent(dataDir, execPath, version) {
|
|
19016
19576
|
const dir = backupDir(dataDir);
|
|
19017
|
-
|
|
19018
|
-
const target =
|
|
19019
|
-
|
|
19020
|
-
|
|
19577
|
+
fs29.mkdirSync(dir, { recursive: true });
|
|
19578
|
+
const target = path36.join(dir, `zqagent-${version}${process.platform === "win32" ? ".exe" : ""}`);
|
|
19579
|
+
fs29.copyFileSync(execPath, target);
|
|
19580
|
+
fs29.chmodSync(target, 493);
|
|
19021
19581
|
return target;
|
|
19022
19582
|
}
|
|
19023
19583
|
function replaceExecutable(execPath, stagedPath) {
|
|
19024
19584
|
if (process.platform === "win32") {
|
|
19025
19585
|
const oldPath = `${execPath}.old`;
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19586
|
+
fs29.rmSync(oldPath, { force: true });
|
|
19587
|
+
fs29.renameSync(execPath, oldPath);
|
|
19588
|
+
fs29.copyFileSync(stagedPath, execPath);
|
|
19589
|
+
fs29.chmodSync(execPath, 493);
|
|
19030
19590
|
return;
|
|
19031
19591
|
}
|
|
19032
19592
|
const tmp = `${execPath}.new`;
|
|
19033
|
-
|
|
19593
|
+
fs29.rmSync(tmp, { force: true });
|
|
19034
19594
|
try {
|
|
19035
|
-
|
|
19036
|
-
|
|
19037
|
-
|
|
19595
|
+
fs29.copyFileSync(stagedPath, tmp);
|
|
19596
|
+
fs29.chmodSync(tmp, 493);
|
|
19597
|
+
fs29.renameSync(tmp, execPath);
|
|
19038
19598
|
} catch (err) {
|
|
19039
|
-
|
|
19599
|
+
fs29.rmSync(tmp, { force: true });
|
|
19040
19600
|
throw err;
|
|
19041
19601
|
}
|
|
19042
|
-
|
|
19602
|
+
fs29.rmSync(stagedPath, { force: true });
|
|
19043
19603
|
}
|
|
19044
19604
|
function installStaged(opts) {
|
|
19045
19605
|
const log = opts.log ?? (() => {
|
|
@@ -19047,7 +19607,7 @@ function installStaged(opts) {
|
|
|
19047
19607
|
const from = currentVersion();
|
|
19048
19608
|
log(`\u6B63\u5728\u5B89\u88C5 v${opts.version}\uFF08\u5F53\u524D v${from}\uFF09`);
|
|
19049
19609
|
const backupPath = backupCurrent(opts.dataDir, opts.execPath, from);
|
|
19050
|
-
log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${
|
|
19610
|
+
log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${path36.basename(backupPath)}`);
|
|
19051
19611
|
replaceExecutable(opts.execPath, opts.stagedPath);
|
|
19052
19612
|
return patchState(opts.dataDir, {
|
|
19053
19613
|
stagedVersion: void 0,
|
|
@@ -19066,18 +19626,18 @@ function rollback(dataDir, execPath, log = () => {
|
|
|
19066
19626
|
}) {
|
|
19067
19627
|
const state = readRollbackTarget(dataDir);
|
|
19068
19628
|
if (!state?.backupPath || !state.backupVersion) return null;
|
|
19069
|
-
if (!
|
|
19629
|
+
if (!fs29.existsSync(state.backupPath)) return null;
|
|
19070
19630
|
log(`\u6B63\u5728\u56DE\u6EDA\u5230 v${state.backupVersion}`);
|
|
19071
19631
|
if (process.platform === "win32") {
|
|
19072
|
-
|
|
19073
|
-
|
|
19074
|
-
|
|
19075
|
-
|
|
19632
|
+
fs29.rmSync(`${execPath}.old`, { force: true });
|
|
19633
|
+
fs29.renameSync(execPath, `${execPath}.old`);
|
|
19634
|
+
fs29.copyFileSync(state.backupPath, execPath);
|
|
19635
|
+
fs29.chmodSync(execPath, 493);
|
|
19076
19636
|
} else {
|
|
19077
19637
|
const tmp = `${execPath}.rollback`;
|
|
19078
|
-
|
|
19079
|
-
|
|
19080
|
-
|
|
19638
|
+
fs29.copyFileSync(state.backupPath, tmp);
|
|
19639
|
+
fs29.chmodSync(tmp, 493);
|
|
19640
|
+
fs29.renameSync(tmp, execPath);
|
|
19081
19641
|
}
|
|
19082
19642
|
patchState(dataDir, {
|
|
19083
19643
|
pendingVersion: state.backupVersion,
|
|
@@ -19091,7 +19651,7 @@ function rollback(dataDir, execPath, log = () => {
|
|
|
19091
19651
|
}
|
|
19092
19652
|
function readRollbackTarget(dataDir) {
|
|
19093
19653
|
try {
|
|
19094
|
-
const raw =
|
|
19654
|
+
const raw = fs29.readFileSync(path36.join(updatesDir(dataDir), "state.json"), "utf-8");
|
|
19095
19655
|
const s = JSON.parse(raw);
|
|
19096
19656
|
if (s.backupPath && s.backupVersion) return s;
|
|
19097
19657
|
} catch {
|
|
@@ -19174,7 +19734,7 @@ var UpdateController = class {
|
|
|
19174
19734
|
reconcile() {
|
|
19175
19735
|
const state = loadState(this.opts.dataDir);
|
|
19176
19736
|
const current = this.currentVersion;
|
|
19177
|
-
if (state.stagedPath && !
|
|
19737
|
+
if (state.stagedPath && !fs30.existsSync(state.stagedPath)) {
|
|
19178
19738
|
patchState(this.opts.dataDir, { stagedVersion: void 0, stagedPath: void 0 });
|
|
19179
19739
|
}
|
|
19180
19740
|
const patch = { runningVersion: current };
|
|
@@ -19189,7 +19749,7 @@ var UpdateController = class {
|
|
|
19189
19749
|
}
|
|
19190
19750
|
patchState(this.opts.dataDir, patch);
|
|
19191
19751
|
if (process.platform === "win32") {
|
|
19192
|
-
|
|
19752
|
+
fs30.rmSync(`${process.execPath}.old`, { force: true });
|
|
19193
19753
|
}
|
|
19194
19754
|
}
|
|
19195
19755
|
schedule(delayMs) {
|
|
@@ -19265,8 +19825,8 @@ var UpdateController = class {
|
|
|
19265
19825
|
const bin = manifest.binaries[platformKey()];
|
|
19266
19826
|
if (!bin) return `\u6E05\u5355\u6CA1\u6709\u5F53\u524D\u5E73\u53F0\uFF08${platformKey()}\uFF09\u7684\u4EA7\u7269`;
|
|
19267
19827
|
let stagedPath = state.stagedPath;
|
|
19268
|
-
if (!(state.stagedVersion === manifest.version && stagedPath &&
|
|
19269
|
-
stagedPath =
|
|
19828
|
+
if (!(state.stagedVersion === manifest.version && stagedPath && fs30.existsSync(stagedPath))) {
|
|
19829
|
+
stagedPath = path37.join(
|
|
19270
19830
|
updatesDir(this.opts.dataDir),
|
|
19271
19831
|
"staged",
|
|
19272
19832
|
`zqagent-${manifest.version}${process.platform === "win32" ? ".exe" : ""}`
|
|
@@ -19278,7 +19838,7 @@ var UpdateController = class {
|
|
|
19278
19838
|
}
|
|
19279
19839
|
const selfTest = selfCheck(stagedPath, manifest.version);
|
|
19280
19840
|
if (!selfTest.ok) {
|
|
19281
|
-
|
|
19841
|
+
fs30.rmSync(stagedPath, { force: true });
|
|
19282
19842
|
this.recordFailure(manifest.version, selfTest.error ?? "\u81EA\u68C0\u5931\u8D25");
|
|
19283
19843
|
return `\u5B89\u88C5\u5305\u81EA\u68C0\u5931\u8D25\uFF1A${selfTest.error}`;
|
|
19284
19844
|
}
|
|
@@ -19419,7 +19979,7 @@ function pemFile(key) {
|
|
|
19419
19979
|
const p = env2(key);
|
|
19420
19980
|
if (!p) return void 0;
|
|
19421
19981
|
try {
|
|
19422
|
-
return
|
|
19982
|
+
return fs31.readFileSync(p);
|
|
19423
19983
|
} catch (e) {
|
|
19424
19984
|
console.warn(`[tls] \u8BFB\u53D6 ${key}=${p} \u5931\u8D25: ${e instanceof Error ? e.message : e}`);
|
|
19425
19985
|
return void 0;
|
|
@@ -19441,7 +20001,7 @@ function seedDemoTenant(store) {
|
|
|
19441
20001
|
apiKey: env2("OPENAI_API_KEY"),
|
|
19442
20002
|
baseURL: env2("OPENAI_BASE_URL")
|
|
19443
20003
|
},
|
|
19444
|
-
workspaceRoot: workspace ?
|
|
20004
|
+
workspaceRoot: workspace ? path38.resolve(workspace) : void 0,
|
|
19445
20005
|
allowedCommands: []
|
|
19446
20006
|
};
|
|
19447
20007
|
store.saveTenant(demo);
|
|
@@ -19495,9 +20055,9 @@ function syncTenantModels(store) {
|
|
|
19495
20055
|
tenant.model = model;
|
|
19496
20056
|
changed = true;
|
|
19497
20057
|
}
|
|
19498
|
-
if (workspace && tenant.workspaceRoot !==
|
|
19499
|
-
console.log(`[sync] \u79DF\u6237 ${id} workspaceRoot \u5DF2\u540C\u6B65: ${tenant.workspaceRoot ?? "(\u6C99\u7BB1)"} \u2192 ${
|
|
19500
|
-
tenant.workspaceRoot =
|
|
20058
|
+
if (workspace && tenant.workspaceRoot !== path38.resolve(workspace)) {
|
|
20059
|
+
console.log(`[sync] \u79DF\u6237 ${id} workspaceRoot \u5DF2\u540C\u6B65: ${tenant.workspaceRoot ?? "(\u6C99\u7BB1)"} \u2192 ${path38.resolve(workspace)}`);
|
|
20060
|
+
tenant.workspaceRoot = path38.resolve(workspace);
|
|
19501
20061
|
changed = true;
|
|
19502
20062
|
}
|
|
19503
20063
|
if (apiKey && tenant.provider.apiKey !== apiKey) {
|
|
@@ -19529,7 +20089,7 @@ function exitWhenOrphaned() {
|
|
|
19529
20089
|
timer.unref();
|
|
19530
20090
|
}
|
|
19531
20091
|
function startServer(opts = {}) {
|
|
19532
|
-
const dataDir =
|
|
20092
|
+
const dataDir = path38.resolve(opts.dataDir ?? env2("DATA_DIR") ?? DEFAULT_DATA_DIR);
|
|
19533
20093
|
const port = opts.port ?? Number(env2("PORT") ?? 8787);
|
|
19534
20094
|
const host = opts.host ?? env2("HOST") ?? "127.0.0.1";
|
|
19535
20095
|
exitWhenOrphaned();
|
|
@@ -19559,7 +20119,7 @@ function startServer(opts = {}) {
|
|
|
19559
20119
|
}
|
|
19560
20120
|
const adminKey = ensureAdminKey();
|
|
19561
20121
|
const secretKey = env2("SECRET_KEY");
|
|
19562
|
-
const secretBox = secretKey ? SecretBox.fromHex(secretKey) : SecretBox.fromFile(
|
|
20122
|
+
const secretBox = secretKey ? SecretBox.fromHex(secretKey) : SecretBox.fromFile(path38.join(dataDir, "secret.key"));
|
|
19563
20123
|
if (secretKey && !secretBox) {
|
|
19564
20124
|
console.warn("[crypto] SECRET_KEY \u975E\u6CD5\uFF08\u9700 64 \u4F4D hex\uFF09\uFF0C\u901A\u77E5\u51ED\u636E\u5C06\u9000\u5316\u4E3A\u660E\u6587");
|
|
19565
20125
|
}
|
|
@@ -19788,7 +20348,7 @@ function printReady(info, qq) {
|
|
|
19788
20348
|
`zqagent server \u76D1\u542C http://${info.host}:${info.port}`,
|
|
19789
20349
|
`\u6570\u636E\u76EE\u5F55: ${info.dataDir}`,
|
|
19790
20350
|
`\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`,
|
|
19791
|
-
env2("ZQAGENT_LOG_ADMIN_KEY") === "0" ? `\u7BA1\u7406\u5458\u5BC6\u94A5: ${maskKey(info.adminKey)}\uFF08\u5DF2\u8131\u654F\uFF0C\u5B8C\u6574\u503C\u89C1 ${
|
|
20351
|
+
env2("ZQAGENT_LOG_ADMIN_KEY") === "0" ? `\u7BA1\u7406\u5458\u5BC6\u94A5: ${maskKey(info.adminKey)}\uFF08\u5DF2\u8131\u654F\uFF0C\u5B8C\u6574\u503C\u89C1 ${path38.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`
|
|
19792
20352
|
];
|
|
19793
20353
|
if (info.fleetNodeId) {
|
|
19794
20354
|
lines.push(
|
|
@@ -19809,7 +20369,7 @@ function printReady(info, qq) {
|
|
|
19809
20369
|
|
|
19810
20370
|
// src/cli/account-enroll.ts
|
|
19811
20371
|
init_esm_shims();
|
|
19812
|
-
import * as
|
|
20372
|
+
import * as path39 from "path";
|
|
19813
20373
|
import * as readline from "readline/promises";
|
|
19814
20374
|
var SKIP_ANSWERS = /* @__PURE__ */ new Set(["-", "skip", "none", "no"]);
|
|
19815
20375
|
function resolveDefaultHub(hubUrl, env3 = process.env) {
|
|
@@ -19821,7 +20381,7 @@ function parseHubAnswer(answer, fallback) {
|
|
|
19821
20381
|
return (a || fallback).replace(/\/$/, "");
|
|
19822
20382
|
}
|
|
19823
20383
|
function resolveDataDir2(dataDir) {
|
|
19824
|
-
return
|
|
20384
|
+
return path39.resolve(dataDir ?? process.env.DATA_DIR ?? DEFAULT_DATA_DIR);
|
|
19825
20385
|
}
|
|
19826
20386
|
async function maybeAccountEnroll(opts = {}) {
|
|
19827
20387
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
@@ -20027,7 +20587,7 @@ async function runServeCommand(argv) {
|
|
|
20027
20587
|
|
|
20028
20588
|
// src/cli/update.ts
|
|
20029
20589
|
init_esm_shims();
|
|
20030
|
-
import * as
|
|
20590
|
+
import * as path40 from "path";
|
|
20031
20591
|
function parse3(argv) {
|
|
20032
20592
|
const out = { status: false, check: false, now: false, rollback: false };
|
|
20033
20593
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -20056,7 +20616,7 @@ function restartHint() {
|
|
|
20056
20616
|
}
|
|
20057
20617
|
async function runUpdateCommand(argv) {
|
|
20058
20618
|
const args = parse3(argv);
|
|
20059
|
-
const dataDir =
|
|
20619
|
+
const dataDir = path40.resolve(args.dataDir ?? process.env.DATA_DIR ?? DEFAULT_DATA_DIR);
|
|
20060
20620
|
const controller = new UpdateController({ dataDir, isIdle: () => true });
|
|
20061
20621
|
if (args.rollback) {
|
|
20062
20622
|
const restored = controller.rollbackNow();
|
|
@@ -20104,17 +20664,17 @@ async function runUpdateCommand(argv) {
|
|
|
20104
20664
|
|
|
20105
20665
|
// src/cli/sessionMeta.ts
|
|
20106
20666
|
init_esm_shims();
|
|
20107
|
-
import * as
|
|
20108
|
-
import * as
|
|
20667
|
+
import * as fs32 from "fs";
|
|
20668
|
+
import * as path41 from "path";
|
|
20109
20669
|
function updateSessionMetaFile(stateDir2, sessionId, patch) {
|
|
20110
|
-
const file =
|
|
20670
|
+
const file = path41.join(stateDir2, `${sessionId}_state.json`);
|
|
20111
20671
|
try {
|
|
20112
|
-
const raw =
|
|
20672
|
+
const raw = fs32.readFileSync(file, "utf8");
|
|
20113
20673
|
const state = JSON.parse(raw);
|
|
20114
20674
|
if (patch.name !== void 0) state.name = patch.name;
|
|
20115
20675
|
if (patch.nameSource !== void 0) state.nameSource = patch.nameSource;
|
|
20116
20676
|
if (patch.taskCount !== void 0) state.taskCount = patch.taskCount;
|
|
20117
|
-
|
|
20677
|
+
fs32.writeFileSync(file, JSON.stringify(state, null, 2), "utf8");
|
|
20118
20678
|
return true;
|
|
20119
20679
|
} catch {
|
|
20120
20680
|
return false;
|
|
@@ -20123,9 +20683,9 @@ function updateSessionMetaFile(stateDir2, sessionId, patch) {
|
|
|
20123
20683
|
|
|
20124
20684
|
// src/cli/debug.ts
|
|
20125
20685
|
init_esm_shims();
|
|
20126
|
-
import * as
|
|
20686
|
+
import * as fs33 from "fs";
|
|
20127
20687
|
import * as os8 from "os";
|
|
20128
|
-
import * as
|
|
20688
|
+
import * as path42 from "path";
|
|
20129
20689
|
var LOG_NAME = "zqagent-debug.log";
|
|
20130
20690
|
var TRUTHY = /^(1|true|yes|on)$/i;
|
|
20131
20691
|
function bootRef() {
|
|
@@ -20136,9 +20696,9 @@ var logFile = null;
|
|
|
20136
20696
|
var startedAt = Date.now();
|
|
20137
20697
|
function resolveLogFile() {
|
|
20138
20698
|
const explicit = process.env.ZQAGENT_DEBUG_LOG;
|
|
20139
|
-
if (explicit) return
|
|
20699
|
+
if (explicit) return path42.resolve(explicit);
|
|
20140
20700
|
const dir = process.env.ZQAGENT_DEBUG_DIR;
|
|
20141
|
-
return
|
|
20701
|
+
return path42.join(dir ? path42.resolve(dir) : os8.tmpdir(), LOG_NAME);
|
|
20142
20702
|
}
|
|
20143
20703
|
function enableDebug(argv = process.argv) {
|
|
20144
20704
|
if (enabled) return true;
|
|
@@ -20154,8 +20714,8 @@ function enableDebug(argv = process.argv) {
|
|
|
20154
20714
|
enabled = true;
|
|
20155
20715
|
logFile = resolveLogFile();
|
|
20156
20716
|
try {
|
|
20157
|
-
|
|
20158
|
-
|
|
20717
|
+
fs33.mkdirSync(path42.dirname(logFile), { recursive: true });
|
|
20718
|
+
fs33.writeFileSync(logFile, "");
|
|
20159
20719
|
} catch {
|
|
20160
20720
|
logFile = null;
|
|
20161
20721
|
}
|
|
@@ -20172,7 +20732,7 @@ function dlog(stage, message = "") {
|
|
|
20172
20732
|
const line = `[+${String(Date.now() - startedAt).padStart(6)}ms] [${stage}] ${message}`;
|
|
20173
20733
|
if (logFile) {
|
|
20174
20734
|
try {
|
|
20175
|
-
|
|
20735
|
+
fs33.appendFileSync(logFile, line + "\n");
|
|
20176
20736
|
} catch {
|
|
20177
20737
|
}
|
|
20178
20738
|
}
|
|
@@ -20332,18 +20892,18 @@ if (enableDebug(process.argv)) {
|
|
|
20332
20892
|
logEnvironment();
|
|
20333
20893
|
}
|
|
20334
20894
|
var MODEL = process.env.OPENAI_MODEL ?? "gpt-4o";
|
|
20335
|
-
var WORKSPACE =
|
|
20895
|
+
var WORKSPACE = path43.resolve(process.env.WORKSPACE ?? ".");
|
|
20336
20896
|
var MAX_ITERATIONS = parseMaxIterations(process.env.MAX_ITERATIONS, 300);
|
|
20337
20897
|
var STATE_DIR = stateDir();
|
|
20338
20898
|
function listSessions() {
|
|
20339
20899
|
try {
|
|
20340
|
-
const files =
|
|
20900
|
+
const files = fs34.readdirSync(STATE_DIR);
|
|
20341
20901
|
const sessions = [];
|
|
20342
20902
|
for (const f of files) {
|
|
20343
20903
|
if (!f.endsWith("_state.json")) continue;
|
|
20344
20904
|
try {
|
|
20345
20905
|
const parsed = JSON.parse(
|
|
20346
|
-
|
|
20906
|
+
fs34.readFileSync(path43.join(STATE_DIR, f), "utf8")
|
|
20347
20907
|
);
|
|
20348
20908
|
if (parsed && typeof parsed.sessionId === "string") {
|
|
20349
20909
|
sessions.push(parsed);
|
|
@@ -20377,11 +20937,11 @@ function printLastAssistantMessage(messages) {
|
|
|
20377
20937
|
out += renderer.flush();
|
|
20378
20938
|
cliWrite(out + "\n");
|
|
20379
20939
|
}
|
|
20380
|
-
var AGENTS_FILE =
|
|
20940
|
+
var AGENTS_FILE = path43.join(WORKSPACE, ".agent", "agents.json");
|
|
20381
20941
|
function loadAgentsFromFile() {
|
|
20382
20942
|
try {
|
|
20383
|
-
if (!
|
|
20384
|
-
const raw =
|
|
20943
|
+
if (!fs34.existsSync(AGENTS_FILE)) return [];
|
|
20944
|
+
const raw = fs34.readFileSync(AGENTS_FILE, "utf8");
|
|
20385
20945
|
const parsed = JSON.parse(raw);
|
|
20386
20946
|
if (!Array.isArray(parsed)) return [];
|
|
20387
20947
|
return parsed.filter(
|
|
@@ -20393,8 +20953,8 @@ function loadAgentsFromFile() {
|
|
|
20393
20953
|
}
|
|
20394
20954
|
function saveAgentsToFile(defs) {
|
|
20395
20955
|
try {
|
|
20396
|
-
|
|
20397
|
-
|
|
20956
|
+
fs34.mkdirSync(path43.dirname(AGENTS_FILE), { recursive: true });
|
|
20957
|
+
fs34.writeFileSync(AGENTS_FILE, JSON.stringify(defs, null, 2) + "\n", "utf8");
|
|
20398
20958
|
} catch (err) {
|
|
20399
20959
|
cliErr(`\u26A0\uFE0F \u4FDD\u5B58 agents \u914D\u7F6E\u5931\u8D25: ${err}`);
|
|
20400
20960
|
}
|
|
@@ -20403,12 +20963,12 @@ var agentRegistry = new AgentRegistry([
|
|
|
20403
20963
|
...BUILTIN_AGENTS,
|
|
20404
20964
|
...loadAgentsFromFile()
|
|
20405
20965
|
]);
|
|
20406
|
-
var MCP_FILE =
|
|
20407
|
-
var COMMAND_TOOLS_FILE =
|
|
20966
|
+
var MCP_FILE = path43.join(WORKSPACE, ".agent", "mcp.json");
|
|
20967
|
+
var COMMAND_TOOLS_FILE = path43.join(WORKSPACE, ".agent", "tools.json");
|
|
20408
20968
|
function loadJsonArray(file, validate) {
|
|
20409
20969
|
try {
|
|
20410
|
-
if (!
|
|
20411
|
-
const parsed = JSON.parse(
|
|
20970
|
+
if (!fs34.existsSync(file)) return [];
|
|
20971
|
+
const parsed = JSON.parse(fs34.readFileSync(file, "utf8"));
|
|
20412
20972
|
if (!Array.isArray(parsed)) return [];
|
|
20413
20973
|
if (!validate) return parsed;
|
|
20414
20974
|
return parsed.filter((v) => validate(v));
|
|
@@ -20418,8 +20978,8 @@ function loadJsonArray(file, validate) {
|
|
|
20418
20978
|
}
|
|
20419
20979
|
function saveJsonFile(file, data) {
|
|
20420
20980
|
try {
|
|
20421
|
-
|
|
20422
|
-
|
|
20981
|
+
fs34.mkdirSync(path43.dirname(file), { recursive: true });
|
|
20982
|
+
fs34.writeFileSync(file, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
20423
20983
|
} catch (err) {
|
|
20424
20984
|
cliErr(`\u26A0\uFE0F \u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${err}`);
|
|
20425
20985
|
}
|
|
@@ -20520,7 +21080,7 @@ function pruneEmptySessions(keepSessionId) {
|
|
|
20520
21080
|
if (!isEmptySession(s) || s.sessionId === keepSessionId) continue;
|
|
20521
21081
|
for (const suffix of ["_state.json", ".jsonl"]) {
|
|
20522
21082
|
try {
|
|
20523
|
-
|
|
21083
|
+
fs34.unlinkSync(path43.join(STATE_DIR, `${s.sessionId}${suffix}`));
|
|
20524
21084
|
} catch {
|
|
20525
21085
|
}
|
|
20526
21086
|
}
|
|
@@ -20710,6 +21270,7 @@ function buildEngine(sessionId, savedState) {
|
|
|
20710
21270
|
// 引擎内置表(lookupContextWindow 兜底,见 TurnEngine._contextWindow)
|
|
20711
21271
|
contextWindow: parseOptionalPositiveNumber(process.env.CONTEXT_WINDOW) ?? modelContextWindow(conn?.modelMeta, model),
|
|
20712
21272
|
thresholdPct: parseRatio(process.env.COMPACTION_THRESHOLD_PCT),
|
|
21273
|
+
softThresholdPct: parseRatio(process.env.COMPACTION_SOFT_THRESHOLD_PCT),
|
|
20713
21274
|
capTokens: parseOptionalPositiveNumber(process.env.COMPACTION_CAP_TOKENS) ?? null,
|
|
20714
21275
|
// 系统提示词按当前 agent 组装(agent prompt + 叙述规则 + 环境信息)
|
|
20715
21276
|
instructions: agentRegistry.buildSystemPrompt(
|
|
@@ -21052,7 +21613,7 @@ async function offerTerminalSetup(engine, canUseAgent) {
|
|
|
21052
21613
|
}
|
|
21053
21614
|
var KNOWN_SUBCOMMANDS = /* @__PURE__ */ new Set(["link", "serve", "update"]);
|
|
21054
21615
|
function cliVersion() {
|
|
21055
|
-
const env3 = "0.2.
|
|
21616
|
+
const env3 = "0.2.7"?.trim();
|
|
21056
21617
|
if (env3) return env3;
|
|
21057
21618
|
try {
|
|
21058
21619
|
const req2 = createRequire4(import.meta.url);
|
|
@@ -21256,8 +21817,8 @@ async function main() {
|
|
|
21256
21817
|
}
|
|
21257
21818
|
});
|
|
21258
21819
|
const savedState = resumeId ? FileLogHook.loadState(resumeId, STATE_DIR) : null;
|
|
21259
|
-
const metaStateFile =
|
|
21260
|
-
const stateForMeta = savedState ?? (
|
|
21820
|
+
const metaStateFile = path43.join(STATE_DIR, `${SESSION_ID}_state.json`);
|
|
21821
|
+
const stateForMeta = savedState ?? (fs34.existsSync(metaStateFile) ? FileLogHook.loadState(SESSION_ID, STATE_DIR) : null);
|
|
21261
21822
|
sessionMeta = {
|
|
21262
21823
|
sessionId: SESSION_ID,
|
|
21263
21824
|
name: stateForMeta?.name,
|