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/main.mjs
CHANGED
|
@@ -141,14 +141,14 @@ var init_live = __esm({
|
|
|
141
141
|
}
|
|
142
142
|
/** 把改动写回 config 文件对应段(找不到文件路径则只内存生效) */
|
|
143
143
|
async persistToFile(dotPath, val) {
|
|
144
|
-
const
|
|
144
|
+
const fs59 = await import("node:fs");
|
|
145
145
|
const cfgPath = this.current?._configFilePath;
|
|
146
|
-
if (!cfgPath || !
|
|
146
|
+
if (!cfgPath || !fs59.existsSync(cfgPath)) {
|
|
147
147
|
console.warn(`[liveConfig] set: no config file path, in-memory only (${dotPath})`);
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
150
|
try {
|
|
151
|
-
const raw = JSON.parse(
|
|
151
|
+
const raw = JSON.parse(fs59.readFileSync(cfgPath, "utf-8"));
|
|
152
152
|
const keys = dotPath.split(".");
|
|
153
153
|
let o = raw;
|
|
154
154
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -156,7 +156,7 @@ var init_live = __esm({
|
|
|
156
156
|
o = o[keys[i]];
|
|
157
157
|
}
|
|
158
158
|
o[keys[keys.length - 1]] = val;
|
|
159
|
-
|
|
159
|
+
fs59.writeFileSync(cfgPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
|
|
160
160
|
console.log(`[liveConfig] persisted ${dotPath} = ${JSON.stringify(val)} to ${cfgPath}`);
|
|
161
161
|
markSelfWrite();
|
|
162
162
|
} catch (e) {
|
|
@@ -575,7 +575,10 @@ function loadConfig(configPath2) {
|
|
|
575
575
|
channels: agentDefaults.channels || [],
|
|
576
576
|
extensions: agentDefaults.extensions,
|
|
577
577
|
maxTurns: agentDefaults.maxTurns,
|
|
578
|
-
|
|
578
|
+
// ⚠️ **默认 true**(0928 翀哥拍板):开源项目,功能不按 license 分档。
|
|
579
|
+
// 要收起来才显式配 `agents.defaults.devMode: false`。
|
|
580
|
+
// 反过来写(=== true)时,任何没手配这一格的配置都会静默落进"基础引擎模式"。
|
|
581
|
+
devMode: agentDefaults.devMode !== false
|
|
579
582
|
};
|
|
580
583
|
const rawSession = raw.session || {};
|
|
581
584
|
const sessionScope = Object.keys(rawSession).length > 0 ? { ...rawSession } : void 0;
|
|
@@ -4565,7 +4568,7 @@ var init_skill_gate = __esm({
|
|
|
4565
4568
|
DEVELOPMENT_HINTS = /代码|源码|编译|构建|测试|报错|错误|bug|修复|排查|调试|实现|开发|接口|脚本|函数|类型|依赖|部署|代码 review|code|build|test|debug|implement|fix|compile|typescript|javascript|python|git/i;
|
|
4566
4569
|
TECHNICAL_OBJECTS = /代码|源码|仓库|项目|模块|插件|接口|服务|引擎|engine|voice[- ]?chat|docker|容器|llm|模型|延迟|工具|脚本|函数|依赖|部署|分支|commit|api/i;
|
|
4567
4570
|
TECHNICAL_ACTIONS = /优化|瘦身|提速|迁移|重构|接线|收尾|改动|加上|删掉|换成|裁剪|升级|降级|修|接入|拆分|合并|调试|排查/i;
|
|
4568
|
-
USER_CHANNELS = /* @__PURE__ */ new Set(["discord", "feishu", "wechat", "telegram", "cli", "console", "voice-chat", "deskBuddy"]);
|
|
4571
|
+
USER_CHANNELS = /* @__PURE__ */ new Set(["discord", "feishu", "wechat", "telegram", "cli", "console", "voice-chat", "deskBuddy", "ccBridge"]);
|
|
4569
4572
|
loadedBySession = /* @__PURE__ */ new Map();
|
|
4570
4573
|
MAX_TRACKED_SESSIONS = 100;
|
|
4571
4574
|
}
|
|
@@ -4582,26 +4585,26 @@ __export(paths_exports, {
|
|
|
4582
4585
|
isAutoMemPath: () => isAutoMemPath,
|
|
4583
4586
|
setMemoryDirOverride: () => setMemoryDirOverride
|
|
4584
4587
|
});
|
|
4585
|
-
import { join as join7, normalize as
|
|
4588
|
+
import { join as join7, normalize as normalize3, sep as sep2, isAbsolute as isAbsolute2, resolve as resolve3 } from "node:path";
|
|
4586
4589
|
function setMemoryDirOverride(dir) {
|
|
4587
4590
|
_overrideMemoryDir = dir;
|
|
4588
4591
|
}
|
|
4589
4592
|
function getAutoMemPath(workspace) {
|
|
4590
4593
|
const dir = _overrideMemoryDir ? isAbsolute2(_overrideMemoryDir) ? _overrideMemoryDir : resolve3(workspace, _overrideMemoryDir) : join7(workspace, AUTO_MEM_DIRNAME);
|
|
4591
|
-
const normalized =
|
|
4594
|
+
const normalized = normalize3(dir);
|
|
4592
4595
|
return (normalized + sep2).normalize("NFC");
|
|
4593
4596
|
}
|
|
4594
4597
|
function getAutoMemEntrypoint(workspace) {
|
|
4595
4598
|
return join7(getAutoMemPath(workspace), ENTRYPOINT_NAME);
|
|
4596
4599
|
}
|
|
4597
4600
|
function isAutoMemPath(absolutePath, workspace) {
|
|
4598
|
-
const normalizedPath =
|
|
4601
|
+
const normalizedPath = normalize3(absolutePath);
|
|
4599
4602
|
return normalizedPath.startsWith(getAutoMemPath(workspace));
|
|
4600
4603
|
}
|
|
4601
4604
|
async function ensureMemoryDirExists(memoryDir) {
|
|
4602
|
-
const
|
|
4605
|
+
const fs59 = await import("node:fs");
|
|
4603
4606
|
try {
|
|
4604
|
-
await
|
|
4607
|
+
await fs59.promises.mkdir(memoryDir, { recursive: true });
|
|
4605
4608
|
} catch (e) {
|
|
4606
4609
|
const code = e?.code;
|
|
4607
4610
|
if (code !== "EEXIST") {
|
|
@@ -7278,7 +7281,13 @@ var init_anthropic_provider = __esm({
|
|
|
7278
7281
|
thinking: { type: "enabled", budget_tokens: thinkingBudget }
|
|
7279
7282
|
} : params.disableThinking ? {
|
|
7280
7283
|
// 显式关闭 thinking(智谱 GLM-5.1 需要传 disabled 才真正关闭)
|
|
7281
|
-
|
|
7284
|
+
// 0928 修:MiniMax M3.1-Flash 系强制 adaptive,disabled 直接 400(码 2013,
|
|
7285
|
+
// 实证:proactive 后台调用换 M3.1 后 LLM UNREACHABLE)——该系降级发 adaptive
|
|
7286
|
+
...params.model?.includes("M3.1-Flash") ? {
|
|
7287
|
+
thinking: { type: "adaptive" }
|
|
7288
|
+
} : {
|
|
7289
|
+
thinking: { type: "disabled" }
|
|
7290
|
+
},
|
|
7282
7291
|
...params.temperature !== void 0 ? { temperature: params.temperature } : {}
|
|
7283
7292
|
} : {
|
|
7284
7293
|
...params.temperature !== void 0 ? { temperature: params.temperature } : {}
|
|
@@ -8108,11 +8117,11 @@ __export(license_exports, {
|
|
|
8108
8117
|
import * as crypto2 from "node:crypto";
|
|
8109
8118
|
import * as fs16 from "node:fs";
|
|
8110
8119
|
import * as path15 from "node:path";
|
|
8111
|
-
function loadLicense(stateDir, devMode) {
|
|
8120
|
+
function loadLicense(stateDir, devMode = true) {
|
|
8112
8121
|
if (_licenseChecked) return _cachedLicense;
|
|
8113
8122
|
_licenseChecked = true;
|
|
8114
8123
|
if (devMode) {
|
|
8115
|
-
console.log("[license]
|
|
8124
|
+
console.log("[license] \u5F00\u653E\u6A21\u5F0F \u2014 \u5168\u90E8 feature \u6FC0\u6D3B\uFF08\u8981\u5206\u6863\uFF1Aagents.defaults.devMode = false\uFF09");
|
|
8116
8125
|
const allActive = {};
|
|
8117
8126
|
for (const id of ALL_PAID_FEATURES) {
|
|
8118
8127
|
allActive[id] = { active: true };
|
|
@@ -8122,7 +8131,7 @@ function loadLicense(stateDir, devMode) {
|
|
|
8122
8131
|
}
|
|
8123
8132
|
const licensePath = path15.join(stateDir, "license.json");
|
|
8124
8133
|
if (!fs16.existsSync(licensePath)) {
|
|
8125
|
-
console.log("[license]
|
|
8134
|
+
console.log("[license] devMode=false \u4E14\u65E0 license.json \u2014\u2014 \u57FA\u7840\u5F15\u64CE\u6A21\u5F0F");
|
|
8126
8135
|
return null;
|
|
8127
8136
|
}
|
|
8128
8137
|
try {
|
|
@@ -8175,25 +8184,21 @@ function isFeatureLicensed(featureId) {
|
|
|
8175
8184
|
const f = _cachedLicense[featureId];
|
|
8176
8185
|
return f?.active === true;
|
|
8177
8186
|
}
|
|
8178
|
-
function getLicenseStatus(stateDir) {
|
|
8179
|
-
const
|
|
8180
|
-
|
|
8181
|
-
return { licensed: false, features: {} };
|
|
8182
|
-
}
|
|
8187
|
+
function getLicenseStatus(stateDir, devMode = true) {
|
|
8188
|
+
const active = loadLicense(stateDir, devMode);
|
|
8189
|
+
let payload = {};
|
|
8183
8190
|
try {
|
|
8184
|
-
|
|
8185
|
-
const license = JSON.parse(raw);
|
|
8186
|
-
const active = loadLicense(stateDir);
|
|
8187
|
-
return {
|
|
8188
|
-
licensed: active !== null,
|
|
8189
|
-
customer: license.customer,
|
|
8190
|
-
licenseId: license.licenseId,
|
|
8191
|
-
expiresAt: license.expiresAt,
|
|
8192
|
-
features: active || {}
|
|
8193
|
-
};
|
|
8191
|
+
payload = JSON.parse(fs16.readFileSync(path15.join(stateDir, "license.json"), "utf-8"));
|
|
8194
8192
|
} catch {
|
|
8195
|
-
return { licensed: false, features: {} };
|
|
8196
8193
|
}
|
|
8194
|
+
return {
|
|
8195
|
+
licensed: active !== null,
|
|
8196
|
+
customer: payload.customer,
|
|
8197
|
+
licenseId: payload.licenseId,
|
|
8198
|
+
expiresAt: payload.expiresAt,
|
|
8199
|
+
openMode: devMode,
|
|
8200
|
+
features: active || {}
|
|
8201
|
+
};
|
|
8197
8202
|
}
|
|
8198
8203
|
function resetLicenseCache() {
|
|
8199
8204
|
_cachedLicense = null;
|
|
@@ -8491,7 +8496,7 @@ async function acquireLock(inboxPath) {
|
|
|
8491
8496
|
for (let i = 0; i < LOCK_RETRIES; i++) {
|
|
8492
8497
|
if (await isLockStale(lockPath2)) {
|
|
8493
8498
|
try {
|
|
8494
|
-
await import("node:fs/promises").then((
|
|
8499
|
+
await import("node:fs/promises").then((fs59) => fs59.rm(lockPath2, { force: true }));
|
|
8495
8500
|
} catch {
|
|
8496
8501
|
}
|
|
8497
8502
|
}
|
|
@@ -8499,7 +8504,7 @@ async function acquireLock(inboxPath) {
|
|
|
8499
8504
|
await writeFile2(lockPath2, `${process.pid}-${Date.now()}`, { encoding: "utf-8", flag: "wx" });
|
|
8500
8505
|
return async () => {
|
|
8501
8506
|
try {
|
|
8502
|
-
await import("node:fs/promises").then((
|
|
8507
|
+
await import("node:fs/promises").then((fs59) => fs59.rm(lockPath2, { force: true }));
|
|
8503
8508
|
} catch {
|
|
8504
8509
|
}
|
|
8505
8510
|
};
|
|
@@ -10139,9 +10144,9 @@ Follow these principles when generating episodic memories:
|
|
|
10139
10144
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
10140
10145
|
import { mkdirSync as mkdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync7, existsSync as existsSync15, readdirSync as readdirSync2 } from "node:fs";
|
|
10141
10146
|
import { join as join24 } from "node:path";
|
|
10142
|
-
import { createHash } from "node:crypto";
|
|
10147
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
10143
10148
|
function contentHash(text) {
|
|
10144
|
-
return
|
|
10149
|
+
return createHash2("sha256").update(text.replace(/\s+/g, "")).digest("hex").slice(0, 40);
|
|
10145
10150
|
}
|
|
10146
10151
|
function mem7Root(stateDir) {
|
|
10147
10152
|
return join24(stateDir, "mem7");
|
|
@@ -16857,11 +16862,11 @@ async function readLastConsolidatedAt(memoryDir) {
|
|
|
16857
16862
|
}
|
|
16858
16863
|
}
|
|
16859
16864
|
async function tryAcquireConsolidationLock(memoryDir) {
|
|
16860
|
-
const
|
|
16865
|
+
const path61 = lockPath(memoryDir);
|
|
16861
16866
|
let mtimeMs;
|
|
16862
16867
|
let holderPid;
|
|
16863
16868
|
try {
|
|
16864
|
-
const [s, raw] = await Promise.all([stat3(
|
|
16869
|
+
const [s, raw] = await Promise.all([stat3(path61), readFile5(path61, "utf8")]);
|
|
16865
16870
|
mtimeMs = s.mtimeMs;
|
|
16866
16871
|
const parsed = parseInt(raw.trim(), 10);
|
|
16867
16872
|
holderPid = Number.isFinite(parsed) ? parsed : void 0;
|
|
@@ -16874,10 +16879,10 @@ async function tryAcquireConsolidationLock(memoryDir) {
|
|
|
16874
16879
|
}
|
|
16875
16880
|
}
|
|
16876
16881
|
await mkdir3(memoryDir, { recursive: true });
|
|
16877
|
-
await writeFile4(
|
|
16882
|
+
await writeFile4(path61, String(process.pid));
|
|
16878
16883
|
let verify2;
|
|
16879
16884
|
try {
|
|
16880
|
-
verify2 = await readFile5(
|
|
16885
|
+
verify2 = await readFile5(path61, "utf8");
|
|
16881
16886
|
} catch {
|
|
16882
16887
|
return null;
|
|
16883
16888
|
}
|
|
@@ -16885,15 +16890,15 @@ async function tryAcquireConsolidationLock(memoryDir) {
|
|
|
16885
16890
|
return mtimeMs ?? 0;
|
|
16886
16891
|
}
|
|
16887
16892
|
async function rollbackConsolidationLock(memoryDir, priorMtime) {
|
|
16888
|
-
const
|
|
16893
|
+
const path61 = lockPath(memoryDir);
|
|
16889
16894
|
try {
|
|
16890
16895
|
if (priorMtime === 0) {
|
|
16891
|
-
await unlink(
|
|
16896
|
+
await unlink(path61);
|
|
16892
16897
|
return;
|
|
16893
16898
|
}
|
|
16894
|
-
await writeFile4(
|
|
16899
|
+
await writeFile4(path61, "");
|
|
16895
16900
|
const t = priorMtime / 1e3;
|
|
16896
|
-
await utimes(
|
|
16901
|
+
await utimes(path61, t, t);
|
|
16897
16902
|
} catch (e) {
|
|
16898
16903
|
console.log(`[autoDream] rollback failed: ${e.message} \u2014 next trigger delayed to minHours`);
|
|
16899
16904
|
}
|
|
@@ -17495,12 +17500,12 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
17495
17500
|
if (promptText.startsWith("@")) {
|
|
17496
17501
|
let filePath = promptText.slice(1).trim();
|
|
17497
17502
|
try {
|
|
17498
|
-
const
|
|
17499
|
-
const
|
|
17500
|
-
if (!
|
|
17501
|
-
filePath =
|
|
17503
|
+
const fs59 = await import("fs");
|
|
17504
|
+
const path61 = await import("path");
|
|
17505
|
+
if (!path61.isAbsolute(filePath)) {
|
|
17506
|
+
filePath = path61.join(deps.sessions["config"].stateDir, filePath);
|
|
17502
17507
|
}
|
|
17503
|
-
promptText =
|
|
17508
|
+
promptText = fs59.readFileSync(filePath, "utf-8");
|
|
17504
17509
|
console.log(`[cron] Loaded prompt from ${filePath} (${promptText.length} chars)`);
|
|
17505
17510
|
} catch (err) {
|
|
17506
17511
|
throw new Error(`Prompt file not found: ${filePath}: ${err.message}`);
|
|
@@ -17538,16 +17543,16 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
17538
17543
|
let finalResult = result;
|
|
17539
17544
|
if (task.postProcess) {
|
|
17540
17545
|
try {
|
|
17541
|
-
const
|
|
17542
|
-
const
|
|
17546
|
+
const path61 = await import("path");
|
|
17547
|
+
const fs59 = await import("fs");
|
|
17543
17548
|
let scriptPath = task.postProcess;
|
|
17544
|
-
if (!
|
|
17545
|
-
scriptPath =
|
|
17549
|
+
if (!path61.isAbsolute(scriptPath)) {
|
|
17550
|
+
scriptPath = path61.join(deps.sessions["config"].stateDir, scriptPath);
|
|
17546
17551
|
}
|
|
17547
|
-
const resultsDirTmp =
|
|
17548
|
-
|
|
17549
|
-
const inputFile =
|
|
17550
|
-
|
|
17552
|
+
const resultsDirTmp = path61.join(getStorageDir(), "results");
|
|
17553
|
+
fs59.mkdirSync(resultsDirTmp, { recursive: true });
|
|
17554
|
+
const inputFile = path61.join(resultsDirTmp, `${task.id}.input.txt`);
|
|
17555
|
+
fs59.writeFileSync(inputFile, result, "utf-8");
|
|
17551
17556
|
const { execFile: execFile5 } = await import("child_process");
|
|
17552
17557
|
await new Promise((resolve12) => {
|
|
17553
17558
|
execFile5("python", [scriptPath, "main", "--file", inputFile], {
|
|
@@ -17574,12 +17579,12 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
17574
17579
|
}
|
|
17575
17580
|
}
|
|
17576
17581
|
try {
|
|
17577
|
-
const
|
|
17578
|
-
const
|
|
17579
|
-
const resultsDir =
|
|
17580
|
-
|
|
17581
|
-
const resultFile =
|
|
17582
|
-
|
|
17582
|
+
const fs59 = await import("fs");
|
|
17583
|
+
const path61 = await import("path");
|
|
17584
|
+
const resultsDir = path61.join(getStorageDir(), "results");
|
|
17585
|
+
fs59.mkdirSync(resultsDir, { recursive: true });
|
|
17586
|
+
const resultFile = path61.join(resultsDir, `${task.id}.json`);
|
|
17587
|
+
fs59.writeFileSync(resultFile, JSON.stringify({
|
|
17583
17588
|
taskId: task.id,
|
|
17584
17589
|
description: task.description,
|
|
17585
17590
|
executedAt: now.toISOString(),
|
|
@@ -17909,30 +17914,30 @@ function registerCronTools() {
|
|
|
17909
17914
|
}
|
|
17910
17915
|
},
|
|
17911
17916
|
handler: async (args2) => {
|
|
17912
|
-
const
|
|
17913
|
-
const
|
|
17914
|
-
const resultsDir =
|
|
17915
|
-
if (!
|
|
17917
|
+
const fs59 = await import("fs");
|
|
17918
|
+
const path61 = await import("path");
|
|
17919
|
+
const resultsDir = path61.join(getStorageDir(), "results");
|
|
17920
|
+
if (!fs59.existsSync(resultsDir)) {
|
|
17916
17921
|
return { content: "\u6682\u65E0cron\u6267\u884C\u7ED3\u679C" };
|
|
17917
17922
|
}
|
|
17918
17923
|
if (args2.task_id) {
|
|
17919
|
-
const file2 =
|
|
17920
|
-
if (!
|
|
17924
|
+
const file2 = path61.join(resultsDir, `${args2.task_id}.json`);
|
|
17925
|
+
if (!fs59.existsSync(file2)) {
|
|
17921
17926
|
return { content: `\u4EFB\u52A1 ${args2.task_id} \u6682\u65E0\u6267\u884C\u7ED3\u679C`, isError: true };
|
|
17922
17927
|
}
|
|
17923
|
-
const data = JSON.parse(
|
|
17928
|
+
const data = JSON.parse(fs59.readFileSync(file2, "utf-8"));
|
|
17924
17929
|
return { content: `## ${data.description}
|
|
17925
17930
|
\u6267\u884C\u65F6\u95F4: ${data.executedAt}
|
|
17926
17931
|
\u7B2C${data.runCount}\u6B21\u6267\u884C
|
|
17927
17932
|
|
|
17928
17933
|
${data.result}` };
|
|
17929
17934
|
}
|
|
17930
|
-
const files =
|
|
17935
|
+
const files = fs59.readdirSync(resultsDir).filter((f) => f.endsWith(".json"));
|
|
17931
17936
|
if (files.length === 0) {
|
|
17932
17937
|
return { content: "\u6682\u65E0cron\u6267\u884C\u7ED3\u679C" };
|
|
17933
17938
|
}
|
|
17934
17939
|
const results = files.map((f) => {
|
|
17935
|
-
const data = JSON.parse(
|
|
17940
|
+
const data = JSON.parse(fs59.readFileSync(path61.join(resultsDir, f), "utf-8"));
|
|
17936
17941
|
return `### ${data.description} (${data.taskId.slice(0, 8)})
|
|
17937
17942
|
\u6267\u884C: ${data.executedAt} | \u7B2C${data.runCount}\u6B21
|
|
17938
17943
|
${data.result.slice(0, 500)}${data.result.length > 500 ? "..." : ""}`;
|
|
@@ -18607,10 +18612,10 @@ function ensureLoaded(workspace, configIds) {
|
|
|
18607
18612
|
if (!state.blockedUserIds.includes(id)) state.blockedUserIds.push(id);
|
|
18608
18613
|
}
|
|
18609
18614
|
}
|
|
18610
|
-
const
|
|
18615
|
+
const path61 = join44(workspace, ".reply-blocklist.json");
|
|
18611
18616
|
try {
|
|
18612
|
-
if (existsSync23(
|
|
18613
|
-
const raw = readFileSync33(
|
|
18617
|
+
if (existsSync23(path61)) {
|
|
18618
|
+
const raw = readFileSync33(path61, "utf-8");
|
|
18614
18619
|
const parsed = JSON.parse(raw);
|
|
18615
18620
|
if (parsed.blockedUserIds) {
|
|
18616
18621
|
for (const id of parsed.blockedUserIds) {
|
|
@@ -18626,9 +18631,9 @@ function ensureLoaded(workspace, configIds) {
|
|
|
18626
18631
|
loaded = true;
|
|
18627
18632
|
}
|
|
18628
18633
|
function save(workspace) {
|
|
18629
|
-
const
|
|
18634
|
+
const path61 = join44(workspace, ".reply-blocklist.json");
|
|
18630
18635
|
try {
|
|
18631
|
-
writeFileSync11(
|
|
18636
|
+
writeFileSync11(path61, JSON.stringify(state, null, 2), "utf-8");
|
|
18632
18637
|
} catch (err) {
|
|
18633
18638
|
console.warn(`[reply-blocklist] Failed to save: ${err.message}`);
|
|
18634
18639
|
}
|
|
@@ -19198,15 +19203,15 @@ var read_exports = {};
|
|
|
19198
19203
|
__export(read_exports, {
|
|
19199
19204
|
readFileState: () => readFileState
|
|
19200
19205
|
});
|
|
19201
|
-
import * as
|
|
19202
|
-
import * as
|
|
19206
|
+
import * as fs52 from "node:fs";
|
|
19207
|
+
import * as path51 from "node:path";
|
|
19203
19208
|
function isBlockedDevicePath(filePath) {
|
|
19204
19209
|
if (BLOCKED_DEVICE_PATHS.has(filePath)) return true;
|
|
19205
19210
|
if (filePath.startsWith("/proc/") && (filePath.endsWith("/fd/0") || filePath.endsWith("/fd/1") || filePath.endsWith("/fd/2"))) return true;
|
|
19206
19211
|
return false;
|
|
19207
19212
|
}
|
|
19208
19213
|
function checkReadLoop(filePath, offset, limit) {
|
|
19209
|
-
const stat4 =
|
|
19214
|
+
const stat4 = fs52.statSync(filePath);
|
|
19210
19215
|
const mtimeMs = stat4.mtimeMs;
|
|
19211
19216
|
const prev = readHistory.get(filePath);
|
|
19212
19217
|
if (prev && prev.offset === offset && prev.limit === limit && prev.mtimeMs === mtimeMs) {
|
|
@@ -19220,19 +19225,19 @@ function checkReadLoop(filePath, offset, limit) {
|
|
|
19220
19225
|
return null;
|
|
19221
19226
|
}
|
|
19222
19227
|
function readFileContent(filePath) {
|
|
19223
|
-
const fd =
|
|
19228
|
+
const fd = fs52.openSync(filePath, "r");
|
|
19224
19229
|
const bom = Buffer.alloc(2);
|
|
19225
|
-
|
|
19226
|
-
|
|
19230
|
+
fs52.readSync(fd, bom, 0, 2, 0);
|
|
19231
|
+
fs52.closeSync(fd);
|
|
19227
19232
|
let encoding = "utf8";
|
|
19228
19233
|
if (bom[0] === 255 && bom[1] === 254) {
|
|
19229
19234
|
encoding = "utf16le";
|
|
19230
19235
|
}
|
|
19231
|
-
const stat4 =
|
|
19236
|
+
const stat4 = fs52.statSync(filePath);
|
|
19232
19237
|
if (stat4.size > MAX_FILE_SIZE) {
|
|
19233
19238
|
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`);
|
|
19234
19239
|
}
|
|
19235
|
-
const raw =
|
|
19240
|
+
const raw = fs52.readFileSync(filePath, encoding);
|
|
19236
19241
|
const content = raw.toString().replaceAll("\r\n", "\n");
|
|
19237
19242
|
return { content, encoding };
|
|
19238
19243
|
}
|
|
@@ -19374,11 +19379,11 @@ Usage:
|
|
|
19374
19379
|
} catch (e) {
|
|
19375
19380
|
return { content: e.message, isError: true };
|
|
19376
19381
|
}
|
|
19377
|
-
if (!
|
|
19382
|
+
if (!fs52.existsSync(filePath)) {
|
|
19378
19383
|
return { content: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`, isError: true };
|
|
19379
19384
|
}
|
|
19380
|
-
const stat4 =
|
|
19381
|
-
const baseName =
|
|
19385
|
+
const stat4 = fs52.statSync(filePath);
|
|
19386
|
+
const baseName = path51.basename(filePath).toUpperCase();
|
|
19382
19387
|
if (BLOCKED_BASENAMES.has(baseName)) {
|
|
19383
19388
|
return { content: `\u8BBE\u5907\u6587\u4EF6\u4E0D\u652F\u6301\u8BFB\u53D6: ${filePath}`, isError: true };
|
|
19384
19389
|
}
|
|
@@ -19386,11 +19391,11 @@ Usage:
|
|
|
19386
19391
|
return { content: `\u8BBE\u5907\u6587\u4EF6\u4F1A\u963B\u585E\u6216\u4EA7\u751F\u65E0\u9650\u8F93\u51FA: ${filePath}`, isError: true };
|
|
19387
19392
|
}
|
|
19388
19393
|
if (stat4.isDirectory()) {
|
|
19389
|
-
const entries2 =
|
|
19394
|
+
const entries2 = fs52.readdirSync(filePath);
|
|
19390
19395
|
const items = entries2.map((e) => {
|
|
19391
|
-
const full =
|
|
19396
|
+
const full = path51.join(filePath, e);
|
|
19392
19397
|
try {
|
|
19393
|
-
const s =
|
|
19398
|
+
const s = fs52.statSync(full);
|
|
19394
19399
|
return s.isDirectory() ? `${e}/` : e;
|
|
19395
19400
|
} catch {
|
|
19396
19401
|
return e;
|
|
@@ -19399,7 +19404,7 @@ Usage:
|
|
|
19399
19404
|
return { content: `\u76EE\u5F55 (${entries2.length} \u9879):
|
|
19400
19405
|
${items.join("\n")}` };
|
|
19401
19406
|
}
|
|
19402
|
-
const ext =
|
|
19407
|
+
const ext = path51.extname(filePath).toLowerCase();
|
|
19403
19408
|
if (BINARY_EXTENSIONS.has(ext)) {
|
|
19404
19409
|
return { content: `\u4E8C\u8FDB\u5236\u6587\u4EF6\u4E0D\u652F\u6301\u8BFB\u53D6 (${ext}): ${filePath}`, isError: true };
|
|
19405
19410
|
}
|
|
@@ -19447,22 +19452,22 @@ ${result}` : result };
|
|
|
19447
19452
|
|
|
19448
19453
|
// src/tools/write.ts
|
|
19449
19454
|
var write_exports = {};
|
|
19450
|
-
import * as
|
|
19451
|
-
import * as
|
|
19455
|
+
import * as fs53 from "node:fs";
|
|
19456
|
+
import * as path52 from "node:path";
|
|
19452
19457
|
function isBlockedPath(filePath) {
|
|
19453
19458
|
return BLOCKED_PATTERNS.some((p) => p.test(filePath));
|
|
19454
19459
|
}
|
|
19455
19460
|
function atomicWrite(filePath, content) {
|
|
19456
19461
|
const tmpPath = filePath + ".tmp." + Date.now() + ".write";
|
|
19457
|
-
|
|
19462
|
+
fs53.writeFileSync(tmpPath, content, "utf-8");
|
|
19458
19463
|
try {
|
|
19459
|
-
|
|
19464
|
+
fs53.renameSync(tmpPath, filePath);
|
|
19460
19465
|
} catch (e) {
|
|
19461
19466
|
try {
|
|
19462
|
-
|
|
19467
|
+
fs53.unlinkSync(tmpPath);
|
|
19463
19468
|
} catch {
|
|
19464
19469
|
}
|
|
19465
|
-
|
|
19470
|
+
fs53.writeFileSync(filePath, content, "utf-8");
|
|
19466
19471
|
}
|
|
19467
19472
|
}
|
|
19468
19473
|
function simpleDiff(oldContent, newContent) {
|
|
@@ -19556,15 +19561,15 @@ Usage:
|
|
|
19556
19561
|
}
|
|
19557
19562
|
const rawContent = args2.content;
|
|
19558
19563
|
const content = rawContent.replaceAll("\r\n", "\n");
|
|
19559
|
-
if (
|
|
19564
|
+
if (fs53.existsSync(filePath) && fs53.statSync(filePath).isDirectory()) {
|
|
19560
19565
|
return { content: `\u8DEF\u5F84\u662F\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6: ${filePath}`, isError: true };
|
|
19561
19566
|
}
|
|
19562
19567
|
let oldContent = null;
|
|
19563
19568
|
let isCreate = true;
|
|
19564
|
-
if (
|
|
19569
|
+
if (fs53.existsSync(filePath)) {
|
|
19565
19570
|
isCreate = false;
|
|
19566
19571
|
try {
|
|
19567
|
-
oldContent =
|
|
19572
|
+
oldContent = fs53.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
|
|
19568
19573
|
} catch {
|
|
19569
19574
|
isCreate = true;
|
|
19570
19575
|
}
|
|
@@ -19580,10 +19585,10 @@ Usage:
|
|
|
19580
19585
|
isError: true
|
|
19581
19586
|
};
|
|
19582
19587
|
}
|
|
19583
|
-
const currentStat =
|
|
19588
|
+
const currentStat = fs53.statSync(filePath);
|
|
19584
19589
|
const lastWriteTime = Math.floor(currentStat.mtimeMs);
|
|
19585
19590
|
if (lastWriteTime > readState.timestamp) {
|
|
19586
|
-
const currentContent =
|
|
19591
|
+
const currentContent = fs53.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
|
|
19587
19592
|
if (currentContent !== oldContent) {
|
|
19588
19593
|
return {
|
|
19589
19594
|
content: `\u6587\u4EF6\u5728\u8BFB\u53D6\u540E\u88AB\u4FEE\u6539\u3002\u8BF7\u5148\u91CD\u65B0\u8BFB\u53D6\u6587\u4EF6\u518D\u5199\u5165: ${filePath}`,
|
|
@@ -19592,9 +19597,9 @@ Usage:
|
|
|
19592
19597
|
}
|
|
19593
19598
|
}
|
|
19594
19599
|
}
|
|
19595
|
-
const dir =
|
|
19600
|
+
const dir = path52.dirname(filePath);
|
|
19596
19601
|
try {
|
|
19597
|
-
|
|
19602
|
+
fs53.mkdirSync(dir, { recursive: true });
|
|
19598
19603
|
} catch (e) {
|
|
19599
19604
|
return { content: `\u65E0\u6CD5\u521B\u5EFA\u76EE\u5F55: ${dir} \u2014 ${e.message}`, isError: true };
|
|
19600
19605
|
}
|
|
@@ -19603,11 +19608,11 @@ Usage:
|
|
|
19603
19608
|
} catch (e) {
|
|
19604
19609
|
return { content: `\u5199\u5165\u5931\u8D25: ${e.message}`, isError: true };
|
|
19605
19610
|
}
|
|
19606
|
-
readFileState.set(filePath, { timestamp:
|
|
19611
|
+
readFileState.set(filePath, { timestamp: fs53.statSync(filePath).mtimeMs });
|
|
19607
19612
|
const action = isCreate ? "\u521B\u5EFA" : "\u66F4\u65B0";
|
|
19608
19613
|
const lines = content.split("\n").length;
|
|
19609
19614
|
const chars = content.length;
|
|
19610
|
-
const stat4 =
|
|
19615
|
+
const stat4 = fs53.statSync(filePath);
|
|
19611
19616
|
let diff = "";
|
|
19612
19617
|
if (!isCreate && oldContent !== null) {
|
|
19613
19618
|
diff = `
|
|
@@ -19627,8 +19632,8 @@ ${simpleDiff(oldContent, content)}`;
|
|
|
19627
19632
|
|
|
19628
19633
|
// src/tools/edit.ts
|
|
19629
19634
|
var edit_exports = {};
|
|
19630
|
-
import * as
|
|
19631
|
-
import * as
|
|
19635
|
+
import * as fs54 from "node:fs";
|
|
19636
|
+
import * as path53 from "node:path";
|
|
19632
19637
|
function normalizeQuotes(str) {
|
|
19633
19638
|
return str.replaceAll(LEFT_SINGLE_CURLY, "'").replaceAll(RIGHT_SINGLE_CURLY, "'").replaceAll(LEFT_DOUBLE_CURLY, '"').replaceAll(RIGHT_DOUBLE_CURLY, '"');
|
|
19634
19639
|
}
|
|
@@ -19781,7 +19786,7 @@ Usage:
|
|
|
19781
19786
|
}
|
|
19782
19787
|
let fileContent = null;
|
|
19783
19788
|
try {
|
|
19784
|
-
const stat4 =
|
|
19789
|
+
const stat4 = fs54.statSync(filePath);
|
|
19785
19790
|
if (stat4.isDirectory()) {
|
|
19786
19791
|
return { content: `\u8DEF\u5F84\u662F\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6: ${filePath}`, isError: true };
|
|
19787
19792
|
}
|
|
@@ -19791,21 +19796,21 @@ Usage:
|
|
|
19791
19796
|
} catch (e) {
|
|
19792
19797
|
if (e.code === "ENOENT") {
|
|
19793
19798
|
if (oldString === "") {
|
|
19794
|
-
const dir =
|
|
19795
|
-
|
|
19796
|
-
|
|
19797
|
-
readFileState.set(filePath, { timestamp:
|
|
19799
|
+
const dir = path53.dirname(filePath);
|
|
19800
|
+
fs54.mkdirSync(dir, { recursive: true });
|
|
19801
|
+
fs54.writeFileSync(filePath, newString, "utf-8");
|
|
19802
|
+
readFileState.set(filePath, { timestamp: fs54.statSync(filePath).mtimeMs });
|
|
19798
19803
|
return { content: `\u521B\u5EFA\u6587\u4EF6: ${filePath} (${newString.split("\n").length} \u884C)` };
|
|
19799
19804
|
}
|
|
19800
19805
|
return { content: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`, isError: true };
|
|
19801
19806
|
}
|
|
19802
19807
|
throw e;
|
|
19803
19808
|
}
|
|
19804
|
-
const rawContent =
|
|
19809
|
+
const rawContent = fs54.readFileSync(filePath, "utf-8");
|
|
19805
19810
|
fileContent = rawContent.replaceAll("\r\n", "\n");
|
|
19806
19811
|
if (oldString === "" && fileContent.trim() === "") {
|
|
19807
|
-
|
|
19808
|
-
readFileState.set(filePath, { timestamp:
|
|
19812
|
+
fs54.writeFileSync(filePath, newString, "utf-8");
|
|
19813
|
+
readFileState.set(filePath, { timestamp: fs54.statSync(filePath).mtimeMs });
|
|
19809
19814
|
return { content: `\u5199\u5165\u7A7A\u6587\u4EF6: ${filePath} (${newString.split("\n").length} \u884C)` };
|
|
19810
19815
|
}
|
|
19811
19816
|
const readState = readFileState.get(filePath);
|
|
@@ -19815,11 +19820,11 @@ Usage:
|
|
|
19815
19820
|
isError: true
|
|
19816
19821
|
};
|
|
19817
19822
|
}
|
|
19818
|
-
const currentStat =
|
|
19823
|
+
const currentStat = fs54.statSync(filePath);
|
|
19819
19824
|
const lastWriteTime = Math.floor(currentStat.mtimeMs);
|
|
19820
19825
|
if (lastWriteTime > readState.timestamp) {
|
|
19821
19826
|
if (fileContent !== rawContent.replaceAll("\r\n", "\n")) {
|
|
19822
|
-
if (fileContent !==
|
|
19827
|
+
if (fileContent !== fs54.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n")) {
|
|
19823
19828
|
return {
|
|
19824
19829
|
content: `\u6587\u4EF6\u5728\u8BFB\u53D6\u540E\u88AB\u4FEE\u6539\u3002\u8BF7\u5148\u91CD\u65B0\u8BFB\u53D6\u6587\u4EF6\u518D\u7F16\u8F91: ${filePath}`,
|
|
19825
19830
|
isError: true
|
|
@@ -19851,8 +19856,8 @@ ${preview}
|
|
|
19851
19856
|
const actualNewString = preserveQuoteStyle(oldString, actualOldString, newString);
|
|
19852
19857
|
const diffView = generateEditDiff(fileContent, actualOldString, actualNewString);
|
|
19853
19858
|
const newContent = applyEditToFile(fileContent, actualOldString, actualNewString, replaceAll);
|
|
19854
|
-
|
|
19855
|
-
readFileState.set(filePath, { timestamp:
|
|
19859
|
+
fs54.writeFileSync(filePath, newContent, "utf-8");
|
|
19860
|
+
readFileState.set(filePath, { timestamp: fs54.statSync(filePath).mtimeMs });
|
|
19856
19861
|
const strategy = actualOldString === oldString ? "\u7CBE\u786E\u5339\u914D" : "\u5F15\u53F7\u89C4\u8303\u5316\u5339\u914D";
|
|
19857
19862
|
const count = replaceAll ? matchCount : 1;
|
|
19858
19863
|
const diff = `${oldString.length}\u2192${newString.length}\u5B57\u7B26`;
|
|
@@ -19870,8 +19875,8 @@ ${diffView}`
|
|
|
19870
19875
|
|
|
19871
19876
|
// src/tools/glob.ts
|
|
19872
19877
|
var glob_exports = {};
|
|
19873
|
-
import * as
|
|
19874
|
-
import * as
|
|
19878
|
+
import * as fs55 from "node:fs";
|
|
19879
|
+
import * as path54 from "node:path";
|
|
19875
19880
|
function globMatch(pattern, filename) {
|
|
19876
19881
|
const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
19877
19882
|
try {
|
|
@@ -19891,18 +19896,18 @@ function findFiles(dir, pattern, limit, baseDir) {
|
|
|
19891
19896
|
}
|
|
19892
19897
|
let entries2;
|
|
19893
19898
|
try {
|
|
19894
|
-
entries2 =
|
|
19899
|
+
entries2 = fs55.readdirSync(currentDir, { withFileTypes: true });
|
|
19895
19900
|
} catch {
|
|
19896
19901
|
return;
|
|
19897
19902
|
}
|
|
19898
19903
|
for (const entry of entries2) {
|
|
19899
19904
|
if (truncated) return;
|
|
19900
|
-
const fullPath =
|
|
19905
|
+
const fullPath = path54.join(currentDir, entry.name);
|
|
19901
19906
|
if (entry.isDirectory()) {
|
|
19902
19907
|
if (VCS_DIRS.has(entry.name)) continue;
|
|
19903
19908
|
walk(fullPath);
|
|
19904
19909
|
} else if (entry.isFile()) {
|
|
19905
|
-
const relativePath =
|
|
19910
|
+
const relativePath = path54.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
19906
19911
|
const patternsToTry = [pattern];
|
|
19907
19912
|
if (pattern.startsWith("**/")) {
|
|
19908
19913
|
patternsToTry.push(pattern.slice(3));
|
|
@@ -19912,7 +19917,7 @@ function findFiles(dir, pattern, limit, baseDir) {
|
|
|
19912
19917
|
);
|
|
19913
19918
|
if (matched) {
|
|
19914
19919
|
try {
|
|
19915
|
-
const stat4 =
|
|
19920
|
+
const stat4 = fs55.statSync(fullPath);
|
|
19916
19921
|
results.push({ path: fullPath, mtimeMs: stat4.mtimeMs });
|
|
19917
19922
|
} catch {
|
|
19918
19923
|
}
|
|
@@ -19929,7 +19934,7 @@ function findFiles(dir, pattern, limit, baseDir) {
|
|
|
19929
19934
|
};
|
|
19930
19935
|
}
|
|
19931
19936
|
function toRelativePath(absolutePath, cwd) {
|
|
19932
|
-
if (absolutePath.startsWith(cwd +
|
|
19937
|
+
if (absolutePath.startsWith(cwd + path54.sep)) {
|
|
19933
19938
|
return absolutePath.slice(cwd.length + 1);
|
|
19934
19939
|
}
|
|
19935
19940
|
return absolutePath;
|
|
@@ -19962,10 +19967,10 @@ var init_glob = __esm({
|
|
|
19962
19967
|
const searchPath = args2.path ? resolvePath(args2.path, ctx.workspace) : ctx.workspace;
|
|
19963
19968
|
const pattern = args2.pattern;
|
|
19964
19969
|
const limit = args2.limit || DEFAULT_LIMIT;
|
|
19965
|
-
if (!
|
|
19970
|
+
if (!fs55.existsSync(searchPath)) {
|
|
19966
19971
|
return { content: `\u76EE\u5F55\u4E0D\u5B58\u5728: ${searchPath}`, isError: true };
|
|
19967
19972
|
}
|
|
19968
|
-
if (!
|
|
19973
|
+
if (!fs55.statSync(searchPath).isDirectory()) {
|
|
19969
19974
|
return { content: `\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55: ${searchPath}`, isError: true };
|
|
19970
19975
|
}
|
|
19971
19976
|
const start = Date.now();
|
|
@@ -19990,7 +19995,7 @@ ${filenames.join("\n")}${truncatedNote}`
|
|
|
19990
19995
|
// src/tools/grep.ts
|
|
19991
19996
|
var grep_exports = {};
|
|
19992
19997
|
import { execFile as execFile4 } from "node:child_process";
|
|
19993
|
-
import * as
|
|
19998
|
+
import * as path55 from "node:path";
|
|
19994
19999
|
function ripGrep(args2, searchPath, signal) {
|
|
19995
20000
|
return new Promise((resolve12) => {
|
|
19996
20001
|
const fullArgs = [...args2, searchPath];
|
|
@@ -20022,7 +20027,7 @@ function applyHeadLimit(items, limit, offset = 0) {
|
|
|
20022
20027
|
};
|
|
20023
20028
|
}
|
|
20024
20029
|
function toRelativePath2(absolutePath, cwd) {
|
|
20025
|
-
if (absolutePath.startsWith(cwd +
|
|
20030
|
+
if (absolutePath.startsWith(cwd + path55.sep)) {
|
|
20026
20031
|
return absolutePath.slice(cwd.length + 1);
|
|
20027
20032
|
}
|
|
20028
20033
|
if (absolutePath.startsWith(cwd)) {
|
|
@@ -21831,7 +21836,7 @@ var init_TeamDeleteTool = __esm({
|
|
|
21831
21836
|
},
|
|
21832
21837
|
isEnabled: () => isAgentSwarmsEnabled(),
|
|
21833
21838
|
handler: async (_args, ctx) => {
|
|
21834
|
-
const teamName = ctx.teamName;
|
|
21839
|
+
const teamName = ctx.teamName || getLeaderTeamName() || void 0;
|
|
21835
21840
|
if (teamName) {
|
|
21836
21841
|
const teamFile = readTeamFile(teamName);
|
|
21837
21842
|
if (teamFile) {
|
|
@@ -21915,7 +21920,7 @@ function getAgentName(ctx) {
|
|
|
21915
21920
|
return ctx.agentName || TEAM_LEAD_NAME;
|
|
21916
21921
|
}
|
|
21917
21922
|
function getTeamName(ctx) {
|
|
21918
|
-
return ctx.teamName;
|
|
21923
|
+
return ctx.teamName || getLeaderTeamName() || void 0;
|
|
21919
21924
|
}
|
|
21920
21925
|
async function handleMessage(recipientName, content, summary, ctx) {
|
|
21921
21926
|
const teamName = getTeamName(ctx);
|
|
@@ -22759,8 +22764,8 @@ __export(manager_exports, {
|
|
|
22759
22764
|
McpManager: () => McpManager,
|
|
22760
22765
|
convertInputSchema: () => convertInputSchema
|
|
22761
22766
|
});
|
|
22762
|
-
import * as
|
|
22763
|
-
import * as
|
|
22767
|
+
import * as fs58 from "node:fs";
|
|
22768
|
+
import * as path58 from "node:path";
|
|
22764
22769
|
import { Client as Client3 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
22765
22770
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
22766
22771
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
@@ -22801,12 +22806,12 @@ function convertInputSchema(inputSchema) {
|
|
|
22801
22806
|
}
|
|
22802
22807
|
function persistBinary(base64Data, mimeType, persistId) {
|
|
22803
22808
|
const ext = mimeType?.split("/")[1] || "bin";
|
|
22804
|
-
const dir =
|
|
22805
|
-
|
|
22806
|
-
const filepath =
|
|
22809
|
+
const dir = path58.join(process.env.ENGINE_STATE_DIR || ".engine", "mcp-blobs");
|
|
22810
|
+
fs58.mkdirSync(dir, { recursive: true });
|
|
22811
|
+
const filepath = path58.join(dir, `${persistId}.${ext}`);
|
|
22807
22812
|
try {
|
|
22808
22813
|
const buf = Buffer.from(base64Data, "base64");
|
|
22809
|
-
|
|
22814
|
+
fs58.writeFileSync(filepath, buf);
|
|
22810
22815
|
return { filepath, size: buf.length };
|
|
22811
22816
|
} catch (err) {
|
|
22812
22817
|
return { error: err.message };
|
|
@@ -23140,7 +23145,7 @@ __export(resources_exports, {
|
|
|
23140
23145
|
registerMcpResourceTools: () => registerMcpResourceTools,
|
|
23141
23146
|
unregisterMcpResourceTools: () => unregisterMcpResourceTools
|
|
23142
23147
|
});
|
|
23143
|
-
import * as
|
|
23148
|
+
import * as path59 from "node:path";
|
|
23144
23149
|
function registerMcpResourceTools(manager) {
|
|
23145
23150
|
mcpManagerRef = manager;
|
|
23146
23151
|
registry.register(listResourcesTool);
|
|
@@ -23158,7 +23163,7 @@ var init_resources = __esm({
|
|
|
23158
23163
|
"use strict";
|
|
23159
23164
|
init_registry();
|
|
23160
23165
|
MAX_RESULT_CHARS2 = 1e5;
|
|
23161
|
-
MEDIA_DIR = process.env.ENGINE_MEDIA_DIR ||
|
|
23166
|
+
MEDIA_DIR = process.env.ENGINE_MEDIA_DIR || path59.join(process.env.ENGINE_STATE_DIR || ".engine", "media", "inbound");
|
|
23162
23167
|
MCP_LIST_RESOURCES_TOOL = "mcp__list_resources";
|
|
23163
23168
|
MCP_READ_RESOURCE_TOOL = "mcp__read_resource";
|
|
23164
23169
|
mcpManagerRef = null;
|
|
@@ -23247,17 +23252,33 @@ ${lines.join("\n\n")}`;
|
|
|
23247
23252
|
init_loader();
|
|
23248
23253
|
|
|
23249
23254
|
// src/config/secrets-env.ts
|
|
23255
|
+
import { createHash } from "node:crypto";
|
|
23250
23256
|
import { chmodSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
23251
|
-
import { basename, dirname, join as join2 } from "node:path";
|
|
23257
|
+
import { basename, dirname, join as join2, normalize } from "node:path";
|
|
23252
23258
|
function secretsHome() {
|
|
23253
23259
|
return process.env.HOME || process.env.USERPROFILE || ".";
|
|
23254
23260
|
}
|
|
23261
|
+
function normalizeStateDir(stateDir) {
|
|
23262
|
+
return normalize(stateDir).split("\\").join("/").replace(/\/+$/, "").toLowerCase();
|
|
23263
|
+
}
|
|
23264
|
+
function stateKey(stateDir) {
|
|
23265
|
+
return createHash("sha256").update(normalizeStateDir(stateDir)).digest("hex").slice(0, 8);
|
|
23266
|
+
}
|
|
23267
|
+
function isDefaultStateDir(stateDir) {
|
|
23268
|
+
return normalizeStateDir(stateDir) === normalizeStateDir(join2(secretsHome(), ".engine7"));
|
|
23269
|
+
}
|
|
23255
23270
|
function secretsCandidates(stateDir, configName) {
|
|
23256
|
-
|
|
23257
|
-
|
|
23271
|
+
const shared = join2(secretsHome(), ".engine7-secrets", `${configName}.env`);
|
|
23272
|
+
const inState = [
|
|
23258
23273
|
join2(stateDir, "configs", `.env.${configName}`),
|
|
23259
23274
|
join2(stateDir, "configs", ".env")
|
|
23260
23275
|
];
|
|
23276
|
+
if (isDefaultStateDir(stateDir)) return [shared, ...inState];
|
|
23277
|
+
return [
|
|
23278
|
+
join2(secretsHome(), ".engine7-secrets", `${configName}-${stateKey(stateDir)}.env`),
|
|
23279
|
+
shared,
|
|
23280
|
+
...inState
|
|
23281
|
+
];
|
|
23261
23282
|
}
|
|
23262
23283
|
function loadSecretsEnv(stateDir, configName) {
|
|
23263
23284
|
for (const p of secretsCandidates(stateDir, configName)) {
|
|
@@ -23290,7 +23311,7 @@ function loadSecretsEnvForConfig(configPath2) {
|
|
|
23290
23311
|
// src/engine-startup.ts
|
|
23291
23312
|
init_query();
|
|
23292
23313
|
init_live();
|
|
23293
|
-
import * as
|
|
23314
|
+
import * as path60 from "node:path";
|
|
23294
23315
|
|
|
23295
23316
|
// src/startup/providers.ts
|
|
23296
23317
|
init_provider_factory();
|
|
@@ -24643,7 +24664,7 @@ ${formatted}` };
|
|
|
24643
24664
|
};
|
|
24644
24665
|
}
|
|
24645
24666
|
function createEverosGetTool() {
|
|
24646
|
-
const
|
|
24667
|
+
const fs59 = __require("node:fs/promises");
|
|
24647
24668
|
return {
|
|
24648
24669
|
name: "memory_get",
|
|
24649
24670
|
description: "Read a memory file by path.",
|
|
@@ -24659,7 +24680,7 @@ function createEverosGetTool() {
|
|
|
24659
24680
|
handler: async (args2) => {
|
|
24660
24681
|
try {
|
|
24661
24682
|
const filePath = args2.path;
|
|
24662
|
-
const content = await
|
|
24683
|
+
const content = await fs59.readFile(filePath, "utf-8");
|
|
24663
24684
|
const lines = content.split("\n");
|
|
24664
24685
|
const fromLine = args2.from ?? 1;
|
|
24665
24686
|
const numLines = args2.lines ?? lines.length;
|
|
@@ -26452,18 +26473,18 @@ function resetSessionStartInjection(sessionId) {
|
|
|
26452
26473
|
}
|
|
26453
26474
|
var externalChanRulesCache = null;
|
|
26454
26475
|
function loadExternalChanRules(workspace) {
|
|
26455
|
-
const
|
|
26456
|
-
if (externalChanRulesCache && externalChanRulesCache.path ===
|
|
26476
|
+
const path61 = join36(workspace, "prompts", "external-chan-rules.md");
|
|
26477
|
+
if (externalChanRulesCache && externalChanRulesCache.path === path61) return externalChanRulesCache;
|
|
26457
26478
|
let content = "";
|
|
26458
|
-
if (existsSync20(
|
|
26479
|
+
if (existsSync20(path61)) {
|
|
26459
26480
|
try {
|
|
26460
|
-
content = readFileSync27(
|
|
26481
|
+
content = readFileSync27(path61, "utf-8").trim();
|
|
26461
26482
|
} catch (e) {
|
|
26462
26483
|
console.warn(`[external-chan-rules] Failed to load: ${e}`);
|
|
26463
26484
|
}
|
|
26464
26485
|
}
|
|
26465
|
-
externalChanRulesCache = { path:
|
|
26466
|
-
console.log(`[external-chan-rules] Loaded ${content.length} chars from ${
|
|
26486
|
+
externalChanRulesCache = { path: path61, content };
|
|
26487
|
+
console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path61}`);
|
|
26467
26488
|
return externalChanRulesCache;
|
|
26468
26489
|
}
|
|
26469
26490
|
function getExternalChanRulesBlock(inboundMeta, workspace) {
|
|
@@ -27350,15 +27371,15 @@ function registerCompactHooks(config2) {
|
|
|
27350
27371
|
const dailyDir = path22.join(workspace, "memory", "daily");
|
|
27351
27372
|
const dailyPath = path22.join(dailyDir, `${dateStr}.md`);
|
|
27352
27373
|
try {
|
|
27353
|
-
const
|
|
27354
|
-
if (!
|
|
27355
|
-
|
|
27374
|
+
const fs59 = await import("node:fs");
|
|
27375
|
+
if (!fs59.existsSync(dailyDir)) {
|
|
27376
|
+
fs59.mkdirSync(dailyDir, { recursive: true });
|
|
27356
27377
|
}
|
|
27357
27378
|
const sessionsDir = path22.join(config2.stateDir, "agents", "main", "sessions");
|
|
27358
27379
|
const sessionFile = path22.join(sessionsDir, `${sessionId}.jsonl`);
|
|
27359
27380
|
const recentLines = [];
|
|
27360
|
-
if (
|
|
27361
|
-
const content =
|
|
27381
|
+
if (fs59.existsSync(sessionFile)) {
|
|
27382
|
+
const content = fs59.readFileSync(sessionFile, "utf-8");
|
|
27362
27383
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
27363
27384
|
const userLines = lines.filter((l) => {
|
|
27364
27385
|
try {
|
|
@@ -27388,10 +27409,10 @@ function registerCompactHooks(config2) {
|
|
|
27388
27409
|
const entry = `${header}
|
|
27389
27410
|
${body}
|
|
27390
27411
|
`;
|
|
27391
|
-
if (
|
|
27392
|
-
|
|
27412
|
+
if (fs59.existsSync(dailyPath)) {
|
|
27413
|
+
fs59.appendFileSync(dailyPath, entry);
|
|
27393
27414
|
} else {
|
|
27394
|
-
|
|
27415
|
+
fs59.writeFileSync(dailyPath, `# ${dateStr} \u65E5\u5FD7
|
|
27395
27416
|
${entry}`);
|
|
27396
27417
|
}
|
|
27397
27418
|
console.log(`[hooks] PreCompact: saved ${recentLines.length} lines to ${dailyPath}`);
|
|
@@ -27407,16 +27428,16 @@ ${entry}`);
|
|
|
27407
27428
|
const workspace = input.cwd || input.workspace || "";
|
|
27408
27429
|
if (!workspace) return { continue: true };
|
|
27409
27430
|
try {
|
|
27410
|
-
const
|
|
27431
|
+
const fs59 = await import("node:fs");
|
|
27411
27432
|
const bufferPath = path22.join(workspace, "memory", "working-buffer.md");
|
|
27412
|
-
if (
|
|
27413
|
-
const stat4 =
|
|
27433
|
+
if (fs59.existsSync(bufferPath)) {
|
|
27434
|
+
const stat4 = fs59.statSync(bufferPath);
|
|
27414
27435
|
const ageMs = Date.now() - stat4.mtimeMs;
|
|
27415
27436
|
const ageMin = Math.round(ageMs / 6e4);
|
|
27416
27437
|
if (ageMin > 10) {
|
|
27417
27438
|
console.warn(`[hooks] PostCompact: \u26A0\uFE0F working-buffer.md is ${ageMin}min old (last modified ${stat4.mtime.toISOString()}) \u2014 content may be stale!`);
|
|
27418
27439
|
}
|
|
27419
|
-
const content =
|
|
27440
|
+
const content = fs59.readFileSync(bufferPath, "utf-8");
|
|
27420
27441
|
if (content.trim()) {
|
|
27421
27442
|
console.log(`[hooks] PostCompact: injecting working-buffer (${content.length} chars, ${ageMin}min old)`);
|
|
27422
27443
|
return {
|
|
@@ -33871,10 +33892,10 @@ function writeReportState(stateDir, s) {
|
|
|
33871
33892
|
}
|
|
33872
33893
|
|
|
33873
33894
|
// src/optional/reporting/payload.ts
|
|
33874
|
-
import { createHash as
|
|
33895
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
33875
33896
|
import * as os3 from "node:os";
|
|
33876
33897
|
function installKeyOf(installId, installSecret) {
|
|
33877
|
-
return
|
|
33898
|
+
return createHash3("sha256").update(installId + installSecret).digest("hex").slice(0, 32);
|
|
33878
33899
|
}
|
|
33879
33900
|
function buildReportPayload(opts) {
|
|
33880
33901
|
const p = {
|
|
@@ -34251,13 +34272,13 @@ var DiscordAdapter = class _DiscordAdapter {
|
|
|
34251
34272
|
}
|
|
34252
34273
|
/** 发送媒体附件(图片/文件/音频)— discord.js channel.send({ files }) */
|
|
34253
34274
|
async sendFile(target, message, attachment) {
|
|
34254
|
-
const
|
|
34255
|
-
const
|
|
34256
|
-
if (!
|
|
34275
|
+
const fs59 = await import("node:fs");
|
|
34276
|
+
const path61 = await import("node:path");
|
|
34277
|
+
if (!fs59.existsSync(attachment.path)) {
|
|
34257
34278
|
throw new Error(`File not found: ${attachment.path}`);
|
|
34258
34279
|
}
|
|
34259
|
-
const filename = attachment.filename ||
|
|
34260
|
-
const fileBuffer =
|
|
34280
|
+
const filename = attachment.filename || path61.basename(attachment.path);
|
|
34281
|
+
const fileBuffer = fs59.readFileSync(attachment.path);
|
|
34261
34282
|
const filePayload = {
|
|
34262
34283
|
attachment: fileBuffer,
|
|
34263
34284
|
name: filename
|
|
@@ -34706,13 +34727,13 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
34706
34727
|
}
|
|
34707
34728
|
/** 发送媒体附件(图片/文件) */
|
|
34708
34729
|
async sendFile(target, message, attachment) {
|
|
34709
|
-
const
|
|
34710
|
-
const
|
|
34711
|
-
if (!
|
|
34730
|
+
const fs59 = await import("node:fs");
|
|
34731
|
+
const path61 = await import("node:path");
|
|
34732
|
+
if (!fs59.existsSync(attachment.path)) {
|
|
34712
34733
|
throw new Error(`File not found: ${attachment.path}`);
|
|
34713
34734
|
}
|
|
34714
|
-
const filename = attachment.filename ||
|
|
34715
|
-
const fileBuffer =
|
|
34735
|
+
const filename = attachment.filename || path61.basename(attachment.path);
|
|
34736
|
+
const fileBuffer = fs59.readFileSync(attachment.path);
|
|
34716
34737
|
const safeTarget = target || this.config.defaultTarget || "";
|
|
34717
34738
|
if (!safeTarget) throw new Error("Feishu sendFile: no target (\u4E3B\u52A8\u53D1\u9001\u9700\u914D\u7F6E defaultTarget \u6216\u4F20\u5165 target)");
|
|
34718
34739
|
const receiveIdType = safeTarget.startsWith("ou_") ? "open_id" : "chat_id";
|
|
@@ -36534,22 +36555,22 @@ var DeskBuddyAdapter = class {
|
|
|
36534
36555
|
try {
|
|
36535
36556
|
if (!this.historyFile) {
|
|
36536
36557
|
const os7 = __require("os");
|
|
36537
|
-
const
|
|
36538
|
-
const
|
|
36539
|
-
const dir =
|
|
36540
|
-
|
|
36541
|
-
this.historyFile =
|
|
36542
|
-
this._fs =
|
|
36543
|
-
}
|
|
36544
|
-
const
|
|
36558
|
+
const fs60 = __require("fs");
|
|
36559
|
+
const path61 = __require("path");
|
|
36560
|
+
const dir = path61.join(os7.homedir(), "xiaowen", "workspace", ".desktopBuddy");
|
|
36561
|
+
fs60.mkdirSync(dir, { recursive: true });
|
|
36562
|
+
this.historyFile = path61.join(dir, "history.json");
|
|
36563
|
+
this._fs = fs60;
|
|
36564
|
+
}
|
|
36565
|
+
const fs59 = this._fs;
|
|
36545
36566
|
let arr = [];
|
|
36546
36567
|
try {
|
|
36547
|
-
arr = JSON.parse(
|
|
36568
|
+
arr = JSON.parse(fs59.readFileSync(this.historyFile, "utf-8")) || [];
|
|
36548
36569
|
} catch {
|
|
36549
36570
|
}
|
|
36550
36571
|
arr.push({ ...entry, ts: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN", { hour12: false }) });
|
|
36551
36572
|
if (arr.length > 500) arr = arr.slice(-500);
|
|
36552
|
-
|
|
36573
|
+
fs59.writeFileSync(this.historyFile, JSON.stringify(arr, null, 1));
|
|
36553
36574
|
} catch (e) {
|
|
36554
36575
|
console.warn("[deskBuddy] history write fail", e);
|
|
36555
36576
|
}
|
|
@@ -36828,8 +36849,365 @@ var DeskBuddyAdapter = class {
|
|
|
36828
36849
|
}
|
|
36829
36850
|
};
|
|
36830
36851
|
|
|
36852
|
+
// src/channels/cc-bridge.ts
|
|
36853
|
+
init_live();
|
|
36854
|
+
import fs41 from "node:fs";
|
|
36855
|
+
import path41 from "node:path";
|
|
36856
|
+
import { WebSocketServer as WebSocketServer2, WebSocket as WebSocket2 } from "ws";
|
|
36857
|
+
var DEFAULT_PORT = 16996;
|
|
36858
|
+
var CcBridgeAdapter = class {
|
|
36859
|
+
name = "ccBridge";
|
|
36860
|
+
config;
|
|
36861
|
+
wss = null;
|
|
36862
|
+
clients = /* @__PURE__ */ new Set();
|
|
36863
|
+
messageHandler = null;
|
|
36864
|
+
seq = 0;
|
|
36865
|
+
constructor(config2) {
|
|
36866
|
+
this.config = config2;
|
|
36867
|
+
}
|
|
36868
|
+
/** 工具流水默认不推(见 CcBridgeConfig.showToolDisplay) */
|
|
36869
|
+
get suppressToolDisplay() {
|
|
36870
|
+
return this.config.showToolDisplay !== true;
|
|
36871
|
+
}
|
|
36872
|
+
/** 实际监听的端口。配 `port: 0` 时由内核分配 —— 测试用它拿真端口,不用赌某个数字没被占 */
|
|
36873
|
+
get boundPort() {
|
|
36874
|
+
const a = this.wss?.address();
|
|
36875
|
+
return a && typeof a === "object" ? a.port : null;
|
|
36876
|
+
}
|
|
36877
|
+
onMessage(handler2) {
|
|
36878
|
+
this.messageHandler = handler2;
|
|
36879
|
+
}
|
|
36880
|
+
// ── outbox(持久队列)──
|
|
36881
|
+
//
|
|
36882
|
+
// 每次现读 stateDir,不缓存 —— stateDir 的 effect 是 restart,缓存下来的
|
|
36883
|
+
// 只会在热加载后变成指向旧目录的幽灵路径(配置访问铁律)。
|
|
36884
|
+
outboxFile() {
|
|
36885
|
+
const sd = liveConfig.get("stateDir") || process.cwd();
|
|
36886
|
+
return path41.join(sd, "state", "cc-bridge", "outbox.jsonl");
|
|
36887
|
+
}
|
|
36888
|
+
/**
|
|
36889
|
+
* 读队列。**只有 ENOENT 才等于"空队列"。**
|
|
36890
|
+
*
|
|
36891
|
+
* ⚠️ 这里原来写的是 `catch { return [] }` —— 那是**会丢数据的**:
|
|
36892
|
+
* 读失败(Windows 上 AV/同步盘/搜索索引的瞬时共享锁 EBUSY,或 EPERM/EACCES)
|
|
36893
|
+
* 返回空数组,紧接着 `enqueue` 拿这份空数组**整份覆写**,待投递的消息全没,
|
|
36894
|
+
* 而且一句警告都没有。而"读不了"和"不存在"是完全不同的两件事 ——
|
|
36895
|
+
* 这个区分正是"宁可重复,不可丢失"能不能成立的支点。
|
|
36896
|
+
*/
|
|
36897
|
+
readOutbox() {
|
|
36898
|
+
let raw;
|
|
36899
|
+
try {
|
|
36900
|
+
raw = fs41.readFileSync(this.outboxFile(), "utf8");
|
|
36901
|
+
} catch (e) {
|
|
36902
|
+
if (e?.code === "ENOENT") return [];
|
|
36903
|
+
throw e;
|
|
36904
|
+
}
|
|
36905
|
+
const out = [];
|
|
36906
|
+
for (const line of raw.split("\n")) {
|
|
36907
|
+
if (!line.trim()) continue;
|
|
36908
|
+
try {
|
|
36909
|
+
out.push(JSON.parse(line));
|
|
36910
|
+
} catch {
|
|
36911
|
+
}
|
|
36912
|
+
}
|
|
36913
|
+
return out;
|
|
36914
|
+
}
|
|
36915
|
+
/**
|
|
36916
|
+
* 写队列:**先写 .tmp 再 rename**。
|
|
36917
|
+
*
|
|
36918
|
+
* `writeFileSync(file, ...)` 是原地截断(`w` 模式先清零再写)——
|
|
36919
|
+
* 在这中间被 taskkill / OOM / 断电,文件就是空的或半截,
|
|
36920
|
+
* 下次读回来是 [] 且无从分辨,最多 maxQueue 条待投递消息整体消失。
|
|
36921
|
+
* rename 到已存在文件走的是替换语义,落盘要么全旧要么全新。
|
|
36922
|
+
*
|
|
36923
|
+
* **失败要抛**:调用方(cron/heartbeat 经 `manager.send`)必须知道没存住 ——
|
|
36924
|
+
* 吞成 warning 的话,磁盘满/只读时客户端在线还能收到实时那条,
|
|
36925
|
+
* 客户端离线就是真丢,而这恰恰是这个通道要保的场景。
|
|
36926
|
+
*/
|
|
36927
|
+
writeOutbox(items) {
|
|
36928
|
+
const file2 = this.outboxFile();
|
|
36929
|
+
const tmp = `${file2}.tmp`;
|
|
36930
|
+
fs41.mkdirSync(path41.dirname(file2), { recursive: true });
|
|
36931
|
+
fs41.writeFileSync(tmp, items.map((i) => JSON.stringify(i)).join("\n") + (items.length ? "\n" : ""), "utf8");
|
|
36932
|
+
fs41.renameSync(tmp, file2);
|
|
36933
|
+
}
|
|
36934
|
+
/** 入队(dedupe 不做 —— id 是新的,重复投递由 ack 收敛)。读/写失败都往外抛,不吞。 */
|
|
36935
|
+
enqueue(item) {
|
|
36936
|
+
const items = this.readOutbox();
|
|
36937
|
+
items.push(item);
|
|
36938
|
+
const cap = this.config.maxQueue ?? 500;
|
|
36939
|
+
if (items.length > cap) {
|
|
36940
|
+
const dropped = items.length - cap;
|
|
36941
|
+
items.splice(0, dropped);
|
|
36942
|
+
console.warn(`[ccBridge] outbox \u8D85\u8FC7 ${cap} \u6761\uFF0C\u4E22\u4E86\u6700\u8001\u7684 ${dropped} \u6761 \u2014\u2014 CC \u592A\u4E45\u6CA1\u6765\u6536`);
|
|
36943
|
+
}
|
|
36944
|
+
this.writeOutbox(items);
|
|
36945
|
+
}
|
|
36946
|
+
/**
|
|
36947
|
+
* ack:从队列删掉客户端确认过的。
|
|
36948
|
+
*
|
|
36949
|
+
* 读失败时**返回 0 而不是抛** —— 这里宁可"删不掉"也不能"删错":
|
|
36950
|
+
* ack 的语义是收敛,失败留给下一轮再收敛,代价只是重复投递。
|
|
36951
|
+
*/
|
|
36952
|
+
ack(ids) {
|
|
36953
|
+
if (!ids.length) return 0;
|
|
36954
|
+
const set = new Set(ids);
|
|
36955
|
+
let items;
|
|
36956
|
+
try {
|
|
36957
|
+
items = this.readOutbox();
|
|
36958
|
+
} catch (e) {
|
|
36959
|
+
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}`);
|
|
36960
|
+
return 0;
|
|
36961
|
+
}
|
|
36962
|
+
const kept = items.filter((i) => !set.has(i.id));
|
|
36963
|
+
const removed = items.length - kept.length;
|
|
36964
|
+
if (removed) {
|
|
36965
|
+
try {
|
|
36966
|
+
this.writeOutbox(kept);
|
|
36967
|
+
} catch (e) {
|
|
36968
|
+
console.warn(`[ccBridge] ack \u5199\u961F\u5217\u5931\u8D25\uFF0C\u672C\u8F6E\u4E0D\u5220\uFF1A${e.message}`);
|
|
36969
|
+
return 0;
|
|
36970
|
+
}
|
|
36971
|
+
}
|
|
36972
|
+
return removed;
|
|
36973
|
+
}
|
|
36974
|
+
/** 队列条数(读不到就算 0 —— 只用于日志/握手,不能用来做删除决策) */
|
|
36975
|
+
queuedCount() {
|
|
36976
|
+
try {
|
|
36977
|
+
return this.readOutbox().length;
|
|
36978
|
+
} catch {
|
|
36979
|
+
return 0;
|
|
36980
|
+
}
|
|
36981
|
+
}
|
|
36982
|
+
/** 读队列但**不因读失败而中断**(握手补发、日志这类"读不到也能继续"的场合) */
|
|
36983
|
+
safeReadOutbox(where) {
|
|
36984
|
+
try {
|
|
36985
|
+
return this.readOutbox();
|
|
36986
|
+
} catch (e) {
|
|
36987
|
+
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}`);
|
|
36988
|
+
return [];
|
|
36989
|
+
}
|
|
36990
|
+
}
|
|
36991
|
+
/**
|
|
36992
|
+
* 升级握手校验。见 `connect()` 里那段注释 —— 绑回环挡不住浏览器。
|
|
36993
|
+
* 返回 false 时 `ws` 会直接拒绝升级(HTTP 401),连接根本建立不起来。
|
|
36994
|
+
*/
|
|
36995
|
+
verifyUpgrade(info) {
|
|
36996
|
+
const h = info?.req?.headers ?? {};
|
|
36997
|
+
if (h.origin) {
|
|
36998
|
+
console.warn(`[ccBridge] \u26D4 \u62D2\u7EDD\u5E26 Origin \u7684\u8FDE\u63A5\uFF1A${h.origin}\uFF08\u7591\u4F3C\u7F51\u9875\u53D1\u8D77\uFF09`);
|
|
36999
|
+
return false;
|
|
37000
|
+
}
|
|
37001
|
+
const host = String(h.host || "").replace(/:\d+$/, "").toLowerCase();
|
|
37002
|
+
const ok = host === "127.0.0.1" || host === "localhost" || host === "[::1]" || host === "::1" || host === "";
|
|
37003
|
+
if (!ok) console.warn(`[ccBridge] \u26D4 \u62D2\u7EDD\u975E\u56DE\u73AF Host\uFF1A${h.host}\uFF08\u7591\u4F3C DNS rebinding\uFF09`);
|
|
37004
|
+
return ok;
|
|
37005
|
+
}
|
|
37006
|
+
// ── 连接 ──
|
|
37007
|
+
async connect() {
|
|
37008
|
+
const port = this.config.port ?? DEFAULT_PORT;
|
|
37009
|
+
try {
|
|
37010
|
+
fs41.mkdirSync(path41.dirname(this.outboxFile()), { recursive: true });
|
|
37011
|
+
} catch {
|
|
37012
|
+
}
|
|
37013
|
+
this.wss = new WebSocketServer2({
|
|
37014
|
+
port,
|
|
37015
|
+
host: "127.0.0.1",
|
|
37016
|
+
maxPayload: 4 * 1024 * 1024,
|
|
37017
|
+
// ⛔ **绑 127.0.0.1 不是安全边界。** WebSocket 不受同源策略约束,
|
|
37018
|
+
// 用户浏览器里**任意** http 页面都能 `new WebSocket('ws://127.0.0.1:16996')`
|
|
37019
|
+
// 连上来 —— 而这里连上就能以任意 `from` 往 agent 主会话注入消息
|
|
37020
|
+
// (实测过:伪造 Origin/Host 能成功注入一条 `from: 翀哥` 的消息)。
|
|
37021
|
+
// 所以必须自己校验:
|
|
37022
|
+
// · 带 `Origin` 头 = 网页发起的 → 直接拒(`scripts/cc-inbox.mjs` 是 Node
|
|
37023
|
+
// 客户端,本来就不发这个头,不受影响)
|
|
37024
|
+
// · `Host` 必须是本机回环 → 挡 DNS rebinding(域名解析到 127.0.0.1 时
|
|
37025
|
+
// Host 会是那个域名,而不是 127.0.0.1)
|
|
37026
|
+
verifyClient: (info) => this.verifyUpgrade(info)
|
|
37027
|
+
});
|
|
37028
|
+
this.wss.on("connection", (ws) => {
|
|
37029
|
+
this.clients.add(ws);
|
|
37030
|
+
console.log(`[ccBridge] CC connected (${this.clients.size} online)`);
|
|
37031
|
+
const pending2 = this.safeReadOutbox("\u63E1\u624B\u8865\u53D1");
|
|
37032
|
+
this.sendTo(ws, { type: "hello", channel: this.name, queued: pending2.length });
|
|
37033
|
+
if (pending2.length) {
|
|
37034
|
+
console.log(`[ccBridge] \u8865\u53D1 ${pending2.length} \u6761\u79EF\u538B`);
|
|
37035
|
+
this.sendTo(ws, { type: "backlog", messages: pending2 });
|
|
37036
|
+
}
|
|
37037
|
+
ws.on("message", (data) => this.handleFrame(ws, data));
|
|
37038
|
+
ws.on("close", () => {
|
|
37039
|
+
this.clients.delete(ws);
|
|
37040
|
+
console.log(`[ccBridge] CC disconnected (${this.clients.size} online)`);
|
|
37041
|
+
});
|
|
37042
|
+
ws.on("error", () => this.clients.delete(ws));
|
|
37043
|
+
});
|
|
37044
|
+
this.wss.on("error", (e) => {
|
|
37045
|
+
console.error(`[ccBridge] server error on 127.0.0.1:${port}: ${e.message}`);
|
|
37046
|
+
});
|
|
37047
|
+
await new Promise((resolve12, reject) => {
|
|
37048
|
+
const wss = this.wss;
|
|
37049
|
+
const ok = () => {
|
|
37050
|
+
wss.off("error", bad);
|
|
37051
|
+
resolve12();
|
|
37052
|
+
};
|
|
37053
|
+
const bad = (e) => {
|
|
37054
|
+
wss.off("listening", ok);
|
|
37055
|
+
reject(e);
|
|
37056
|
+
};
|
|
37057
|
+
wss.once("listening", ok);
|
|
37058
|
+
wss.once("error", bad);
|
|
37059
|
+
});
|
|
37060
|
+
console.log(`[ccBridge] listening on 127.0.0.1:${this.boundPort} (${this.queuedCount()} queued)`);
|
|
37061
|
+
}
|
|
37062
|
+
handleFrame(ws, data) {
|
|
37063
|
+
let msg2;
|
|
37064
|
+
try {
|
|
37065
|
+
msg2 = JSON.parse(String(data));
|
|
37066
|
+
} catch {
|
|
37067
|
+
this.sendTo(ws, { type: "error", error: "bad json" });
|
|
37068
|
+
return;
|
|
37069
|
+
}
|
|
37070
|
+
if (msg2.type === "ping") {
|
|
37071
|
+
this.sendTo(ws, { type: "pong" });
|
|
37072
|
+
return;
|
|
37073
|
+
}
|
|
37074
|
+
if (msg2.type === "ack") {
|
|
37075
|
+
const ids = Array.isArray(msg2.ids) ? msg2.ids.map(String) : [];
|
|
37076
|
+
const n = this.ack(ids);
|
|
37077
|
+
this.sendTo(ws, { type: "ack-ok", removed: n, queued: this.queuedCount() });
|
|
37078
|
+
return;
|
|
37079
|
+
}
|
|
37080
|
+
if (msg2.type === "chat") {
|
|
37081
|
+
const text = String(msg2.text ?? "").trim();
|
|
37082
|
+
if (!text) {
|
|
37083
|
+
this.sendTo(ws, { type: "error", error: "empty text" });
|
|
37084
|
+
return;
|
|
37085
|
+
}
|
|
37086
|
+
const from = String(msg2.from || this.config.userId || "cc");
|
|
37087
|
+
const inbound = {
|
|
37088
|
+
content: text,
|
|
37089
|
+
from,
|
|
37090
|
+
fromName: String(msg2.fromName || from),
|
|
37091
|
+
channel_id: this.name,
|
|
37092
|
+
channel: this.name,
|
|
37093
|
+
channelType: "dm",
|
|
37094
|
+
isBot: false,
|
|
37095
|
+
messageId: `ccb-${Date.now()}-${++this.seq}`,
|
|
37096
|
+
recvAt: Date.now()
|
|
37097
|
+
};
|
|
37098
|
+
console.log(`[ccBridge] \u2190 ${inbound.fromName}: ${text.slice(0, 80)}`);
|
|
37099
|
+
this.messageHandler?.(inbound);
|
|
37100
|
+
return;
|
|
37101
|
+
}
|
|
37102
|
+
this.sendTo(ws, { type: "error", error: `unknown type: ${msg2.type}` });
|
|
37103
|
+
}
|
|
37104
|
+
sendTo(ws, payload) {
|
|
37105
|
+
if (ws.readyState !== WebSocket2.OPEN) return;
|
|
37106
|
+
try {
|
|
37107
|
+
ws.send(JSON.stringify(payload));
|
|
37108
|
+
} catch {
|
|
37109
|
+
}
|
|
37110
|
+
}
|
|
37111
|
+
/** 广播给出站消息之外的控制帧(握手/pong 走 sendTo 单播) */
|
|
37112
|
+
broadcast(payload) {
|
|
37113
|
+
const s = JSON.stringify(payload);
|
|
37114
|
+
let n = 0;
|
|
37115
|
+
for (const ws of [...this.clients]) {
|
|
37116
|
+
if (ws.readyState !== WebSocket2.OPEN) {
|
|
37117
|
+
this.clients.delete(ws);
|
|
37118
|
+
continue;
|
|
37119
|
+
}
|
|
37120
|
+
try {
|
|
37121
|
+
ws.send(s);
|
|
37122
|
+
n++;
|
|
37123
|
+
} catch {
|
|
37124
|
+
this.clients.delete(ws);
|
|
37125
|
+
}
|
|
37126
|
+
}
|
|
37127
|
+
return n;
|
|
37128
|
+
}
|
|
37129
|
+
async disconnect() {
|
|
37130
|
+
for (const ws of [...this.clients]) {
|
|
37131
|
+
try {
|
|
37132
|
+
ws.close();
|
|
37133
|
+
} catch {
|
|
37134
|
+
}
|
|
37135
|
+
}
|
|
37136
|
+
this.clients.clear();
|
|
37137
|
+
this.wss?.close();
|
|
37138
|
+
this.wss = null;
|
|
37139
|
+
}
|
|
37140
|
+
// ── 出站 ──
|
|
37141
|
+
/**
|
|
37142
|
+
* 发消息给 CC。
|
|
37143
|
+
*
|
|
37144
|
+
* **先落 outbox 再推实时** —— 顺序反过来的话,推送成功但 CC 恰好这时断开,
|
|
37145
|
+
* 这条消息就既不在队列里也没送达,静默消失。宁可重复,不可丢失:
|
|
37146
|
+
* 重复只是多读一遍,丢失是 agent 主动说了话而没人知道。
|
|
37147
|
+
*/
|
|
37148
|
+
async send(_target, message, _options) {
|
|
37149
|
+
if (!message.trim()) return;
|
|
37150
|
+
const item = {
|
|
37151
|
+
id: `m${Date.now()}-${++this.seq}`,
|
|
37152
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
37153
|
+
text: message,
|
|
37154
|
+
agent: this.config.agentName || "agent"
|
|
37155
|
+
};
|
|
37156
|
+
this.enqueue(item);
|
|
37157
|
+
const n = this.broadcast({ type: "message", message: item });
|
|
37158
|
+
if (n === 0) console.log(`[ccBridge] CC \u4E0D\u5728\u7EBF\uFF0C\u5DF2\u5165\u961F\uFF08${this.queuedCount()} \u6761\u5F85\u6536\uFF09`);
|
|
37159
|
+
}
|
|
37160
|
+
/**
|
|
37161
|
+
* 发文件。
|
|
37162
|
+
*
|
|
37163
|
+
* **只送路径,不送内容**(`mode: 'path-ref'`)。这个通道两端都在 127.0.0.1 上
|
|
37164
|
+
* (引擎与 CC 同机),把文件 base64 塞进 WS 帧纯属浪费 —— 大文件还会把 outbox 撑爆。
|
|
37165
|
+
*
|
|
37166
|
+
* ⚠️ **但"只送路径"是把解析推迟到接收端,推迟不等于可以不做。**
|
|
37167
|
+
* 它有一个很难看的退化形态:发送端返回「已发送」(路径形式上没问题),
|
|
37168
|
+
* 接收端拿着路径在**它自己的机器上**找不到 —— **失败发生在接收端,
|
|
37169
|
+
* 而发送端日志一切正常**。(张小T 0928 指出的,她是对的。)
|
|
37170
|
+
*
|
|
37171
|
+
* 所以这里补两道:
|
|
37172
|
+
* 1. **入队前 `statSync`** —— 路径打错/文件被挪走,**在本机就报错**,
|
|
37173
|
+
* 而不是等接收端去找一个不存在的路径
|
|
37174
|
+
* 2. 帧里带 `size`/`mtimeMs` + `mode: 'path-ref'` —— 接收端知道这是**引用不是内容**,
|
|
37175
|
+
* 且能对一下"拿到的是不是你说的那份"
|
|
37176
|
+
*
|
|
37177
|
+
* 客户端那侧对应地**读不到就不 ack**(见 `scripts/cc-inbox.mjs`)——
|
|
37178
|
+
* ack 的语义是"我拿到了",拿到一半不能算。
|
|
37179
|
+
*/
|
|
37180
|
+
async sendFile(_target, message, attachment) {
|
|
37181
|
+
let st;
|
|
37182
|
+
try {
|
|
37183
|
+
st = fs41.statSync(attachment.path);
|
|
37184
|
+
} catch (e) {
|
|
37185
|
+
throw new Error(`\u9644\u4EF6\u4E0D\u5B58\u5728\uFF0C\u672A\u53D1\u9001\uFF1A${attachment.path}\uFF08${e?.code || e?.message}\uFF09`);
|
|
37186
|
+
}
|
|
37187
|
+
const file2 = {
|
|
37188
|
+
mode: "path-ref",
|
|
37189
|
+
path: attachment.path,
|
|
37190
|
+
filename: attachment.filename || path41.basename(attachment.path),
|
|
37191
|
+
mimeType: attachment.mimeType || "application/octet-stream",
|
|
37192
|
+
size: st.size,
|
|
37193
|
+
mtimeMs: Math.round(st.mtimeMs)
|
|
37194
|
+
};
|
|
37195
|
+
const item = {
|
|
37196
|
+
id: `f${Date.now()}-${++this.seq}`,
|
|
37197
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
37198
|
+
text: message || "",
|
|
37199
|
+
agent: this.config.agentName || "agent",
|
|
37200
|
+
file: file2
|
|
37201
|
+
};
|
|
37202
|
+
this.enqueue(item);
|
|
37203
|
+
if (this.broadcast({ type: "message", message: item }) === 0) {
|
|
37204
|
+
console.log(`[ccBridge] CC \u4E0D\u5728\u7EBF\uFF0C\u9644\u4EF6\u5DF2\u5165\u961F\uFF1A${file2.filename}`);
|
|
37205
|
+
}
|
|
37206
|
+
}
|
|
37207
|
+
};
|
|
37208
|
+
|
|
36831
37209
|
// src/channels/manager.ts
|
|
36832
|
-
var MANAGED_CHANNEL_NAMES = ["discord", "feishu", "wechat", "deskBuddy"];
|
|
37210
|
+
var MANAGED_CHANNEL_NAMES = ["discord", "feishu", "wechat", "deskBuddy", "ccBridge"];
|
|
36833
37211
|
var defaultFactories = {
|
|
36834
37212
|
discord: (block) => {
|
|
36835
37213
|
const accounts = block.accounts || {};
|
|
@@ -36891,6 +37269,9 @@ var defaultFactories = {
|
|
|
36891
37269
|
},
|
|
36892
37270
|
deskBuddy: (block) => {
|
|
36893
37271
|
return new DeskBuddyAdapter(block);
|
|
37272
|
+
},
|
|
37273
|
+
ccBridge: (block) => {
|
|
37274
|
+
return new CcBridgeAdapter(block);
|
|
36894
37275
|
}
|
|
36895
37276
|
};
|
|
36896
37277
|
var ChannelManager = class {
|
|
@@ -37162,20 +37543,20 @@ var ChannelManager = class {
|
|
|
37162
37543
|
// src/session/session-manager.ts
|
|
37163
37544
|
init_types();
|
|
37164
37545
|
init_types();
|
|
37165
|
-
import * as
|
|
37166
|
-
import * as
|
|
37546
|
+
import * as path43 from "node:path";
|
|
37547
|
+
import * as fs43 from "node:fs";
|
|
37167
37548
|
import * as crypto6 from "node:crypto";
|
|
37168
37549
|
|
|
37169
37550
|
// src/session/reader.ts
|
|
37170
37551
|
init_attachments();
|
|
37171
|
-
import * as
|
|
37172
|
-
import * as
|
|
37552
|
+
import * as fs42 from "node:fs";
|
|
37553
|
+
import * as path42 from "node:path";
|
|
37173
37554
|
import * as readline2 from "node:readline";
|
|
37174
37555
|
var SKIP_PRECOMPACT_THRESHOLD = 5 * 1024 * 1024;
|
|
37175
37556
|
var TRANSCRIPT_READ_CHUNK_SIZE = 1024 * 1024;
|
|
37176
37557
|
var MAX_TRANSCRIPT_READ_BYTES = 50 * 1024 * 1024;
|
|
37177
37558
|
async function readSessionHeader(filePath) {
|
|
37178
|
-
const stream =
|
|
37559
|
+
const stream = fs42.createReadStream(filePath, { encoding: "utf-8" });
|
|
37179
37560
|
const rl = readline2.createInterface({ input: stream });
|
|
37180
37561
|
return new Promise((resolve12) => {
|
|
37181
37562
|
let resolved = false;
|
|
@@ -37206,7 +37587,7 @@ async function readSessionHeader(filePath) {
|
|
|
37206
37587
|
});
|
|
37207
37588
|
}
|
|
37208
37589
|
async function readLargeFilePostBoundary(filePath) {
|
|
37209
|
-
const stat4 =
|
|
37590
|
+
const stat4 = fs42.statSync(filePath);
|
|
37210
37591
|
const fileSize = stat4.size;
|
|
37211
37592
|
const boundaryMarker = '"compact_boundary"';
|
|
37212
37593
|
const CHUNK_SIZE = TRANSCRIPT_READ_CHUNK_SIZE;
|
|
@@ -37214,13 +37595,13 @@ async function readLargeFilePostBoundary(filePath) {
|
|
|
37214
37595
|
let outLen = 0;
|
|
37215
37596
|
let boundaryStartOffset = 0;
|
|
37216
37597
|
const chunk = Buffer.allocUnsafe(CHUNK_SIZE);
|
|
37217
|
-
const fd =
|
|
37598
|
+
const fd = fs42.openSync(filePath, "r");
|
|
37218
37599
|
try {
|
|
37219
37600
|
let filePos = 0;
|
|
37220
37601
|
let carryBuf;
|
|
37221
37602
|
let carryLen = 0;
|
|
37222
37603
|
while (filePos < fileSize) {
|
|
37223
|
-
const bytesRead =
|
|
37604
|
+
const bytesRead = fs42.readSync(fd, chunk, 0, Math.min(CHUNK_SIZE, fileSize - filePos), filePos);
|
|
37224
37605
|
if (bytesRead === 0) break;
|
|
37225
37606
|
filePos += bytesRead;
|
|
37226
37607
|
let scanBuf;
|
|
@@ -37272,7 +37653,7 @@ async function readLargeFilePostBoundary(filePath) {
|
|
|
37272
37653
|
}
|
|
37273
37654
|
}
|
|
37274
37655
|
} finally {
|
|
37275
|
-
|
|
37656
|
+
fs42.closeSync(fd);
|
|
37276
37657
|
}
|
|
37277
37658
|
const postBoundaryText = outBuf.subarray(0, outLen).toString("utf-8");
|
|
37278
37659
|
return postBoundaryText.split("\n").filter((l) => l.trim().length > 0);
|
|
@@ -37396,7 +37777,7 @@ function recoverOrphanedParallelToolResults(allEntries, chain, byUuid, seen) {
|
|
|
37396
37777
|
return result;
|
|
37397
37778
|
}
|
|
37398
37779
|
async function readSessionHistory(filePath) {
|
|
37399
|
-
const stat4 =
|
|
37780
|
+
const stat4 = fs42.statSync(filePath);
|
|
37400
37781
|
const fileSize = stat4.size;
|
|
37401
37782
|
if (fileSize > MAX_TRANSCRIPT_READ_BYTES) {
|
|
37402
37783
|
console.warn(`[reader] Session file exceeds 50MB limit (${(fileSize / 1024 / 1024).toFixed(1)}MB), using chunked fd read with boundary skip`);
|
|
@@ -37435,7 +37816,7 @@ async function readSessionHistory(filePath) {
|
|
|
37435
37816
|
return [...sessionStarts, ...filtered, ...restAttachments];
|
|
37436
37817
|
}
|
|
37437
37818
|
async function readAllLines(filePath) {
|
|
37438
|
-
const stream =
|
|
37819
|
+
const stream = fs42.createReadStream(filePath, { encoding: "utf-8" });
|
|
37439
37820
|
const rl = readline2.createInterface({ input: stream });
|
|
37440
37821
|
const lines = [];
|
|
37441
37822
|
return new Promise((resolve12, reject) => {
|
|
@@ -37589,12 +37970,12 @@ function extractText4(content) {
|
|
|
37589
37970
|
return content.filter((b) => b.type === "text").map((b) => b.text || "").join("") || null;
|
|
37590
37971
|
}
|
|
37591
37972
|
function listSessions(sessionsDir) {
|
|
37592
|
-
if (!
|
|
37593
|
-
const files =
|
|
37594
|
-
file:
|
|
37973
|
+
if (!fs42.existsSync(sessionsDir)) return [];
|
|
37974
|
+
const files = fs42.readdirSync(sessionsDir).filter((f) => f.endsWith(".jsonl") || f.includes(".jsonl.")).map((f) => ({
|
|
37975
|
+
file: path42.join(sessionsDir, f),
|
|
37595
37976
|
id: f.replace(/\.jsonl.*$/, ""),
|
|
37596
37977
|
// strip .jsonl and everything after (archived suffix)
|
|
37597
|
-
modified: new Date(
|
|
37978
|
+
modified: new Date(fs42.statSync(path42.join(sessionsDir, f)).mtimeMs)
|
|
37598
37979
|
})).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
37599
37980
|
return files;
|
|
37600
37981
|
}
|
|
@@ -37629,24 +38010,24 @@ var SessionManager = class {
|
|
|
37629
38010
|
constructor(config2) {
|
|
37630
38011
|
this.config = config2;
|
|
37631
38012
|
this.scopeConfig = config2.scopeConfig;
|
|
37632
|
-
this.sessionsDir =
|
|
37633
|
-
this.indexPath =
|
|
37634
|
-
this.platformMapPath =
|
|
37635
|
-
|
|
38013
|
+
this.sessionsDir = path43.join(config2.stateDir, "agents", "main", "sessions");
|
|
38014
|
+
this.indexPath = path43.join(this.sessionsDir, "session-index.json");
|
|
38015
|
+
this.platformMapPath = path43.join(this.sessionsDir, "platform-map.json");
|
|
38016
|
+
fs43.mkdirSync(this.sessionsDir, { recursive: true });
|
|
37636
38017
|
}
|
|
37637
38018
|
// === 索引操作 ===
|
|
37638
38019
|
loadIndex() {
|
|
37639
38020
|
try {
|
|
37640
|
-
if (
|
|
37641
|
-
const raw =
|
|
38021
|
+
if (fs43.existsSync(this.indexPath)) {
|
|
38022
|
+
const raw = fs43.readFileSync(this.indexPath, "utf-8");
|
|
37642
38023
|
const obj = JSON.parse(raw);
|
|
37643
38024
|
this.index = new Map(Object.entries(obj));
|
|
37644
38025
|
let fixed = 0;
|
|
37645
38026
|
for (const [key, entry] of this.index) {
|
|
37646
|
-
if (
|
|
37647
|
-
const basename10 =
|
|
37648
|
-
const localPath =
|
|
37649
|
-
if (
|
|
38027
|
+
if (fs43.existsSync(entry.file)) continue;
|
|
38028
|
+
const basename10 = path43.basename(entry.file);
|
|
38029
|
+
const localPath = path43.join(this.sessionsDir, basename10);
|
|
38030
|
+
if (fs43.existsSync(localPath)) {
|
|
37650
38031
|
entry.file = localPath;
|
|
37651
38032
|
fixed++;
|
|
37652
38033
|
}
|
|
@@ -37665,7 +38046,7 @@ var SessionManager = class {
|
|
|
37665
38046
|
try {
|
|
37666
38047
|
const obj = {};
|
|
37667
38048
|
for (const [k, v] of this.index) obj[k] = v;
|
|
37668
|
-
|
|
38049
|
+
fs43.writeFileSync(this.indexPath, JSON.stringify(obj, null, 2));
|
|
37669
38050
|
} catch (err) {
|
|
37670
38051
|
console.error(`[session] Failed to save index: ${err.message}`);
|
|
37671
38052
|
}
|
|
@@ -37673,8 +38054,8 @@ var SessionManager = class {
|
|
|
37673
38054
|
// === 平台映射 ===
|
|
37674
38055
|
loadPlatformMap() {
|
|
37675
38056
|
try {
|
|
37676
|
-
if (
|
|
37677
|
-
const raw =
|
|
38057
|
+
if (fs43.existsSync(this.platformMapPath)) {
|
|
38058
|
+
const raw = fs43.readFileSync(this.platformMapPath, "utf-8");
|
|
37678
38059
|
const obj = JSON.parse(raw);
|
|
37679
38060
|
this.platformMap = new Map(Object.entries(obj));
|
|
37680
38061
|
console.log(`[session] Platform map loaded: ${this.platformMap.size} entries`);
|
|
@@ -37686,7 +38067,7 @@ var SessionManager = class {
|
|
|
37686
38067
|
try {
|
|
37687
38068
|
const obj = {};
|
|
37688
38069
|
for (const [k, v] of this.platformMap) obj[k] = v;
|
|
37689
|
-
|
|
38070
|
+
fs43.writeFileSync(this.platformMapPath, JSON.stringify(obj, null, 2));
|
|
37690
38071
|
} catch (err) {
|
|
37691
38072
|
console.error(`[session] Failed to save platform map: ${err.message}`);
|
|
37692
38073
|
}
|
|
@@ -37725,17 +38106,17 @@ var SessionManager = class {
|
|
|
37725
38106
|
*/
|
|
37726
38107
|
getSessionFilePath(sessionId) {
|
|
37727
38108
|
const entry = this.index.get(sessionId);
|
|
37728
|
-
if (entry &&
|
|
37729
|
-
const direct =
|
|
37730
|
-
return
|
|
38109
|
+
if (entry && fs43.existsSync(entry.file)) return entry.file;
|
|
38110
|
+
const direct = path43.join(this.sessionsDir, `${sessionId}.jsonl`);
|
|
38111
|
+
return fs43.existsSync(direct) ? direct : null;
|
|
37731
38112
|
}
|
|
37732
38113
|
// === Writer 管理 ===
|
|
37733
38114
|
getWriter(sessionId) {
|
|
37734
38115
|
let writer = this.writers.get(sessionId);
|
|
37735
38116
|
if (!writer) {
|
|
37736
38117
|
const existing = this.index.get(sessionId);
|
|
37737
|
-
if (existing &&
|
|
37738
|
-
const existingFileId =
|
|
38118
|
+
if (existing && fs43.existsSync(existing.file)) {
|
|
38119
|
+
const existingFileId = path43.basename(existing.file, ".jsonl");
|
|
37739
38120
|
writer = new SessionWriter(this.sessionsDir, existingFileId);
|
|
37740
38121
|
writer.open(this.config.workspace, sessionId);
|
|
37741
38122
|
} else {
|
|
@@ -37747,7 +38128,7 @@ var SessionManager = class {
|
|
|
37747
38128
|
this.saveIndex();
|
|
37748
38129
|
this.writers.set(sessionId, writer);
|
|
37749
38130
|
this.onWriterCreated?.(writer, sessionId);
|
|
37750
|
-
console.log(`[session] ${existing ? "Resumed" : "New"}: ${sessionId} \u2192 ${
|
|
38131
|
+
console.log(`[session] ${existing ? "Resumed" : "New"}: ${sessionId} \u2192 ${path43.basename(writer.path)}`);
|
|
37751
38132
|
}
|
|
37752
38133
|
return writer;
|
|
37753
38134
|
}
|
|
@@ -37766,7 +38147,7 @@ var SessionManager = class {
|
|
|
37766
38147
|
if (!writer) return null;
|
|
37767
38148
|
try {
|
|
37768
38149
|
this.lastArchiveCheck = Date.now();
|
|
37769
|
-
const stat4 =
|
|
38150
|
+
const stat4 = fs43.statSync(writer.path);
|
|
37770
38151
|
if (stat4.size < threshold) return null;
|
|
37771
38152
|
const sizeMB = (stat4.size / 1024 / 1024).toFixed(1);
|
|
37772
38153
|
const thresholdMB = (threshold / 1024 / 1024).toFixed(1);
|
|
@@ -37775,10 +38156,10 @@ var SessionManager = class {
|
|
|
37775
38156
|
if (archivedPath) {
|
|
37776
38157
|
this.index.set(sessionId, { file: writer.path, updatedAt: Date.now() });
|
|
37777
38158
|
this.saveIndex();
|
|
37778
|
-
const newSize =
|
|
38159
|
+
const newSize = fs43.statSync(writer.path).size;
|
|
37779
38160
|
const oldSizeMB = (stat4.size / 1024 / 1024).toFixed(1);
|
|
37780
|
-
console.log(`[session] Archive complete: ${
|
|
37781
|
-
const fileId =
|
|
38161
|
+
console.log(`[session] Archive complete: ${path43.basename(archivedPath)} (${oldSizeMB}MB) \u2192 new JSONL created (${newSize} bytes)`);
|
|
38162
|
+
const fileId = path43.basename(writer.path).replace(/\.jsonl$/, "");
|
|
37782
38163
|
this.purgeOldArchives(fileId, 3);
|
|
37783
38164
|
return archivedPath;
|
|
37784
38165
|
}
|
|
@@ -37794,15 +38175,15 @@ var SessionManager = class {
|
|
|
37794
38175
|
purgeOldArchives(sessionId, keepCount = 3) {
|
|
37795
38176
|
try {
|
|
37796
38177
|
const dir = this.sessionsDir;
|
|
37797
|
-
if (!
|
|
37798
|
-
const allFiles =
|
|
38178
|
+
if (!fs43.existsSync(dir)) return;
|
|
38179
|
+
const allFiles = fs43.readdirSync(dir);
|
|
37799
38180
|
const archiveFiles = allFiles.filter((f) => {
|
|
37800
38181
|
if (!f.startsWith(sessionId)) return false;
|
|
37801
38182
|
return f.includes(".archived.") || f.includes(".compaction.");
|
|
37802
38183
|
}).map((f) => ({
|
|
37803
38184
|
name: f,
|
|
37804
|
-
path:
|
|
37805
|
-
mtime:
|
|
38185
|
+
path: path43.join(dir, f),
|
|
38186
|
+
mtime: fs43.statSync(path43.join(dir, f)).mtimeMs
|
|
37806
38187
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
37807
38188
|
console.log(`[session] purgeOldArchives: sessionId=${sessionId.substring(0, 8)}... found=${archiveFiles.length} keep=${keepCount}`);
|
|
37808
38189
|
if (archiveFiles.length <= keepCount) return;
|
|
@@ -37812,7 +38193,7 @@ var SessionManager = class {
|
|
|
37812
38193
|
console.log(`[session] Skipping archived (kept for history): ${f.name}`);
|
|
37813
38194
|
continue;
|
|
37814
38195
|
}
|
|
37815
|
-
|
|
38196
|
+
fs43.unlinkSync(f.path);
|
|
37816
38197
|
console.log(`[session] Purged old archive: ${f.name}`);
|
|
37817
38198
|
}
|
|
37818
38199
|
if (toDelete.length > 0) {
|
|
@@ -37861,29 +38242,29 @@ var SessionManager = class {
|
|
|
37861
38242
|
/** 列出 session 的所有文件(当前 + 归档),按修改时间排序 */
|
|
37862
38243
|
findAllSessionFiles(sessionId) {
|
|
37863
38244
|
const results = [];
|
|
37864
|
-
const mainFile =
|
|
37865
|
-
if (
|
|
38245
|
+
const mainFile = path43.join(this.sessionsDir, `${sessionId}.jsonl`);
|
|
38246
|
+
if (fs43.existsSync(mainFile)) {
|
|
37866
38247
|
results.push(mainFile);
|
|
37867
38248
|
}
|
|
37868
|
-
if (
|
|
37869
|
-
const compactionFiles =
|
|
38249
|
+
if (fs43.existsSync(this.sessionsDir)) {
|
|
38250
|
+
const compactionFiles = fs43.readdirSync(this.sessionsDir).filter((f) => f.startsWith(`${sessionId}.jsonl.compaction.`)).map((f) => ({
|
|
37870
38251
|
name: f,
|
|
37871
|
-
path:
|
|
37872
|
-
mtime:
|
|
38252
|
+
path: path43.join(this.sessionsDir, f),
|
|
38253
|
+
mtime: fs43.statSync(path43.join(this.sessionsDir, f)).mtimeMs
|
|
37873
38254
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
37874
38255
|
if (compactionFiles.length > 0) {
|
|
37875
38256
|
results.push(compactionFiles[0].path);
|
|
37876
38257
|
}
|
|
37877
38258
|
}
|
|
37878
38259
|
results.sort((a, b) => {
|
|
37879
|
-
const ta =
|
|
37880
|
-
const tb =
|
|
38260
|
+
const ta = fs43.statSync(a).mtimeMs;
|
|
38261
|
+
const tb = fs43.statSync(b).mtimeMs;
|
|
37881
38262
|
return ta - tb;
|
|
37882
38263
|
});
|
|
37883
38264
|
return results;
|
|
37884
38265
|
}
|
|
37885
38266
|
async findSessionFiles(sessionId) {
|
|
37886
|
-
if (!
|
|
38267
|
+
if (!fs43.existsSync(this.sessionsDir)) {
|
|
37887
38268
|
console.log(`[session:find] SESSIONS_DIR not found: ${this.sessionsDir}`);
|
|
37888
38269
|
return [];
|
|
37889
38270
|
}
|
|
@@ -37895,15 +38276,15 @@ var SessionManager = class {
|
|
|
37895
38276
|
return mappedFiles;
|
|
37896
38277
|
}
|
|
37897
38278
|
const indexedEntry = this.index.get(mappedId);
|
|
37898
|
-
if (indexedEntry &&
|
|
37899
|
-
const fileUuid =
|
|
38279
|
+
if (indexedEntry && fs43.existsSync(indexedEntry.file)) {
|
|
38280
|
+
const fileUuid = path43.basename(indexedEntry.file, ".jsonl");
|
|
37900
38281
|
return this.findAllSessionFiles(fileUuid);
|
|
37901
38282
|
}
|
|
37902
38283
|
}
|
|
37903
38284
|
const allFiles = this.findAllSessionFiles(sessionId);
|
|
37904
38285
|
if (allFiles.length > 0) {
|
|
37905
38286
|
const labels = allFiles.map((f) => {
|
|
37906
|
-
const base =
|
|
38287
|
+
const base = path43.basename(f);
|
|
37907
38288
|
return base.endsWith(".jsonl") ? base : `${base} (archived)`;
|
|
37908
38289
|
});
|
|
37909
38290
|
console.log(`[session:find] Found ${allFiles.length} file(s) for ${sessionId}: ${labels.join(", ")}`);
|
|
@@ -37911,9 +38292,9 @@ var SessionManager = class {
|
|
|
37911
38292
|
}
|
|
37912
38293
|
const indexed = this.index.get(sessionId);
|
|
37913
38294
|
if (indexed) {
|
|
37914
|
-
console.log(`[session:find] Index hit: ${sessionId} \u2192 ${indexed.file} (exists=${
|
|
37915
|
-
if (
|
|
37916
|
-
return this.findAllSessionFiles(
|
|
38295
|
+
console.log(`[session:find] Index hit: ${sessionId} \u2192 ${indexed.file} (exists=${fs43.existsSync(indexed.file)})`);
|
|
38296
|
+
if (fs43.existsSync(indexed.file)) {
|
|
38297
|
+
return this.findAllSessionFiles(path43.basename(indexed.file, ".jsonl"));
|
|
37917
38298
|
}
|
|
37918
38299
|
} else {
|
|
37919
38300
|
console.log(`[session:find] Index miss: ${sessionId} (index has ${this.index.size} entries)`);
|
|
@@ -37942,8 +38323,8 @@ var SessionManager = class {
|
|
|
37942
38323
|
return [];
|
|
37943
38324
|
}
|
|
37944
38325
|
files.sort((a, b) => {
|
|
37945
|
-
const ta =
|
|
37946
|
-
const tb =
|
|
38326
|
+
const ta = fs43.statSync(a).mtimeMs;
|
|
38327
|
+
const tb = fs43.statSync(b).mtimeMs;
|
|
37947
38328
|
return ta - tb;
|
|
37948
38329
|
});
|
|
37949
38330
|
const latestFile = files[files.length - 1];
|
|
@@ -37951,7 +38332,7 @@ var SessionManager = class {
|
|
|
37951
38332
|
if (!currentEntry || currentEntry.file !== latestFile) {
|
|
37952
38333
|
this.index.set(sessionId, { file: latestFile, updatedAt: Date.now() });
|
|
37953
38334
|
this.saveIndex();
|
|
37954
|
-
console.log(`[session:restore] Synced index: ${sessionId} \u2192 ${
|
|
38335
|
+
console.log(`[session:restore] Synced index: ${sessionId} \u2192 ${path43.basename(latestFile)}`);
|
|
37955
38336
|
}
|
|
37956
38337
|
const allMessages = [];
|
|
37957
38338
|
for (const file2 of files) {
|
|
@@ -37990,7 +38371,7 @@ var SessionManager = class {
|
|
|
37990
38371
|
if (isAttachmentMessage(m) && m.attachment.type === "relevant_memories") {
|
|
37991
38372
|
for (const mem of m.attachment.memories) {
|
|
37992
38373
|
rawCount++;
|
|
37993
|
-
restoredPaths.add(
|
|
38374
|
+
restoredPaths.add(path43.resolve(mem.path));
|
|
37994
38375
|
}
|
|
37995
38376
|
}
|
|
37996
38377
|
}
|
|
@@ -38022,15 +38403,15 @@ var SessionManager = class {
|
|
|
38022
38403
|
*/
|
|
38023
38404
|
rotateMainSession() {
|
|
38024
38405
|
const entry = this.index.get("main");
|
|
38025
|
-
if (!entry || !
|
|
38026
|
-
const oldFileId =
|
|
38027
|
-
const stat4 =
|
|
38406
|
+
if (!entry || !fs43.existsSync(entry.file)) return;
|
|
38407
|
+
const oldFileId = path43.basename(entry.file, ".jsonl");
|
|
38408
|
+
const stat4 = fs43.statSync(entry.file);
|
|
38028
38409
|
if (stat4.size < 1024) return;
|
|
38029
38410
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
|
|
38030
38411
|
const archivedPath = `${entry.file}.archived.${ts}`;
|
|
38031
|
-
|
|
38412
|
+
fs43.renameSync(entry.file, archivedPath);
|
|
38032
38413
|
const sizeMB = (stat4.size / 1024 / 1024).toFixed(1);
|
|
38033
|
-
console.log(`[session] Archived: ${
|
|
38414
|
+
console.log(`[session] Archived: ${path43.basename(entry.file)} \u2192 ${path43.basename(archivedPath)} (${sizeMB}MB)`);
|
|
38034
38415
|
const newWriter = new SessionWriter(this.sessionsDir);
|
|
38035
38416
|
newWriter.open(this.config.workspace, "main");
|
|
38036
38417
|
newWriter.writeModelChange(this.config.providerId, this.config.model);
|
|
@@ -38039,7 +38420,7 @@ var SessionManager = class {
|
|
|
38039
38420
|
this.index.set("main", { file: newPath, updatedAt: Date.now() });
|
|
38040
38421
|
this.saveIndex();
|
|
38041
38422
|
this.purgeOldArchives(oldFileId, 3);
|
|
38042
|
-
console.log(`[session] Rotated main: ${oldFileId}.jsonl \u2192 ${
|
|
38423
|
+
console.log(`[session] Rotated main: ${oldFileId}.jsonl \u2192 ${path43.basename(newPath)}`);
|
|
38043
38424
|
}
|
|
38044
38425
|
// === 迁移 ===
|
|
38045
38426
|
/** 迁移旧索引:session-index.json 里有 "discord:xxx" 格式的 key 时,生成 UUID 替换 */
|
|
@@ -38842,7 +39223,7 @@ Examples:
|
|
|
38842
39223
|
properties: {
|
|
38843
39224
|
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" },
|
|
38844
39225
|
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" },
|
|
38845
|
-
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"] },
|
|
39226
|
+
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"] },
|
|
38846
39227
|
content: { type: "string", description: "\u6D88\u606F\u6B63\u6587" }
|
|
38847
39228
|
},
|
|
38848
39229
|
required: ["content", "to"]
|
|
@@ -38931,12 +39312,12 @@ Examples:
|
|
|
38931
39312
|
// src/tools/msg-husband.ts
|
|
38932
39313
|
init_registry();
|
|
38933
39314
|
init_live();
|
|
38934
|
-
import
|
|
38935
|
-
import
|
|
39315
|
+
import fs44 from "node:fs";
|
|
39316
|
+
import path44 from "node:path";
|
|
38936
39317
|
function getHusbandFeishuId(workspace) {
|
|
38937
|
-
const contactsPath =
|
|
39318
|
+
const contactsPath = path44.join(workspace, "prompts", "contacts.md");
|
|
38938
39319
|
try {
|
|
38939
|
-
const text =
|
|
39320
|
+
const text = fs44.readFileSync(contactsPath, "utf-8");
|
|
38940
39321
|
const m = text.match(/\|\s*翀哥\s*\|\s*(ou_[a-f0-9]+)\s*\|/);
|
|
38941
39322
|
return m ? m[1] : null;
|
|
38942
39323
|
} catch {
|
|
@@ -39078,8 +39459,8 @@ Examples:
|
|
|
39078
39459
|
if (!to && !resolvedChannelId) {
|
|
39079
39460
|
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 };
|
|
39080
39461
|
}
|
|
39081
|
-
const
|
|
39082
|
-
if (!
|
|
39462
|
+
const fs59 = await import("node:fs");
|
|
39463
|
+
if (!fs59.existsSync(filePath)) {
|
|
39083
39464
|
return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6\u4E0D\u5B58\u5728 ${filePath}`, isError: true };
|
|
39084
39465
|
}
|
|
39085
39466
|
const toIds = to ? to.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
@@ -39107,7 +39488,7 @@ Examples:
|
|
|
39107
39488
|
md: "text/markdown"
|
|
39108
39489
|
};
|
|
39109
39490
|
const mimeType = mimeTypeMap[ext] || "application/octet-stream";
|
|
39110
|
-
const stat4 =
|
|
39491
|
+
const stat4 = fs59.statSync(filePath);
|
|
39111
39492
|
const sizeMB = stat4.size / 1024 / 1024;
|
|
39112
39493
|
if (sizeMB > 25) {
|
|
39113
39494
|
return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6 ${sizeMB.toFixed(1)}MB \u8D85\u8FC7 Discord 25MB \u9650\u5236`, isError: true };
|
|
@@ -39137,8 +39518,8 @@ Examples:
|
|
|
39137
39518
|
init_live();
|
|
39138
39519
|
init_registry();
|
|
39139
39520
|
init_well_formed();
|
|
39140
|
-
import * as
|
|
39141
|
-
import * as
|
|
39521
|
+
import * as fs45 from "node:fs";
|
|
39522
|
+
import * as path45 from "node:path";
|
|
39142
39523
|
var MIME_MAP = {
|
|
39143
39524
|
".jpg": "jpeg",
|
|
39144
39525
|
".jpeg": "jpeg",
|
|
@@ -39150,9 +39531,9 @@ var MIME_MAP = {
|
|
|
39150
39531
|
var VIDEO_EXT = /\.(mp4|mov)$/i;
|
|
39151
39532
|
var VIDEO_MAX_BYTES = 40 * 1024 * 1024;
|
|
39152
39533
|
function resolveLatestImage(specifiedPath, mediaDir) {
|
|
39153
|
-
if (specifiedPath &&
|
|
39154
|
-
if (!
|
|
39155
|
-
const files =
|
|
39534
|
+
if (specifiedPath && fs45.existsSync(specifiedPath)) return specifiedPath;
|
|
39535
|
+
if (!fs45.existsSync(mediaDir)) return null;
|
|
39536
|
+
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);
|
|
39156
39537
|
return files[0]?.p || null;
|
|
39157
39538
|
}
|
|
39158
39539
|
registry.register({
|
|
@@ -39183,21 +39564,21 @@ registry.register({
|
|
|
39183
39564
|
if (!provider?.streamChat) {
|
|
39184
39565
|
return { content: "Error: provider \u4E0D\u53EF\u7528\u3002", isError: true };
|
|
39185
39566
|
}
|
|
39186
|
-
const mediaDir =
|
|
39567
|
+
const mediaDir = path45.join(ctx.stateDir, "media", "inbound");
|
|
39187
39568
|
const imagePath = resolveLatestImage(args2.image_path, mediaDir);
|
|
39188
39569
|
if (!imagePath) {
|
|
39189
39570
|
return { content: "Error: no image found. Provide image_path or ensure media/inbound has images.", isError: true };
|
|
39190
39571
|
}
|
|
39191
39572
|
const rawPrompt = args2.prompt?.trim() || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
|
|
39192
|
-
const ext =
|
|
39573
|
+
const ext = path45.extname(imagePath).toLowerCase();
|
|
39193
39574
|
if (VIDEO_EXT.test(imagePath)) {
|
|
39194
|
-
const stat4 =
|
|
39575
|
+
const stat4 = fs45.statSync(imagePath);
|
|
39195
39576
|
if (stat4.size > VIDEO_MAX_BYTES) {
|
|
39196
39577
|
return { content: `Error: video too large (${(stat4.size / 1e6).toFixed(1)}MB > 40MB). Compress or trim first.`, isError: true };
|
|
39197
39578
|
}
|
|
39198
39579
|
const explicit = liveConfig.get("tools.my_eyes.videoBaseUrl");
|
|
39199
39580
|
const baseUrl = explicit || providerCfg.baseUrl.replace(/\/anthropic\/?$/, "/v1").replace(/\/$/, "");
|
|
39200
|
-
const vidB64 =
|
|
39581
|
+
const vidB64 = fs45.readFileSync(imagePath).toString("base64");
|
|
39201
39582
|
try {
|
|
39202
39583
|
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
39203
39584
|
method: "POST",
|
|
@@ -39227,7 +39608,7 @@ registry.register({
|
|
|
39227
39608
|
}
|
|
39228
39609
|
}
|
|
39229
39610
|
const mime = MIME_MAP[ext] || "jpeg";
|
|
39230
|
-
const imgB64 =
|
|
39611
|
+
const imgB64 = fs45.readFileSync(imagePath).toString("base64");
|
|
39231
39612
|
const userMsg = {
|
|
39232
39613
|
role: "user",
|
|
39233
39614
|
content: [
|
|
@@ -39305,7 +39686,7 @@ var TTS_PROVIDERS = [
|
|
|
39305
39686
|
init_registry();
|
|
39306
39687
|
|
|
39307
39688
|
// src/tools/opus-encoder.ts
|
|
39308
|
-
import * as
|
|
39689
|
+
import * as fs46 from "node:fs";
|
|
39309
39690
|
var FRAME_MS = 20;
|
|
39310
39691
|
var PRESKIP_48K = 312;
|
|
39311
39692
|
var OPUS_RATE = 48e3;
|
|
@@ -39422,7 +39803,7 @@ async function wavToOggOpusNative(wavPath) {
|
|
|
39422
39803
|
const mod = await import("@discordjs/opus");
|
|
39423
39804
|
const OpusEncoder = mod.OpusEncoder ?? mod.default?.OpusEncoder;
|
|
39424
39805
|
if (!OpusEncoder) throw new Error("@discordjs/opus export shape unexpected");
|
|
39425
|
-
const wav = parseWav(
|
|
39806
|
+
const wav = parseWav(fs46.readFileSync(wavPath));
|
|
39426
39807
|
if (wav.channels !== 1) throw new Error(`unsupported ${wav.channels}ch (need mono)`);
|
|
39427
39808
|
if (!NATIVE_RATES.includes(wav.sampleRate)) throw new Error(`unsupported rate ${wav.sampleRate}`);
|
|
39428
39809
|
if (wav.pcm.length === 0) throw new Error("empty pcm data");
|
|
@@ -39458,7 +39839,7 @@ async function wavToOggOpusNative(wavPath) {
|
|
|
39458
39839
|
}
|
|
39459
39840
|
ogg.page(cur, pageGranule, false, true);
|
|
39460
39841
|
const oggPath = wavPath.replace(/\.wav$/, ".ogg");
|
|
39461
|
-
|
|
39842
|
+
fs46.writeFileSync(oggPath, ogg.finish());
|
|
39462
39843
|
return oggPath;
|
|
39463
39844
|
}
|
|
39464
39845
|
|
|
@@ -39487,10 +39868,10 @@ function fallbackChainOf(raw) {
|
|
|
39487
39868
|
init_live();
|
|
39488
39869
|
|
|
39489
39870
|
// src/tools/tts/relay.ts
|
|
39490
|
-
import * as
|
|
39871
|
+
import * as fs47 from "node:fs";
|
|
39491
39872
|
import * as os6 from "node:os";
|
|
39492
|
-
import * as
|
|
39493
|
-
var VOICE_DIR2 =
|
|
39873
|
+
import * as path46 from "node:path";
|
|
39874
|
+
var VOICE_DIR2 = path46.join(os6.tmpdir(), "engine-voice");
|
|
39494
39875
|
var fetchFn = fetch;
|
|
39495
39876
|
async function synthRelay(text, cfg) {
|
|
39496
39877
|
const res = await fetchFn(`${cfg.baseUrl}/audio/speech`, {
|
|
@@ -39509,9 +39890,9 @@ async function synthRelay(text, cfg) {
|
|
|
39509
39890
|
}
|
|
39510
39891
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
39511
39892
|
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`);
|
|
39512
|
-
|
|
39513
|
-
const out =
|
|
39514
|
-
|
|
39893
|
+
fs47.mkdirSync(VOICE_DIR2, { recursive: true });
|
|
39894
|
+
const out = path46.join(VOICE_DIR2, `tts_relay_${Date.now()}.mp3`);
|
|
39895
|
+
fs47.writeFileSync(out, buf);
|
|
39515
39896
|
return {
|
|
39516
39897
|
path: out,
|
|
39517
39898
|
format: "mp3",
|
|
@@ -39630,8 +40011,8 @@ function synthesize(id, text, sink) {
|
|
|
39630
40011
|
// src/tools/my-voice.ts
|
|
39631
40012
|
import { execFile as execFile3 } from "node:child_process";
|
|
39632
40013
|
import { promisify as promisify3 } from "node:util";
|
|
39633
|
-
import * as
|
|
39634
|
-
import * as
|
|
40014
|
+
import * as fs48 from "node:fs";
|
|
40015
|
+
import * as path47 from "node:path";
|
|
39635
40016
|
var execFileAsync3 = promisify3(execFile3);
|
|
39636
40017
|
async function toWav24kWithDuration(audioPath) {
|
|
39637
40018
|
let wavPath = audioPath;
|
|
@@ -39649,15 +40030,15 @@ async function toWav24kWithDuration(audioPath) {
|
|
|
39649
40030
|
"pcm_s16le",
|
|
39650
40031
|
wavPath
|
|
39651
40032
|
], { timeout: 3e4 });
|
|
39652
|
-
if (!
|
|
40033
|
+
if (!fs48.existsSync(wavPath) || fs48.statSync(wavPath).size < 44) {
|
|
39653
40034
|
throw new Error("toWav24k produced empty output");
|
|
39654
40035
|
}
|
|
39655
40036
|
try {
|
|
39656
|
-
|
|
40037
|
+
fs48.unlinkSync(audioPath);
|
|
39657
40038
|
} catch {
|
|
39658
40039
|
}
|
|
39659
40040
|
}
|
|
39660
|
-
const durationSec = wavDurationSec(
|
|
40041
|
+
const durationSec = wavDurationSec(fs48.readFileSync(wavPath));
|
|
39661
40042
|
return { wavPath, durationSec };
|
|
39662
40043
|
}
|
|
39663
40044
|
async function compressWav(wavPath) {
|
|
@@ -39675,7 +40056,7 @@ async function compressWav(wavPath) {
|
|
|
39675
40056
|
"+faststart",
|
|
39676
40057
|
m4aPath
|
|
39677
40058
|
], { timeout: 3e4 });
|
|
39678
|
-
|
|
40059
|
+
fs48.unlinkSync(wavPath);
|
|
39679
40060
|
return m4aPath;
|
|
39680
40061
|
} catch {
|
|
39681
40062
|
return wavPath;
|
|
@@ -39702,10 +40083,10 @@ async function wavToOggOpus(wavPath) {
|
|
|
39702
40083
|
oggPath
|
|
39703
40084
|
], { timeout: 3e4 });
|
|
39704
40085
|
}
|
|
39705
|
-
if (!
|
|
40086
|
+
if (!fs48.existsSync(oggPath) || fs48.statSync(oggPath).size < 20) {
|
|
39706
40087
|
throw new Error("ogg/opus encode produced empty output");
|
|
39707
40088
|
}
|
|
39708
|
-
|
|
40089
|
+
fs48.unlinkSync(wavPath);
|
|
39709
40090
|
return oggPath;
|
|
39710
40091
|
}
|
|
39711
40092
|
async function sendVoiceToMulticaIssue(text, issueId, opts) {
|
|
@@ -39720,7 +40101,7 @@ async function sendVoiceToMulticaIssue(text, issueId, opts) {
|
|
|
39720
40101
|
} catch (e) {
|
|
39721
40102
|
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 };
|
|
39722
40103
|
}
|
|
39723
|
-
if (!audioPath || !
|
|
40104
|
+
if (!audioPath || !fs48.existsSync(audioPath)) {
|
|
39724
40105
|
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 };
|
|
39725
40106
|
}
|
|
39726
40107
|
if (audioPath.endsWith(".wav")) {
|
|
@@ -39730,21 +40111,21 @@ async function sendVoiceToMulticaIssue(text, issueId, opts) {
|
|
|
39730
40111
|
console.warn(`[my-voice:multaca] opus encode failed (${e.message}), attaching wav`);
|
|
39731
40112
|
}
|
|
39732
40113
|
}
|
|
39733
|
-
const ext =
|
|
39734
|
-
const sizeKB =
|
|
40114
|
+
const ext = path47.extname(audioPath).toLowerCase();
|
|
40115
|
+
const sizeKB = fs48.statSync(audioPath).size / 1024;
|
|
39735
40116
|
const voiceFile = `voice_${Date.now()}${ext}`;
|
|
39736
|
-
const cwdCopy =
|
|
40117
|
+
const cwdCopy = path47.join(process.cwd(), voiceFile);
|
|
39737
40118
|
try {
|
|
39738
|
-
|
|
40119
|
+
fs48.copyFileSync(audioPath, cwdCopy);
|
|
39739
40120
|
} catch (e) {
|
|
39740
40121
|
try {
|
|
39741
|
-
|
|
40122
|
+
fs48.unlinkSync(audioPath);
|
|
39742
40123
|
} catch {
|
|
39743
40124
|
}
|
|
39744
40125
|
return { content: `\u8BED\u97F3\u6302\u8F7D\u5931\u8D25\uFF08\u62F7\u8D1D\u5230\u5DE5\u4F5C\u76EE\u5F55\uFF09\uFF1A${e.message}`, isError: true };
|
|
39745
40126
|
}
|
|
39746
40127
|
try {
|
|
39747
|
-
|
|
40128
|
+
fs48.unlinkSync(audioPath);
|
|
39748
40129
|
} catch {
|
|
39749
40130
|
}
|
|
39750
40131
|
const content = opts.caption || `\u{1F399}\uFE0F \u8BED\u97F3\u6D88\u606F\uFF08${text.length} \u5B57\uFF09`;
|
|
@@ -39773,13 +40154,13 @@ async function sendVoiceToMulticaIssue(text, issueId, opts) {
|
|
|
39773
40154
|
}
|
|
39774
40155
|
if (!ok) throw new Error(`unexpected CLI output: ${String(stdout).slice(0, 200)}`);
|
|
39775
40156
|
try {
|
|
39776
|
-
|
|
40157
|
+
fs48.unlinkSync(cwdCopy);
|
|
39777
40158
|
} catch {
|
|
39778
40159
|
}
|
|
39779
40160
|
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)` };
|
|
39780
40161
|
} catch (e) {
|
|
39781
40162
|
try {
|
|
39782
|
-
|
|
40163
|
+
fs48.unlinkSync(cwdCopy);
|
|
39783
40164
|
} catch {
|
|
39784
40165
|
}
|
|
39785
40166
|
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 };
|
|
@@ -39896,10 +40277,10 @@ registry.register({
|
|
|
39896
40277
|
audioPath = await compressWav(audioPath);
|
|
39897
40278
|
}
|
|
39898
40279
|
}
|
|
39899
|
-
const ext =
|
|
40280
|
+
const ext = path47.extname(audioPath).toLowerCase();
|
|
39900
40281
|
const mimeMap = { ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".ogg": "audio/ogg", ".silk": "audio/silk" };
|
|
39901
40282
|
const mimeType = mimeMap[ext] || "audio/mpeg";
|
|
39902
|
-
const sizeKB =
|
|
40283
|
+
const sizeKB = fs48.statSync(audioPath).size / 1024;
|
|
39903
40284
|
try {
|
|
39904
40285
|
await mgr.sendFile(resolvedChannel, target, caption, {
|
|
39905
40286
|
path: audioPath,
|
|
@@ -39908,7 +40289,7 @@ registry.register({
|
|
|
39908
40289
|
...voiceDurationSec !== void 0 ? { voiceDurationSec } : {}
|
|
39909
40290
|
});
|
|
39910
40291
|
try {
|
|
39911
|
-
|
|
40292
|
+
fs48.unlinkSync(audioPath);
|
|
39912
40293
|
} catch {
|
|
39913
40294
|
}
|
|
39914
40295
|
return { content: `Voice sent! (${actualEngine}, ${sizeKB.toFixed(0)}KB, ${resolvedChannel})` };
|
|
@@ -39926,8 +40307,8 @@ registry.register({
|
|
|
39926
40307
|
init_live();
|
|
39927
40308
|
init_registry();
|
|
39928
40309
|
init_well_formed();
|
|
39929
|
-
import * as
|
|
39930
|
-
import * as
|
|
40310
|
+
import * as fs49 from "node:fs";
|
|
40311
|
+
import * as path48 from "node:path";
|
|
39931
40312
|
function getProxyDispatcher2() {
|
|
39932
40313
|
const cfg = liveConfig.all();
|
|
39933
40314
|
const proxy = cfg.providers?.xai?.proxy;
|
|
@@ -39941,7 +40322,7 @@ function getProxyDispatcher2() {
|
|
|
39941
40322
|
}
|
|
39942
40323
|
function dimsOf(p) {
|
|
39943
40324
|
try {
|
|
39944
|
-
const b =
|
|
40325
|
+
const b = fs49.readFileSync(p);
|
|
39945
40326
|
let w = 0, h = 0;
|
|
39946
40327
|
if (b.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
|
|
39947
40328
|
w = b.readUInt32BE(16);
|
|
@@ -40064,9 +40445,9 @@ function registerPortrait(relPath, name) {
|
|
|
40064
40445
|
const cur = cfg?.tools?.my_selfie?.references || cfg?.my_selfie?.references || [];
|
|
40065
40446
|
const next = Array.isArray(cur) ? cur.filter((r) => r?.name !== name) : [];
|
|
40066
40447
|
next.unshift({ name, path: relPath });
|
|
40067
|
-
const
|
|
40068
|
-
void liveConfig.set(
|
|
40069
|
-
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${
|
|
40448
|
+
const path61 = cfg?.tools?.my_selfie ? "tools.my_selfie.references" : "my_selfie.references";
|
|
40449
|
+
void liveConfig.set(path61, next);
|
|
40450
|
+
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${path61}[0] = ${relPath}`);
|
|
40070
40451
|
} catch (e) {
|
|
40071
40452
|
console.warn(`[my-selfie] \u5199\u5165 references \u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u672C\u6B21\u51FA\u56FE\uFF09: ${e.message}`);
|
|
40072
40453
|
}
|
|
@@ -40347,10 +40728,10 @@ registry.register({
|
|
|
40347
40728
|
const REFERENCES = getReferences(ctx);
|
|
40348
40729
|
const refName = args2.reference || "default";
|
|
40349
40730
|
const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
|
|
40350
|
-
let refPath =
|
|
40731
|
+
let refPath = path48.join(ctx.workspace, refEntry.p);
|
|
40351
40732
|
const adoptImg = args2.referenceImage?.trim();
|
|
40352
40733
|
if (adoptImg) {
|
|
40353
|
-
if (!
|
|
40734
|
+
if (!fs49.existsSync(adoptImg)) {
|
|
40354
40735
|
return { content: `\u627E\u4E0D\u5230\u90A3\u5F20\u56FE\uFF1A${adoptImg}`, isError: true };
|
|
40355
40736
|
}
|
|
40356
40737
|
const acheck = dimsOf(adoptImg);
|
|
@@ -40361,16 +40742,16 @@ registry.register({
|
|
|
40361
40742
|
isError: true
|
|
40362
40743
|
};
|
|
40363
40744
|
}
|
|
40364
|
-
const adir =
|
|
40365
|
-
if (!
|
|
40366
|
-
const aext = (
|
|
40745
|
+
const adir = path48.join(ctx.workspace, "images");
|
|
40746
|
+
if (!fs49.existsSync(adir)) fs49.mkdirSync(adir, { recursive: true });
|
|
40747
|
+
const aext = (path48.extname(adoptImg) || ".png").toLowerCase();
|
|
40367
40748
|
const afname = `portrait_${Date.now()}${aext}`;
|
|
40368
|
-
|
|
40749
|
+
fs49.copyFileSync(adoptImg, path48.join(adir, afname));
|
|
40369
40750
|
registerPortrait(`images/${afname}`, refName);
|
|
40370
|
-
refPath =
|
|
40751
|
+
refPath = path48.join(adir, afname);
|
|
40371
40752
|
console.log(`[my-selfie] \u91C7\u7EB3\u73B0\u6210\u56FE\u5F53\u5F62\u8C61\uFF1Aimages/${afname}${acheck ? ` (${acheck})` : ""}`);
|
|
40372
40753
|
}
|
|
40373
|
-
if (provider !== "autodl" && !
|
|
40754
|
+
if (provider !== "autodl" && !fs49.existsSync(refPath)) {
|
|
40374
40755
|
const cfgAll = liveConfig.all();
|
|
40375
40756
|
const portraitDesc = resolvePortraitDesc(
|
|
40376
40757
|
args2.portrait,
|
|
@@ -40395,16 +40776,16 @@ registry.register({
|
|
|
40395
40776
|
} catch (e) {
|
|
40396
40777
|
return { content: `\u5851\u9020\u5F62\u8C61\u5931\u8D25\uFF1A${e.message}`, isError: true };
|
|
40397
40778
|
}
|
|
40398
|
-
const pdir =
|
|
40399
|
-
if (!
|
|
40779
|
+
const pdir = path48.join(ctx.workspace, "images");
|
|
40780
|
+
if (!fs49.existsSync(pdir)) fs49.mkdirSync(pdir, { recursive: true });
|
|
40400
40781
|
const pfname = `portrait_${Date.now()}.png`;
|
|
40401
|
-
|
|
40782
|
+
fs49.writeFileSync(path48.join(pdir, pfname), portraitBuf);
|
|
40402
40783
|
registerPortrait(`images/${pfname}`, refName);
|
|
40403
|
-
refPath =
|
|
40784
|
+
refPath = path48.join(pdir, pfname);
|
|
40404
40785
|
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u5B58 images/${pfname}\uFF0C\u7EE7\u7EED\u8FD9\u6B21\u81EA\u62CD`);
|
|
40405
40786
|
}
|
|
40406
40787
|
const resolution = args2.resolution || DEFAULT_RESOLUTION;
|
|
40407
|
-
const refB64 =
|
|
40788
|
+
const refB64 = fs49.existsSync(refPath) ? fs49.readFileSync(refPath).toString("base64") : "";
|
|
40408
40789
|
let imageBuffer;
|
|
40409
40790
|
try {
|
|
40410
40791
|
if (provider === "autodl") {
|
|
@@ -40420,7 +40801,7 @@ registry.register({
|
|
|
40420
40801
|
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 };
|
|
40421
40802
|
}
|
|
40422
40803
|
const model = cfgAll?.my_selfie?.relayModel || cfgAll?.tools?.my_selfie?.relayModel || "engine7-img-free";
|
|
40423
|
-
const ext =
|
|
40804
|
+
const ext = path48.extname(refPath).toLowerCase();
|
|
40424
40805
|
const mime = ext === ".png" ? "image/png" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
40425
40806
|
imageBuffer = await generateWithRelay(refB64, fullPrompt, mime, relay.baseUrl, relay.apiKey, model);
|
|
40426
40807
|
} else {
|
|
@@ -40429,11 +40810,11 @@ registry.register({
|
|
|
40429
40810
|
} catch (err) {
|
|
40430
40811
|
return { content: `Selfie generation failed: ${err.message}`, isError: true };
|
|
40431
40812
|
}
|
|
40432
|
-
const imagesDir =
|
|
40433
|
-
if (!
|
|
40813
|
+
const imagesDir = path48.join(ctx.workspace, "images");
|
|
40814
|
+
if (!fs49.existsSync(imagesDir)) fs49.mkdirSync(imagesDir, { recursive: true });
|
|
40434
40815
|
const filename = `selfie_${Date.now()}.jpg`;
|
|
40435
|
-
const outputPath =
|
|
40436
|
-
|
|
40816
|
+
const outputPath = path48.join(imagesDir, filename);
|
|
40817
|
+
fs49.writeFileSync(outputPath, imageBuffer);
|
|
40437
40818
|
const mgr = ctx.channelManager;
|
|
40438
40819
|
if (mgr) {
|
|
40439
40820
|
const resolvedChannel = ctx.channel || "feishu";
|
|
@@ -40444,11 +40825,11 @@ registry.register({
|
|
|
40444
40825
|
mimeType: "image/jpeg"
|
|
40445
40826
|
});
|
|
40446
40827
|
} catch (err) {
|
|
40447
|
-
return { content: `Selfie generated but send failed: ${err.message}. Image: ${
|
|
40828
|
+
return { content: `Selfie generated but send failed: ${err.message}. Image: ${path48.resolve(outputPath)}`, isError: false };
|
|
40448
40829
|
}
|
|
40449
40830
|
return { content: `Selfie sent! Mode: ${mode}, Provider: ${provider}, Ref: ${refEntry.name}` };
|
|
40450
40831
|
}
|
|
40451
|
-
return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${
|
|
40832
|
+
return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path48.resolve(outputPath)}` };
|
|
40452
40833
|
},
|
|
40453
40834
|
isConcurrencySafe: () => false,
|
|
40454
40835
|
interruptBehavior: () => "block",
|
|
@@ -41000,8 +41381,8 @@ ${lines.join("\n")}` };
|
|
|
41000
41381
|
init_registry();
|
|
41001
41382
|
init_live();
|
|
41002
41383
|
init_clock();
|
|
41003
|
-
import * as
|
|
41004
|
-
import * as
|
|
41384
|
+
import * as fs50 from "node:fs";
|
|
41385
|
+
import * as path49 from "node:path";
|
|
41005
41386
|
import { exec as exec2, spawn as spawn6 } from "node:child_process";
|
|
41006
41387
|
import { promisify as promisify4 } from "node:util";
|
|
41007
41388
|
var execAsync = promisify4(exec2);
|
|
@@ -41010,9 +41391,9 @@ function getServices() {
|
|
|
41010
41391
|
}
|
|
41011
41392
|
function serviceLogPath(fileName) {
|
|
41012
41393
|
const stateDir = String(liveConfig.get("stateDir") ?? "");
|
|
41013
|
-
const logDir =
|
|
41014
|
-
if (!
|
|
41015
|
-
return
|
|
41394
|
+
const logDir = path49.join(stateDir, "logs");
|
|
41395
|
+
if (!fs50.existsSync(logDir)) fs50.mkdirSync(logDir, { recursive: true });
|
|
41396
|
+
return path49.join(logDir, fileName);
|
|
41016
41397
|
}
|
|
41017
41398
|
async function runCmd(cmd, timeoutMs = 15e3) {
|
|
41018
41399
|
try {
|
|
@@ -41331,16 +41712,16 @@ var EXIT_PLAN_MODE_TOOL_NAME = "ExitPlanMode";
|
|
|
41331
41712
|
init_planModeState();
|
|
41332
41713
|
|
|
41333
41714
|
// src/utils/plans.ts
|
|
41334
|
-
import * as
|
|
41335
|
-
import * as
|
|
41715
|
+
import * as fs51 from "node:fs";
|
|
41716
|
+
import * as path50 from "node:path";
|
|
41336
41717
|
import * as crypto7 from "node:crypto";
|
|
41337
41718
|
var MAX_SLUG_RETRIES = 10;
|
|
41338
41719
|
function generateSlug() {
|
|
41339
41720
|
return crypto7.randomBytes(4).toString("hex");
|
|
41340
41721
|
}
|
|
41341
41722
|
function getPlansDirectory(stateDir) {
|
|
41342
|
-
const plansDir =
|
|
41343
|
-
|
|
41723
|
+
const plansDir = path50.join(stateDir, "plans");
|
|
41724
|
+
fs51.mkdirSync(plansDir, { recursive: true });
|
|
41344
41725
|
return plansDir;
|
|
41345
41726
|
}
|
|
41346
41727
|
var planSlugCache = /* @__PURE__ */ new Map();
|
|
@@ -41350,8 +41731,8 @@ function getPlanSlug(sessionId, stateDir) {
|
|
|
41350
41731
|
const plansDir = getPlansDirectory(stateDir);
|
|
41351
41732
|
for (let i = 0; i < MAX_SLUG_RETRIES; i++) {
|
|
41352
41733
|
slug = generateSlug();
|
|
41353
|
-
const filePath =
|
|
41354
|
-
if (!
|
|
41734
|
+
const filePath = path50.join(plansDir, `${slug}.md`);
|
|
41735
|
+
if (!fs51.existsSync(filePath)) {
|
|
41355
41736
|
break;
|
|
41356
41737
|
}
|
|
41357
41738
|
}
|
|
@@ -41362,21 +41743,21 @@ function getPlanSlug(sessionId, stateDir) {
|
|
|
41362
41743
|
function getPlanFilePath(sessionId, stateDir, agentId) {
|
|
41363
41744
|
const slug = getPlanSlug(sessionId, stateDir);
|
|
41364
41745
|
if (!agentId) {
|
|
41365
|
-
return
|
|
41746
|
+
return path50.join(getPlansDirectory(stateDir), `${slug}.md`);
|
|
41366
41747
|
}
|
|
41367
|
-
return
|
|
41748
|
+
return path50.join(getPlansDirectory(stateDir), `${slug}-agent-${agentId}.md`);
|
|
41368
41749
|
}
|
|
41369
41750
|
function getPlan(sessionId, stateDir, agentId) {
|
|
41370
41751
|
const filePath = getPlanFilePath(sessionId, stateDir, agentId);
|
|
41371
41752
|
try {
|
|
41372
|
-
return
|
|
41753
|
+
return fs51.readFileSync(filePath, "utf-8");
|
|
41373
41754
|
} catch {
|
|
41374
41755
|
return null;
|
|
41375
41756
|
}
|
|
41376
41757
|
}
|
|
41377
41758
|
function writePlan(sessionId, stateDir, content, agentId) {
|
|
41378
41759
|
const filePath = getPlanFilePath(sessionId, stateDir, agentId);
|
|
41379
|
-
|
|
41760
|
+
fs51.writeFileSync(filePath, content, "utf-8");
|
|
41380
41761
|
return filePath;
|
|
41381
41762
|
}
|
|
41382
41763
|
|
|
@@ -42017,8 +42398,8 @@ init_features2();
|
|
|
42017
42398
|
init_agent_registry();
|
|
42018
42399
|
|
|
42019
42400
|
// src/commands/work.ts
|
|
42020
|
-
import * as
|
|
42021
|
-
import * as
|
|
42401
|
+
import * as fs56 from "node:fs";
|
|
42402
|
+
import * as path56 from "node:path";
|
|
42022
42403
|
registerCommand({
|
|
42023
42404
|
name: "work",
|
|
42024
42405
|
description: "\u5DE5\u4F5C\u6A21\u5F0F\uFF1A\u65E0\u53C2\u770B\u72B6\u6001\uFF0Con=\u5F3A\u5236work\uFF0Coff=\u5F3A\u5236emotion\uFF0Cauto=\u81EA\u52A8\u5224\u5B9A",
|
|
@@ -42031,20 +42412,20 @@ registerCommand({
|
|
|
42031
42412
|
],
|
|
42032
42413
|
handler: async (ctx, deps) => {
|
|
42033
42414
|
const mode = (ctx.args.mode || "").trim().toLowerCase();
|
|
42034
|
-
const modeFile =
|
|
42415
|
+
const modeFile = path56.join(deps.config.workspace, ".work-mode");
|
|
42035
42416
|
if (mode === "on" || mode === "off") {
|
|
42036
|
-
|
|
42417
|
+
fs56.writeFileSync(modeFile, mode, "utf8");
|
|
42037
42418
|
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");
|
|
42038
42419
|
} else if (mode === "auto") {
|
|
42039
42420
|
try {
|
|
42040
|
-
|
|
42421
|
+
fs56.unlinkSync(modeFile);
|
|
42041
42422
|
} catch {
|
|
42042
42423
|
}
|
|
42043
42424
|
await ctx.reply("\u5DE5\u4F5C\u6A21\u5F0F\u5DF2\u6062\u590D **auto**\uFF08emotion_state_machine \u9010\u6761\u81EA\u52A8\u5224\u5B9A\uFF09");
|
|
42044
42425
|
} else {
|
|
42045
42426
|
let cur = "auto";
|
|
42046
42427
|
try {
|
|
42047
|
-
cur =
|
|
42428
|
+
cur = fs56.readFileSync(modeFile, "utf8").trim() || "auto";
|
|
42048
42429
|
} catch {
|
|
42049
42430
|
}
|
|
42050
42431
|
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";
|
|
@@ -42514,8 +42895,8 @@ ${err.stack}`);
|
|
|
42514
42895
|
});
|
|
42515
42896
|
|
|
42516
42897
|
// src/commands/model.ts
|
|
42517
|
-
import * as
|
|
42518
|
-
import * as
|
|
42898
|
+
import * as fs57 from "node:fs";
|
|
42899
|
+
import * as path57 from "node:path";
|
|
42519
42900
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
42520
42901
|
function collectModels(config2) {
|
|
42521
42902
|
const aliases = config2.modelAliases || {};
|
|
@@ -42547,11 +42928,11 @@ function resolveConfigWritePath(config2, label) {
|
|
|
42547
42928
|
const configPath2 = config2._configFilePath;
|
|
42548
42929
|
if (!configPath2) return void 0;
|
|
42549
42930
|
let writePath = configPath2;
|
|
42550
|
-
if (configPath2 && !
|
|
42931
|
+
if (configPath2 && !fs57.existsSync(configPath2)) {
|
|
42551
42932
|
const __pFile = fileURLToPath8(import.meta.url);
|
|
42552
|
-
const __pDir =
|
|
42553
|
-
const altPath =
|
|
42554
|
-
if (
|
|
42933
|
+
const __pDir = path57.dirname(__pFile);
|
|
42934
|
+
const altPath = path57.join(path57.resolve(__pDir, "../configs"), path57.basename(configPath2));
|
|
42935
|
+
if (fs57.existsSync(altPath)) {
|
|
42555
42936
|
console.warn(`[${label}] Config not found at ${configPath2}, falling back to ${altPath}`);
|
|
42556
42937
|
writePath = altPath;
|
|
42557
42938
|
}
|
|
@@ -42706,14 +43087,14 @@ Use full ref like \`/primary ${candidates[0].ref}\``);
|
|
|
42706
43087
|
return;
|
|
42707
43088
|
}
|
|
42708
43089
|
try {
|
|
42709
|
-
const raw = await
|
|
43090
|
+
const raw = await fs57.promises.readFile(writePath, "utf-8");
|
|
42710
43091
|
const cfg = JSON.parse(raw);
|
|
42711
43092
|
if (!cfg.agents?.defaults?.model) {
|
|
42712
43093
|
await ctx.reply(`\u26A0\uFE0F Config structure mismatch: agents.defaults.model not found`);
|
|
42713
43094
|
return;
|
|
42714
43095
|
}
|
|
42715
43096
|
cfg.agents.defaults.model.primary = target;
|
|
42716
|
-
await
|
|
43097
|
+
await fs57.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
42717
43098
|
console.log(`[primary] Persisted primary=${target} to ${writePath}`);
|
|
42718
43099
|
await ctx.reply(`\u2705 Primary model set to **${target}** (${candidates[0].name})
|
|
42719
43100
|
Written to config, hot-reloading provider chain now.`);
|
|
@@ -42760,7 +43141,7 @@ Use full ref like \`/vision-primary ${candidates[0].ref}\``);
|
|
|
42760
43141
|
return;
|
|
42761
43142
|
}
|
|
42762
43143
|
try {
|
|
42763
|
-
const raw = await
|
|
43144
|
+
const raw = await fs57.promises.readFile(writePath, "utf-8");
|
|
42764
43145
|
const cfg = JSON.parse(raw);
|
|
42765
43146
|
if (!cfg.agents?.defaults?.model) {
|
|
42766
43147
|
await ctx.reply(`\u26A0\uFE0F Config structure mismatch: agents.defaults.model not found`);
|
|
@@ -42768,12 +43149,12 @@ Use full ref like \`/vision-primary ${candidates[0].ref}\``);
|
|
|
42768
43149
|
}
|
|
42769
43150
|
if (input === "auto" || input === "reset") {
|
|
42770
43151
|
delete cfg.agents.defaults.model.vision;
|
|
42771
|
-
await
|
|
43152
|
+
await fs57.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
42772
43153
|
console.log(`[vision-primary] Cleared vision primary in ${writePath}`);
|
|
42773
43154
|
await ctx.reply(`\u2705 Vision primary cleared (auto). Written to config. Hot-reload will apply.`);
|
|
42774
43155
|
} else {
|
|
42775
43156
|
cfg.agents.defaults.model.vision = target;
|
|
42776
|
-
await
|
|
43157
|
+
await fs57.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
42777
43158
|
console.log(`[vision-primary] Persisted vision=${target} to ${writePath}`);
|
|
42778
43159
|
await ctx.reply(`\u2705 Vision primary set to **${target}** (${candidates[0].name})
|
|
42779
43160
|
Written to config. Hot-reload will apply.`);
|
|
@@ -43181,7 +43562,7 @@ async function startEngine(config2, opts) {
|
|
|
43181
43562
|
const { initSessionMemory: initSessionMemory2 } = await Promise.resolve().then(() => (init_sessionMemory(), sessionMemory_exports));
|
|
43182
43563
|
initSessionMemory2({
|
|
43183
43564
|
workspace: config2.workspace,
|
|
43184
|
-
stateDir:
|
|
43565
|
+
stateDir: path60.join(config2.stateDir, "session-memory"),
|
|
43185
43566
|
provider,
|
|
43186
43567
|
model: config2.provider.modelId || config2.model || "deepseek-v4-flash",
|
|
43187
43568
|
features: config2.profile.features
|
|
@@ -43757,10 +44138,12 @@ if (args[0] === "features") {
|
|
|
43757
44138
|
const status = getLicenseStatus2(stateDir);
|
|
43758
44139
|
const allFeatures = listFeatures2();
|
|
43759
44140
|
console.log("");
|
|
43760
|
-
if (status.
|
|
44141
|
+
if (status.licenseId) {
|
|
43761
44142
|
console.log(`\u{1F4C4} License: ${status.licenseId} | ${status.customer} | \u5230\u671F: ${status.expiresAt}`);
|
|
44143
|
+
} else if (status.openMode) {
|
|
44144
|
+
console.log("\u{1F4C4} License: \u65E0 \u2014\u2014 **\u5F00\u653E\u6A21\u5F0F**\uFF0C\u5168\u90E8 feature \u6FC0\u6D3B");
|
|
43762
44145
|
} else {
|
|
43763
|
-
console.log("\u{1F4C4} License: \u65E0 (\u57FA\u7840\u5F15\u64CE\u6A21\u5F0F)");
|
|
44146
|
+
console.log("\u{1F4C4} License: \u65E0 (\u57FA\u7840\u5F15\u64CE\u6A21\u5F0F \u2014 \u914D\u4E86 devMode=false)");
|
|
43764
44147
|
}
|
|
43765
44148
|
console.log("");
|
|
43766
44149
|
for (const f of allFeatures) {
|
|
@@ -43773,15 +44156,10 @@ if (args[0] === "features") {
|
|
|
43773
44156
|
} else if (info && !info.active) {
|
|
43774
44157
|
console.log(`\u23F0 ${f.name} \u2014 \u5DF2\u8FC7\u671F${info.expiresAt ? ` (${info.expiresAt})` : ""}`);
|
|
43775
44158
|
} else {
|
|
43776
|
-
console.log(`\u274C ${f.name} \u2014 \u672A\
|
|
44159
|
+
console.log(`\u274C ${f.name} \u2014 \u672A\u542F\u7528 (devMode=false \u4E14\u65E0 license)`);
|
|
43777
44160
|
}
|
|
43778
44161
|
} else {
|
|
43779
|
-
|
|
43780
|
-
if (enabled) {
|
|
43781
|
-
console.log(`\u2705 ${f.name} (always)`);
|
|
43782
|
-
} else {
|
|
43783
|
-
console.log(`\u2B1C ${f.name} \u2014 \u672A\u542F\u7528`);
|
|
43784
|
-
}
|
|
44162
|
+
console.log(`\u26AA ${f.name} \u2014 \u4E0E license \u65E0\u5173\uFF08\u5F00\u4E0D\u5F00\u770B config\uFF09`);
|
|
43785
44163
|
}
|
|
43786
44164
|
}
|
|
43787
44165
|
console.log("");
|