engine7 7.1.112 → 7.1.113
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/dist/cli.mjs +48 -33
- package/dist/engine-startup.mjs +729 -344
- package/dist/main.mjs +750 -372
- package/package.json +2 -2
package/dist/engine-startup.mjs
CHANGED
|
@@ -2445,14 +2445,14 @@ var init_live = __esm({
|
|
|
2445
2445
|
}
|
|
2446
2446
|
/** 把改动写回 config 文件对应段(找不到文件路径则只内存生效) */
|
|
2447
2447
|
async persistToFile(dotPath, val) {
|
|
2448
|
-
const
|
|
2448
|
+
const fs59 = await import("node:fs");
|
|
2449
2449
|
const cfgPath = this.current?._configFilePath;
|
|
2450
|
-
if (!cfgPath || !
|
|
2450
|
+
if (!cfgPath || !fs59.existsSync(cfgPath)) {
|
|
2451
2451
|
console.warn(`[liveConfig] set: no config file path, in-memory only (${dotPath})`);
|
|
2452
2452
|
return;
|
|
2453
2453
|
}
|
|
2454
2454
|
try {
|
|
2455
|
-
const raw = JSON.parse(
|
|
2455
|
+
const raw = JSON.parse(fs59.readFileSync(cfgPath, "utf-8"));
|
|
2456
2456
|
const keys = dotPath.split(".");
|
|
2457
2457
|
let o = raw;
|
|
2458
2458
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -2460,7 +2460,7 @@ var init_live = __esm({
|
|
|
2460
2460
|
o = o[keys[i]];
|
|
2461
2461
|
}
|
|
2462
2462
|
o[keys[keys.length - 1]] = val;
|
|
2463
|
-
|
|
2463
|
+
fs59.writeFileSync(cfgPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
|
|
2464
2464
|
console.log(`[liveConfig] persisted ${dotPath} = ${JSON.stringify(val)} to ${cfgPath}`);
|
|
2465
2465
|
markSelfWrite();
|
|
2466
2466
|
} catch (e) {
|
|
@@ -4004,7 +4004,7 @@ var init_skill_gate = __esm({
|
|
|
4004
4004
|
DEVELOPMENT_HINTS = /代码|源码|编译|构建|测试|报错|错误|bug|修复|排查|调试|实现|开发|接口|脚本|函数|类型|依赖|部署|代码 review|code|build|test|debug|implement|fix|compile|typescript|javascript|python|git/i;
|
|
4005
4005
|
TECHNICAL_OBJECTS = /代码|源码|仓库|项目|模块|插件|接口|服务|引擎|engine|voice[- ]?chat|docker|容器|llm|模型|延迟|工具|脚本|函数|依赖|部署|分支|commit|api/i;
|
|
4006
4006
|
TECHNICAL_ACTIONS = /优化|瘦身|提速|迁移|重构|接线|收尾|改动|加上|删掉|换成|裁剪|升级|降级|修|接入|拆分|合并|调试|排查/i;
|
|
4007
|
-
USER_CHANNELS = /* @__PURE__ */ new Set(["discord", "feishu", "wechat", "telegram", "cli", "console", "voice-chat", "deskBuddy"]);
|
|
4007
|
+
USER_CHANNELS = /* @__PURE__ */ new Set(["discord", "feishu", "wechat", "telegram", "cli", "console", "voice-chat", "deskBuddy", "ccBridge"]);
|
|
4008
4008
|
loadedBySession = /* @__PURE__ */ new Map();
|
|
4009
4009
|
MAX_TRACKED_SESSIONS = 100;
|
|
4010
4010
|
}
|
|
@@ -4038,9 +4038,9 @@ function isAutoMemPath(absolutePath, workspace) {
|
|
|
4038
4038
|
return normalizedPath.startsWith(getAutoMemPath(workspace));
|
|
4039
4039
|
}
|
|
4040
4040
|
async function ensureMemoryDirExists(memoryDir) {
|
|
4041
|
-
const
|
|
4041
|
+
const fs59 = await import("node:fs");
|
|
4042
4042
|
try {
|
|
4043
|
-
await
|
|
4043
|
+
await fs59.promises.mkdir(memoryDir, { recursive: true });
|
|
4044
4044
|
} catch (e) {
|
|
4045
4045
|
const code = e?.code;
|
|
4046
4046
|
if (code !== "EEXIST") {
|
|
@@ -6717,7 +6717,13 @@ var init_anthropic_provider = __esm({
|
|
|
6717
6717
|
thinking: { type: "enabled", budget_tokens: thinkingBudget }
|
|
6718
6718
|
} : params.disableThinking ? {
|
|
6719
6719
|
// 显式关闭 thinking(智谱 GLM-5.1 需要传 disabled 才真正关闭)
|
|
6720
|
-
|
|
6720
|
+
// 0928 修:MiniMax M3.1-Flash 系强制 adaptive,disabled 直接 400(码 2013,
|
|
6721
|
+
// 实证:proactive 后台调用换 M3.1 后 LLM UNREACHABLE)——该系降级发 adaptive
|
|
6722
|
+
...params.model?.includes("M3.1-Flash") ? {
|
|
6723
|
+
thinking: { type: "adaptive" }
|
|
6724
|
+
} : {
|
|
6725
|
+
thinking: { type: "disabled" }
|
|
6726
|
+
},
|
|
6721
6727
|
...params.temperature !== void 0 ? { temperature: params.temperature } : {}
|
|
6722
6728
|
} : {
|
|
6723
6729
|
...params.temperature !== void 0 ? { temperature: params.temperature } : {}
|
|
@@ -7786,7 +7792,10 @@ function loadConfig(configPath) {
|
|
|
7786
7792
|
channels: agentDefaults.channels || [],
|
|
7787
7793
|
extensions: agentDefaults.extensions,
|
|
7788
7794
|
maxTurns: agentDefaults.maxTurns,
|
|
7789
|
-
|
|
7795
|
+
// ⚠️ **默认 true**(0928 翀哥拍板):开源项目,功能不按 license 分档。
|
|
7796
|
+
// 要收起来才显式配 `agents.defaults.devMode: false`。
|
|
7797
|
+
// 反过来写(=== true)时,任何没手配这一格的配置都会静默落进"基础引擎模式"。
|
|
7798
|
+
devMode: agentDefaults.devMode !== false
|
|
7790
7799
|
};
|
|
7791
7800
|
const rawSession = raw.session || {};
|
|
7792
7801
|
const sessionScope = Object.keys(rawSession).length > 0 ? { ...rawSession } : void 0;
|
|
@@ -8353,7 +8362,7 @@ async function acquireLock(inboxPath) {
|
|
|
8353
8362
|
for (let i = 0; i < LOCK_RETRIES; i++) {
|
|
8354
8363
|
if (await isLockStale(lockPath2)) {
|
|
8355
8364
|
try {
|
|
8356
|
-
await import("node:fs/promises").then((
|
|
8365
|
+
await import("node:fs/promises").then((fs59) => fs59.rm(lockPath2, { force: true }));
|
|
8357
8366
|
} catch {
|
|
8358
8367
|
}
|
|
8359
8368
|
}
|
|
@@ -8361,7 +8370,7 @@ async function acquireLock(inboxPath) {
|
|
|
8361
8370
|
await writeFile2(lockPath2, `${process.pid}-${Date.now()}`, { encoding: "utf-8", flag: "wx" });
|
|
8362
8371
|
return async () => {
|
|
8363
8372
|
try {
|
|
8364
|
-
await import("node:fs/promises").then((
|
|
8373
|
+
await import("node:fs/promises").then((fs59) => fs59.rm(lockPath2, { force: true }));
|
|
8365
8374
|
} catch {
|
|
8366
8375
|
}
|
|
8367
8376
|
};
|
|
@@ -10001,9 +10010,9 @@ Follow these principles when generating episodic memories:
|
|
|
10001
10010
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
10002
10011
|
import { mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync6, existsSync as existsSync14, readdirSync as readdirSync2 } from "node:fs";
|
|
10003
10012
|
import { join as join24 } from "node:path";
|
|
10004
|
-
import { createHash } from "node:crypto";
|
|
10013
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
10005
10014
|
function contentHash(text) {
|
|
10006
|
-
return
|
|
10015
|
+
return createHash2("sha256").update(text.replace(/\s+/g, "")).digest("hex").slice(0, 40);
|
|
10007
10016
|
}
|
|
10008
10017
|
function mem7Root(stateDir) {
|
|
10009
10018
|
return join24(stateDir, "mem7");
|
|
@@ -16719,11 +16728,11 @@ async function readLastConsolidatedAt(memoryDir) {
|
|
|
16719
16728
|
}
|
|
16720
16729
|
}
|
|
16721
16730
|
async function tryAcquireConsolidationLock(memoryDir) {
|
|
16722
|
-
const
|
|
16731
|
+
const path61 = lockPath(memoryDir);
|
|
16723
16732
|
let mtimeMs;
|
|
16724
16733
|
let holderPid;
|
|
16725
16734
|
try {
|
|
16726
|
-
const [s, raw] = await Promise.all([stat3(
|
|
16735
|
+
const [s, raw] = await Promise.all([stat3(path61), readFile5(path61, "utf8")]);
|
|
16727
16736
|
mtimeMs = s.mtimeMs;
|
|
16728
16737
|
const parsed = parseInt(raw.trim(), 10);
|
|
16729
16738
|
holderPid = Number.isFinite(parsed) ? parsed : void 0;
|
|
@@ -16736,10 +16745,10 @@ async function tryAcquireConsolidationLock(memoryDir) {
|
|
|
16736
16745
|
}
|
|
16737
16746
|
}
|
|
16738
16747
|
await mkdir3(memoryDir, { recursive: true });
|
|
16739
|
-
await writeFile4(
|
|
16748
|
+
await writeFile4(path61, String(process.pid));
|
|
16740
16749
|
let verify2;
|
|
16741
16750
|
try {
|
|
16742
|
-
verify2 = await readFile5(
|
|
16751
|
+
verify2 = await readFile5(path61, "utf8");
|
|
16743
16752
|
} catch {
|
|
16744
16753
|
return null;
|
|
16745
16754
|
}
|
|
@@ -16747,15 +16756,15 @@ async function tryAcquireConsolidationLock(memoryDir) {
|
|
|
16747
16756
|
return mtimeMs ?? 0;
|
|
16748
16757
|
}
|
|
16749
16758
|
async function rollbackConsolidationLock(memoryDir, priorMtime) {
|
|
16750
|
-
const
|
|
16759
|
+
const path61 = lockPath(memoryDir);
|
|
16751
16760
|
try {
|
|
16752
16761
|
if (priorMtime === 0) {
|
|
16753
|
-
await unlink(
|
|
16762
|
+
await unlink(path61);
|
|
16754
16763
|
return;
|
|
16755
16764
|
}
|
|
16756
|
-
await writeFile4(
|
|
16765
|
+
await writeFile4(path61, "");
|
|
16757
16766
|
const t = priorMtime / 1e3;
|
|
16758
|
-
await utimes(
|
|
16767
|
+
await utimes(path61, t, t);
|
|
16759
16768
|
} catch (e) {
|
|
16760
16769
|
console.log(`[autoDream] rollback failed: ${e.message} \u2014 next trigger delayed to minHours`);
|
|
16761
16770
|
}
|
|
@@ -17357,12 +17366,12 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
17357
17366
|
if (promptText.startsWith("@")) {
|
|
17358
17367
|
let filePath = promptText.slice(1).trim();
|
|
17359
17368
|
try {
|
|
17360
|
-
const
|
|
17361
|
-
const
|
|
17362
|
-
if (!
|
|
17363
|
-
filePath =
|
|
17369
|
+
const fs59 = await import("fs");
|
|
17370
|
+
const path61 = await import("path");
|
|
17371
|
+
if (!path61.isAbsolute(filePath)) {
|
|
17372
|
+
filePath = path61.join(deps.sessions["config"].stateDir, filePath);
|
|
17364
17373
|
}
|
|
17365
|
-
promptText =
|
|
17374
|
+
promptText = fs59.readFileSync(filePath, "utf-8");
|
|
17366
17375
|
console.log(`[cron] Loaded prompt from ${filePath} (${promptText.length} chars)`);
|
|
17367
17376
|
} catch (err) {
|
|
17368
17377
|
throw new Error(`Prompt file not found: ${filePath}: ${err.message}`);
|
|
@@ -17400,16 +17409,16 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
17400
17409
|
let finalResult = result;
|
|
17401
17410
|
if (task.postProcess) {
|
|
17402
17411
|
try {
|
|
17403
|
-
const
|
|
17404
|
-
const
|
|
17412
|
+
const path61 = await import("path");
|
|
17413
|
+
const fs59 = await import("fs");
|
|
17405
17414
|
let scriptPath = task.postProcess;
|
|
17406
|
-
if (!
|
|
17407
|
-
scriptPath =
|
|
17415
|
+
if (!path61.isAbsolute(scriptPath)) {
|
|
17416
|
+
scriptPath = path61.join(deps.sessions["config"].stateDir, scriptPath);
|
|
17408
17417
|
}
|
|
17409
|
-
const resultsDirTmp =
|
|
17410
|
-
|
|
17411
|
-
const inputFile =
|
|
17412
|
-
|
|
17418
|
+
const resultsDirTmp = path61.join(getStorageDir(), "results");
|
|
17419
|
+
fs59.mkdirSync(resultsDirTmp, { recursive: true });
|
|
17420
|
+
const inputFile = path61.join(resultsDirTmp, `${task.id}.input.txt`);
|
|
17421
|
+
fs59.writeFileSync(inputFile, result, "utf-8");
|
|
17413
17422
|
const { execFile: execFile5 } = await import("child_process");
|
|
17414
17423
|
await new Promise((resolve12) => {
|
|
17415
17424
|
execFile5("python", [scriptPath, "main", "--file", inputFile], {
|
|
@@ -17436,12 +17445,12 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
17436
17445
|
}
|
|
17437
17446
|
}
|
|
17438
17447
|
try {
|
|
17439
|
-
const
|
|
17440
|
-
const
|
|
17441
|
-
const resultsDir =
|
|
17442
|
-
|
|
17443
|
-
const resultFile =
|
|
17444
|
-
|
|
17448
|
+
const fs59 = await import("fs");
|
|
17449
|
+
const path61 = await import("path");
|
|
17450
|
+
const resultsDir = path61.join(getStorageDir(), "results");
|
|
17451
|
+
fs59.mkdirSync(resultsDir, { recursive: true });
|
|
17452
|
+
const resultFile = path61.join(resultsDir, `${task.id}.json`);
|
|
17453
|
+
fs59.writeFileSync(resultFile, JSON.stringify({
|
|
17445
17454
|
taskId: task.id,
|
|
17446
17455
|
description: task.description,
|
|
17447
17456
|
executedAt: now.toISOString(),
|
|
@@ -17771,30 +17780,30 @@ function registerCronTools() {
|
|
|
17771
17780
|
}
|
|
17772
17781
|
},
|
|
17773
17782
|
handler: async (args) => {
|
|
17774
|
-
const
|
|
17775
|
-
const
|
|
17776
|
-
const resultsDir =
|
|
17777
|
-
if (!
|
|
17783
|
+
const fs59 = await import("fs");
|
|
17784
|
+
const path61 = await import("path");
|
|
17785
|
+
const resultsDir = path61.join(getStorageDir(), "results");
|
|
17786
|
+
if (!fs59.existsSync(resultsDir)) {
|
|
17778
17787
|
return { content: "\u6682\u65E0cron\u6267\u884C\u7ED3\u679C" };
|
|
17779
17788
|
}
|
|
17780
17789
|
if (args.task_id) {
|
|
17781
|
-
const file2 =
|
|
17782
|
-
if (!
|
|
17790
|
+
const file2 = path61.join(resultsDir, `${args.task_id}.json`);
|
|
17791
|
+
if (!fs59.existsSync(file2)) {
|
|
17783
17792
|
return { content: `\u4EFB\u52A1 ${args.task_id} \u6682\u65E0\u6267\u884C\u7ED3\u679C`, isError: true };
|
|
17784
17793
|
}
|
|
17785
|
-
const data = JSON.parse(
|
|
17794
|
+
const data = JSON.parse(fs59.readFileSync(file2, "utf-8"));
|
|
17786
17795
|
return { content: `## ${data.description}
|
|
17787
17796
|
\u6267\u884C\u65F6\u95F4: ${data.executedAt}
|
|
17788
17797
|
\u7B2C${data.runCount}\u6B21\u6267\u884C
|
|
17789
17798
|
|
|
17790
17799
|
${data.result}` };
|
|
17791
17800
|
}
|
|
17792
|
-
const files =
|
|
17801
|
+
const files = fs59.readdirSync(resultsDir).filter((f) => f.endsWith(".json"));
|
|
17793
17802
|
if (files.length === 0) {
|
|
17794
17803
|
return { content: "\u6682\u65E0cron\u6267\u884C\u7ED3\u679C" };
|
|
17795
17804
|
}
|
|
17796
17805
|
const results = files.map((f) => {
|
|
17797
|
-
const data = JSON.parse(
|
|
17806
|
+
const data = JSON.parse(fs59.readFileSync(path61.join(resultsDir, f), "utf-8"));
|
|
17798
17807
|
return `### ${data.description} (${data.taskId.slice(0, 8)})
|
|
17799
17808
|
\u6267\u884C: ${data.executedAt} | \u7B2C${data.runCount}\u6B21
|
|
17800
17809
|
${data.result.slice(0, 500)}${data.result.length > 500 ? "..." : ""}`;
|
|
@@ -18469,10 +18478,10 @@ function ensureLoaded(workspace, configIds) {
|
|
|
18469
18478
|
if (!state.blockedUserIds.includes(id)) state.blockedUserIds.push(id);
|
|
18470
18479
|
}
|
|
18471
18480
|
}
|
|
18472
|
-
const
|
|
18481
|
+
const path61 = join44(workspace, ".reply-blocklist.json");
|
|
18473
18482
|
try {
|
|
18474
|
-
if (existsSync22(
|
|
18475
|
-
const raw = readFileSync32(
|
|
18483
|
+
if (existsSync22(path61)) {
|
|
18484
|
+
const raw = readFileSync32(path61, "utf-8");
|
|
18476
18485
|
const parsed = JSON.parse(raw);
|
|
18477
18486
|
if (parsed.blockedUserIds) {
|
|
18478
18487
|
for (const id of parsed.blockedUserIds) {
|
|
@@ -18488,9 +18497,9 @@ function ensureLoaded(workspace, configIds) {
|
|
|
18488
18497
|
loaded = true;
|
|
18489
18498
|
}
|
|
18490
18499
|
function save(workspace) {
|
|
18491
|
-
const
|
|
18500
|
+
const path61 = join44(workspace, ".reply-blocklist.json");
|
|
18492
18501
|
try {
|
|
18493
|
-
writeFileSync10(
|
|
18502
|
+
writeFileSync10(path61, JSON.stringify(state, null, 2), "utf-8");
|
|
18494
18503
|
} catch (err) {
|
|
18495
18504
|
console.warn(`[reply-blocklist] Failed to save: ${err.message}`);
|
|
18496
18505
|
}
|
|
@@ -19060,15 +19069,15 @@ var read_exports = {};
|
|
|
19060
19069
|
__export(read_exports, {
|
|
19061
19070
|
readFileState: () => readFileState
|
|
19062
19071
|
});
|
|
19063
|
-
import * as
|
|
19064
|
-
import * as
|
|
19072
|
+
import * as fs52 from "node:fs";
|
|
19073
|
+
import * as path51 from "node:path";
|
|
19065
19074
|
function isBlockedDevicePath(filePath) {
|
|
19066
19075
|
if (BLOCKED_DEVICE_PATHS.has(filePath)) return true;
|
|
19067
19076
|
if (filePath.startsWith("/proc/") && (filePath.endsWith("/fd/0") || filePath.endsWith("/fd/1") || filePath.endsWith("/fd/2"))) return true;
|
|
19068
19077
|
return false;
|
|
19069
19078
|
}
|
|
19070
19079
|
function checkReadLoop(filePath, offset, limit) {
|
|
19071
|
-
const stat4 =
|
|
19080
|
+
const stat4 = fs52.statSync(filePath);
|
|
19072
19081
|
const mtimeMs = stat4.mtimeMs;
|
|
19073
19082
|
const prev = readHistory.get(filePath);
|
|
19074
19083
|
if (prev && prev.offset === offset && prev.limit === limit && prev.mtimeMs === mtimeMs) {
|
|
@@ -19082,19 +19091,19 @@ function checkReadLoop(filePath, offset, limit) {
|
|
|
19082
19091
|
return null;
|
|
19083
19092
|
}
|
|
19084
19093
|
function readFileContent(filePath) {
|
|
19085
|
-
const fd =
|
|
19094
|
+
const fd = fs52.openSync(filePath, "r");
|
|
19086
19095
|
const bom = Buffer.alloc(2);
|
|
19087
|
-
|
|
19088
|
-
|
|
19096
|
+
fs52.readSync(fd, bom, 0, 2, 0);
|
|
19097
|
+
fs52.closeSync(fd);
|
|
19089
19098
|
let encoding = "utf8";
|
|
19090
19099
|
if (bom[0] === 255 && bom[1] === 254) {
|
|
19091
19100
|
encoding = "utf16le";
|
|
19092
19101
|
}
|
|
19093
|
-
const stat4 =
|
|
19102
|
+
const stat4 = fs52.statSync(filePath);
|
|
19094
19103
|
if (stat4.size > MAX_FILE_SIZE) {
|
|
19095
19104
|
throw new Error(`\u6587\u4EF6\u592A\u5927 (${(stat4.size / 1024).toFixed(1)}KB)\uFF0C\u8D85\u8FC7 ${MAX_FILE_SIZE / 1024}KB \u9650\u5236\u3002\u8BF7\u4F7F\u7528 offset + limit \u5206\u6BB5\u8BFB\u53D6\u3002`);
|
|
19096
19105
|
}
|
|
19097
|
-
const raw =
|
|
19106
|
+
const raw = fs52.readFileSync(filePath, encoding);
|
|
19098
19107
|
const content = raw.toString().replaceAll("\r\n", "\n");
|
|
19099
19108
|
return { content, encoding };
|
|
19100
19109
|
}
|
|
@@ -19236,11 +19245,11 @@ Usage:
|
|
|
19236
19245
|
} catch (e) {
|
|
19237
19246
|
return { content: e.message, isError: true };
|
|
19238
19247
|
}
|
|
19239
|
-
if (!
|
|
19248
|
+
if (!fs52.existsSync(filePath)) {
|
|
19240
19249
|
return { content: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`, isError: true };
|
|
19241
19250
|
}
|
|
19242
|
-
const stat4 =
|
|
19243
|
-
const baseName =
|
|
19251
|
+
const stat4 = fs52.statSync(filePath);
|
|
19252
|
+
const baseName = path51.basename(filePath).toUpperCase();
|
|
19244
19253
|
if (BLOCKED_BASENAMES.has(baseName)) {
|
|
19245
19254
|
return { content: `\u8BBE\u5907\u6587\u4EF6\u4E0D\u652F\u6301\u8BFB\u53D6: ${filePath}`, isError: true };
|
|
19246
19255
|
}
|
|
@@ -19248,11 +19257,11 @@ Usage:
|
|
|
19248
19257
|
return { content: `\u8BBE\u5907\u6587\u4EF6\u4F1A\u963B\u585E\u6216\u4EA7\u751F\u65E0\u9650\u8F93\u51FA: ${filePath}`, isError: true };
|
|
19249
19258
|
}
|
|
19250
19259
|
if (stat4.isDirectory()) {
|
|
19251
|
-
const entries2 =
|
|
19260
|
+
const entries2 = fs52.readdirSync(filePath);
|
|
19252
19261
|
const items = entries2.map((e) => {
|
|
19253
|
-
const full =
|
|
19262
|
+
const full = path51.join(filePath, e);
|
|
19254
19263
|
try {
|
|
19255
|
-
const s =
|
|
19264
|
+
const s = fs52.statSync(full);
|
|
19256
19265
|
return s.isDirectory() ? `${e}/` : e;
|
|
19257
19266
|
} catch {
|
|
19258
19267
|
return e;
|
|
@@ -19261,7 +19270,7 @@ Usage:
|
|
|
19261
19270
|
return { content: `\u76EE\u5F55 (${entries2.length} \u9879):
|
|
19262
19271
|
${items.join("\n")}` };
|
|
19263
19272
|
}
|
|
19264
|
-
const ext =
|
|
19273
|
+
const ext = path51.extname(filePath).toLowerCase();
|
|
19265
19274
|
if (BINARY_EXTENSIONS.has(ext)) {
|
|
19266
19275
|
return { content: `\u4E8C\u8FDB\u5236\u6587\u4EF6\u4E0D\u652F\u6301\u8BFB\u53D6 (${ext}): ${filePath}`, isError: true };
|
|
19267
19276
|
}
|
|
@@ -19309,22 +19318,22 @@ ${result}` : result };
|
|
|
19309
19318
|
|
|
19310
19319
|
// src/tools/write.ts
|
|
19311
19320
|
var write_exports = {};
|
|
19312
|
-
import * as
|
|
19313
|
-
import * as
|
|
19321
|
+
import * as fs53 from "node:fs";
|
|
19322
|
+
import * as path52 from "node:path";
|
|
19314
19323
|
function isBlockedPath(filePath) {
|
|
19315
19324
|
return BLOCKED_PATTERNS.some((p) => p.test(filePath));
|
|
19316
19325
|
}
|
|
19317
19326
|
function atomicWrite(filePath, content) {
|
|
19318
19327
|
const tmpPath = filePath + ".tmp." + Date.now() + ".write";
|
|
19319
|
-
|
|
19328
|
+
fs53.writeFileSync(tmpPath, content, "utf-8");
|
|
19320
19329
|
try {
|
|
19321
|
-
|
|
19330
|
+
fs53.renameSync(tmpPath, filePath);
|
|
19322
19331
|
} catch (e) {
|
|
19323
19332
|
try {
|
|
19324
|
-
|
|
19333
|
+
fs53.unlinkSync(tmpPath);
|
|
19325
19334
|
} catch {
|
|
19326
19335
|
}
|
|
19327
|
-
|
|
19336
|
+
fs53.writeFileSync(filePath, content, "utf-8");
|
|
19328
19337
|
}
|
|
19329
19338
|
}
|
|
19330
19339
|
function simpleDiff(oldContent, newContent) {
|
|
@@ -19418,15 +19427,15 @@ Usage:
|
|
|
19418
19427
|
}
|
|
19419
19428
|
const rawContent = args.content;
|
|
19420
19429
|
const content = rawContent.replaceAll("\r\n", "\n");
|
|
19421
|
-
if (
|
|
19430
|
+
if (fs53.existsSync(filePath) && fs53.statSync(filePath).isDirectory()) {
|
|
19422
19431
|
return { content: `\u8DEF\u5F84\u662F\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6: ${filePath}`, isError: true };
|
|
19423
19432
|
}
|
|
19424
19433
|
let oldContent = null;
|
|
19425
19434
|
let isCreate = true;
|
|
19426
|
-
if (
|
|
19435
|
+
if (fs53.existsSync(filePath)) {
|
|
19427
19436
|
isCreate = false;
|
|
19428
19437
|
try {
|
|
19429
|
-
oldContent =
|
|
19438
|
+
oldContent = fs53.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
|
|
19430
19439
|
} catch {
|
|
19431
19440
|
isCreate = true;
|
|
19432
19441
|
}
|
|
@@ -19442,10 +19451,10 @@ Usage:
|
|
|
19442
19451
|
isError: true
|
|
19443
19452
|
};
|
|
19444
19453
|
}
|
|
19445
|
-
const currentStat =
|
|
19454
|
+
const currentStat = fs53.statSync(filePath);
|
|
19446
19455
|
const lastWriteTime = Math.floor(currentStat.mtimeMs);
|
|
19447
19456
|
if (lastWriteTime > readState.timestamp) {
|
|
19448
|
-
const currentContent =
|
|
19457
|
+
const currentContent = fs53.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
|
|
19449
19458
|
if (currentContent !== oldContent) {
|
|
19450
19459
|
return {
|
|
19451
19460
|
content: `\u6587\u4EF6\u5728\u8BFB\u53D6\u540E\u88AB\u4FEE\u6539\u3002\u8BF7\u5148\u91CD\u65B0\u8BFB\u53D6\u6587\u4EF6\u518D\u5199\u5165: ${filePath}`,
|
|
@@ -19454,9 +19463,9 @@ Usage:
|
|
|
19454
19463
|
}
|
|
19455
19464
|
}
|
|
19456
19465
|
}
|
|
19457
|
-
const dir =
|
|
19466
|
+
const dir = path52.dirname(filePath);
|
|
19458
19467
|
try {
|
|
19459
|
-
|
|
19468
|
+
fs53.mkdirSync(dir, { recursive: true });
|
|
19460
19469
|
} catch (e) {
|
|
19461
19470
|
return { content: `\u65E0\u6CD5\u521B\u5EFA\u76EE\u5F55: ${dir} \u2014 ${e.message}`, isError: true };
|
|
19462
19471
|
}
|
|
@@ -19465,11 +19474,11 @@ Usage:
|
|
|
19465
19474
|
} catch (e) {
|
|
19466
19475
|
return { content: `\u5199\u5165\u5931\u8D25: ${e.message}`, isError: true };
|
|
19467
19476
|
}
|
|
19468
|
-
readFileState.set(filePath, { timestamp:
|
|
19477
|
+
readFileState.set(filePath, { timestamp: fs53.statSync(filePath).mtimeMs });
|
|
19469
19478
|
const action = isCreate ? "\u521B\u5EFA" : "\u66F4\u65B0";
|
|
19470
19479
|
const lines = content.split("\n").length;
|
|
19471
19480
|
const chars = content.length;
|
|
19472
|
-
const stat4 =
|
|
19481
|
+
const stat4 = fs53.statSync(filePath);
|
|
19473
19482
|
let diff = "";
|
|
19474
19483
|
if (!isCreate && oldContent !== null) {
|
|
19475
19484
|
diff = `
|
|
@@ -19489,8 +19498,8 @@ ${simpleDiff(oldContent, content)}`;
|
|
|
19489
19498
|
|
|
19490
19499
|
// src/tools/edit.ts
|
|
19491
19500
|
var edit_exports = {};
|
|
19492
|
-
import * as
|
|
19493
|
-
import * as
|
|
19501
|
+
import * as fs54 from "node:fs";
|
|
19502
|
+
import * as path53 from "node:path";
|
|
19494
19503
|
function normalizeQuotes(str) {
|
|
19495
19504
|
return str.replaceAll(LEFT_SINGLE_CURLY, "'").replaceAll(RIGHT_SINGLE_CURLY, "'").replaceAll(LEFT_DOUBLE_CURLY, '"').replaceAll(RIGHT_DOUBLE_CURLY, '"');
|
|
19496
19505
|
}
|
|
@@ -19643,7 +19652,7 @@ Usage:
|
|
|
19643
19652
|
}
|
|
19644
19653
|
let fileContent = null;
|
|
19645
19654
|
try {
|
|
19646
|
-
const stat4 =
|
|
19655
|
+
const stat4 = fs54.statSync(filePath);
|
|
19647
19656
|
if (stat4.isDirectory()) {
|
|
19648
19657
|
return { content: `\u8DEF\u5F84\u662F\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6: ${filePath}`, isError: true };
|
|
19649
19658
|
}
|
|
@@ -19653,21 +19662,21 @@ Usage:
|
|
|
19653
19662
|
} catch (e) {
|
|
19654
19663
|
if (e.code === "ENOENT") {
|
|
19655
19664
|
if (oldString === "") {
|
|
19656
|
-
const dir =
|
|
19657
|
-
|
|
19658
|
-
|
|
19659
|
-
readFileState.set(filePath, { timestamp:
|
|
19665
|
+
const dir = path53.dirname(filePath);
|
|
19666
|
+
fs54.mkdirSync(dir, { recursive: true });
|
|
19667
|
+
fs54.writeFileSync(filePath, newString, "utf-8");
|
|
19668
|
+
readFileState.set(filePath, { timestamp: fs54.statSync(filePath).mtimeMs });
|
|
19660
19669
|
return { content: `\u521B\u5EFA\u6587\u4EF6: ${filePath} (${newString.split("\n").length} \u884C)` };
|
|
19661
19670
|
}
|
|
19662
19671
|
return { content: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`, isError: true };
|
|
19663
19672
|
}
|
|
19664
19673
|
throw e;
|
|
19665
19674
|
}
|
|
19666
|
-
const rawContent =
|
|
19675
|
+
const rawContent = fs54.readFileSync(filePath, "utf-8");
|
|
19667
19676
|
fileContent = rawContent.replaceAll("\r\n", "\n");
|
|
19668
19677
|
if (oldString === "" && fileContent.trim() === "") {
|
|
19669
|
-
|
|
19670
|
-
readFileState.set(filePath, { timestamp:
|
|
19678
|
+
fs54.writeFileSync(filePath, newString, "utf-8");
|
|
19679
|
+
readFileState.set(filePath, { timestamp: fs54.statSync(filePath).mtimeMs });
|
|
19671
19680
|
return { content: `\u5199\u5165\u7A7A\u6587\u4EF6: ${filePath} (${newString.split("\n").length} \u884C)` };
|
|
19672
19681
|
}
|
|
19673
19682
|
const readState = readFileState.get(filePath);
|
|
@@ -19677,11 +19686,11 @@ Usage:
|
|
|
19677
19686
|
isError: true
|
|
19678
19687
|
};
|
|
19679
19688
|
}
|
|
19680
|
-
const currentStat =
|
|
19689
|
+
const currentStat = fs54.statSync(filePath);
|
|
19681
19690
|
const lastWriteTime = Math.floor(currentStat.mtimeMs);
|
|
19682
19691
|
if (lastWriteTime > readState.timestamp) {
|
|
19683
19692
|
if (fileContent !== rawContent.replaceAll("\r\n", "\n")) {
|
|
19684
|
-
if (fileContent !==
|
|
19693
|
+
if (fileContent !== fs54.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n")) {
|
|
19685
19694
|
return {
|
|
19686
19695
|
content: `\u6587\u4EF6\u5728\u8BFB\u53D6\u540E\u88AB\u4FEE\u6539\u3002\u8BF7\u5148\u91CD\u65B0\u8BFB\u53D6\u6587\u4EF6\u518D\u7F16\u8F91: ${filePath}`,
|
|
19687
19696
|
isError: true
|
|
@@ -19713,8 +19722,8 @@ ${preview}
|
|
|
19713
19722
|
const actualNewString = preserveQuoteStyle(oldString, actualOldString, newString);
|
|
19714
19723
|
const diffView = generateEditDiff(fileContent, actualOldString, actualNewString);
|
|
19715
19724
|
const newContent = applyEditToFile(fileContent, actualOldString, actualNewString, replaceAll);
|
|
19716
|
-
|
|
19717
|
-
readFileState.set(filePath, { timestamp:
|
|
19725
|
+
fs54.writeFileSync(filePath, newContent, "utf-8");
|
|
19726
|
+
readFileState.set(filePath, { timestamp: fs54.statSync(filePath).mtimeMs });
|
|
19718
19727
|
const strategy = actualOldString === oldString ? "\u7CBE\u786E\u5339\u914D" : "\u5F15\u53F7\u89C4\u8303\u5316\u5339\u914D";
|
|
19719
19728
|
const count = replaceAll ? matchCount : 1;
|
|
19720
19729
|
const diff = `${oldString.length}\u2192${newString.length}\u5B57\u7B26`;
|
|
@@ -19732,8 +19741,8 @@ ${diffView}`
|
|
|
19732
19741
|
|
|
19733
19742
|
// src/tools/glob.ts
|
|
19734
19743
|
var glob_exports = {};
|
|
19735
|
-
import * as
|
|
19736
|
-
import * as
|
|
19744
|
+
import * as fs55 from "node:fs";
|
|
19745
|
+
import * as path54 from "node:path";
|
|
19737
19746
|
function globMatch(pattern, filename) {
|
|
19738
19747
|
const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
19739
19748
|
try {
|
|
@@ -19753,18 +19762,18 @@ function findFiles(dir, pattern, limit, baseDir) {
|
|
|
19753
19762
|
}
|
|
19754
19763
|
let entries2;
|
|
19755
19764
|
try {
|
|
19756
|
-
entries2 =
|
|
19765
|
+
entries2 = fs55.readdirSync(currentDir, { withFileTypes: true });
|
|
19757
19766
|
} catch {
|
|
19758
19767
|
return;
|
|
19759
19768
|
}
|
|
19760
19769
|
for (const entry of entries2) {
|
|
19761
19770
|
if (truncated) return;
|
|
19762
|
-
const fullPath =
|
|
19771
|
+
const fullPath = path54.join(currentDir, entry.name);
|
|
19763
19772
|
if (entry.isDirectory()) {
|
|
19764
19773
|
if (VCS_DIRS.has(entry.name)) continue;
|
|
19765
19774
|
walk(fullPath);
|
|
19766
19775
|
} else if (entry.isFile()) {
|
|
19767
|
-
const relativePath =
|
|
19776
|
+
const relativePath = path54.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
19768
19777
|
const patternsToTry = [pattern];
|
|
19769
19778
|
if (pattern.startsWith("**/")) {
|
|
19770
19779
|
patternsToTry.push(pattern.slice(3));
|
|
@@ -19774,7 +19783,7 @@ function findFiles(dir, pattern, limit, baseDir) {
|
|
|
19774
19783
|
);
|
|
19775
19784
|
if (matched) {
|
|
19776
19785
|
try {
|
|
19777
|
-
const stat4 =
|
|
19786
|
+
const stat4 = fs55.statSync(fullPath);
|
|
19778
19787
|
results.push({ path: fullPath, mtimeMs: stat4.mtimeMs });
|
|
19779
19788
|
} catch {
|
|
19780
19789
|
}
|
|
@@ -19791,7 +19800,7 @@ function findFiles(dir, pattern, limit, baseDir) {
|
|
|
19791
19800
|
};
|
|
19792
19801
|
}
|
|
19793
19802
|
function toRelativePath(absolutePath, cwd) {
|
|
19794
|
-
if (absolutePath.startsWith(cwd +
|
|
19803
|
+
if (absolutePath.startsWith(cwd + path54.sep)) {
|
|
19795
19804
|
return absolutePath.slice(cwd.length + 1);
|
|
19796
19805
|
}
|
|
19797
19806
|
return absolutePath;
|
|
@@ -19824,10 +19833,10 @@ var init_glob = __esm({
|
|
|
19824
19833
|
const searchPath = args.path ? resolvePath(args.path, ctx.workspace) : ctx.workspace;
|
|
19825
19834
|
const pattern = args.pattern;
|
|
19826
19835
|
const limit = args.limit || DEFAULT_LIMIT;
|
|
19827
|
-
if (!
|
|
19836
|
+
if (!fs55.existsSync(searchPath)) {
|
|
19828
19837
|
return { content: `\u76EE\u5F55\u4E0D\u5B58\u5728: ${searchPath}`, isError: true };
|
|
19829
19838
|
}
|
|
19830
|
-
if (!
|
|
19839
|
+
if (!fs55.statSync(searchPath).isDirectory()) {
|
|
19831
19840
|
return { content: `\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55: ${searchPath}`, isError: true };
|
|
19832
19841
|
}
|
|
19833
19842
|
const start = Date.now();
|
|
@@ -19852,7 +19861,7 @@ ${filenames.join("\n")}${truncatedNote}`
|
|
|
19852
19861
|
// src/tools/grep.ts
|
|
19853
19862
|
var grep_exports = {};
|
|
19854
19863
|
import { execFile as execFile4 } from "node:child_process";
|
|
19855
|
-
import * as
|
|
19864
|
+
import * as path55 from "node:path";
|
|
19856
19865
|
function ripGrep(args, searchPath, signal) {
|
|
19857
19866
|
return new Promise((resolve12) => {
|
|
19858
19867
|
const fullArgs = [...args, searchPath];
|
|
@@ -19884,7 +19893,7 @@ function applyHeadLimit(items, limit, offset = 0) {
|
|
|
19884
19893
|
};
|
|
19885
19894
|
}
|
|
19886
19895
|
function toRelativePath2(absolutePath, cwd) {
|
|
19887
|
-
if (absolutePath.startsWith(cwd +
|
|
19896
|
+
if (absolutePath.startsWith(cwd + path55.sep)) {
|
|
19888
19897
|
return absolutePath.slice(cwd.length + 1);
|
|
19889
19898
|
}
|
|
19890
19899
|
if (absolutePath.startsWith(cwd)) {
|
|
@@ -21693,7 +21702,7 @@ var init_TeamDeleteTool = __esm({
|
|
|
21693
21702
|
},
|
|
21694
21703
|
isEnabled: () => isAgentSwarmsEnabled(),
|
|
21695
21704
|
handler: async (_args, ctx) => {
|
|
21696
|
-
const teamName = ctx.teamName;
|
|
21705
|
+
const teamName = ctx.teamName || getLeaderTeamName() || void 0;
|
|
21697
21706
|
if (teamName) {
|
|
21698
21707
|
const teamFile = readTeamFile(teamName);
|
|
21699
21708
|
if (teamFile) {
|
|
@@ -21777,7 +21786,7 @@ function getAgentName(ctx) {
|
|
|
21777
21786
|
return ctx.agentName || TEAM_LEAD_NAME;
|
|
21778
21787
|
}
|
|
21779
21788
|
function getTeamName(ctx) {
|
|
21780
|
-
return ctx.teamName;
|
|
21789
|
+
return ctx.teamName || getLeaderTeamName() || void 0;
|
|
21781
21790
|
}
|
|
21782
21791
|
async function handleMessage(recipientName, content, summary, ctx) {
|
|
21783
21792
|
const teamName = getTeamName(ctx);
|
|
@@ -22406,8 +22415,8 @@ __export(manager_exports, {
|
|
|
22406
22415
|
McpManager: () => McpManager,
|
|
22407
22416
|
convertInputSchema: () => convertInputSchema
|
|
22408
22417
|
});
|
|
22409
|
-
import * as
|
|
22410
|
-
import * as
|
|
22418
|
+
import * as fs58 from "node:fs";
|
|
22419
|
+
import * as path58 from "node:path";
|
|
22411
22420
|
import { Client as Client3 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
22412
22421
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
22413
22422
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
@@ -22448,12 +22457,12 @@ function convertInputSchema(inputSchema) {
|
|
|
22448
22457
|
}
|
|
22449
22458
|
function persistBinary(base64Data, mimeType, persistId) {
|
|
22450
22459
|
const ext = mimeType?.split("/")[1] || "bin";
|
|
22451
|
-
const dir =
|
|
22452
|
-
|
|
22453
|
-
const filepath =
|
|
22460
|
+
const dir = path58.join(process.env.ENGINE_STATE_DIR || ".engine", "mcp-blobs");
|
|
22461
|
+
fs58.mkdirSync(dir, { recursive: true });
|
|
22462
|
+
const filepath = path58.join(dir, `${persistId}.${ext}`);
|
|
22454
22463
|
try {
|
|
22455
22464
|
const buf = Buffer.from(base64Data, "base64");
|
|
22456
|
-
|
|
22465
|
+
fs58.writeFileSync(filepath, buf);
|
|
22457
22466
|
return { filepath, size: buf.length };
|
|
22458
22467
|
} catch (err) {
|
|
22459
22468
|
return { error: err.message };
|
|
@@ -22787,7 +22796,7 @@ __export(resources_exports, {
|
|
|
22787
22796
|
registerMcpResourceTools: () => registerMcpResourceTools,
|
|
22788
22797
|
unregisterMcpResourceTools: () => unregisterMcpResourceTools
|
|
22789
22798
|
});
|
|
22790
|
-
import * as
|
|
22799
|
+
import * as path59 from "node:path";
|
|
22791
22800
|
function registerMcpResourceTools(manager) {
|
|
22792
22801
|
mcpManagerRef = manager;
|
|
22793
22802
|
registry.register(listResourcesTool);
|
|
@@ -22805,7 +22814,7 @@ var init_resources = __esm({
|
|
|
22805
22814
|
"use strict";
|
|
22806
22815
|
init_registry();
|
|
22807
22816
|
MAX_RESULT_CHARS2 = 1e5;
|
|
22808
|
-
MEDIA_DIR = process.env.ENGINE_MEDIA_DIR ||
|
|
22817
|
+
MEDIA_DIR = process.env.ENGINE_MEDIA_DIR || path59.join(process.env.ENGINE_STATE_DIR || ".engine", "media", "inbound");
|
|
22809
22818
|
MCP_LIST_RESOURCES_TOOL = "mcp__list_resources";
|
|
22810
22819
|
MCP_READ_RESOURCE_TOOL = "mcp__read_resource";
|
|
22811
22820
|
mcpManagerRef = null;
|
|
@@ -22893,7 +22902,7 @@ ${lines.join("\n\n")}`;
|
|
|
22893
22902
|
// src/engine-startup.ts
|
|
22894
22903
|
init_query();
|
|
22895
22904
|
init_live();
|
|
22896
|
-
import * as
|
|
22905
|
+
import * as path60 from "node:path";
|
|
22897
22906
|
|
|
22898
22907
|
// src/startup/providers.ts
|
|
22899
22908
|
init_provider_factory();
|
|
@@ -23138,16 +23147,32 @@ import * as path10 from "node:path";
|
|
|
23138
23147
|
import { fileURLToPath } from "node:url";
|
|
23139
23148
|
|
|
23140
23149
|
// src/config/secrets-env.ts
|
|
23141
|
-
import {
|
|
23150
|
+
import { createHash } from "node:crypto";
|
|
23151
|
+
import { basename, dirname, join as join9, normalize as normalize3 } from "node:path";
|
|
23142
23152
|
function secretsHome() {
|
|
23143
23153
|
return process.env.HOME || process.env.USERPROFILE || ".";
|
|
23144
23154
|
}
|
|
23155
|
+
function normalizeStateDir(stateDir) {
|
|
23156
|
+
return normalize3(stateDir).split("\\").join("/").replace(/\/+$/, "").toLowerCase();
|
|
23157
|
+
}
|
|
23158
|
+
function stateKey(stateDir) {
|
|
23159
|
+
return createHash("sha256").update(normalizeStateDir(stateDir)).digest("hex").slice(0, 8);
|
|
23160
|
+
}
|
|
23161
|
+
function isDefaultStateDir(stateDir) {
|
|
23162
|
+
return normalizeStateDir(stateDir) === normalizeStateDir(join9(secretsHome(), ".engine7"));
|
|
23163
|
+
}
|
|
23145
23164
|
function secretsCandidates(stateDir, configName) {
|
|
23146
|
-
|
|
23147
|
-
|
|
23165
|
+
const shared = join9(secretsHome(), ".engine7-secrets", `${configName}.env`);
|
|
23166
|
+
const inState = [
|
|
23148
23167
|
join9(stateDir, "configs", `.env.${configName}`),
|
|
23149
23168
|
join9(stateDir, "configs", ".env")
|
|
23150
23169
|
];
|
|
23170
|
+
if (isDefaultStateDir(stateDir)) return [shared, ...inState];
|
|
23171
|
+
return [
|
|
23172
|
+
join9(secretsHome(), ".engine7-secrets", `${configName}-${stateKey(stateDir)}.env`),
|
|
23173
|
+
shared,
|
|
23174
|
+
...inState
|
|
23175
|
+
];
|
|
23151
23176
|
}
|
|
23152
23177
|
|
|
23153
23178
|
// src/reload/orchestrator.ts
|
|
@@ -24259,7 +24284,7 @@ ${formatted}` };
|
|
|
24259
24284
|
};
|
|
24260
24285
|
}
|
|
24261
24286
|
function createEverosGetTool() {
|
|
24262
|
-
const
|
|
24287
|
+
const fs59 = __require("node:fs/promises");
|
|
24263
24288
|
return {
|
|
24264
24289
|
name: "memory_get",
|
|
24265
24290
|
description: "Read a memory file by path.",
|
|
@@ -24275,7 +24300,7 @@ function createEverosGetTool() {
|
|
|
24275
24300
|
handler: async (args) => {
|
|
24276
24301
|
try {
|
|
24277
24302
|
const filePath = args.path;
|
|
24278
|
-
const content = await
|
|
24303
|
+
const content = await fs59.readFile(filePath, "utf-8");
|
|
24279
24304
|
const lines = content.split("\n");
|
|
24280
24305
|
const fromLine = args.from ?? 1;
|
|
24281
24306
|
const numLines = args.lines ?? lines.length;
|
|
@@ -24327,11 +24352,11 @@ var ALL_PAID_FEATURES = [
|
|
|
24327
24352
|
"report-gen",
|
|
24328
24353
|
"custom-soul"
|
|
24329
24354
|
];
|
|
24330
|
-
function loadLicense(stateDir, devMode) {
|
|
24355
|
+
function loadLicense(stateDir, devMode = true) {
|
|
24331
24356
|
if (_licenseChecked) return _cachedLicense;
|
|
24332
24357
|
_licenseChecked = true;
|
|
24333
24358
|
if (devMode) {
|
|
24334
|
-
console.log("[license]
|
|
24359
|
+
console.log("[license] \u5F00\u653E\u6A21\u5F0F \u2014 \u5168\u90E8 feature \u6FC0\u6D3B\uFF08\u8981\u5206\u6863\uFF1Aagents.defaults.devMode = false\uFF09");
|
|
24335
24360
|
const allActive = {};
|
|
24336
24361
|
for (const id of ALL_PAID_FEATURES) {
|
|
24337
24362
|
allActive[id] = { active: true };
|
|
@@ -24341,7 +24366,7 @@ function loadLicense(stateDir, devMode) {
|
|
|
24341
24366
|
}
|
|
24342
24367
|
const licensePath = path15.join(stateDir, "license.json");
|
|
24343
24368
|
if (!fs16.existsSync(licensePath)) {
|
|
24344
|
-
console.log("[license]
|
|
24369
|
+
console.log("[license] devMode=false \u4E14\u65E0 license.json \u2014\u2014 \u57FA\u7840\u5F15\u64CE\u6A21\u5F0F");
|
|
24345
24370
|
return null;
|
|
24346
24371
|
}
|
|
24347
24372
|
try {
|
|
@@ -26163,18 +26188,18 @@ function resetSessionStartInjection(sessionId) {
|
|
|
26163
26188
|
}
|
|
26164
26189
|
var externalChanRulesCache = null;
|
|
26165
26190
|
function loadExternalChanRules(workspace) {
|
|
26166
|
-
const
|
|
26167
|
-
if (externalChanRulesCache && externalChanRulesCache.path ===
|
|
26191
|
+
const path61 = join36(workspace, "prompts", "external-chan-rules.md");
|
|
26192
|
+
if (externalChanRulesCache && externalChanRulesCache.path === path61) return externalChanRulesCache;
|
|
26168
26193
|
let content = "";
|
|
26169
|
-
if (existsSync19(
|
|
26194
|
+
if (existsSync19(path61)) {
|
|
26170
26195
|
try {
|
|
26171
|
-
content = readFileSync26(
|
|
26196
|
+
content = readFileSync26(path61, "utf-8").trim();
|
|
26172
26197
|
} catch (e) {
|
|
26173
26198
|
console.warn(`[external-chan-rules] Failed to load: ${e}`);
|
|
26174
26199
|
}
|
|
26175
26200
|
}
|
|
26176
|
-
externalChanRulesCache = { path:
|
|
26177
|
-
console.log(`[external-chan-rules] Loaded ${content.length} chars from ${
|
|
26201
|
+
externalChanRulesCache = { path: path61, content };
|
|
26202
|
+
console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path61}`);
|
|
26178
26203
|
return externalChanRulesCache;
|
|
26179
26204
|
}
|
|
26180
26205
|
function getExternalChanRulesBlock(inboundMeta, workspace) {
|
|
@@ -27061,15 +27086,15 @@ function registerCompactHooks(config) {
|
|
|
27061
27086
|
const dailyDir = path22.join(workspace, "memory", "daily");
|
|
27062
27087
|
const dailyPath = path22.join(dailyDir, `${dateStr}.md`);
|
|
27063
27088
|
try {
|
|
27064
|
-
const
|
|
27065
|
-
if (!
|
|
27066
|
-
|
|
27089
|
+
const fs59 = await import("node:fs");
|
|
27090
|
+
if (!fs59.existsSync(dailyDir)) {
|
|
27091
|
+
fs59.mkdirSync(dailyDir, { recursive: true });
|
|
27067
27092
|
}
|
|
27068
27093
|
const sessionsDir = path22.join(config.stateDir, "agents", "main", "sessions");
|
|
27069
27094
|
const sessionFile = path22.join(sessionsDir, `${sessionId}.jsonl`);
|
|
27070
27095
|
const recentLines = [];
|
|
27071
|
-
if (
|
|
27072
|
-
const content =
|
|
27096
|
+
if (fs59.existsSync(sessionFile)) {
|
|
27097
|
+
const content = fs59.readFileSync(sessionFile, "utf-8");
|
|
27073
27098
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
27074
27099
|
const userLines = lines.filter((l) => {
|
|
27075
27100
|
try {
|
|
@@ -27099,10 +27124,10 @@ function registerCompactHooks(config) {
|
|
|
27099
27124
|
const entry = `${header}
|
|
27100
27125
|
${body}
|
|
27101
27126
|
`;
|
|
27102
|
-
if (
|
|
27103
|
-
|
|
27127
|
+
if (fs59.existsSync(dailyPath)) {
|
|
27128
|
+
fs59.appendFileSync(dailyPath, entry);
|
|
27104
27129
|
} else {
|
|
27105
|
-
|
|
27130
|
+
fs59.writeFileSync(dailyPath, `# ${dateStr} \u65E5\u5FD7
|
|
27106
27131
|
${entry}`);
|
|
27107
27132
|
}
|
|
27108
27133
|
console.log(`[hooks] PreCompact: saved ${recentLines.length} lines to ${dailyPath}`);
|
|
@@ -27118,16 +27143,16 @@ ${entry}`);
|
|
|
27118
27143
|
const workspace = input.cwd || input.workspace || "";
|
|
27119
27144
|
if (!workspace) return { continue: true };
|
|
27120
27145
|
try {
|
|
27121
|
-
const
|
|
27146
|
+
const fs59 = await import("node:fs");
|
|
27122
27147
|
const bufferPath = path22.join(workspace, "memory", "working-buffer.md");
|
|
27123
|
-
if (
|
|
27124
|
-
const stat4 =
|
|
27148
|
+
if (fs59.existsSync(bufferPath)) {
|
|
27149
|
+
const stat4 = fs59.statSync(bufferPath);
|
|
27125
27150
|
const ageMs = Date.now() - stat4.mtimeMs;
|
|
27126
27151
|
const ageMin = Math.round(ageMs / 6e4);
|
|
27127
27152
|
if (ageMin > 10) {
|
|
27128
27153
|
console.warn(`[hooks] PostCompact: \u26A0\uFE0F working-buffer.md is ${ageMin}min old (last modified ${stat4.mtime.toISOString()}) \u2014 content may be stale!`);
|
|
27129
27154
|
}
|
|
27130
|
-
const content =
|
|
27155
|
+
const content = fs59.readFileSync(bufferPath, "utf-8");
|
|
27131
27156
|
if (content.trim()) {
|
|
27132
27157
|
console.log(`[hooks] PostCompact: injecting working-buffer (${content.length} chars, ${ageMin}min old)`);
|
|
27133
27158
|
return {
|
|
@@ -33582,10 +33607,10 @@ function writeReportState(stateDir, s) {
|
|
|
33582
33607
|
}
|
|
33583
33608
|
|
|
33584
33609
|
// src/optional/reporting/payload.ts
|
|
33585
|
-
import { createHash as
|
|
33610
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
33586
33611
|
import * as os3 from "node:os";
|
|
33587
33612
|
function installKeyOf(installId, installSecret) {
|
|
33588
|
-
return
|
|
33613
|
+
return createHash3("sha256").update(installId + installSecret).digest("hex").slice(0, 32);
|
|
33589
33614
|
}
|
|
33590
33615
|
function buildReportPayload(opts) {
|
|
33591
33616
|
const p = {
|
|
@@ -33962,13 +33987,13 @@ var DiscordAdapter = class _DiscordAdapter {
|
|
|
33962
33987
|
}
|
|
33963
33988
|
/** 发送媒体附件(图片/文件/音频)— discord.js channel.send({ files }) */
|
|
33964
33989
|
async sendFile(target, message, attachment) {
|
|
33965
|
-
const
|
|
33966
|
-
const
|
|
33967
|
-
if (!
|
|
33990
|
+
const fs59 = await import("node:fs");
|
|
33991
|
+
const path61 = await import("node:path");
|
|
33992
|
+
if (!fs59.existsSync(attachment.path)) {
|
|
33968
33993
|
throw new Error(`File not found: ${attachment.path}`);
|
|
33969
33994
|
}
|
|
33970
|
-
const filename = attachment.filename ||
|
|
33971
|
-
const fileBuffer =
|
|
33995
|
+
const filename = attachment.filename || path61.basename(attachment.path);
|
|
33996
|
+
const fileBuffer = fs59.readFileSync(attachment.path);
|
|
33972
33997
|
const filePayload = {
|
|
33973
33998
|
attachment: fileBuffer,
|
|
33974
33999
|
name: filename
|
|
@@ -34417,13 +34442,13 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
34417
34442
|
}
|
|
34418
34443
|
/** 发送媒体附件(图片/文件) */
|
|
34419
34444
|
async sendFile(target, message, attachment) {
|
|
34420
|
-
const
|
|
34421
|
-
const
|
|
34422
|
-
if (!
|
|
34445
|
+
const fs59 = await import("node:fs");
|
|
34446
|
+
const path61 = await import("node:path");
|
|
34447
|
+
if (!fs59.existsSync(attachment.path)) {
|
|
34423
34448
|
throw new Error(`File not found: ${attachment.path}`);
|
|
34424
34449
|
}
|
|
34425
|
-
const filename = attachment.filename ||
|
|
34426
|
-
const fileBuffer =
|
|
34450
|
+
const filename = attachment.filename || path61.basename(attachment.path);
|
|
34451
|
+
const fileBuffer = fs59.readFileSync(attachment.path);
|
|
34427
34452
|
const safeTarget = target || this.config.defaultTarget || "";
|
|
34428
34453
|
if (!safeTarget) throw new Error("Feishu sendFile: no target (\u4E3B\u52A8\u53D1\u9001\u9700\u914D\u7F6E defaultTarget \u6216\u4F20\u5165 target)");
|
|
34429
34454
|
const receiveIdType = safeTarget.startsWith("ou_") ? "open_id" : "chat_id";
|
|
@@ -36245,22 +36270,22 @@ var DeskBuddyAdapter = class {
|
|
|
36245
36270
|
try {
|
|
36246
36271
|
if (!this.historyFile) {
|
|
36247
36272
|
const os7 = __require("os");
|
|
36248
|
-
const
|
|
36249
|
-
const
|
|
36250
|
-
const dir =
|
|
36251
|
-
|
|
36252
|
-
this.historyFile =
|
|
36253
|
-
this._fs =
|
|
36254
|
-
}
|
|
36255
|
-
const
|
|
36273
|
+
const fs60 = __require("fs");
|
|
36274
|
+
const path61 = __require("path");
|
|
36275
|
+
const dir = path61.join(os7.homedir(), "xiaowen", "workspace", ".desktopBuddy");
|
|
36276
|
+
fs60.mkdirSync(dir, { recursive: true });
|
|
36277
|
+
this.historyFile = path61.join(dir, "history.json");
|
|
36278
|
+
this._fs = fs60;
|
|
36279
|
+
}
|
|
36280
|
+
const fs59 = this._fs;
|
|
36256
36281
|
let arr = [];
|
|
36257
36282
|
try {
|
|
36258
|
-
arr = JSON.parse(
|
|
36283
|
+
arr = JSON.parse(fs59.readFileSync(this.historyFile, "utf-8")) || [];
|
|
36259
36284
|
} catch {
|
|
36260
36285
|
}
|
|
36261
36286
|
arr.push({ ...entry, ts: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN", { hour12: false }) });
|
|
36262
36287
|
if (arr.length > 500) arr = arr.slice(-500);
|
|
36263
|
-
|
|
36288
|
+
fs59.writeFileSync(this.historyFile, JSON.stringify(arr, null, 1));
|
|
36264
36289
|
} catch (e) {
|
|
36265
36290
|
console.warn("[deskBuddy] history write fail", e);
|
|
36266
36291
|
}
|
|
@@ -36539,8 +36564,365 @@ var DeskBuddyAdapter = class {
|
|
|
36539
36564
|
}
|
|
36540
36565
|
};
|
|
36541
36566
|
|
|
36567
|
+
// src/channels/cc-bridge.ts
|
|
36568
|
+
init_live();
|
|
36569
|
+
import fs41 from "node:fs";
|
|
36570
|
+
import path41 from "node:path";
|
|
36571
|
+
import { WebSocketServer as WebSocketServer2, WebSocket as WebSocket2 } from "ws";
|
|
36572
|
+
var DEFAULT_PORT = 16996;
|
|
36573
|
+
var CcBridgeAdapter = class {
|
|
36574
|
+
name = "ccBridge";
|
|
36575
|
+
config;
|
|
36576
|
+
wss = null;
|
|
36577
|
+
clients = /* @__PURE__ */ new Set();
|
|
36578
|
+
messageHandler = null;
|
|
36579
|
+
seq = 0;
|
|
36580
|
+
constructor(config) {
|
|
36581
|
+
this.config = config;
|
|
36582
|
+
}
|
|
36583
|
+
/** 工具流水默认不推(见 CcBridgeConfig.showToolDisplay) */
|
|
36584
|
+
get suppressToolDisplay() {
|
|
36585
|
+
return this.config.showToolDisplay !== true;
|
|
36586
|
+
}
|
|
36587
|
+
/** 实际监听的端口。配 `port: 0` 时由内核分配 —— 测试用它拿真端口,不用赌某个数字没被占 */
|
|
36588
|
+
get boundPort() {
|
|
36589
|
+
const a = this.wss?.address();
|
|
36590
|
+
return a && typeof a === "object" ? a.port : null;
|
|
36591
|
+
}
|
|
36592
|
+
onMessage(handler2) {
|
|
36593
|
+
this.messageHandler = handler2;
|
|
36594
|
+
}
|
|
36595
|
+
// ── outbox(持久队列)──
|
|
36596
|
+
//
|
|
36597
|
+
// 每次现读 stateDir,不缓存 —— stateDir 的 effect 是 restart,缓存下来的
|
|
36598
|
+
// 只会在热加载后变成指向旧目录的幽灵路径(配置访问铁律)。
|
|
36599
|
+
outboxFile() {
|
|
36600
|
+
const sd = liveConfig.get("stateDir") || process.cwd();
|
|
36601
|
+
return path41.join(sd, "state", "cc-bridge", "outbox.jsonl");
|
|
36602
|
+
}
|
|
36603
|
+
/**
|
|
36604
|
+
* 读队列。**只有 ENOENT 才等于"空队列"。**
|
|
36605
|
+
*
|
|
36606
|
+
* ⚠️ 这里原来写的是 `catch { return [] }` —— 那是**会丢数据的**:
|
|
36607
|
+
* 读失败(Windows 上 AV/同步盘/搜索索引的瞬时共享锁 EBUSY,或 EPERM/EACCES)
|
|
36608
|
+
* 返回空数组,紧接着 `enqueue` 拿这份空数组**整份覆写**,待投递的消息全没,
|
|
36609
|
+
* 而且一句警告都没有。而"读不了"和"不存在"是完全不同的两件事 ——
|
|
36610
|
+
* 这个区分正是"宁可重复,不可丢失"能不能成立的支点。
|
|
36611
|
+
*/
|
|
36612
|
+
readOutbox() {
|
|
36613
|
+
let raw;
|
|
36614
|
+
try {
|
|
36615
|
+
raw = fs41.readFileSync(this.outboxFile(), "utf8");
|
|
36616
|
+
} catch (e) {
|
|
36617
|
+
if (e?.code === "ENOENT") return [];
|
|
36618
|
+
throw e;
|
|
36619
|
+
}
|
|
36620
|
+
const out = [];
|
|
36621
|
+
for (const line of raw.split("\n")) {
|
|
36622
|
+
if (!line.trim()) continue;
|
|
36623
|
+
try {
|
|
36624
|
+
out.push(JSON.parse(line));
|
|
36625
|
+
} catch {
|
|
36626
|
+
}
|
|
36627
|
+
}
|
|
36628
|
+
return out;
|
|
36629
|
+
}
|
|
36630
|
+
/**
|
|
36631
|
+
* 写队列:**先写 .tmp 再 rename**。
|
|
36632
|
+
*
|
|
36633
|
+
* `writeFileSync(file, ...)` 是原地截断(`w` 模式先清零再写)——
|
|
36634
|
+
* 在这中间被 taskkill / OOM / 断电,文件就是空的或半截,
|
|
36635
|
+
* 下次读回来是 [] 且无从分辨,最多 maxQueue 条待投递消息整体消失。
|
|
36636
|
+
* rename 到已存在文件走的是替换语义,落盘要么全旧要么全新。
|
|
36637
|
+
*
|
|
36638
|
+
* **失败要抛**:调用方(cron/heartbeat 经 `manager.send`)必须知道没存住 ——
|
|
36639
|
+
* 吞成 warning 的话,磁盘满/只读时客户端在线还能收到实时那条,
|
|
36640
|
+
* 客户端离线就是真丢,而这恰恰是这个通道要保的场景。
|
|
36641
|
+
*/
|
|
36642
|
+
writeOutbox(items) {
|
|
36643
|
+
const file2 = this.outboxFile();
|
|
36644
|
+
const tmp = `${file2}.tmp`;
|
|
36645
|
+
fs41.mkdirSync(path41.dirname(file2), { recursive: true });
|
|
36646
|
+
fs41.writeFileSync(tmp, items.map((i) => JSON.stringify(i)).join("\n") + (items.length ? "\n" : ""), "utf8");
|
|
36647
|
+
fs41.renameSync(tmp, file2);
|
|
36648
|
+
}
|
|
36649
|
+
/** 入队(dedupe 不做 —— id 是新的,重复投递由 ack 收敛)。读/写失败都往外抛,不吞。 */
|
|
36650
|
+
enqueue(item) {
|
|
36651
|
+
const items = this.readOutbox();
|
|
36652
|
+
items.push(item);
|
|
36653
|
+
const cap = this.config.maxQueue ?? 500;
|
|
36654
|
+
if (items.length > cap) {
|
|
36655
|
+
const dropped = items.length - cap;
|
|
36656
|
+
items.splice(0, dropped);
|
|
36657
|
+
console.warn(`[ccBridge] outbox \u8D85\u8FC7 ${cap} \u6761\uFF0C\u4E22\u4E86\u6700\u8001\u7684 ${dropped} \u6761 \u2014\u2014 CC \u592A\u4E45\u6CA1\u6765\u6536`);
|
|
36658
|
+
}
|
|
36659
|
+
this.writeOutbox(items);
|
|
36660
|
+
}
|
|
36661
|
+
/**
|
|
36662
|
+
* ack:从队列删掉客户端确认过的。
|
|
36663
|
+
*
|
|
36664
|
+
* 读失败时**返回 0 而不是抛** —— 这里宁可"删不掉"也不能"删错":
|
|
36665
|
+
* ack 的语义是收敛,失败留给下一轮再收敛,代价只是重复投递。
|
|
36666
|
+
*/
|
|
36667
|
+
ack(ids) {
|
|
36668
|
+
if (!ids.length) return 0;
|
|
36669
|
+
const set = new Set(ids);
|
|
36670
|
+
let items;
|
|
36671
|
+
try {
|
|
36672
|
+
items = this.readOutbox();
|
|
36673
|
+
} catch (e) {
|
|
36674
|
+
console.warn(`[ccBridge] ack \u8BFB\u961F\u5217\u5931\u8D25\uFF0C\u672C\u8F6E\u4E0D\u5220\uFF08\u6D88\u606F\u4F1A\u91CD\u590D\u6295\u9012\uFF09\uFF1A${e.message}`);
|
|
36675
|
+
return 0;
|
|
36676
|
+
}
|
|
36677
|
+
const kept = items.filter((i) => !set.has(i.id));
|
|
36678
|
+
const removed = items.length - kept.length;
|
|
36679
|
+
if (removed) {
|
|
36680
|
+
try {
|
|
36681
|
+
this.writeOutbox(kept);
|
|
36682
|
+
} catch (e) {
|
|
36683
|
+
console.warn(`[ccBridge] ack \u5199\u961F\u5217\u5931\u8D25\uFF0C\u672C\u8F6E\u4E0D\u5220\uFF1A${e.message}`);
|
|
36684
|
+
return 0;
|
|
36685
|
+
}
|
|
36686
|
+
}
|
|
36687
|
+
return removed;
|
|
36688
|
+
}
|
|
36689
|
+
/** 队列条数(读不到就算 0 —— 只用于日志/握手,不能用来做删除决策) */
|
|
36690
|
+
queuedCount() {
|
|
36691
|
+
try {
|
|
36692
|
+
return this.readOutbox().length;
|
|
36693
|
+
} catch {
|
|
36694
|
+
return 0;
|
|
36695
|
+
}
|
|
36696
|
+
}
|
|
36697
|
+
/** 读队列但**不因读失败而中断**(握手补发、日志这类"读不到也能继续"的场合) */
|
|
36698
|
+
safeReadOutbox(where) {
|
|
36699
|
+
try {
|
|
36700
|
+
return this.readOutbox();
|
|
36701
|
+
} catch (e) {
|
|
36702
|
+
console.warn(`[ccBridge] ${where}\uFF1A\u8BFB\u961F\u5217\u5931\u8D25\uFF0C\u672C\u8F6E\u5F53\u4F5C\u7A7A\uFF08**\u672A\u5220\u9664\u4EFB\u4F55\u4E1C\u897F**\uFF09\uFF1A${e.message}`);
|
|
36703
|
+
return [];
|
|
36704
|
+
}
|
|
36705
|
+
}
|
|
36706
|
+
/**
|
|
36707
|
+
* 升级握手校验。见 `connect()` 里那段注释 —— 绑回环挡不住浏览器。
|
|
36708
|
+
* 返回 false 时 `ws` 会直接拒绝升级(HTTP 401),连接根本建立不起来。
|
|
36709
|
+
*/
|
|
36710
|
+
verifyUpgrade(info) {
|
|
36711
|
+
const h = info?.req?.headers ?? {};
|
|
36712
|
+
if (h.origin) {
|
|
36713
|
+
console.warn(`[ccBridge] \u26D4 \u62D2\u7EDD\u5E26 Origin \u7684\u8FDE\u63A5\uFF1A${h.origin}\uFF08\u7591\u4F3C\u7F51\u9875\u53D1\u8D77\uFF09`);
|
|
36714
|
+
return false;
|
|
36715
|
+
}
|
|
36716
|
+
const host = String(h.host || "").replace(/:\d+$/, "").toLowerCase();
|
|
36717
|
+
const ok = host === "127.0.0.1" || host === "localhost" || host === "[::1]" || host === "::1" || host === "";
|
|
36718
|
+
if (!ok) console.warn(`[ccBridge] \u26D4 \u62D2\u7EDD\u975E\u56DE\u73AF Host\uFF1A${h.host}\uFF08\u7591\u4F3C DNS rebinding\uFF09`);
|
|
36719
|
+
return ok;
|
|
36720
|
+
}
|
|
36721
|
+
// ── 连接 ──
|
|
36722
|
+
async connect() {
|
|
36723
|
+
const port = this.config.port ?? DEFAULT_PORT;
|
|
36724
|
+
try {
|
|
36725
|
+
fs41.mkdirSync(path41.dirname(this.outboxFile()), { recursive: true });
|
|
36726
|
+
} catch {
|
|
36727
|
+
}
|
|
36728
|
+
this.wss = new WebSocketServer2({
|
|
36729
|
+
port,
|
|
36730
|
+
host: "127.0.0.1",
|
|
36731
|
+
maxPayload: 4 * 1024 * 1024,
|
|
36732
|
+
// ⛔ **绑 127.0.0.1 不是安全边界。** WebSocket 不受同源策略约束,
|
|
36733
|
+
// 用户浏览器里**任意** http 页面都能 `new WebSocket('ws://127.0.0.1:16996')`
|
|
36734
|
+
// 连上来 —— 而这里连上就能以任意 `from` 往 agent 主会话注入消息
|
|
36735
|
+
// (实测过:伪造 Origin/Host 能成功注入一条 `from: 翀哥` 的消息)。
|
|
36736
|
+
// 所以必须自己校验:
|
|
36737
|
+
// · 带 `Origin` 头 = 网页发起的 → 直接拒(`scripts/cc-inbox.mjs` 是 Node
|
|
36738
|
+
// 客户端,本来就不发这个头,不受影响)
|
|
36739
|
+
// · `Host` 必须是本机回环 → 挡 DNS rebinding(域名解析到 127.0.0.1 时
|
|
36740
|
+
// Host 会是那个域名,而不是 127.0.0.1)
|
|
36741
|
+
verifyClient: (info) => this.verifyUpgrade(info)
|
|
36742
|
+
});
|
|
36743
|
+
this.wss.on("connection", (ws) => {
|
|
36744
|
+
this.clients.add(ws);
|
|
36745
|
+
console.log(`[ccBridge] CC connected (${this.clients.size} online)`);
|
|
36746
|
+
const pending2 = this.safeReadOutbox("\u63E1\u624B\u8865\u53D1");
|
|
36747
|
+
this.sendTo(ws, { type: "hello", channel: this.name, queued: pending2.length });
|
|
36748
|
+
if (pending2.length) {
|
|
36749
|
+
console.log(`[ccBridge] \u8865\u53D1 ${pending2.length} \u6761\u79EF\u538B`);
|
|
36750
|
+
this.sendTo(ws, { type: "backlog", messages: pending2 });
|
|
36751
|
+
}
|
|
36752
|
+
ws.on("message", (data) => this.handleFrame(ws, data));
|
|
36753
|
+
ws.on("close", () => {
|
|
36754
|
+
this.clients.delete(ws);
|
|
36755
|
+
console.log(`[ccBridge] CC disconnected (${this.clients.size} online)`);
|
|
36756
|
+
});
|
|
36757
|
+
ws.on("error", () => this.clients.delete(ws));
|
|
36758
|
+
});
|
|
36759
|
+
this.wss.on("error", (e) => {
|
|
36760
|
+
console.error(`[ccBridge] server error on 127.0.0.1:${port}: ${e.message}`);
|
|
36761
|
+
});
|
|
36762
|
+
await new Promise((resolve12, reject) => {
|
|
36763
|
+
const wss = this.wss;
|
|
36764
|
+
const ok = () => {
|
|
36765
|
+
wss.off("error", bad);
|
|
36766
|
+
resolve12();
|
|
36767
|
+
};
|
|
36768
|
+
const bad = (e) => {
|
|
36769
|
+
wss.off("listening", ok);
|
|
36770
|
+
reject(e);
|
|
36771
|
+
};
|
|
36772
|
+
wss.once("listening", ok);
|
|
36773
|
+
wss.once("error", bad);
|
|
36774
|
+
});
|
|
36775
|
+
console.log(`[ccBridge] listening on 127.0.0.1:${this.boundPort} (${this.queuedCount()} queued)`);
|
|
36776
|
+
}
|
|
36777
|
+
handleFrame(ws, data) {
|
|
36778
|
+
let msg2;
|
|
36779
|
+
try {
|
|
36780
|
+
msg2 = JSON.parse(String(data));
|
|
36781
|
+
} catch {
|
|
36782
|
+
this.sendTo(ws, { type: "error", error: "bad json" });
|
|
36783
|
+
return;
|
|
36784
|
+
}
|
|
36785
|
+
if (msg2.type === "ping") {
|
|
36786
|
+
this.sendTo(ws, { type: "pong" });
|
|
36787
|
+
return;
|
|
36788
|
+
}
|
|
36789
|
+
if (msg2.type === "ack") {
|
|
36790
|
+
const ids = Array.isArray(msg2.ids) ? msg2.ids.map(String) : [];
|
|
36791
|
+
const n = this.ack(ids);
|
|
36792
|
+
this.sendTo(ws, { type: "ack-ok", removed: n, queued: this.queuedCount() });
|
|
36793
|
+
return;
|
|
36794
|
+
}
|
|
36795
|
+
if (msg2.type === "chat") {
|
|
36796
|
+
const text = String(msg2.text ?? "").trim();
|
|
36797
|
+
if (!text) {
|
|
36798
|
+
this.sendTo(ws, { type: "error", error: "empty text" });
|
|
36799
|
+
return;
|
|
36800
|
+
}
|
|
36801
|
+
const from = String(msg2.from || this.config.userId || "cc");
|
|
36802
|
+
const inbound = {
|
|
36803
|
+
content: text,
|
|
36804
|
+
from,
|
|
36805
|
+
fromName: String(msg2.fromName || from),
|
|
36806
|
+
channel_id: this.name,
|
|
36807
|
+
channel: this.name,
|
|
36808
|
+
channelType: "dm",
|
|
36809
|
+
isBot: false,
|
|
36810
|
+
messageId: `ccb-${Date.now()}-${++this.seq}`,
|
|
36811
|
+
recvAt: Date.now()
|
|
36812
|
+
};
|
|
36813
|
+
console.log(`[ccBridge] \u2190 ${inbound.fromName}: ${text.slice(0, 80)}`);
|
|
36814
|
+
this.messageHandler?.(inbound);
|
|
36815
|
+
return;
|
|
36816
|
+
}
|
|
36817
|
+
this.sendTo(ws, { type: "error", error: `unknown type: ${msg2.type}` });
|
|
36818
|
+
}
|
|
36819
|
+
sendTo(ws, payload) {
|
|
36820
|
+
if (ws.readyState !== WebSocket2.OPEN) return;
|
|
36821
|
+
try {
|
|
36822
|
+
ws.send(JSON.stringify(payload));
|
|
36823
|
+
} catch {
|
|
36824
|
+
}
|
|
36825
|
+
}
|
|
36826
|
+
/** 广播给出站消息之外的控制帧(握手/pong 走 sendTo 单播) */
|
|
36827
|
+
broadcast(payload) {
|
|
36828
|
+
const s = JSON.stringify(payload);
|
|
36829
|
+
let n = 0;
|
|
36830
|
+
for (const ws of [...this.clients]) {
|
|
36831
|
+
if (ws.readyState !== WebSocket2.OPEN) {
|
|
36832
|
+
this.clients.delete(ws);
|
|
36833
|
+
continue;
|
|
36834
|
+
}
|
|
36835
|
+
try {
|
|
36836
|
+
ws.send(s);
|
|
36837
|
+
n++;
|
|
36838
|
+
} catch {
|
|
36839
|
+
this.clients.delete(ws);
|
|
36840
|
+
}
|
|
36841
|
+
}
|
|
36842
|
+
return n;
|
|
36843
|
+
}
|
|
36844
|
+
async disconnect() {
|
|
36845
|
+
for (const ws of [...this.clients]) {
|
|
36846
|
+
try {
|
|
36847
|
+
ws.close();
|
|
36848
|
+
} catch {
|
|
36849
|
+
}
|
|
36850
|
+
}
|
|
36851
|
+
this.clients.clear();
|
|
36852
|
+
this.wss?.close();
|
|
36853
|
+
this.wss = null;
|
|
36854
|
+
}
|
|
36855
|
+
// ── 出站 ──
|
|
36856
|
+
/**
|
|
36857
|
+
* 发消息给 CC。
|
|
36858
|
+
*
|
|
36859
|
+
* **先落 outbox 再推实时** —— 顺序反过来的话,推送成功但 CC 恰好这时断开,
|
|
36860
|
+
* 这条消息就既不在队列里也没送达,静默消失。宁可重复,不可丢失:
|
|
36861
|
+
* 重复只是多读一遍,丢失是 agent 主动说了话而没人知道。
|
|
36862
|
+
*/
|
|
36863
|
+
async send(_target, message, _options) {
|
|
36864
|
+
if (!message.trim()) return;
|
|
36865
|
+
const item = {
|
|
36866
|
+
id: `m${Date.now()}-${++this.seq}`,
|
|
36867
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36868
|
+
text: message,
|
|
36869
|
+
agent: this.config.agentName || "agent"
|
|
36870
|
+
};
|
|
36871
|
+
this.enqueue(item);
|
|
36872
|
+
const n = this.broadcast({ type: "message", message: item });
|
|
36873
|
+
if (n === 0) console.log(`[ccBridge] CC \u4E0D\u5728\u7EBF\uFF0C\u5DF2\u5165\u961F\uFF08${this.queuedCount()} \u6761\u5F85\u6536\uFF09`);
|
|
36874
|
+
}
|
|
36875
|
+
/**
|
|
36876
|
+
* 发文件。
|
|
36877
|
+
*
|
|
36878
|
+
* **只送路径,不送内容**(`mode: 'path-ref'`)。这个通道两端都在 127.0.0.1 上
|
|
36879
|
+
* (引擎与 CC 同机),把文件 base64 塞进 WS 帧纯属浪费 —— 大文件还会把 outbox 撑爆。
|
|
36880
|
+
*
|
|
36881
|
+
* ⚠️ **但"只送路径"是把解析推迟到接收端,推迟不等于可以不做。**
|
|
36882
|
+
* 它有一个很难看的退化形态:发送端返回「已发送」(路径形式上没问题),
|
|
36883
|
+
* 接收端拿着路径在**它自己的机器上**找不到 —— **失败发生在接收端,
|
|
36884
|
+
* 而发送端日志一切正常**。(张小T 0928 指出的,她是对的。)
|
|
36885
|
+
*
|
|
36886
|
+
* 所以这里补两道:
|
|
36887
|
+
* 1. **入队前 `statSync`** —— 路径打错/文件被挪走,**在本机就报错**,
|
|
36888
|
+
* 而不是等接收端去找一个不存在的路径
|
|
36889
|
+
* 2. 帧里带 `size`/`mtimeMs` + `mode: 'path-ref'` —— 接收端知道这是**引用不是内容**,
|
|
36890
|
+
* 且能对一下"拿到的是不是你说的那份"
|
|
36891
|
+
*
|
|
36892
|
+
* 客户端那侧对应地**读不到就不 ack**(见 `scripts/cc-inbox.mjs`)——
|
|
36893
|
+
* ack 的语义是"我拿到了",拿到一半不能算。
|
|
36894
|
+
*/
|
|
36895
|
+
async sendFile(_target, message, attachment) {
|
|
36896
|
+
let st;
|
|
36897
|
+
try {
|
|
36898
|
+
st = fs41.statSync(attachment.path);
|
|
36899
|
+
} catch (e) {
|
|
36900
|
+
throw new Error(`\u9644\u4EF6\u4E0D\u5B58\u5728\uFF0C\u672A\u53D1\u9001\uFF1A${attachment.path}\uFF08${e?.code || e?.message}\uFF09`);
|
|
36901
|
+
}
|
|
36902
|
+
const file2 = {
|
|
36903
|
+
mode: "path-ref",
|
|
36904
|
+
path: attachment.path,
|
|
36905
|
+
filename: attachment.filename || path41.basename(attachment.path),
|
|
36906
|
+
mimeType: attachment.mimeType || "application/octet-stream",
|
|
36907
|
+
size: st.size,
|
|
36908
|
+
mtimeMs: Math.round(st.mtimeMs)
|
|
36909
|
+
};
|
|
36910
|
+
const item = {
|
|
36911
|
+
id: `f${Date.now()}-${++this.seq}`,
|
|
36912
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36913
|
+
text: message || "",
|
|
36914
|
+
agent: this.config.agentName || "agent",
|
|
36915
|
+
file: file2
|
|
36916
|
+
};
|
|
36917
|
+
this.enqueue(item);
|
|
36918
|
+
if (this.broadcast({ type: "message", message: item }) === 0) {
|
|
36919
|
+
console.log(`[ccBridge] CC \u4E0D\u5728\u7EBF\uFF0C\u9644\u4EF6\u5DF2\u5165\u961F\uFF1A${file2.filename}`);
|
|
36920
|
+
}
|
|
36921
|
+
}
|
|
36922
|
+
};
|
|
36923
|
+
|
|
36542
36924
|
// src/channels/manager.ts
|
|
36543
|
-
var MANAGED_CHANNEL_NAMES = ["discord", "feishu", "wechat", "deskBuddy"];
|
|
36925
|
+
var MANAGED_CHANNEL_NAMES = ["discord", "feishu", "wechat", "deskBuddy", "ccBridge"];
|
|
36544
36926
|
var defaultFactories = {
|
|
36545
36927
|
discord: (block) => {
|
|
36546
36928
|
const accounts = block.accounts || {};
|
|
@@ -36602,6 +36984,9 @@ var defaultFactories = {
|
|
|
36602
36984
|
},
|
|
36603
36985
|
deskBuddy: (block) => {
|
|
36604
36986
|
return new DeskBuddyAdapter(block);
|
|
36987
|
+
},
|
|
36988
|
+
ccBridge: (block) => {
|
|
36989
|
+
return new CcBridgeAdapter(block);
|
|
36605
36990
|
}
|
|
36606
36991
|
};
|
|
36607
36992
|
var ChannelManager = class {
|
|
@@ -36873,20 +37258,20 @@ var ChannelManager = class {
|
|
|
36873
37258
|
// src/session/session-manager.ts
|
|
36874
37259
|
init_types();
|
|
36875
37260
|
init_types();
|
|
36876
|
-
import * as
|
|
36877
|
-
import * as
|
|
37261
|
+
import * as path43 from "node:path";
|
|
37262
|
+
import * as fs43 from "node:fs";
|
|
36878
37263
|
import * as crypto6 from "node:crypto";
|
|
36879
37264
|
|
|
36880
37265
|
// src/session/reader.ts
|
|
36881
37266
|
init_attachments();
|
|
36882
|
-
import * as
|
|
36883
|
-
import * as
|
|
37267
|
+
import * as fs42 from "node:fs";
|
|
37268
|
+
import * as path42 from "node:path";
|
|
36884
37269
|
import * as readline2 from "node:readline";
|
|
36885
37270
|
var SKIP_PRECOMPACT_THRESHOLD = 5 * 1024 * 1024;
|
|
36886
37271
|
var TRANSCRIPT_READ_CHUNK_SIZE = 1024 * 1024;
|
|
36887
37272
|
var MAX_TRANSCRIPT_READ_BYTES = 50 * 1024 * 1024;
|
|
36888
37273
|
async function readSessionHeader(filePath) {
|
|
36889
|
-
const stream =
|
|
37274
|
+
const stream = fs42.createReadStream(filePath, { encoding: "utf-8" });
|
|
36890
37275
|
const rl = readline2.createInterface({ input: stream });
|
|
36891
37276
|
return new Promise((resolve12) => {
|
|
36892
37277
|
let resolved = false;
|
|
@@ -36917,7 +37302,7 @@ async function readSessionHeader(filePath) {
|
|
|
36917
37302
|
});
|
|
36918
37303
|
}
|
|
36919
37304
|
async function readLargeFilePostBoundary(filePath) {
|
|
36920
|
-
const stat4 =
|
|
37305
|
+
const stat4 = fs42.statSync(filePath);
|
|
36921
37306
|
const fileSize = stat4.size;
|
|
36922
37307
|
const boundaryMarker = '"compact_boundary"';
|
|
36923
37308
|
const CHUNK_SIZE = TRANSCRIPT_READ_CHUNK_SIZE;
|
|
@@ -36925,13 +37310,13 @@ async function readLargeFilePostBoundary(filePath) {
|
|
|
36925
37310
|
let outLen = 0;
|
|
36926
37311
|
let boundaryStartOffset = 0;
|
|
36927
37312
|
const chunk = Buffer.allocUnsafe(CHUNK_SIZE);
|
|
36928
|
-
const fd =
|
|
37313
|
+
const fd = fs42.openSync(filePath, "r");
|
|
36929
37314
|
try {
|
|
36930
37315
|
let filePos = 0;
|
|
36931
37316
|
let carryBuf;
|
|
36932
37317
|
let carryLen = 0;
|
|
36933
37318
|
while (filePos < fileSize) {
|
|
36934
|
-
const bytesRead =
|
|
37319
|
+
const bytesRead = fs42.readSync(fd, chunk, 0, Math.min(CHUNK_SIZE, fileSize - filePos), filePos);
|
|
36935
37320
|
if (bytesRead === 0) break;
|
|
36936
37321
|
filePos += bytesRead;
|
|
36937
37322
|
let scanBuf;
|
|
@@ -36983,7 +37368,7 @@ async function readLargeFilePostBoundary(filePath) {
|
|
|
36983
37368
|
}
|
|
36984
37369
|
}
|
|
36985
37370
|
} finally {
|
|
36986
|
-
|
|
37371
|
+
fs42.closeSync(fd);
|
|
36987
37372
|
}
|
|
36988
37373
|
const postBoundaryText = outBuf.subarray(0, outLen).toString("utf-8");
|
|
36989
37374
|
return postBoundaryText.split("\n").filter((l) => l.trim().length > 0);
|
|
@@ -37107,7 +37492,7 @@ function recoverOrphanedParallelToolResults(allEntries, chain, byUuid, seen) {
|
|
|
37107
37492
|
return result;
|
|
37108
37493
|
}
|
|
37109
37494
|
async function readSessionHistory(filePath) {
|
|
37110
|
-
const stat4 =
|
|
37495
|
+
const stat4 = fs42.statSync(filePath);
|
|
37111
37496
|
const fileSize = stat4.size;
|
|
37112
37497
|
if (fileSize > MAX_TRANSCRIPT_READ_BYTES) {
|
|
37113
37498
|
console.warn(`[reader] Session file exceeds 50MB limit (${(fileSize / 1024 / 1024).toFixed(1)}MB), using chunked fd read with boundary skip`);
|
|
@@ -37146,7 +37531,7 @@ async function readSessionHistory(filePath) {
|
|
|
37146
37531
|
return [...sessionStarts, ...filtered, ...restAttachments];
|
|
37147
37532
|
}
|
|
37148
37533
|
async function readAllLines(filePath) {
|
|
37149
|
-
const stream =
|
|
37534
|
+
const stream = fs42.createReadStream(filePath, { encoding: "utf-8" });
|
|
37150
37535
|
const rl = readline2.createInterface({ input: stream });
|
|
37151
37536
|
const lines = [];
|
|
37152
37537
|
return new Promise((resolve12, reject) => {
|
|
@@ -37300,12 +37685,12 @@ function extractText4(content) {
|
|
|
37300
37685
|
return content.filter((b) => b.type === "text").map((b) => b.text || "").join("") || null;
|
|
37301
37686
|
}
|
|
37302
37687
|
function listSessions(sessionsDir) {
|
|
37303
|
-
if (!
|
|
37304
|
-
const files =
|
|
37305
|
-
file:
|
|
37688
|
+
if (!fs42.existsSync(sessionsDir)) return [];
|
|
37689
|
+
const files = fs42.readdirSync(sessionsDir).filter((f) => f.endsWith(".jsonl") || f.includes(".jsonl.")).map((f) => ({
|
|
37690
|
+
file: path42.join(sessionsDir, f),
|
|
37306
37691
|
id: f.replace(/\.jsonl.*$/, ""),
|
|
37307
37692
|
// strip .jsonl and everything after (archived suffix)
|
|
37308
|
-
modified: new Date(
|
|
37693
|
+
modified: new Date(fs42.statSync(path42.join(sessionsDir, f)).mtimeMs)
|
|
37309
37694
|
})).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
37310
37695
|
return files;
|
|
37311
37696
|
}
|
|
@@ -37340,24 +37725,24 @@ var SessionManager = class {
|
|
|
37340
37725
|
constructor(config) {
|
|
37341
37726
|
this.config = config;
|
|
37342
37727
|
this.scopeConfig = config.scopeConfig;
|
|
37343
|
-
this.sessionsDir =
|
|
37344
|
-
this.indexPath =
|
|
37345
|
-
this.platformMapPath =
|
|
37346
|
-
|
|
37728
|
+
this.sessionsDir = path43.join(config.stateDir, "agents", "main", "sessions");
|
|
37729
|
+
this.indexPath = path43.join(this.sessionsDir, "session-index.json");
|
|
37730
|
+
this.platformMapPath = path43.join(this.sessionsDir, "platform-map.json");
|
|
37731
|
+
fs43.mkdirSync(this.sessionsDir, { recursive: true });
|
|
37347
37732
|
}
|
|
37348
37733
|
// === 索引操作 ===
|
|
37349
37734
|
loadIndex() {
|
|
37350
37735
|
try {
|
|
37351
|
-
if (
|
|
37352
|
-
const raw =
|
|
37736
|
+
if (fs43.existsSync(this.indexPath)) {
|
|
37737
|
+
const raw = fs43.readFileSync(this.indexPath, "utf-8");
|
|
37353
37738
|
const obj = JSON.parse(raw);
|
|
37354
37739
|
this.index = new Map(Object.entries(obj));
|
|
37355
37740
|
let fixed = 0;
|
|
37356
37741
|
for (const [key, entry] of this.index) {
|
|
37357
|
-
if (
|
|
37358
|
-
const basename10 =
|
|
37359
|
-
const localPath =
|
|
37360
|
-
if (
|
|
37742
|
+
if (fs43.existsSync(entry.file)) continue;
|
|
37743
|
+
const basename10 = path43.basename(entry.file);
|
|
37744
|
+
const localPath = path43.join(this.sessionsDir, basename10);
|
|
37745
|
+
if (fs43.existsSync(localPath)) {
|
|
37361
37746
|
entry.file = localPath;
|
|
37362
37747
|
fixed++;
|
|
37363
37748
|
}
|
|
@@ -37376,7 +37761,7 @@ var SessionManager = class {
|
|
|
37376
37761
|
try {
|
|
37377
37762
|
const obj = {};
|
|
37378
37763
|
for (const [k, v] of this.index) obj[k] = v;
|
|
37379
|
-
|
|
37764
|
+
fs43.writeFileSync(this.indexPath, JSON.stringify(obj, null, 2));
|
|
37380
37765
|
} catch (err) {
|
|
37381
37766
|
console.error(`[session] Failed to save index: ${err.message}`);
|
|
37382
37767
|
}
|
|
@@ -37384,8 +37769,8 @@ var SessionManager = class {
|
|
|
37384
37769
|
// === 平台映射 ===
|
|
37385
37770
|
loadPlatformMap() {
|
|
37386
37771
|
try {
|
|
37387
|
-
if (
|
|
37388
|
-
const raw =
|
|
37772
|
+
if (fs43.existsSync(this.platformMapPath)) {
|
|
37773
|
+
const raw = fs43.readFileSync(this.platformMapPath, "utf-8");
|
|
37389
37774
|
const obj = JSON.parse(raw);
|
|
37390
37775
|
this.platformMap = new Map(Object.entries(obj));
|
|
37391
37776
|
console.log(`[session] Platform map loaded: ${this.platformMap.size} entries`);
|
|
@@ -37397,7 +37782,7 @@ var SessionManager = class {
|
|
|
37397
37782
|
try {
|
|
37398
37783
|
const obj = {};
|
|
37399
37784
|
for (const [k, v] of this.platformMap) obj[k] = v;
|
|
37400
|
-
|
|
37785
|
+
fs43.writeFileSync(this.platformMapPath, JSON.stringify(obj, null, 2));
|
|
37401
37786
|
} catch (err) {
|
|
37402
37787
|
console.error(`[session] Failed to save platform map: ${err.message}`);
|
|
37403
37788
|
}
|
|
@@ -37436,17 +37821,17 @@ var SessionManager = class {
|
|
|
37436
37821
|
*/
|
|
37437
37822
|
getSessionFilePath(sessionId) {
|
|
37438
37823
|
const entry = this.index.get(sessionId);
|
|
37439
|
-
if (entry &&
|
|
37440
|
-
const direct =
|
|
37441
|
-
return
|
|
37824
|
+
if (entry && fs43.existsSync(entry.file)) return entry.file;
|
|
37825
|
+
const direct = path43.join(this.sessionsDir, `${sessionId}.jsonl`);
|
|
37826
|
+
return fs43.existsSync(direct) ? direct : null;
|
|
37442
37827
|
}
|
|
37443
37828
|
// === Writer 管理 ===
|
|
37444
37829
|
getWriter(sessionId) {
|
|
37445
37830
|
let writer = this.writers.get(sessionId);
|
|
37446
37831
|
if (!writer) {
|
|
37447
37832
|
const existing = this.index.get(sessionId);
|
|
37448
|
-
if (existing &&
|
|
37449
|
-
const existingFileId =
|
|
37833
|
+
if (existing && fs43.existsSync(existing.file)) {
|
|
37834
|
+
const existingFileId = path43.basename(existing.file, ".jsonl");
|
|
37450
37835
|
writer = new SessionWriter(this.sessionsDir, existingFileId);
|
|
37451
37836
|
writer.open(this.config.workspace, sessionId);
|
|
37452
37837
|
} else {
|
|
@@ -37458,7 +37843,7 @@ var SessionManager = class {
|
|
|
37458
37843
|
this.saveIndex();
|
|
37459
37844
|
this.writers.set(sessionId, writer);
|
|
37460
37845
|
this.onWriterCreated?.(writer, sessionId);
|
|
37461
|
-
console.log(`[session] ${existing ? "Resumed" : "New"}: ${sessionId} \u2192 ${
|
|
37846
|
+
console.log(`[session] ${existing ? "Resumed" : "New"}: ${sessionId} \u2192 ${path43.basename(writer.path)}`);
|
|
37462
37847
|
}
|
|
37463
37848
|
return writer;
|
|
37464
37849
|
}
|
|
@@ -37477,7 +37862,7 @@ var SessionManager = class {
|
|
|
37477
37862
|
if (!writer) return null;
|
|
37478
37863
|
try {
|
|
37479
37864
|
this.lastArchiveCheck = Date.now();
|
|
37480
|
-
const stat4 =
|
|
37865
|
+
const stat4 = fs43.statSync(writer.path);
|
|
37481
37866
|
if (stat4.size < threshold) return null;
|
|
37482
37867
|
const sizeMB = (stat4.size / 1024 / 1024).toFixed(1);
|
|
37483
37868
|
const thresholdMB = (threshold / 1024 / 1024).toFixed(1);
|
|
@@ -37486,10 +37871,10 @@ var SessionManager = class {
|
|
|
37486
37871
|
if (archivedPath) {
|
|
37487
37872
|
this.index.set(sessionId, { file: writer.path, updatedAt: Date.now() });
|
|
37488
37873
|
this.saveIndex();
|
|
37489
|
-
const newSize =
|
|
37874
|
+
const newSize = fs43.statSync(writer.path).size;
|
|
37490
37875
|
const oldSizeMB = (stat4.size / 1024 / 1024).toFixed(1);
|
|
37491
|
-
console.log(`[session] Archive complete: ${
|
|
37492
|
-
const fileId =
|
|
37876
|
+
console.log(`[session] Archive complete: ${path43.basename(archivedPath)} (${oldSizeMB}MB) \u2192 new JSONL created (${newSize} bytes)`);
|
|
37877
|
+
const fileId = path43.basename(writer.path).replace(/\.jsonl$/, "");
|
|
37493
37878
|
this.purgeOldArchives(fileId, 3);
|
|
37494
37879
|
return archivedPath;
|
|
37495
37880
|
}
|
|
@@ -37505,15 +37890,15 @@ var SessionManager = class {
|
|
|
37505
37890
|
purgeOldArchives(sessionId, keepCount = 3) {
|
|
37506
37891
|
try {
|
|
37507
37892
|
const dir = this.sessionsDir;
|
|
37508
|
-
if (!
|
|
37509
|
-
const allFiles =
|
|
37893
|
+
if (!fs43.existsSync(dir)) return;
|
|
37894
|
+
const allFiles = fs43.readdirSync(dir);
|
|
37510
37895
|
const archiveFiles = allFiles.filter((f) => {
|
|
37511
37896
|
if (!f.startsWith(sessionId)) return false;
|
|
37512
37897
|
return f.includes(".archived.") || f.includes(".compaction.");
|
|
37513
37898
|
}).map((f) => ({
|
|
37514
37899
|
name: f,
|
|
37515
|
-
path:
|
|
37516
|
-
mtime:
|
|
37900
|
+
path: path43.join(dir, f),
|
|
37901
|
+
mtime: fs43.statSync(path43.join(dir, f)).mtimeMs
|
|
37517
37902
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
37518
37903
|
console.log(`[session] purgeOldArchives: sessionId=${sessionId.substring(0, 8)}... found=${archiveFiles.length} keep=${keepCount}`);
|
|
37519
37904
|
if (archiveFiles.length <= keepCount) return;
|
|
@@ -37523,7 +37908,7 @@ var SessionManager = class {
|
|
|
37523
37908
|
console.log(`[session] Skipping archived (kept for history): ${f.name}`);
|
|
37524
37909
|
continue;
|
|
37525
37910
|
}
|
|
37526
|
-
|
|
37911
|
+
fs43.unlinkSync(f.path);
|
|
37527
37912
|
console.log(`[session] Purged old archive: ${f.name}`);
|
|
37528
37913
|
}
|
|
37529
37914
|
if (toDelete.length > 0) {
|
|
@@ -37572,29 +37957,29 @@ var SessionManager = class {
|
|
|
37572
37957
|
/** 列出 session 的所有文件(当前 + 归档),按修改时间排序 */
|
|
37573
37958
|
findAllSessionFiles(sessionId) {
|
|
37574
37959
|
const results = [];
|
|
37575
|
-
const mainFile =
|
|
37576
|
-
if (
|
|
37960
|
+
const mainFile = path43.join(this.sessionsDir, `${sessionId}.jsonl`);
|
|
37961
|
+
if (fs43.existsSync(mainFile)) {
|
|
37577
37962
|
results.push(mainFile);
|
|
37578
37963
|
}
|
|
37579
|
-
if (
|
|
37580
|
-
const compactionFiles =
|
|
37964
|
+
if (fs43.existsSync(this.sessionsDir)) {
|
|
37965
|
+
const compactionFiles = fs43.readdirSync(this.sessionsDir).filter((f) => f.startsWith(`${sessionId}.jsonl.compaction.`)).map((f) => ({
|
|
37581
37966
|
name: f,
|
|
37582
|
-
path:
|
|
37583
|
-
mtime:
|
|
37967
|
+
path: path43.join(this.sessionsDir, f),
|
|
37968
|
+
mtime: fs43.statSync(path43.join(this.sessionsDir, f)).mtimeMs
|
|
37584
37969
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
37585
37970
|
if (compactionFiles.length > 0) {
|
|
37586
37971
|
results.push(compactionFiles[0].path);
|
|
37587
37972
|
}
|
|
37588
37973
|
}
|
|
37589
37974
|
results.sort((a, b) => {
|
|
37590
|
-
const ta =
|
|
37591
|
-
const tb =
|
|
37975
|
+
const ta = fs43.statSync(a).mtimeMs;
|
|
37976
|
+
const tb = fs43.statSync(b).mtimeMs;
|
|
37592
37977
|
return ta - tb;
|
|
37593
37978
|
});
|
|
37594
37979
|
return results;
|
|
37595
37980
|
}
|
|
37596
37981
|
async findSessionFiles(sessionId) {
|
|
37597
|
-
if (!
|
|
37982
|
+
if (!fs43.existsSync(this.sessionsDir)) {
|
|
37598
37983
|
console.log(`[session:find] SESSIONS_DIR not found: ${this.sessionsDir}`);
|
|
37599
37984
|
return [];
|
|
37600
37985
|
}
|
|
@@ -37606,15 +37991,15 @@ var SessionManager = class {
|
|
|
37606
37991
|
return mappedFiles;
|
|
37607
37992
|
}
|
|
37608
37993
|
const indexedEntry = this.index.get(mappedId);
|
|
37609
|
-
if (indexedEntry &&
|
|
37610
|
-
const fileUuid =
|
|
37994
|
+
if (indexedEntry && fs43.existsSync(indexedEntry.file)) {
|
|
37995
|
+
const fileUuid = path43.basename(indexedEntry.file, ".jsonl");
|
|
37611
37996
|
return this.findAllSessionFiles(fileUuid);
|
|
37612
37997
|
}
|
|
37613
37998
|
}
|
|
37614
37999
|
const allFiles = this.findAllSessionFiles(sessionId);
|
|
37615
38000
|
if (allFiles.length > 0) {
|
|
37616
38001
|
const labels = allFiles.map((f) => {
|
|
37617
|
-
const base =
|
|
38002
|
+
const base = path43.basename(f);
|
|
37618
38003
|
return base.endsWith(".jsonl") ? base : `${base} (archived)`;
|
|
37619
38004
|
});
|
|
37620
38005
|
console.log(`[session:find] Found ${allFiles.length} file(s) for ${sessionId}: ${labels.join(", ")}`);
|
|
@@ -37622,9 +38007,9 @@ var SessionManager = class {
|
|
|
37622
38007
|
}
|
|
37623
38008
|
const indexed = this.index.get(sessionId);
|
|
37624
38009
|
if (indexed) {
|
|
37625
|
-
console.log(`[session:find] Index hit: ${sessionId} \u2192 ${indexed.file} (exists=${
|
|
37626
|
-
if (
|
|
37627
|
-
return this.findAllSessionFiles(
|
|
38010
|
+
console.log(`[session:find] Index hit: ${sessionId} \u2192 ${indexed.file} (exists=${fs43.existsSync(indexed.file)})`);
|
|
38011
|
+
if (fs43.existsSync(indexed.file)) {
|
|
38012
|
+
return this.findAllSessionFiles(path43.basename(indexed.file, ".jsonl"));
|
|
37628
38013
|
}
|
|
37629
38014
|
} else {
|
|
37630
38015
|
console.log(`[session:find] Index miss: ${sessionId} (index has ${this.index.size} entries)`);
|
|
@@ -37653,8 +38038,8 @@ var SessionManager = class {
|
|
|
37653
38038
|
return [];
|
|
37654
38039
|
}
|
|
37655
38040
|
files.sort((a, b) => {
|
|
37656
|
-
const ta =
|
|
37657
|
-
const tb =
|
|
38041
|
+
const ta = fs43.statSync(a).mtimeMs;
|
|
38042
|
+
const tb = fs43.statSync(b).mtimeMs;
|
|
37658
38043
|
return ta - tb;
|
|
37659
38044
|
});
|
|
37660
38045
|
const latestFile = files[files.length - 1];
|
|
@@ -37662,7 +38047,7 @@ var SessionManager = class {
|
|
|
37662
38047
|
if (!currentEntry || currentEntry.file !== latestFile) {
|
|
37663
38048
|
this.index.set(sessionId, { file: latestFile, updatedAt: Date.now() });
|
|
37664
38049
|
this.saveIndex();
|
|
37665
|
-
console.log(`[session:restore] Synced index: ${sessionId} \u2192 ${
|
|
38050
|
+
console.log(`[session:restore] Synced index: ${sessionId} \u2192 ${path43.basename(latestFile)}`);
|
|
37666
38051
|
}
|
|
37667
38052
|
const allMessages = [];
|
|
37668
38053
|
for (const file2 of files) {
|
|
@@ -37701,7 +38086,7 @@ var SessionManager = class {
|
|
|
37701
38086
|
if (isAttachmentMessage(m) && m.attachment.type === "relevant_memories") {
|
|
37702
38087
|
for (const mem of m.attachment.memories) {
|
|
37703
38088
|
rawCount++;
|
|
37704
|
-
restoredPaths.add(
|
|
38089
|
+
restoredPaths.add(path43.resolve(mem.path));
|
|
37705
38090
|
}
|
|
37706
38091
|
}
|
|
37707
38092
|
}
|
|
@@ -37733,15 +38118,15 @@ var SessionManager = class {
|
|
|
37733
38118
|
*/
|
|
37734
38119
|
rotateMainSession() {
|
|
37735
38120
|
const entry = this.index.get("main");
|
|
37736
|
-
if (!entry || !
|
|
37737
|
-
const oldFileId =
|
|
37738
|
-
const stat4 =
|
|
38121
|
+
if (!entry || !fs43.existsSync(entry.file)) return;
|
|
38122
|
+
const oldFileId = path43.basename(entry.file, ".jsonl");
|
|
38123
|
+
const stat4 = fs43.statSync(entry.file);
|
|
37739
38124
|
if (stat4.size < 1024) return;
|
|
37740
38125
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
|
|
37741
38126
|
const archivedPath = `${entry.file}.archived.${ts}`;
|
|
37742
|
-
|
|
38127
|
+
fs43.renameSync(entry.file, archivedPath);
|
|
37743
38128
|
const sizeMB = (stat4.size / 1024 / 1024).toFixed(1);
|
|
37744
|
-
console.log(`[session] Archived: ${
|
|
38129
|
+
console.log(`[session] Archived: ${path43.basename(entry.file)} \u2192 ${path43.basename(archivedPath)} (${sizeMB}MB)`);
|
|
37745
38130
|
const newWriter = new SessionWriter(this.sessionsDir);
|
|
37746
38131
|
newWriter.open(this.config.workspace, "main");
|
|
37747
38132
|
newWriter.writeModelChange(this.config.providerId, this.config.model);
|
|
@@ -37750,7 +38135,7 @@ var SessionManager = class {
|
|
|
37750
38135
|
this.index.set("main", { file: newPath, updatedAt: Date.now() });
|
|
37751
38136
|
this.saveIndex();
|
|
37752
38137
|
this.purgeOldArchives(oldFileId, 3);
|
|
37753
|
-
console.log(`[session] Rotated main: ${oldFileId}.jsonl \u2192 ${
|
|
38138
|
+
console.log(`[session] Rotated main: ${oldFileId}.jsonl \u2192 ${path43.basename(newPath)}`);
|
|
37754
38139
|
}
|
|
37755
38140
|
// === 迁移 ===
|
|
37756
38141
|
/** 迁移旧索引:session-index.json 里有 "discord:xxx" 格式的 key 时,生成 UUID 替换 */
|
|
@@ -38553,7 +38938,7 @@ Examples:
|
|
|
38553
38938
|
properties: {
|
|
38554
38939
|
to: { type: "string", description: "\u63A5\u6536\u8005ID\u3002\u586B channel_id \u65F6\u81EA\u52A8 @\u5BF9\u65B9\uFF1B\u4E0D\u586B channel_id \u65F6\u53D1 DM\u3002\u591A\u4E2A\u6536\u4EF6\u4EBA\u7528\u9017\u53F7\u5206\u9694\u3002" },
|
|
38555
38940
|
channel_id: { type: "string", description: "\u9891\u9053 ID\uFF08\u53EF\u9009\uFF09\u3002\u4E0D\u586B\u5219\u53D1 DM \u7ED9 to\u3002\u90FD\u4E0D\u586B\u65F6\u56DE\u590D\u6765\u6E90\u9891\u9053\u3002" },
|
|
38556
|
-
source: { type: "string", description: "\u6765\u6E90\u901A\u9053\uFF1Adiscord / feishu / wechat / deskBuddy\uFF08\u684C\u9762\u5C0F\u96EF\u8BED\u97F3\uFF09\u3002\u4E0D\u586B\u65F6\u4F7F\u7528\u5F53\u524D\u6765\u6E90\u901A\u9053\u3002", enum: ["discord", "feishu", "wechat", "deskBuddy"] },
|
|
38941
|
+
source: { type: "string", description: "\u6765\u6E90\u901A\u9053\uFF1Adiscord / feishu / wechat / deskBuddy\uFF08\u684C\u9762\u5C0F\u96EF\u8BED\u97F3\uFF09/ ccBridge\uFF08CC\uFF0C\u672C\u673A\u5F00\u53D1\u901A\u9053\uFF09\u3002\u4E0D\u586B\u65F6\u4F7F\u7528\u5F53\u524D\u6765\u6E90\u901A\u9053\u3002", enum: ["discord", "feishu", "wechat", "deskBuddy", "ccBridge"] },
|
|
38557
38942
|
content: { type: "string", description: "\u6D88\u606F\u6B63\u6587" }
|
|
38558
38943
|
},
|
|
38559
38944
|
required: ["content", "to"]
|
|
@@ -38642,12 +39027,12 @@ Examples:
|
|
|
38642
39027
|
// src/tools/msg-husband.ts
|
|
38643
39028
|
init_registry();
|
|
38644
39029
|
init_live();
|
|
38645
|
-
import
|
|
38646
|
-
import
|
|
39030
|
+
import fs44 from "node:fs";
|
|
39031
|
+
import path44 from "node:path";
|
|
38647
39032
|
function getHusbandFeishuId(workspace) {
|
|
38648
|
-
const contactsPath =
|
|
39033
|
+
const contactsPath = path44.join(workspace, "prompts", "contacts.md");
|
|
38649
39034
|
try {
|
|
38650
|
-
const text =
|
|
39035
|
+
const text = fs44.readFileSync(contactsPath, "utf-8");
|
|
38651
39036
|
const m = text.match(/\|\s*翀哥\s*\|\s*(ou_[a-f0-9]+)\s*\|/);
|
|
38652
39037
|
return m ? m[1] : null;
|
|
38653
39038
|
} catch {
|
|
@@ -38789,8 +39174,8 @@ Examples:
|
|
|
38789
39174
|
if (!to && !resolvedChannelId) {
|
|
38790
39175
|
return { content: "to \u548C channel_id \u4E0D\u80FD\u540C\u65F6\u4E3A\u7A7A\uFF0C\u4E14\u6CA1\u6709\u53EF\u7528\u7684\u6765\u6E90\u9891\u9053", isError: true };
|
|
38791
39176
|
}
|
|
38792
|
-
const
|
|
38793
|
-
if (!
|
|
39177
|
+
const fs59 = await import("node:fs");
|
|
39178
|
+
if (!fs59.existsSync(filePath)) {
|
|
38794
39179
|
return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6\u4E0D\u5B58\u5728 ${filePath}`, isError: true };
|
|
38795
39180
|
}
|
|
38796
39181
|
const toIds = to ? to.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
@@ -38818,7 +39203,7 @@ Examples:
|
|
|
38818
39203
|
md: "text/markdown"
|
|
38819
39204
|
};
|
|
38820
39205
|
const mimeType = mimeTypeMap[ext] || "application/octet-stream";
|
|
38821
|
-
const stat4 =
|
|
39206
|
+
const stat4 = fs59.statSync(filePath);
|
|
38822
39207
|
const sizeMB = stat4.size / 1024 / 1024;
|
|
38823
39208
|
if (sizeMB > 25) {
|
|
38824
39209
|
return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6 ${sizeMB.toFixed(1)}MB \u8D85\u8FC7 Discord 25MB \u9650\u5236`, isError: true };
|
|
@@ -38848,8 +39233,8 @@ Examples:
|
|
|
38848
39233
|
init_live();
|
|
38849
39234
|
init_registry();
|
|
38850
39235
|
init_well_formed();
|
|
38851
|
-
import * as
|
|
38852
|
-
import * as
|
|
39236
|
+
import * as fs45 from "node:fs";
|
|
39237
|
+
import * as path45 from "node:path";
|
|
38853
39238
|
var MIME_MAP = {
|
|
38854
39239
|
".jpg": "jpeg",
|
|
38855
39240
|
".jpeg": "jpeg",
|
|
@@ -38861,9 +39246,9 @@ var MIME_MAP = {
|
|
|
38861
39246
|
var VIDEO_EXT = /\.(mp4|mov)$/i;
|
|
38862
39247
|
var VIDEO_MAX_BYTES = 40 * 1024 * 1024;
|
|
38863
39248
|
function resolveLatestImage(specifiedPath, mediaDir) {
|
|
38864
|
-
if (specifiedPath &&
|
|
38865
|
-
if (!
|
|
38866
|
-
const files =
|
|
39249
|
+
if (specifiedPath && fs45.existsSync(specifiedPath)) return specifiedPath;
|
|
39250
|
+
if (!fs45.existsSync(mediaDir)) return null;
|
|
39251
|
+
const files = fs45.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp|mp4|mov)$/i.test(f)).map((f) => ({ name: f, p: path45.join(mediaDir, f), mtime: fs45.statSync(path45.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
38867
39252
|
return files[0]?.p || null;
|
|
38868
39253
|
}
|
|
38869
39254
|
registry.register({
|
|
@@ -38894,21 +39279,21 @@ registry.register({
|
|
|
38894
39279
|
if (!provider?.streamChat) {
|
|
38895
39280
|
return { content: "Error: provider \u4E0D\u53EF\u7528\u3002", isError: true };
|
|
38896
39281
|
}
|
|
38897
|
-
const mediaDir =
|
|
39282
|
+
const mediaDir = path45.join(ctx.stateDir, "media", "inbound");
|
|
38898
39283
|
const imagePath = resolveLatestImage(args.image_path, mediaDir);
|
|
38899
39284
|
if (!imagePath) {
|
|
38900
39285
|
return { content: "Error: no image found. Provide image_path or ensure media/inbound has images.", isError: true };
|
|
38901
39286
|
}
|
|
38902
39287
|
const rawPrompt = args.prompt?.trim() || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
|
|
38903
|
-
const ext =
|
|
39288
|
+
const ext = path45.extname(imagePath).toLowerCase();
|
|
38904
39289
|
if (VIDEO_EXT.test(imagePath)) {
|
|
38905
|
-
const stat4 =
|
|
39290
|
+
const stat4 = fs45.statSync(imagePath);
|
|
38906
39291
|
if (stat4.size > VIDEO_MAX_BYTES) {
|
|
38907
39292
|
return { content: `Error: video too large (${(stat4.size / 1e6).toFixed(1)}MB > 40MB). Compress or trim first.`, isError: true };
|
|
38908
39293
|
}
|
|
38909
39294
|
const explicit = liveConfig.get("tools.my_eyes.videoBaseUrl");
|
|
38910
39295
|
const baseUrl = explicit || providerCfg.baseUrl.replace(/\/anthropic\/?$/, "/v1").replace(/\/$/, "");
|
|
38911
|
-
const vidB64 =
|
|
39296
|
+
const vidB64 = fs45.readFileSync(imagePath).toString("base64");
|
|
38912
39297
|
try {
|
|
38913
39298
|
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
38914
39299
|
method: "POST",
|
|
@@ -38938,7 +39323,7 @@ registry.register({
|
|
|
38938
39323
|
}
|
|
38939
39324
|
}
|
|
38940
39325
|
const mime = MIME_MAP[ext] || "jpeg";
|
|
38941
|
-
const imgB64 =
|
|
39326
|
+
const imgB64 = fs45.readFileSync(imagePath).toString("base64");
|
|
38942
39327
|
const userMsg = {
|
|
38943
39328
|
role: "user",
|
|
38944
39329
|
content: [
|
|
@@ -39016,7 +39401,7 @@ var TTS_PROVIDERS = [
|
|
|
39016
39401
|
init_registry();
|
|
39017
39402
|
|
|
39018
39403
|
// src/tools/opus-encoder.ts
|
|
39019
|
-
import * as
|
|
39404
|
+
import * as fs46 from "node:fs";
|
|
39020
39405
|
var FRAME_MS = 20;
|
|
39021
39406
|
var PRESKIP_48K = 312;
|
|
39022
39407
|
var OPUS_RATE = 48e3;
|
|
@@ -39133,7 +39518,7 @@ async function wavToOggOpusNative(wavPath) {
|
|
|
39133
39518
|
const mod = await import("@discordjs/opus");
|
|
39134
39519
|
const OpusEncoder = mod.OpusEncoder ?? mod.default?.OpusEncoder;
|
|
39135
39520
|
if (!OpusEncoder) throw new Error("@discordjs/opus export shape unexpected");
|
|
39136
|
-
const wav = parseWav(
|
|
39521
|
+
const wav = parseWav(fs46.readFileSync(wavPath));
|
|
39137
39522
|
if (wav.channels !== 1) throw new Error(`unsupported ${wav.channels}ch (need mono)`);
|
|
39138
39523
|
if (!NATIVE_RATES.includes(wav.sampleRate)) throw new Error(`unsupported rate ${wav.sampleRate}`);
|
|
39139
39524
|
if (wav.pcm.length === 0) throw new Error("empty pcm data");
|
|
@@ -39169,7 +39554,7 @@ async function wavToOggOpusNative(wavPath) {
|
|
|
39169
39554
|
}
|
|
39170
39555
|
ogg.page(cur, pageGranule, false, true);
|
|
39171
39556
|
const oggPath = wavPath.replace(/\.wav$/, ".ogg");
|
|
39172
|
-
|
|
39557
|
+
fs46.writeFileSync(oggPath, ogg.finish());
|
|
39173
39558
|
return oggPath;
|
|
39174
39559
|
}
|
|
39175
39560
|
|
|
@@ -39198,10 +39583,10 @@ function fallbackChainOf(raw) {
|
|
|
39198
39583
|
init_live();
|
|
39199
39584
|
|
|
39200
39585
|
// src/tools/tts/relay.ts
|
|
39201
|
-
import * as
|
|
39586
|
+
import * as fs47 from "node:fs";
|
|
39202
39587
|
import * as os6 from "node:os";
|
|
39203
|
-
import * as
|
|
39204
|
-
var VOICE_DIR2 =
|
|
39588
|
+
import * as path46 from "node:path";
|
|
39589
|
+
var VOICE_DIR2 = path46.join(os6.tmpdir(), "engine-voice");
|
|
39205
39590
|
var fetchFn = fetch;
|
|
39206
39591
|
async function synthRelay(text, cfg) {
|
|
39207
39592
|
const res = await fetchFn(`${cfg.baseUrl}/audio/speech`, {
|
|
@@ -39220,9 +39605,9 @@ async function synthRelay(text, cfg) {
|
|
|
39220
39605
|
}
|
|
39221
39606
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
39222
39607
|
if (buf.length < 20) throw new Error(`relay tts: \u54CD\u5E94\u4F53\u53EA\u6709 ${buf.length} \u5B57\u8282\uFF0C\u4E0D\u662F\u6709\u6548\u97F3\u9891`);
|
|
39223
|
-
|
|
39224
|
-
const out =
|
|
39225
|
-
|
|
39608
|
+
fs47.mkdirSync(VOICE_DIR2, { recursive: true });
|
|
39609
|
+
const out = path46.join(VOICE_DIR2, `tts_relay_${Date.now()}.mp3`);
|
|
39610
|
+
fs47.writeFileSync(out, buf);
|
|
39226
39611
|
return {
|
|
39227
39612
|
path: out,
|
|
39228
39613
|
format: "mp3",
|
|
@@ -39341,8 +39726,8 @@ function synthesize(id, text, sink) {
|
|
|
39341
39726
|
// src/tools/my-voice.ts
|
|
39342
39727
|
import { execFile as execFile3 } from "node:child_process";
|
|
39343
39728
|
import { promisify as promisify3 } from "node:util";
|
|
39344
|
-
import * as
|
|
39345
|
-
import * as
|
|
39729
|
+
import * as fs48 from "node:fs";
|
|
39730
|
+
import * as path47 from "node:path";
|
|
39346
39731
|
var execFileAsync3 = promisify3(execFile3);
|
|
39347
39732
|
async function toWav24kWithDuration(audioPath) {
|
|
39348
39733
|
let wavPath = audioPath;
|
|
@@ -39360,15 +39745,15 @@ async function toWav24kWithDuration(audioPath) {
|
|
|
39360
39745
|
"pcm_s16le",
|
|
39361
39746
|
wavPath
|
|
39362
39747
|
], { timeout: 3e4 });
|
|
39363
|
-
if (!
|
|
39748
|
+
if (!fs48.existsSync(wavPath) || fs48.statSync(wavPath).size < 44) {
|
|
39364
39749
|
throw new Error("toWav24k produced empty output");
|
|
39365
39750
|
}
|
|
39366
39751
|
try {
|
|
39367
|
-
|
|
39752
|
+
fs48.unlinkSync(audioPath);
|
|
39368
39753
|
} catch {
|
|
39369
39754
|
}
|
|
39370
39755
|
}
|
|
39371
|
-
const durationSec = wavDurationSec(
|
|
39756
|
+
const durationSec = wavDurationSec(fs48.readFileSync(wavPath));
|
|
39372
39757
|
return { wavPath, durationSec };
|
|
39373
39758
|
}
|
|
39374
39759
|
async function compressWav(wavPath) {
|
|
@@ -39386,7 +39771,7 @@ async function compressWav(wavPath) {
|
|
|
39386
39771
|
"+faststart",
|
|
39387
39772
|
m4aPath
|
|
39388
39773
|
], { timeout: 3e4 });
|
|
39389
|
-
|
|
39774
|
+
fs48.unlinkSync(wavPath);
|
|
39390
39775
|
return m4aPath;
|
|
39391
39776
|
} catch {
|
|
39392
39777
|
return wavPath;
|
|
@@ -39413,10 +39798,10 @@ async function wavToOggOpus(wavPath) {
|
|
|
39413
39798
|
oggPath
|
|
39414
39799
|
], { timeout: 3e4 });
|
|
39415
39800
|
}
|
|
39416
|
-
if (!
|
|
39801
|
+
if (!fs48.existsSync(oggPath) || fs48.statSync(oggPath).size < 20) {
|
|
39417
39802
|
throw new Error("ogg/opus encode produced empty output");
|
|
39418
39803
|
}
|
|
39419
|
-
|
|
39804
|
+
fs48.unlinkSync(wavPath);
|
|
39420
39805
|
return oggPath;
|
|
39421
39806
|
}
|
|
39422
39807
|
async function sendVoiceToMulticaIssue(text, issueId, opts) {
|
|
@@ -39431,7 +39816,7 @@ async function sendVoiceToMulticaIssue(text, issueId, opts) {
|
|
|
39431
39816
|
} catch (e) {
|
|
39432
39817
|
return { content: `\u8BED\u97F3\u751F\u6210\u5931\u8D25\uFF08headless\u2192multaca\uFF09\uFF1A${e.message}\u3002\u8BF7\u7528\u6587\u5B57\u56DE\u590D\u4EE3\u66FF\u3002`, isError: true };
|
|
39433
39818
|
}
|
|
39434
|
-
if (!audioPath || !
|
|
39819
|
+
if (!audioPath || !fs48.existsSync(audioPath)) {
|
|
39435
39820
|
return { content: "\u8BED\u97F3\u751F\u6210\u5931\u8D25\uFF08headless\u2192multaca\uFF09\uFF1ATTS \u672A\u4EA7\u51FA\u6587\u4EF6\u3002\u8BF7\u7528\u6587\u5B57\u56DE\u590D\u4EE3\u66FF\u3002", isError: true };
|
|
39436
39821
|
}
|
|
39437
39822
|
if (audioPath.endsWith(".wav")) {
|
|
@@ -39441,21 +39826,21 @@ async function sendVoiceToMulticaIssue(text, issueId, opts) {
|
|
|
39441
39826
|
console.warn(`[my-voice:multaca] opus encode failed (${e.message}), attaching wav`);
|
|
39442
39827
|
}
|
|
39443
39828
|
}
|
|
39444
|
-
const ext =
|
|
39445
|
-
const sizeKB =
|
|
39829
|
+
const ext = path47.extname(audioPath).toLowerCase();
|
|
39830
|
+
const sizeKB = fs48.statSync(audioPath).size / 1024;
|
|
39446
39831
|
const voiceFile = `voice_${Date.now()}${ext}`;
|
|
39447
|
-
const cwdCopy =
|
|
39832
|
+
const cwdCopy = path47.join(process.cwd(), voiceFile);
|
|
39448
39833
|
try {
|
|
39449
|
-
|
|
39834
|
+
fs48.copyFileSync(audioPath, cwdCopy);
|
|
39450
39835
|
} catch (e) {
|
|
39451
39836
|
try {
|
|
39452
|
-
|
|
39837
|
+
fs48.unlinkSync(audioPath);
|
|
39453
39838
|
} catch {
|
|
39454
39839
|
}
|
|
39455
39840
|
return { content: `\u8BED\u97F3\u6302\u8F7D\u5931\u8D25\uFF08\u62F7\u8D1D\u5230\u5DE5\u4F5C\u76EE\u5F55\uFF09\uFF1A${e.message}`, isError: true };
|
|
39456
39841
|
}
|
|
39457
39842
|
try {
|
|
39458
|
-
|
|
39843
|
+
fs48.unlinkSync(audioPath);
|
|
39459
39844
|
} catch {
|
|
39460
39845
|
}
|
|
39461
39846
|
const content = opts.caption || `\u{1F399}\uFE0F \u8BED\u97F3\u6D88\u606F\uFF08${text.length} \u5B57\uFF09`;
|
|
@@ -39484,13 +39869,13 @@ async function sendVoiceToMulticaIssue(text, issueId, opts) {
|
|
|
39484
39869
|
}
|
|
39485
39870
|
if (!ok) throw new Error(`unexpected CLI output: ${String(stdout).slice(0, 200)}`);
|
|
39486
39871
|
try {
|
|
39487
|
-
|
|
39872
|
+
fs48.unlinkSync(cwdCopy);
|
|
39488
39873
|
} catch {
|
|
39489
39874
|
}
|
|
39490
39875
|
return { content: `Voice sent to multaca issue ${issueId}! (${actualEngine}, ${sizeKB.toFixed(0)}KB, \u9644\u4EF6\u5DF2\u6302\u5230\u8BC4\u8BBA\u2014\u2014\u5728 multaca \u5BA2\u6237\u7AEF\u64AD\u653E)` };
|
|
39491
39876
|
} catch (e) {
|
|
39492
39877
|
try {
|
|
39493
|
-
|
|
39878
|
+
fs48.unlinkSync(cwdCopy);
|
|
39494
39879
|
} catch {
|
|
39495
39880
|
}
|
|
39496
39881
|
return { content: `\u8BED\u97F3\u6302\u5230 multaca issue \u5931\u8D25\uFF1A${e.message}\u3002\u8BF7\u7528\u6587\u5B57\u56DE\u590D\u4EE3\u66FF\u3002`, isError: true };
|
|
@@ -39607,10 +39992,10 @@ registry.register({
|
|
|
39607
39992
|
audioPath = await compressWav(audioPath);
|
|
39608
39993
|
}
|
|
39609
39994
|
}
|
|
39610
|
-
const ext =
|
|
39995
|
+
const ext = path47.extname(audioPath).toLowerCase();
|
|
39611
39996
|
const mimeMap = { ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".ogg": "audio/ogg", ".silk": "audio/silk" };
|
|
39612
39997
|
const mimeType = mimeMap[ext] || "audio/mpeg";
|
|
39613
|
-
const sizeKB =
|
|
39998
|
+
const sizeKB = fs48.statSync(audioPath).size / 1024;
|
|
39614
39999
|
try {
|
|
39615
40000
|
await mgr.sendFile(resolvedChannel, target, caption, {
|
|
39616
40001
|
path: audioPath,
|
|
@@ -39619,7 +40004,7 @@ registry.register({
|
|
|
39619
40004
|
...voiceDurationSec !== void 0 ? { voiceDurationSec } : {}
|
|
39620
40005
|
});
|
|
39621
40006
|
try {
|
|
39622
|
-
|
|
40007
|
+
fs48.unlinkSync(audioPath);
|
|
39623
40008
|
} catch {
|
|
39624
40009
|
}
|
|
39625
40010
|
return { content: `Voice sent! (${actualEngine}, ${sizeKB.toFixed(0)}KB, ${resolvedChannel})` };
|
|
@@ -39637,8 +40022,8 @@ registry.register({
|
|
|
39637
40022
|
init_live();
|
|
39638
40023
|
init_registry();
|
|
39639
40024
|
init_well_formed();
|
|
39640
|
-
import * as
|
|
39641
|
-
import * as
|
|
40025
|
+
import * as fs49 from "node:fs";
|
|
40026
|
+
import * as path48 from "node:path";
|
|
39642
40027
|
function getProxyDispatcher2() {
|
|
39643
40028
|
const cfg = liveConfig.all();
|
|
39644
40029
|
const proxy = cfg.providers?.xai?.proxy;
|
|
@@ -39652,7 +40037,7 @@ function getProxyDispatcher2() {
|
|
|
39652
40037
|
}
|
|
39653
40038
|
function dimsOf(p) {
|
|
39654
40039
|
try {
|
|
39655
|
-
const b =
|
|
40040
|
+
const b = fs49.readFileSync(p);
|
|
39656
40041
|
let w = 0, h = 0;
|
|
39657
40042
|
if (b.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
|
|
39658
40043
|
w = b.readUInt32BE(16);
|
|
@@ -39775,9 +40160,9 @@ function registerPortrait(relPath, name) {
|
|
|
39775
40160
|
const cur = cfg?.tools?.my_selfie?.references || cfg?.my_selfie?.references || [];
|
|
39776
40161
|
const next = Array.isArray(cur) ? cur.filter((r) => r?.name !== name) : [];
|
|
39777
40162
|
next.unshift({ name, path: relPath });
|
|
39778
|
-
const
|
|
39779
|
-
void liveConfig.set(
|
|
39780
|
-
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${
|
|
40163
|
+
const path61 = cfg?.tools?.my_selfie ? "tools.my_selfie.references" : "my_selfie.references";
|
|
40164
|
+
void liveConfig.set(path61, next);
|
|
40165
|
+
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${path61}[0] = ${relPath}`);
|
|
39781
40166
|
} catch (e) {
|
|
39782
40167
|
console.warn(`[my-selfie] \u5199\u5165 references \u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u672C\u6B21\u51FA\u56FE\uFF09: ${e.message}`);
|
|
39783
40168
|
}
|
|
@@ -40058,10 +40443,10 @@ registry.register({
|
|
|
40058
40443
|
const REFERENCES = getReferences(ctx);
|
|
40059
40444
|
const refName = args.reference || "default";
|
|
40060
40445
|
const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
|
|
40061
|
-
let refPath =
|
|
40446
|
+
let refPath = path48.join(ctx.workspace, refEntry.p);
|
|
40062
40447
|
const adoptImg = args.referenceImage?.trim();
|
|
40063
40448
|
if (adoptImg) {
|
|
40064
|
-
if (!
|
|
40449
|
+
if (!fs49.existsSync(adoptImg)) {
|
|
40065
40450
|
return { content: `\u627E\u4E0D\u5230\u90A3\u5F20\u56FE\uFF1A${adoptImg}`, isError: true };
|
|
40066
40451
|
}
|
|
40067
40452
|
const acheck = dimsOf(adoptImg);
|
|
@@ -40072,16 +40457,16 @@ registry.register({
|
|
|
40072
40457
|
isError: true
|
|
40073
40458
|
};
|
|
40074
40459
|
}
|
|
40075
|
-
const adir =
|
|
40076
|
-
if (!
|
|
40077
|
-
const aext = (
|
|
40460
|
+
const adir = path48.join(ctx.workspace, "images");
|
|
40461
|
+
if (!fs49.existsSync(adir)) fs49.mkdirSync(adir, { recursive: true });
|
|
40462
|
+
const aext = (path48.extname(adoptImg) || ".png").toLowerCase();
|
|
40078
40463
|
const afname = `portrait_${Date.now()}${aext}`;
|
|
40079
|
-
|
|
40464
|
+
fs49.copyFileSync(adoptImg, path48.join(adir, afname));
|
|
40080
40465
|
registerPortrait(`images/${afname}`, refName);
|
|
40081
|
-
refPath =
|
|
40466
|
+
refPath = path48.join(adir, afname);
|
|
40082
40467
|
console.log(`[my-selfie] \u91C7\u7EB3\u73B0\u6210\u56FE\u5F53\u5F62\u8C61\uFF1Aimages/${afname}${acheck ? ` (${acheck})` : ""}`);
|
|
40083
40468
|
}
|
|
40084
|
-
if (provider !== "autodl" && !
|
|
40469
|
+
if (provider !== "autodl" && !fs49.existsSync(refPath)) {
|
|
40085
40470
|
const cfgAll = liveConfig.all();
|
|
40086
40471
|
const portraitDesc = resolvePortraitDesc(
|
|
40087
40472
|
args.portrait,
|
|
@@ -40106,16 +40491,16 @@ registry.register({
|
|
|
40106
40491
|
} catch (e) {
|
|
40107
40492
|
return { content: `\u5851\u9020\u5F62\u8C61\u5931\u8D25\uFF1A${e.message}`, isError: true };
|
|
40108
40493
|
}
|
|
40109
|
-
const pdir =
|
|
40110
|
-
if (!
|
|
40494
|
+
const pdir = path48.join(ctx.workspace, "images");
|
|
40495
|
+
if (!fs49.existsSync(pdir)) fs49.mkdirSync(pdir, { recursive: true });
|
|
40111
40496
|
const pfname = `portrait_${Date.now()}.png`;
|
|
40112
|
-
|
|
40497
|
+
fs49.writeFileSync(path48.join(pdir, pfname), portraitBuf);
|
|
40113
40498
|
registerPortrait(`images/${pfname}`, refName);
|
|
40114
|
-
refPath =
|
|
40499
|
+
refPath = path48.join(pdir, pfname);
|
|
40115
40500
|
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u5B58 images/${pfname}\uFF0C\u7EE7\u7EED\u8FD9\u6B21\u81EA\u62CD`);
|
|
40116
40501
|
}
|
|
40117
40502
|
const resolution = args.resolution || DEFAULT_RESOLUTION;
|
|
40118
|
-
const refB64 =
|
|
40503
|
+
const refB64 = fs49.existsSync(refPath) ? fs49.readFileSync(refPath).toString("base64") : "";
|
|
40119
40504
|
let imageBuffer;
|
|
40120
40505
|
try {
|
|
40121
40506
|
if (provider === "autodl") {
|
|
@@ -40131,7 +40516,7 @@ registry.register({
|
|
|
40131
40516
|
return { content: `Error: relay provider \u672A\u914D\u7F6E\u3002config \u91CC\u8981\u6709 models.providers.${cfgAll?.my_selfie?.relayProviderId || "7yi"} \u7684 baseUrl \u548C apiKey\u3002`, isError: true };
|
|
40132
40517
|
}
|
|
40133
40518
|
const model = cfgAll?.my_selfie?.relayModel || cfgAll?.tools?.my_selfie?.relayModel || "engine7-img-free";
|
|
40134
|
-
const ext =
|
|
40519
|
+
const ext = path48.extname(refPath).toLowerCase();
|
|
40135
40520
|
const mime = ext === ".png" ? "image/png" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
40136
40521
|
imageBuffer = await generateWithRelay(refB64, fullPrompt, mime, relay.baseUrl, relay.apiKey, model);
|
|
40137
40522
|
} else {
|
|
@@ -40140,11 +40525,11 @@ registry.register({
|
|
|
40140
40525
|
} catch (err) {
|
|
40141
40526
|
return { content: `Selfie generation failed: ${err.message}`, isError: true };
|
|
40142
40527
|
}
|
|
40143
|
-
const imagesDir =
|
|
40144
|
-
if (!
|
|
40528
|
+
const imagesDir = path48.join(ctx.workspace, "images");
|
|
40529
|
+
if (!fs49.existsSync(imagesDir)) fs49.mkdirSync(imagesDir, { recursive: true });
|
|
40145
40530
|
const filename = `selfie_${Date.now()}.jpg`;
|
|
40146
|
-
const outputPath =
|
|
40147
|
-
|
|
40531
|
+
const outputPath = path48.join(imagesDir, filename);
|
|
40532
|
+
fs49.writeFileSync(outputPath, imageBuffer);
|
|
40148
40533
|
const mgr = ctx.channelManager;
|
|
40149
40534
|
if (mgr) {
|
|
40150
40535
|
const resolvedChannel = ctx.channel || "feishu";
|
|
@@ -40155,11 +40540,11 @@ registry.register({
|
|
|
40155
40540
|
mimeType: "image/jpeg"
|
|
40156
40541
|
});
|
|
40157
40542
|
} catch (err) {
|
|
40158
|
-
return { content: `Selfie generated but send failed: ${err.message}. Image: ${
|
|
40543
|
+
return { content: `Selfie generated but send failed: ${err.message}. Image: ${path48.resolve(outputPath)}`, isError: false };
|
|
40159
40544
|
}
|
|
40160
40545
|
return { content: `Selfie sent! Mode: ${mode}, Provider: ${provider}, Ref: ${refEntry.name}` };
|
|
40161
40546
|
}
|
|
40162
|
-
return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${
|
|
40547
|
+
return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path48.resolve(outputPath)}` };
|
|
40163
40548
|
},
|
|
40164
40549
|
isConcurrencySafe: () => false,
|
|
40165
40550
|
interruptBehavior: () => "block",
|
|
@@ -40711,8 +41096,8 @@ ${lines.join("\n")}` };
|
|
|
40711
41096
|
init_registry();
|
|
40712
41097
|
init_live();
|
|
40713
41098
|
init_clock();
|
|
40714
|
-
import * as
|
|
40715
|
-
import * as
|
|
41099
|
+
import * as fs50 from "node:fs";
|
|
41100
|
+
import * as path49 from "node:path";
|
|
40716
41101
|
import { exec as exec2, spawn as spawn6 } from "node:child_process";
|
|
40717
41102
|
import { promisify as promisify4 } from "node:util";
|
|
40718
41103
|
var execAsync = promisify4(exec2);
|
|
@@ -40721,9 +41106,9 @@ function getServices() {
|
|
|
40721
41106
|
}
|
|
40722
41107
|
function serviceLogPath(fileName) {
|
|
40723
41108
|
const stateDir = String(liveConfig.get("stateDir") ?? "");
|
|
40724
|
-
const logDir =
|
|
40725
|
-
if (!
|
|
40726
|
-
return
|
|
41109
|
+
const logDir = path49.join(stateDir, "logs");
|
|
41110
|
+
if (!fs50.existsSync(logDir)) fs50.mkdirSync(logDir, { recursive: true });
|
|
41111
|
+
return path49.join(logDir, fileName);
|
|
40727
41112
|
}
|
|
40728
41113
|
async function runCmd(cmd, timeoutMs = 15e3) {
|
|
40729
41114
|
try {
|
|
@@ -41042,16 +41427,16 @@ var EXIT_PLAN_MODE_TOOL_NAME = "ExitPlanMode";
|
|
|
41042
41427
|
init_planModeState();
|
|
41043
41428
|
|
|
41044
41429
|
// src/utils/plans.ts
|
|
41045
|
-
import * as
|
|
41046
|
-
import * as
|
|
41430
|
+
import * as fs51 from "node:fs";
|
|
41431
|
+
import * as path50 from "node:path";
|
|
41047
41432
|
import * as crypto7 from "node:crypto";
|
|
41048
41433
|
var MAX_SLUG_RETRIES = 10;
|
|
41049
41434
|
function generateSlug() {
|
|
41050
41435
|
return crypto7.randomBytes(4).toString("hex");
|
|
41051
41436
|
}
|
|
41052
41437
|
function getPlansDirectory(stateDir) {
|
|
41053
|
-
const plansDir =
|
|
41054
|
-
|
|
41438
|
+
const plansDir = path50.join(stateDir, "plans");
|
|
41439
|
+
fs51.mkdirSync(plansDir, { recursive: true });
|
|
41055
41440
|
return plansDir;
|
|
41056
41441
|
}
|
|
41057
41442
|
var planSlugCache = /* @__PURE__ */ new Map();
|
|
@@ -41061,8 +41446,8 @@ function getPlanSlug(sessionId, stateDir) {
|
|
|
41061
41446
|
const plansDir = getPlansDirectory(stateDir);
|
|
41062
41447
|
for (let i = 0; i < MAX_SLUG_RETRIES; i++) {
|
|
41063
41448
|
slug = generateSlug();
|
|
41064
|
-
const filePath =
|
|
41065
|
-
if (!
|
|
41449
|
+
const filePath = path50.join(plansDir, `${slug}.md`);
|
|
41450
|
+
if (!fs51.existsSync(filePath)) {
|
|
41066
41451
|
break;
|
|
41067
41452
|
}
|
|
41068
41453
|
}
|
|
@@ -41073,21 +41458,21 @@ function getPlanSlug(sessionId, stateDir) {
|
|
|
41073
41458
|
function getPlanFilePath(sessionId, stateDir, agentId) {
|
|
41074
41459
|
const slug = getPlanSlug(sessionId, stateDir);
|
|
41075
41460
|
if (!agentId) {
|
|
41076
|
-
return
|
|
41461
|
+
return path50.join(getPlansDirectory(stateDir), `${slug}.md`);
|
|
41077
41462
|
}
|
|
41078
|
-
return
|
|
41463
|
+
return path50.join(getPlansDirectory(stateDir), `${slug}-agent-${agentId}.md`);
|
|
41079
41464
|
}
|
|
41080
41465
|
function getPlan(sessionId, stateDir, agentId) {
|
|
41081
41466
|
const filePath = getPlanFilePath(sessionId, stateDir, agentId);
|
|
41082
41467
|
try {
|
|
41083
|
-
return
|
|
41468
|
+
return fs51.readFileSync(filePath, "utf-8");
|
|
41084
41469
|
} catch {
|
|
41085
41470
|
return null;
|
|
41086
41471
|
}
|
|
41087
41472
|
}
|
|
41088
41473
|
function writePlan(sessionId, stateDir, content, agentId) {
|
|
41089
41474
|
const filePath = getPlanFilePath(sessionId, stateDir, agentId);
|
|
41090
|
-
|
|
41475
|
+
fs51.writeFileSync(filePath, content, "utf-8");
|
|
41091
41476
|
return filePath;
|
|
41092
41477
|
}
|
|
41093
41478
|
|
|
@@ -41905,8 +42290,8 @@ async function setupFeatures(features, licensedFeatures) {
|
|
|
41905
42290
|
init_agent_registry();
|
|
41906
42291
|
|
|
41907
42292
|
// src/commands/work.ts
|
|
41908
|
-
import * as
|
|
41909
|
-
import * as
|
|
42293
|
+
import * as fs56 from "node:fs";
|
|
42294
|
+
import * as path56 from "node:path";
|
|
41910
42295
|
registerCommand({
|
|
41911
42296
|
name: "work",
|
|
41912
42297
|
description: "\u5DE5\u4F5C\u6A21\u5F0F\uFF1A\u65E0\u53C2\u770B\u72B6\u6001\uFF0Con=\u5F3A\u5236work\uFF0Coff=\u5F3A\u5236emotion\uFF0Cauto=\u81EA\u52A8\u5224\u5B9A",
|
|
@@ -41919,20 +42304,20 @@ registerCommand({
|
|
|
41919
42304
|
],
|
|
41920
42305
|
handler: async (ctx, deps) => {
|
|
41921
42306
|
const mode = (ctx.args.mode || "").trim().toLowerCase();
|
|
41922
|
-
const modeFile =
|
|
42307
|
+
const modeFile = path56.join(deps.config.workspace, ".work-mode");
|
|
41923
42308
|
if (mode === "on" || mode === "off") {
|
|
41924
|
-
|
|
42309
|
+
fs56.writeFileSync(modeFile, mode, "utf8");
|
|
41925
42310
|
await ctx.reply(mode === "on" ? "\u5DE5\u4F5C\u6A21\u5F0F\u5DF2\u5F3A\u5236 **work**\uFF08\u6240\u6709\u6D88\u606F\u5168\u91CF\u4E0A\u4E0B\u6587\uFF0C\u4E0B\u4E00\u6761\u751F\u6548\uFF09" : "\u5DE5\u4F5C\u6A21\u5F0F\u5DF2\u5F3A\u5236 **emotion**\uFF08\u6240\u6709\u6D88\u606F\u8F7B\u91CF\u95F2\u804A\uFF0C\u4E0B\u4E00\u6761\u751F\u6548\uFF09");
|
|
41926
42311
|
} else if (mode === "auto") {
|
|
41927
42312
|
try {
|
|
41928
|
-
|
|
42313
|
+
fs56.unlinkSync(modeFile);
|
|
41929
42314
|
} catch {
|
|
41930
42315
|
}
|
|
41931
42316
|
await ctx.reply("\u5DE5\u4F5C\u6A21\u5F0F\u5DF2\u6062\u590D **auto**\uFF08emotion_state_machine \u9010\u6761\u81EA\u52A8\u5224\u5B9A\uFF09");
|
|
41932
42317
|
} else {
|
|
41933
42318
|
let cur = "auto";
|
|
41934
42319
|
try {
|
|
41935
|
-
cur =
|
|
42320
|
+
cur = fs56.readFileSync(modeFile, "utf8").trim() || "auto";
|
|
41936
42321
|
} catch {
|
|
41937
42322
|
}
|
|
41938
42323
|
const hint = cur === "on" ? "\uFF08\u5F3A\u5236 work\uFF1A\u5168\u91CF\u4E0A\u4E0B\u6587\uFF09" : cur === "off" ? "\uFF08\u5F3A\u5236 emotion\uFF1A\u8F7B\u91CF\u4E0A\u4E0B\u6587\uFF09" : "\uFF08\u6BCF\u6761\u6D88\u606F\u7531 emotion_state_machine \u81EA\u52A8\u5224\u5B9A\uFF09";
|
|
@@ -42402,8 +42787,8 @@ ${err.stack}`);
|
|
|
42402
42787
|
});
|
|
42403
42788
|
|
|
42404
42789
|
// src/commands/model.ts
|
|
42405
|
-
import * as
|
|
42406
|
-
import * as
|
|
42790
|
+
import * as fs57 from "node:fs";
|
|
42791
|
+
import * as path57 from "node:path";
|
|
42407
42792
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
42408
42793
|
function collectModels(config) {
|
|
42409
42794
|
const aliases = config.modelAliases || {};
|
|
@@ -42435,11 +42820,11 @@ function resolveConfigWritePath(config, label) {
|
|
|
42435
42820
|
const configPath = config._configFilePath;
|
|
42436
42821
|
if (!configPath) return void 0;
|
|
42437
42822
|
let writePath = configPath;
|
|
42438
|
-
if (configPath && !
|
|
42823
|
+
if (configPath && !fs57.existsSync(configPath)) {
|
|
42439
42824
|
const __pFile = fileURLToPath8(import.meta.url);
|
|
42440
|
-
const __pDir =
|
|
42441
|
-
const altPath =
|
|
42442
|
-
if (
|
|
42825
|
+
const __pDir = path57.dirname(__pFile);
|
|
42826
|
+
const altPath = path57.join(path57.resolve(__pDir, "../configs"), path57.basename(configPath));
|
|
42827
|
+
if (fs57.existsSync(altPath)) {
|
|
42443
42828
|
console.warn(`[${label}] Config not found at ${configPath}, falling back to ${altPath}`);
|
|
42444
42829
|
writePath = altPath;
|
|
42445
42830
|
}
|
|
@@ -42594,14 +42979,14 @@ Use full ref like \`/primary ${candidates[0].ref}\``);
|
|
|
42594
42979
|
return;
|
|
42595
42980
|
}
|
|
42596
42981
|
try {
|
|
42597
|
-
const raw = await
|
|
42982
|
+
const raw = await fs57.promises.readFile(writePath, "utf-8");
|
|
42598
42983
|
const cfg = JSON.parse(raw);
|
|
42599
42984
|
if (!cfg.agents?.defaults?.model) {
|
|
42600
42985
|
await ctx.reply(`\u26A0\uFE0F Config structure mismatch: agents.defaults.model not found`);
|
|
42601
42986
|
return;
|
|
42602
42987
|
}
|
|
42603
42988
|
cfg.agents.defaults.model.primary = target;
|
|
42604
|
-
await
|
|
42989
|
+
await fs57.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
42605
42990
|
console.log(`[primary] Persisted primary=${target} to ${writePath}`);
|
|
42606
42991
|
await ctx.reply(`\u2705 Primary model set to **${target}** (${candidates[0].name})
|
|
42607
42992
|
Written to config, hot-reloading provider chain now.`);
|
|
@@ -42648,7 +43033,7 @@ Use full ref like \`/vision-primary ${candidates[0].ref}\``);
|
|
|
42648
43033
|
return;
|
|
42649
43034
|
}
|
|
42650
43035
|
try {
|
|
42651
|
-
const raw = await
|
|
43036
|
+
const raw = await fs57.promises.readFile(writePath, "utf-8");
|
|
42652
43037
|
const cfg = JSON.parse(raw);
|
|
42653
43038
|
if (!cfg.agents?.defaults?.model) {
|
|
42654
43039
|
await ctx.reply(`\u26A0\uFE0F Config structure mismatch: agents.defaults.model not found`);
|
|
@@ -42656,12 +43041,12 @@ Use full ref like \`/vision-primary ${candidates[0].ref}\``);
|
|
|
42656
43041
|
}
|
|
42657
43042
|
if (input === "auto" || input === "reset") {
|
|
42658
43043
|
delete cfg.agents.defaults.model.vision;
|
|
42659
|
-
await
|
|
43044
|
+
await fs57.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
42660
43045
|
console.log(`[vision-primary] Cleared vision primary in ${writePath}`);
|
|
42661
43046
|
await ctx.reply(`\u2705 Vision primary cleared (auto). Written to config. Hot-reload will apply.`);
|
|
42662
43047
|
} else {
|
|
42663
43048
|
cfg.agents.defaults.model.vision = target;
|
|
42664
|
-
await
|
|
43049
|
+
await fs57.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
42665
43050
|
console.log(`[vision-primary] Persisted vision=${target} to ${writePath}`);
|
|
42666
43051
|
await ctx.reply(`\u2705 Vision primary set to **${target}** (${candidates[0].name})
|
|
42667
43052
|
Written to config. Hot-reload will apply.`);
|
|
@@ -43069,7 +43454,7 @@ async function startEngine(config, opts) {
|
|
|
43069
43454
|
const { initSessionMemory: initSessionMemory2 } = await Promise.resolve().then(() => (init_sessionMemory(), sessionMemory_exports));
|
|
43070
43455
|
initSessionMemory2({
|
|
43071
43456
|
workspace: config.workspace,
|
|
43072
|
-
stateDir:
|
|
43457
|
+
stateDir: path60.join(config.stateDir, "session-memory"),
|
|
43073
43458
|
provider,
|
|
43074
43459
|
model: config.provider.modelId || config.model || "deepseek-v4-flash",
|
|
43075
43460
|
features: config.profile.features
|