engine7 7.1.96 → 7.1.106
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 +1808 -1339
- package/dist/engine-startup.mjs +1713 -1094
- package/dist/main.mjs +1713 -1094
- package/package.json +87 -85
- package/templates/workspace/scripts/emotion_state_machine.mjs +6 -6
package/dist/cli.mjs
CHANGED
|
@@ -413,14 +413,14 @@ var init_live = __esm({
|
|
|
413
413
|
}
|
|
414
414
|
/** 把改动写回 config 文件对应段(找不到文件路径则只内存生效) */
|
|
415
415
|
async persistToFile(dotPath, val) {
|
|
416
|
-
const
|
|
416
|
+
const fs33 = await import("node:fs");
|
|
417
417
|
const cfgPath = this.current?._configFilePath;
|
|
418
|
-
if (!cfgPath || !
|
|
418
|
+
if (!cfgPath || !fs33.existsSync(cfgPath)) {
|
|
419
419
|
console.warn(`[liveConfig] set: no config file path, in-memory only (${dotPath})`);
|
|
420
420
|
return;
|
|
421
421
|
}
|
|
422
422
|
try {
|
|
423
|
-
const raw = JSON.parse(
|
|
423
|
+
const raw = JSON.parse(fs33.readFileSync(cfgPath, "utf-8"));
|
|
424
424
|
const keys = dotPath.split(".");
|
|
425
425
|
let o = raw;
|
|
426
426
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -428,7 +428,7 @@ var init_live = __esm({
|
|
|
428
428
|
o = o[keys[i]];
|
|
429
429
|
}
|
|
430
430
|
o[keys[keys.length - 1]] = val;
|
|
431
|
-
|
|
431
|
+
fs33.writeFileSync(cfgPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
|
|
432
432
|
console.log(`[liveConfig] persisted ${dotPath} = ${JSON.stringify(val)} to ${cfgPath}`);
|
|
433
433
|
markSelfWrite();
|
|
434
434
|
} catch (e) {
|
|
@@ -625,114 +625,536 @@ var init_clock = __esm({
|
|
|
625
625
|
}
|
|
626
626
|
});
|
|
627
627
|
|
|
628
|
-
// src/config
|
|
629
|
-
import
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
return process.env.HOME || process.env.USERPROFILE || ".";
|
|
628
|
+
// src/cli-init-config.ts
|
|
629
|
+
import * as path2 from "node:path";
|
|
630
|
+
function isFreeTier(v) {
|
|
631
|
+
return v.freeTier === true || v.primaryProvider === "7yi";
|
|
633
632
|
}
|
|
634
|
-
function
|
|
635
|
-
|
|
636
|
-
const candidates = [
|
|
637
|
-
join3(home, ".engine7-secrets", `${configName}.env`),
|
|
638
|
-
join3(stateDir, "configs", `.env.${configName}`),
|
|
639
|
-
join3(stateDir, "configs", ".env")
|
|
640
|
-
];
|
|
641
|
-
for (const p of candidates) {
|
|
642
|
-
if (!existsSync3(p)) continue;
|
|
643
|
-
for (const line of readFileSync3(p, "utf-8").split("\n")) {
|
|
644
|
-
const t = line.trim();
|
|
645
|
-
if (!t || t.startsWith("#")) continue;
|
|
646
|
-
const eq = t.indexOf("=");
|
|
647
|
-
if (eq <= 0) continue;
|
|
648
|
-
const k = t.slice(0, eq).trim();
|
|
649
|
-
const v = t.slice(eq + 1).trim();
|
|
650
|
-
const cur = process.env[k];
|
|
651
|
-
if (k && (cur === void 0 || cur === "")) process.env[k] = v;
|
|
652
|
-
}
|
|
653
|
-
return p;
|
|
654
|
-
}
|
|
655
|
-
return null;
|
|
633
|
+
function resolveRelayUrl(relayArg, env = process.env) {
|
|
634
|
+
return (relayArg || "").trim() || (env.E7_RELAY_URL || "").trim() || FREE_TIER_RELAY_URL;
|
|
656
635
|
}
|
|
657
|
-
function
|
|
658
|
-
const
|
|
659
|
-
|
|
660
|
-
mkdirSync2(dir, { recursive: true });
|
|
661
|
-
const file2 = join3(dir, `${configName}.env`);
|
|
662
|
-
const lines = [];
|
|
663
|
-
if (existsSync3(file2)) {
|
|
664
|
-
for (const line of readFileSync3(file2, "utf8").split(/\r?\n/)) {
|
|
665
|
-
if (new RegExp(`^\\s*${envKey}\\s*=`).test(line)) continue;
|
|
666
|
-
lines.push(line);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
while (lines.length && lines[lines.length - 1] === "") lines.pop();
|
|
670
|
-
lines.push(`${envKey}=${value}`);
|
|
671
|
-
writeFileSync2(file2, lines.join("\n") + "\n", { mode: 384 });
|
|
672
|
-
try {
|
|
673
|
-
chmodSync(file2, 384);
|
|
674
|
-
} catch {
|
|
675
|
-
}
|
|
676
|
-
return file2;
|
|
636
|
+
function modelIdOf(ref) {
|
|
637
|
+
const i = ref.indexOf("/");
|
|
638
|
+
return i >= 0 ? ref.slice(i + 1) : ref;
|
|
677
639
|
}
|
|
678
|
-
|
|
679
|
-
"
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
640
|
+
function menuProviderOf(providerId) {
|
|
641
|
+
return PROVIDER_DEFS[providerId] ? providerId : "custom";
|
|
642
|
+
}
|
|
643
|
+
function resolveProviderTarget(input) {
|
|
644
|
+
const isCustom = !PROVIDER_DEFS[input.menuProvider];
|
|
645
|
+
const providerId = isCustom ? (input.customProviderId || "").trim() || "custom" : input.menuProvider;
|
|
646
|
+
const rawRef = (input.modelRef || "").trim();
|
|
647
|
+
const modelId = (rawRef.includes("/") ? modelIdOf(rawRef) : rawRef) || (input.modelId || "").trim();
|
|
648
|
+
return { providerId, modelId, modelRef: modelId ? `${providerId}/${modelId}` : "" };
|
|
649
|
+
}
|
|
650
|
+
function visionOf(ref, catalog) {
|
|
651
|
+
const hit = catalog?.find((m) => m.id === modelIdOf(ref));
|
|
652
|
+
if (hit && typeof hit.vision === "boolean") return hit.vision;
|
|
653
|
+
return VISION_CAPABLE[ref];
|
|
654
|
+
}
|
|
655
|
+
function builtinVisionModels(providerId) {
|
|
656
|
+
const def = PROVIDER_DEFS[providerId];
|
|
657
|
+
if (!def) return [];
|
|
658
|
+
return def.models.filter((m) => (m.input || []).includes("image")).map((m) => `${providerId}/${m.id}`);
|
|
659
|
+
}
|
|
660
|
+
function resolveVisionModel(providerId, primaryModel, catalog) {
|
|
661
|
+
if (visionOf(primaryModel, catalog) !== false) return primaryModel;
|
|
662
|
+
const candidates = catalog?.length ? catalog.filter((m) => m.vision === true).map((m) => `${providerId}/${m.id}`) : builtinVisionModels(providerId);
|
|
663
|
+
return candidates[0] || "";
|
|
664
|
+
}
|
|
665
|
+
function generateConfig(v) {
|
|
666
|
+
const workspace = path2.join(v.stateDir, "workspace").replace(/\\/g, "/");
|
|
667
|
+
const config = {
|
|
668
|
+
schemaVersion: SCHEMA_VERSION,
|
|
669
|
+
// 全引擎统一时区(design §3.1)。真实 init 走 getDefaultValues() 探测本机,
|
|
670
|
+
// 模板走 {{TIMEZONE}} 占位符。
|
|
671
|
+
timezone: v.timezone,
|
|
672
|
+
stateDir: v.stateDir.replace(/\\/g, "/"),
|
|
673
|
+
mediaDir: `${v.stateDir.replace(/\\/g, "/")}/media/inbound`,
|
|
674
|
+
models: {
|
|
675
|
+
providers: {}
|
|
691
676
|
},
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
677
|
+
// 0917:my_eyes(看图工具)读的是 tools.my_eyes.model,不是 tools.vision——
|
|
678
|
+
// 后者是模板里遗留的死键(全仓零读者),已删。visionModel 解析不出就不写这个块。
|
|
679
|
+
tools: {
|
|
680
|
+
...v.tavilyKey ? { tavily: { apiKey: v.tavilyKey } } : {},
|
|
681
|
+
...v.visionModel ? { my_eyes: { model: v.visionModel } } : {},
|
|
682
|
+
// 自拍(§4.3):免费档必须显式写 provider:'relay'。不写的话 getProvider()
|
|
683
|
+
// 会落回默认值 'fal',而免费装机没有 fal key——第一次自拍就失败。
|
|
684
|
+
// 非免费档只在用户真给了形象时才写这个块,且不替他决定 provider。
|
|
685
|
+
...isFreeTier(v) || v.selfiePhotoRelPath || v.selfiePortraitDesc ? {
|
|
686
|
+
my_selfie: {
|
|
687
|
+
...isFreeTier(v) ? { provider: "relay" } : {},
|
|
688
|
+
...v.selfiePhotoRelPath ? { references: [{ name: "portrait", path: v.selfiePhotoRelPath }] } : {},
|
|
689
|
+
// 只在没有照片时才写描述——两个都给的话以照片为准,写进去会让人以为
|
|
690
|
+
// 描述也生效了(冷启读的就是这个键)
|
|
691
|
+
...!v.selfiePhotoRelPath && v.selfiePortraitDesc ? { portraitDesc: v.selfiePortraitDesc } : {}
|
|
692
|
+
}
|
|
693
|
+
} : {}
|
|
695
694
|
},
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
695
|
+
session: { dmScope: "main", groupScope: "main" },
|
|
696
|
+
sandbox: { mode: "off" },
|
|
697
|
+
display: {
|
|
698
|
+
thinking: { enabled: false, emoji: "\u{1F4AD}", maxLen: 300 },
|
|
699
|
+
toolUse: { enabled: true, emoji: "\u{1F527}", displayMode: "raw", bashDisplayMode: "both" },
|
|
700
|
+
toolResult: { enabled: false },
|
|
701
|
+
reactions: { enabled: true, start: "\u{1F440}", done: "\u2705", error: "\u274C" },
|
|
702
|
+
preview: { enabled: true, agentName: v.agentName, color: "orange" },
|
|
703
|
+
typing: { enabled: true }
|
|
699
704
|
},
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
705
|
+
agents: {
|
|
706
|
+
defaults: {
|
|
707
|
+
workspace,
|
|
708
|
+
name: v.agentName,
|
|
709
|
+
// 人设落 config 是为了**复跑能读回来**(loadExistingValues)。引擎运行时不读它
|
|
710
|
+
// ——SOUL.md 已经按人设渲染好了。不写的话复跑 init 会退回 'assistant',
|
|
711
|
+
// 而 execute() 用模板重写 SOUL.md,用户选的"情感陪伴"重装一次就变回通用助手。
|
|
712
|
+
persona: v.persona,
|
|
713
|
+
model: {
|
|
714
|
+
primary: v.primaryModel,
|
|
715
|
+
// 条件写入:vision 引用解析不出时省略键(loader 按 if (visionRef) 处理缺失)
|
|
716
|
+
...v.visionModel ? { vision: v.visionModel } : {}
|
|
717
|
+
},
|
|
718
|
+
features: {
|
|
719
|
+
filesystem: true,
|
|
720
|
+
shell: true,
|
|
721
|
+
webSearch: true,
|
|
722
|
+
webFetch: true,
|
|
723
|
+
memory: true,
|
|
724
|
+
topics: true,
|
|
725
|
+
"topic-extract": true,
|
|
726
|
+
"topic-recall": true,
|
|
727
|
+
cron: true,
|
|
728
|
+
agent: true,
|
|
729
|
+
agentTeams: true,
|
|
730
|
+
processOutput: "minimal",
|
|
731
|
+
interrupt: "command",
|
|
732
|
+
debounceMs: 5e3,
|
|
733
|
+
// 免费档的装机目标是「能对话、能看图、能自拍」,这两个 flag 默认 false
|
|
734
|
+
// (src/config/features.ts)。显式打开,它们是声明这两件事的地方。
|
|
735
|
+
...isFreeTier(v) ? { selfie: true, eyes: true } : {}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
703
738
|
},
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
739
|
+
channels: {
|
|
740
|
+
discord: {
|
|
741
|
+
enabled: v.discordEnabled,
|
|
742
|
+
dmPolicy: "pairing",
|
|
743
|
+
groupPolicy: "open",
|
|
744
|
+
accounts: { default: { token: v.discordToken } },
|
|
745
|
+
replyToMode: "all"
|
|
746
|
+
},
|
|
747
|
+
feishu: {
|
|
748
|
+
enabled: v.feishuEnabled,
|
|
749
|
+
appId: v.feishuAppId,
|
|
750
|
+
appSecret: v.feishuAppSecret,
|
|
751
|
+
connectionMode: "websocket",
|
|
752
|
+
dmPolicy: "pairing",
|
|
753
|
+
groupPolicy: "open"
|
|
754
|
+
},
|
|
755
|
+
...v.wechatEnabled && v.wechatToken ? {
|
|
756
|
+
wechat: {
|
|
757
|
+
enabled: true,
|
|
758
|
+
token: v.wechatToken,
|
|
759
|
+
accountId: v.wechatAccountId,
|
|
760
|
+
dmPolicy: "pairing",
|
|
761
|
+
group: { policy: "disabled" }
|
|
762
|
+
}
|
|
763
|
+
} : {},
|
|
764
|
+
// 0920 翀哥拍板:emotion 通道默认开启(含 liveSystemPrompt 加载 EMOTION.md)
|
|
765
|
+
// 即使没装 EMOTION.md,buildLightStablePrompt 也会硬编码"想 TA 就发"那段兜底
|
|
766
|
+
emotion: {
|
|
767
|
+
enabled: true,
|
|
768
|
+
liveSystemPrompt: {
|
|
769
|
+
files: ["SOUL.md", "EMOTION.md"]
|
|
770
|
+
}
|
|
771
|
+
}
|
|
707
772
|
},
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
773
|
+
api: { port: v.apiPort },
|
|
774
|
+
// 0909 npm 包默认开:inner-voice + proactive 是开箱即用的核心体验
|
|
775
|
+
// (不写 model → 都兜底主模型;proactive 领域/persona 可用 workspace/prompts/ 覆盖)
|
|
776
|
+
innerVoice: { enabled: true },
|
|
777
|
+
proactive: { enabled: true },
|
|
778
|
+
prompt: {
|
|
779
|
+
mode: "custom",
|
|
780
|
+
staticFiles: ["AGENTS.md", "USER.md", "MEMORY.md", "memory/distill-output.md"],
|
|
781
|
+
order: ["soul", "AGENTS.md", "prompts/contacts.md", "using-tools", "USER.md", "MEMORY.md", "memory/distill-output.md", "auto-memory-instructions"]
|
|
782
|
+
},
|
|
783
|
+
compaction: {
|
|
784
|
+
enabled: true,
|
|
785
|
+
bufferTokens: 23e3,
|
|
786
|
+
maxOutputTokens: 16384,
|
|
787
|
+
minReductionRatio: 0.3,
|
|
788
|
+
ruleBased: { enabled: true, essentialFields: [] },
|
|
789
|
+
memoryFlush: { enabled: true, forceFlushTranscriptBytes: "2.0mb" }
|
|
790
|
+
},
|
|
791
|
+
skills: {
|
|
792
|
+
enabled: true,
|
|
793
|
+
path: "skills"
|
|
711
794
|
}
|
|
712
795
|
};
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
796
|
+
if (!PROVIDER_DEFS[v.primaryProvider]) {
|
|
797
|
+
const models = v.existingModels?.length ? [...v.existingModels] : [];
|
|
798
|
+
const customModelId = v.customModelId || models[0]?.id || modelIdOf(v.primaryModel);
|
|
799
|
+
if (!customModelId) {
|
|
800
|
+
throw new Error(
|
|
801
|
+
`\u81EA\u5B9A\u4E49 provider "${v.primaryProvider}" \u6CA1\u6709\u6A21\u578B id\u2014\u2014init \u751F\u6210\u4E0D\u51FA\u53EF\u7528\u7684 config\u3002\u8BF7\u5728\u5411\u5BFC\u91CC\u586B\u300C\u6A21\u578B ID\u300D\uFF0C\u6216\u8865\u4E0A configs/main7.json \u91CC models.providers.${v.primaryProvider}.models[0].id\u3002`
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
if (models.length && !models.some((m) => m?.id === customModelId)) {
|
|
805
|
+
models.unshift({ id: customModelId, name: customModelId, reasoning: false, input: CUSTOM_DEFAULT_INPUT, contextWindow: 2e5, maxTokens: 8192 });
|
|
806
|
+
}
|
|
807
|
+
config.models.providers[v.primaryProvider] = {
|
|
808
|
+
baseUrl: (v.customBaseUrl || "").trim(),
|
|
809
|
+
api: v.customApi,
|
|
810
|
+
apiKey: "env:" + v.primaryProvider.toUpperCase().replace(/[^A-Z0-9_]/g, "_") + "_API_KEY",
|
|
811
|
+
models: models.length ? models : [
|
|
812
|
+
{ id: customModelId, name: customModelId, reasoning: false, input: CUSTOM_DEFAULT_INPUT, contextWindow: 2e5, maxTokens: 8192 }
|
|
813
|
+
]
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
if (PROVIDER_DEFS[v.primaryProvider]) {
|
|
817
|
+
const def = { ...PROVIDER_DEFS[v.primaryProvider] };
|
|
818
|
+
if (v.primaryProvider === "zhipu" && v.zhipuPlan === "token") {
|
|
819
|
+
def.baseUrl = "https://open.bigmodel.cn/api/paas/v4";
|
|
820
|
+
}
|
|
821
|
+
if (v.primaryProvider === "dashscope" && v.dashscopePlan === "token") {
|
|
822
|
+
def.baseUrl = "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1";
|
|
823
|
+
}
|
|
824
|
+
if (v.primaryProvider === "7yi" && (v.customBaseUrl || "").trim()) {
|
|
825
|
+
def.baseUrl = v.customBaseUrl.trim();
|
|
826
|
+
}
|
|
827
|
+
if (v.providerModels && v.providerModels.length > 0) {
|
|
828
|
+
def.models = v.providerModels.map((m) => ({
|
|
829
|
+
id: m.id,
|
|
830
|
+
name: m.name || m.id,
|
|
831
|
+
reasoning: m.reasoning ?? true,
|
|
832
|
+
// 没标 vision 的条目按「不支持」写 input(目录是权威),但 visionOf 那边对
|
|
833
|
+
// 「没标」仍按未知处理——若用户手输的型号恰好是主模型,vision 仍会宽复用它
|
|
834
|
+
input: m.vision ? ["text", "image"] : ["text"],
|
|
835
|
+
contextWindow: m.contextWindow ?? 2e5,
|
|
836
|
+
maxTokens: m.maxTokens ?? 8192
|
|
837
|
+
}));
|
|
838
|
+
}
|
|
839
|
+
const primaryModelId2 = modelIdOf(v.primaryModel);
|
|
840
|
+
if (primaryModelId2 && !def.models.some((m) => m?.id === primaryModelId2)) {
|
|
841
|
+
const old = (v.existingModels || []).find((m) => m?.id === primaryModelId2);
|
|
842
|
+
def.models = [...def.models, old || { id: primaryModelId2, name: primaryModelId2 }];
|
|
843
|
+
}
|
|
844
|
+
config.models.providers[v.primaryProvider] = {
|
|
845
|
+
...def,
|
|
846
|
+
apiKey: "env:" + v.primaryProvider.toUpperCase().replace(/[^A-Z0-9_]/g, "_") + "_API_KEY",
|
|
847
|
+
...def.baseUrl ? { baseUrl: def.baseUrl.trim() } : {}
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
const providerKeys = Object.keys(config.models.providers);
|
|
851
|
+
if (providerKeys.length === 0) {
|
|
852
|
+
throw new Error("\u6CA1\u6709\u751F\u6210\u4EFB\u4F55 provider\u2014\u2014\u68C0\u67E5 init \u7684 provider \u9009\u62E9\u4E0E\u6A21\u578B ID\u3002");
|
|
853
|
+
}
|
|
854
|
+
const primaryRef = String(config.agents.defaults.model.primary);
|
|
855
|
+
const primaryProviderId = primaryRef.split("/")[0];
|
|
856
|
+
if (!config.models.providers[primaryProviderId]) {
|
|
857
|
+
throw new Error(
|
|
858
|
+
`\u751F\u6210\u7684 config \u4E0D\u81EA\u6D3D\uFF1Aagents.defaults.model.primary = "${primaryRef}"\uFF0C\u4F46 models.providers \u91CC\u53EA\u6709 ${providerKeys.join(", ")}\u2014\u2014\u5F15\u64CE\u542F\u52A8\u4F1A\u76F4\u63A5\u629B Provider not found\u3002\u8FD9\u662F init \u7684 bug\uFF0C\u8BF7\u8FDE\u540C\u8FD9\u6761\u6D88\u606F\u4E00\u8D77\u53CD\u9988\u3002`
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
const primaryModelId = primaryRef.split("/").slice(1).join("/");
|
|
862
|
+
const listed = config.models.providers[primaryProviderId].models || [];
|
|
863
|
+
if (primaryModelId && !listed.some((m) => m?.id === primaryModelId)) {
|
|
864
|
+
throw new Error(
|
|
865
|
+
`\u751F\u6210\u7684 config \u4E0D\u81EA\u6D3D\uFF1A\u4E3B\u6A21\u578B "${primaryRef}" \u4E0D\u5728 ${primaryProviderId} \u7684\u6A21\u578B\u8868\u91CC\uFF08\u8868\u91CC\u662F ${listed.map((m) => m?.id).join(", ") || "\u7A7A"}\uFF09\u3002\u8FD9\u662F init \u7684 bug\uFF0C\u8BF7\u8FDE\u540C\u8FD9\u6761\u6D88\u606F\u4E00\u8D77\u53CD\u9988\u3002`
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
if (v.visionModel) {
|
|
869
|
+
const visionProvider = v.visionModel.split("/")[0];
|
|
870
|
+
if (!config.models.providers[visionProvider]) {
|
|
871
|
+
console.warn(
|
|
872
|
+
`[config] \u5FFD\u7565 vision \u5F15\u7528 "${v.visionModel}"\uFF1Aprovider "${visionProvider}" \u4E0D\u5728\u672C\u6B21\u751F\u6210\u7684 providers\uFF08${Object.keys(config.models.providers).join(", ") || "\u7A7A"}\uFF09\u91CC\u2014\u2014\u5199\u8FDB\u53BB\u4F1A\u8BA9 loader \u89E3\u6790\u5931\u8D25\u3001\u5165\u7AD9\u56FE\u7247\u88AB\u9759\u9ED8\u4E22\u5F03\u3002\u56FE\u7247\u8BC6\u522B\u672C\u6B21\u5173\u95ED\u3002`
|
|
873
|
+
);
|
|
874
|
+
delete config.agents.defaults.model.vision;
|
|
875
|
+
delete config.tools.my_eyes;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return config;
|
|
879
|
+
}
|
|
880
|
+
var SCHEMA_VERSION, FREE_TIER_RELAY_URL, DEFAULT_PRIMARY_MODEL, VISION_CAPABLE, PROVIDER_DEFS, MODEL_CHOICES, CUSTOM_DEFAULT_INPUT, TEMPLATE_VALUES;
|
|
881
|
+
var init_cli_init_config = __esm({
|
|
882
|
+
"src/cli-init-config.ts"() {
|
|
883
|
+
"use strict";
|
|
884
|
+
SCHEMA_VERSION = 1;
|
|
885
|
+
FREE_TIER_RELAY_URL = "https://www.twinsun.top:8866";
|
|
886
|
+
DEFAULT_PRIMARY_MODEL = {
|
|
887
|
+
dashscope: "dashscope/qwen3.8-max",
|
|
888
|
+
minimax: "minimax/MiniMax-M3",
|
|
889
|
+
zhipu: "zhipu/glm-5.3",
|
|
890
|
+
deepseek: "deepseek/deepseek-v4-pro",
|
|
891
|
+
"7yi": "7yi/engine7-free"
|
|
892
|
+
};
|
|
893
|
+
VISION_CAPABLE = {
|
|
894
|
+
// 0917:原先标 true,但同一个文件里 qwen3.8-max 的 input 是 ['text'],安装器目录
|
|
895
|
+
// 也明确标 vision=false(只有 Flash 叫"多模态")。矛盾直接导致**默认 CLI 装机路径**
|
|
896
|
+
// 重演 P0-2 的症状:vision 复用主模型 → 该型号 modelInputs=['text'] → 引擎把历史和
|
|
897
|
+
// 当前消息里的图全剥成 "[image]"。改成 false 后 resolveVisionModel 会落到
|
|
898
|
+
// qwen3.8-flash(input 真含 image)。
|
|
899
|
+
"dashscope/qwen3.8-max": false,
|
|
900
|
+
"dashscope/qwen3.8-flash": true,
|
|
901
|
+
"minimax/MiniMax-M3": true,
|
|
902
|
+
// 0918 移除 'minimax/MiniMax-M2.7'(内置表同时去掉该型号)
|
|
903
|
+
"zhipu/glm-5v-turbo": true,
|
|
904
|
+
"zhipu/glm-5.3": false,
|
|
905
|
+
// 0917:DeepSeek 全线按支持图片处理。依据(官方更新日志 + Vision 文档):
|
|
906
|
+
// - `deepseek-flash`(V4.1 Flash)原生多模态,官方 Vision 文档明确收图
|
|
907
|
+
// https://api-docs.deepseek.com/guides/vision
|
|
908
|
+
// - `deepseek-v4-flash` / `deepseek-v4-flash-vision-exp`:退役名,请求由 V4.1 Flash
|
|
909
|
+
// 承接(临时兼容路由,官方未给移除日期)→ 同样收图
|
|
910
|
+
// - `deepseek-v4-pro`:官方旗舰已停,9/14 起请求同样由 V4.1 Flash 承接;新的
|
|
911
|
+
// deepseek-pro 发布后大概率也走多模态(flash 都支持了,pro 不会倒退)
|
|
912
|
+
// 原先两个都标 false:v4-pro 标 false 会让今天用它的 agent **静默丢图**,
|
|
913
|
+
// deepseek-flash 标 false 还会触发 P0-2 的幽灵引用兜底。
|
|
914
|
+
// 声明多一分的代价是上游响亮报错,少一分的代价是无声丢图——选前者。
|
|
915
|
+
"deepseek/deepseek-flash": true,
|
|
916
|
+
"deepseek/deepseek-v4-flash": true,
|
|
917
|
+
"deepseek/deepseek-v4-pro": true,
|
|
918
|
+
// 免费档(0919):engine7-free 只收文本,engine7-vis-free 才带 image。
|
|
919
|
+
// 标错会让主模型被当成能看图 → 图发给一个 input=['text'] 的型号 → 静默丢图。
|
|
920
|
+
"7yi/engine7-free": false,
|
|
921
|
+
"7yi/engine7-vis-free": true
|
|
922
|
+
};
|
|
923
|
+
PROVIDER_DEFS = {
|
|
924
|
+
dashscope: {
|
|
925
|
+
// 双通道(0916):按量付费(sk-)走 compatible-mode;Token Plan 订阅(sk-sp-)走专属域名,
|
|
926
|
+
// 两边密钥/端点不可互换——用错就"订阅了还按量计费"。计划选择见 dashscopePlan。
|
|
927
|
+
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
928
|
+
api: "openai-completions",
|
|
929
|
+
models: [
|
|
930
|
+
{ id: "qwen3.8-max", name: "Qwen 3.8 Max", reasoning: true, input: ["text"], contextWindow: 1e6, maxTokens: 65536 },
|
|
931
|
+
{ id: "qwen3.8-flash", name: "Qwen 3.8 Flash\uFF08\u591A\u6A21\u6001\uFF09", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 65536 }
|
|
932
|
+
]
|
|
933
|
+
},
|
|
934
|
+
minimax: {
|
|
935
|
+
baseUrl: "https://api.minimaxi.com/anthropic",
|
|
936
|
+
api: "anthropic",
|
|
937
|
+
models: [
|
|
938
|
+
// 0918 去掉 MiniMax-M2.7:有 M3 之后没人用了(翀哥)
|
|
939
|
+
{ id: "MiniMax-M3", name: "MiniMax M3", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 64e3 }
|
|
940
|
+
]
|
|
941
|
+
},
|
|
942
|
+
zhipu: {
|
|
943
|
+
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
944
|
+
api: "openai-completions",
|
|
945
|
+
models: [
|
|
946
|
+
{ id: "glm-5.3", name: "GLM-5.3 (\u6700\u65B0\u4E3B\u529B)", reasoning: true, input: ["text"], contextWindow: 1e6, maxTokens: 128e3 },
|
|
947
|
+
{ id: "glm-5v-turbo", name: "GLM-5V-Turbo (\u89C6\u89C9)", reasoning: true, input: ["text", "image"], contextWindow: 2e5, maxTokens: 128e3 }
|
|
948
|
+
]
|
|
949
|
+
},
|
|
950
|
+
deepseek: {
|
|
951
|
+
baseUrl: "https://api.deepseek.com/anthropic",
|
|
952
|
+
api: "anthropic",
|
|
953
|
+
thinking: { enabled: true, budgetTokens: 8192 },
|
|
954
|
+
// 0923:官方 anthropic 端点 thinking 模式下,历史里"带 tool_use 却没 thinking 块"的 assistant 轮
|
|
955
|
+
// 会让下一次请求 400 must be passed back。这种轮是引擎自己造的——空响应重试会发
|
|
956
|
+
// thinking:{type:'disabled'}(见 query.ts)。开了这个开关就给它补占位块、并让无签名的
|
|
957
|
+
// thinking 也回传。实测矩阵见 anthropic-provider.ts 的 AnthropicProviderConfig。
|
|
958
|
+
requireThinkingBlocks: true,
|
|
959
|
+
models: [
|
|
960
|
+
// 0916 改名:deepseek-v4-flash → deepseek-flash(旧名仍可调,请求由 V4.1 Flash 承接)
|
|
961
|
+
// 0917 补 image:V4.1 Flash 原生多模态(见 VISION_CAPABLE 注释)
|
|
962
|
+
{ id: "deepseek-flash", name: "DeepSeek Flash", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 384e3 },
|
|
963
|
+
// 0917 补 image:官方旗舰已停、9/14 起请求由 V4.1 Flash 承接,实际收图
|
|
964
|
+
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 384e3 }
|
|
965
|
+
]
|
|
966
|
+
},
|
|
967
|
+
"7yi": {
|
|
968
|
+
// 引擎免费档的中转站。baseUrl 是兜底值——真正的权威值来自领号响应
|
|
969
|
+
// (服务端按请求 Host 自报地址),由安装器作为 customBaseUrl 下发。
|
|
970
|
+
// 这样挪机器、换域名只改服务端,不用重发安装包。
|
|
971
|
+
//
|
|
972
|
+
// ⚠️ 0921 修:这里原本写的是 `https://www.twinsun.top/v1`(443),而 **443 上不是
|
|
973
|
+
// 中转站**(实测回的是另一个服务的 FastAPI 404)。安装器那条路每次都会下发
|
|
974
|
+
// customBaseUrl 把它盖掉,所以正式装机撞不到;但**终端向导选 7yi 时不问 baseUrl**,
|
|
975
|
+
// 直接落到这个兜底值——于是命令行用户拿到的是「号领到了、引擎起来了、一发消息就废」。
|
|
976
|
+
// 用常量拼,别再手写一遍(漏端口就是这个坑)。
|
|
977
|
+
baseUrl: `${FREE_TIER_RELAY_URL}/v1`,
|
|
978
|
+
api: "anthropic",
|
|
979
|
+
models: [
|
|
980
|
+
// contextWindow 拍板 500000(0922 翀哥),两条依据:
|
|
981
|
+
// 1. token 估算有偏差——声明 500k → 压缩阈值约 42 万(×0.906 减 overhead),
|
|
982
|
+
// 就算估算再偏低一截,离 1M 真实窗口还留着 200k+ 余量,不会被 context overflow 掉。
|
|
983
|
+
// 2. 免费档烧的是中转站背后的 token,500k 上限封住单 session 消耗,够用了。
|
|
984
|
+
//
|
|
985
|
+
// 病例史(改这个值前必读):
|
|
986
|
+
// - 0919(声明 1M 的代价):阈值 906137 高到永不触发,上下文滚到 191080 仍不压缩,
|
|
987
|
+
// 191k 的请求把 SenseNova 的 TPM 打爆(429 inference exceeds tpm/rpm limit),
|
|
988
|
+
// 整条链退到付费的 MiniMax。当时据此取过 150000(实测成功线 114K)。
|
|
989
|
+
// - 0922(声明 150k 的代价,反向):把 7yi 只当 vision 旁路的部署(小柯)里,
|
|
990
|
+
// vision 引擎按 150k 算出阈值 49071,对共享 session 的 50 万 token(1M 主模型
|
|
991
|
+
// glm-5.3 的正常水位)连触 3 次 compact,47 万历史被 ruleCompact 折叠、不可逆。
|
|
992
|
+
// → **7yi 当 vision 旁路的部署,注册值必须跟主模型一致(1M),别用本条的 500k**;
|
|
993
|
+
// 本条 500k 只服务"7yi 当主模型"的装机场景。
|
|
994
|
+
{ id: "engine7-free", name: "Engine7 \u514D\u8D39\uFF08\u6587\u672C\uFF09", reasoning: true, input: ["text"], contextWindow: 5e5, maxTokens: 64e3 },
|
|
995
|
+
// input 带 image:实测 Claude 格式的图片块经 /v1/messages 能被上游正确识别
|
|
996
|
+
{ id: "engine7-vis-free", name: "Engine7 \u514D\u8D39\uFF08\u89C6\u89C9\uFF09", reasoning: true, input: ["text", "image"], contextWindow: 5e5, maxTokens: 64e3 }
|
|
997
|
+
// 注意:engine7-img-free 不在这里。它在 chat 端点是 404(不是 chat 模型),
|
|
998
|
+
// 生图由 my_selfie 自己拼 /v1/images/edits,relayModel 默认值已经是它。
|
|
999
|
+
]
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
1002
|
+
MODEL_CHOICES = Object.fromEntries(
|
|
1003
|
+
Object.entries(PROVIDER_DEFS).map(([pid, def]) => [
|
|
1004
|
+
pid,
|
|
1005
|
+
def.models.map((m) => `${pid}/${m.id}`)
|
|
1006
|
+
])
|
|
1007
|
+
);
|
|
1008
|
+
CUSTOM_DEFAULT_INPUT = ["text", "image"];
|
|
1009
|
+
TEMPLATE_VALUES = {
|
|
1010
|
+
stateDir: "{{STATE_DIR}}",
|
|
1011
|
+
timezone: "{{TIMEZONE}}",
|
|
1012
|
+
agentName: "{{AGENT_NAME}}",
|
|
1013
|
+
persona: "assistant",
|
|
1014
|
+
chatMode: "auto",
|
|
1015
|
+
primaryProvider: "dashscope",
|
|
1016
|
+
zhipuPlan: "",
|
|
1017
|
+
dashscopePlan: "",
|
|
1018
|
+
providerModels: null,
|
|
1019
|
+
// 走内置模型表,模板里展示内置形状
|
|
1020
|
+
primaryApiKey: "",
|
|
1021
|
+
// generateConfig 只写 env: 占位符,明文不落 config
|
|
1022
|
+
primaryModel: "dashscope/qwen3.8-max",
|
|
1023
|
+
// 派生而非手写:qwen3.8-max 不支持图片,vision 落到 flash。手写就会再制造一次
|
|
1024
|
+
// "vision 指向一个 input 里没有 image 的型号"(见 VISION_CAPABLE 注释)。
|
|
1025
|
+
visionModel: resolveVisionModel("dashscope", "dashscope/qwen3.8-max"),
|
|
1026
|
+
discordEnabled: false,
|
|
1027
|
+
discordToken: "",
|
|
1028
|
+
discordUserId: "",
|
|
1029
|
+
feishuEnabled: true,
|
|
1030
|
+
feishuAppId: "{{FEISHU_APP_ID}}",
|
|
1031
|
+
feishuAppSecret: "{{FEISHU_APP_SECRET}}",
|
|
1032
|
+
feishuOpenId: "",
|
|
1033
|
+
wechatEnabled: false,
|
|
1034
|
+
// 微信块是条件写入的,模板只展示通用形状
|
|
1035
|
+
wechatToken: "",
|
|
1036
|
+
wechatAccountId: "",
|
|
1037
|
+
customBaseUrl: "",
|
|
1038
|
+
customModelId: "",
|
|
1039
|
+
customApi: "anthropic",
|
|
1040
|
+
customProviderId: "",
|
|
1041
|
+
// 用 env: 而不是 {{}}:{{}} 是脚手架占位符({{STATE_DIR}} 那种,由工具替换),
|
|
1042
|
+
// 而这一格真实 init 写的就是个 key。写成 {{TAVILY_API_KEY}} 会误导人以为有人会替换它。
|
|
1043
|
+
// env:TAVILY_API_KEY 是引擎真认的形式(loader 会解析),照抄也能跑通。
|
|
1044
|
+
tavilyKey: "env:TAVILY_API_KEY",
|
|
1045
|
+
apiPort: 16990
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
// src/config/secrets-env.ts
|
|
1051
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1052
|
+
import { basename as basename2, dirname as dirname2, join as join3 } from "node:path";
|
|
1053
|
+
function secretsHome() {
|
|
1054
|
+
return process.env.HOME || process.env.USERPROFILE || ".";
|
|
1055
|
+
}
|
|
1056
|
+
function loadSecretsEnv(stateDir, configName) {
|
|
1057
|
+
const home = secretsHome();
|
|
1058
|
+
const candidates = [
|
|
1059
|
+
join3(home, ".engine7-secrets", `${configName}.env`),
|
|
1060
|
+
join3(stateDir, "configs", `.env.${configName}`),
|
|
1061
|
+
join3(stateDir, "configs", ".env")
|
|
1062
|
+
];
|
|
1063
|
+
for (const p of candidates) {
|
|
1064
|
+
if (!existsSync3(p)) continue;
|
|
1065
|
+
for (const line of readFileSync3(p, "utf-8").split("\n")) {
|
|
1066
|
+
const t = line.trim();
|
|
1067
|
+
if (!t || t.startsWith("#")) continue;
|
|
1068
|
+
const eq = t.indexOf("=");
|
|
1069
|
+
if (eq <= 0) continue;
|
|
1070
|
+
const k = t.slice(0, eq).trim();
|
|
1071
|
+
const v = t.slice(eq + 1).trim();
|
|
1072
|
+
const cur = process.env[k];
|
|
1073
|
+
if (k && (cur === void 0 || cur === "")) process.env[k] = v;
|
|
1074
|
+
}
|
|
1075
|
+
return p;
|
|
1076
|
+
}
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
function writeSecretsValue(configPath, envKey, value) {
|
|
1080
|
+
const configName = basename2(configPath, ".json");
|
|
1081
|
+
const dir = join3(secretsHome(), ".engine7-secrets");
|
|
1082
|
+
mkdirSync2(dir, { recursive: true });
|
|
1083
|
+
const file2 = join3(dir, `${configName}.env`);
|
|
1084
|
+
const lines = [];
|
|
1085
|
+
if (existsSync3(file2)) {
|
|
1086
|
+
for (const line of readFileSync3(file2, "utf8").split(/\r?\n/)) {
|
|
1087
|
+
if (new RegExp(`^\\s*${envKey}\\s*=`).test(line)) continue;
|
|
1088
|
+
lines.push(line);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
while (lines.length && lines[lines.length - 1] === "") lines.pop();
|
|
1092
|
+
lines.push(`${envKey}=${value}`);
|
|
1093
|
+
writeFileSync2(file2, lines.join("\n") + "\n", { mode: 384 });
|
|
1094
|
+
try {
|
|
1095
|
+
chmodSync(file2, 384);
|
|
1096
|
+
} catch {
|
|
1097
|
+
}
|
|
1098
|
+
return file2;
|
|
1099
|
+
}
|
|
1100
|
+
var init_secrets_env = __esm({
|
|
1101
|
+
"src/config/secrets-env.ts"() {
|
|
1102
|
+
"use strict";
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
// src/config/display.ts
|
|
1107
|
+
function loadDisplayConfig(raw) {
|
|
1108
|
+
if (!raw) return { ...DEFAULT_DISPLAY };
|
|
1109
|
+
return {
|
|
1110
|
+
thinking: {
|
|
1111
|
+
...DEFAULT_DISPLAY.thinking,
|
|
1112
|
+
...raw.thinking || {}
|
|
1113
|
+
},
|
|
1114
|
+
toolUse: {
|
|
1115
|
+
...DEFAULT_DISPLAY.toolUse,
|
|
1116
|
+
...raw.toolUse || {}
|
|
1117
|
+
},
|
|
1118
|
+
toolResult: {
|
|
1119
|
+
...DEFAULT_DISPLAY.toolResult,
|
|
1120
|
+
...raw.toolResult || {}
|
|
1121
|
+
},
|
|
1122
|
+
reactions: {
|
|
1123
|
+
...DEFAULT_DISPLAY.reactions,
|
|
1124
|
+
...raw.reactions || {}
|
|
1125
|
+
},
|
|
1126
|
+
preview: {
|
|
1127
|
+
...DEFAULT_DISPLAY.preview,
|
|
1128
|
+
...raw.preview || {}
|
|
1129
|
+
},
|
|
1130
|
+
typing: {
|
|
1131
|
+
...DEFAULT_DISPLAY.typing,
|
|
1132
|
+
...raw.typing || {}
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
var DEFAULT_DISPLAY;
|
|
1137
|
+
var init_display = __esm({
|
|
1138
|
+
"src/config/display.ts"() {
|
|
1139
|
+
"use strict";
|
|
1140
|
+
DEFAULT_DISPLAY = {
|
|
1141
|
+
thinking: { enabled: true, emoji: "\u{1F4AD}", maxLen: 300 },
|
|
1142
|
+
toolUse: { enabled: true, emoji: "\u{1F527}", displayMode: "raw", bashDisplayMode: "description", displayLimits: { write: 200 } },
|
|
1143
|
+
toolResult: { enabled: true, emoji: "\u{1F4E4}" },
|
|
1144
|
+
reactions: { enabled: true, start: "\u{1F440}", done: "\u2705", error: "\u274C" },
|
|
1145
|
+
preview: { enabled: true, agentName: "Agent" },
|
|
1146
|
+
typing: { enabled: true }
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
// src/config/features.ts
|
|
1152
|
+
function getFeature(key) {
|
|
1153
|
+
const v = liveConfig.get(`agents.defaults.features.${key}`);
|
|
1154
|
+
return v === void 0 ? FEATURE_DEFAULTS[key] : v;
|
|
1155
|
+
}
|
|
1156
|
+
var FEATURE_DEFAULTS;
|
|
1157
|
+
var init_features = __esm({
|
|
736
1158
|
"src/config/features.ts"() {
|
|
737
1159
|
"use strict";
|
|
738
1160
|
init_live();
|
|
@@ -1386,9 +1808,9 @@ function isAutoMemPath(absolutePath, workspace) {
|
|
|
1386
1808
|
return normalizedPath.startsWith(getAutoMemPath(workspace));
|
|
1387
1809
|
}
|
|
1388
1810
|
async function ensureMemoryDirExists(memoryDir) {
|
|
1389
|
-
const
|
|
1811
|
+
const fs33 = await import("node:fs");
|
|
1390
1812
|
try {
|
|
1391
|
-
await
|
|
1813
|
+
await fs33.promises.mkdir(memoryDir, { recursive: true });
|
|
1392
1814
|
} catch (e) {
|
|
1393
1815
|
const code = e?.code;
|
|
1394
1816
|
if (code !== "EEXIST") {
|
|
@@ -3070,7 +3492,7 @@ ${rawSample.join("\n")}`);
|
|
|
3070
3492
|
|
|
3071
3493
|
// src/models/anthropic-provider.ts
|
|
3072
3494
|
import { randomUUID } from "node:crypto";
|
|
3073
|
-
var AnthropicProvider;
|
|
3495
|
+
var PLACEHOLDER_THINKING_TEXT, PLACEHOLDER_THINKING_SIGNATURE, MISSING_TOOL_RESULT_TEXT, AnthropicProvider;
|
|
3074
3496
|
var init_anthropic_provider = __esm({
|
|
3075
3497
|
"src/models/anthropic-provider.ts"() {
|
|
3076
3498
|
"use strict";
|
|
@@ -3078,6 +3500,9 @@ var init_anthropic_provider = __esm({
|
|
|
3078
3500
|
init_provider();
|
|
3079
3501
|
init_think_stripper();
|
|
3080
3502
|
init_well_formed();
|
|
3503
|
+
PLACEHOLDER_THINKING_TEXT = "(no reasoning recorded)";
|
|
3504
|
+
PLACEHOLDER_THINKING_SIGNATURE = "00000000-0000-4000-8000-000000000000";
|
|
3505
|
+
MISSING_TOOL_RESULT_TEXT = "[tool result missing: this tool call has no recorded result in history]";
|
|
3081
3506
|
AnthropicProvider = class {
|
|
3082
3507
|
// 模拟 CC 的 X-Claude-Code-Session-Id
|
|
3083
3508
|
constructor(config) {
|
|
@@ -3092,11 +3517,15 @@ var init_anthropic_provider = __esm({
|
|
|
3092
3517
|
name = "anthropic";
|
|
3093
3518
|
baseUrl;
|
|
3094
3519
|
sessionId;
|
|
3095
|
-
/** Anthropic: system prompt 单独传,不在 messages 里
|
|
3096
|
-
|
|
3520
|
+
/** Anthropic: system prompt 单独传,不在 messages 里
|
|
3521
|
+
* opts.thinkingEnabled:本次请求是否开着 thinking(streamChat 现算后传入)。
|
|
3522
|
+
* 只影响 requireThinkingBlocks 的占位块回填——那个约束只对 thinking 开启的请求成立。 */
|
|
3523
|
+
formatMessages(systemPrompt, messages, opts) {
|
|
3524
|
+
const requireThinking = this.config.requireThinkingBlocks ?? (opts?.model ? /deepseek/i.test(opts.model) : false);
|
|
3097
3525
|
const formatted = [];
|
|
3098
3526
|
for (const m of messages) {
|
|
3099
3527
|
if (m.role === "system") continue;
|
|
3528
|
+
const thinkingBlock = m.role === "assistant" ? this.thinkingBlockFor(m, requireThinking) : null;
|
|
3100
3529
|
if (m.role === "tool") {
|
|
3101
3530
|
const toolBlock = {
|
|
3102
3531
|
type: "tool_result",
|
|
@@ -3112,13 +3541,7 @@ var init_anthropic_provider = __esm({
|
|
|
3112
3541
|
}
|
|
3113
3542
|
} else if (m.role === "assistant" && m.tool_calls) {
|
|
3114
3543
|
const content = [];
|
|
3115
|
-
if (
|
|
3116
|
-
content.push({
|
|
3117
|
-
type: "thinking",
|
|
3118
|
-
thinking: m.reasoning_content,
|
|
3119
|
-
signature: m.thinking_signature
|
|
3120
|
-
});
|
|
3121
|
-
}
|
|
3544
|
+
if (thinkingBlock) content.push(thinkingBlock);
|
|
3122
3545
|
if (m.content) {
|
|
3123
3546
|
const textContent = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
3124
3547
|
content.push({ type: "text", text: textContent });
|
|
@@ -3137,12 +3560,12 @@ var init_anthropic_provider = __esm({
|
|
|
3137
3560
|
});
|
|
3138
3561
|
}
|
|
3139
3562
|
formatted.push({ role: "assistant", content });
|
|
3140
|
-
} else if (m.role === "assistant" &&
|
|
3563
|
+
} else if (m.role === "assistant" && thinkingBlock) {
|
|
3141
3564
|
const textContent = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
3142
3565
|
formatted.push({
|
|
3143
3566
|
role: "assistant",
|
|
3144
3567
|
content: [
|
|
3145
|
-
|
|
3568
|
+
thinkingBlock,
|
|
3146
3569
|
...textContent ? [{ type: "text", text: textContent }] : []
|
|
3147
3570
|
]
|
|
3148
3571
|
});
|
|
@@ -3190,18 +3613,116 @@ var init_anthropic_provider = __esm({
|
|
|
3190
3613
|
repaired.push(msg2);
|
|
3191
3614
|
}
|
|
3192
3615
|
}
|
|
3616
|
+
const filled = this.fillMissingToolResults(repaired);
|
|
3617
|
+
if (filled.patched > 0) {
|
|
3618
|
+
console.log(`[anthropic] tool_result \u56DE\u586B ${filled.patched} \u4E2A assistant \u8F6E (0924 \u4E0A\u6E38\u8981\u6C42\u914D\u5BF9)`);
|
|
3619
|
+
}
|
|
3620
|
+
if (requireThinking && opts?.thinkingEnabled) {
|
|
3621
|
+
const patched = this.backfillMissingThinking(filled.messages);
|
|
3622
|
+
if (patched > 0) console.log(`[anthropic] thinking \u5757\u56DE\u586B ${patched} \u4E2A assistant \u8F6E (0923 \u4E0A\u6E38\u8981\u6C42)`);
|
|
3623
|
+
}
|
|
3193
3624
|
return {
|
|
3194
|
-
messages:
|
|
3625
|
+
messages: filled.messages,
|
|
3195
3626
|
system: systemPrompt || void 0
|
|
3196
3627
|
};
|
|
3197
3628
|
}
|
|
3629
|
+
/**
|
|
3630
|
+
* 给"带 tool_use 却没有 tool_result"的 assistant 轮补合成结果(对齐 CC ensureToolResultPairing 第 3 步)。
|
|
3631
|
+
*
|
|
3632
|
+
* 上游的判据是**位置**:tool_result 必须出现在紧跟在 tool_use 之后的那条消息里
|
|
3633
|
+
* ("...blocks immediately after")。所以补两块:
|
|
3634
|
+
* - 下一条就是带 tool_result 的 user 轮 → 直接塞进那条轮
|
|
3635
|
+
* - 否则(正常对话的 user 轮 / 下一条是 assistant / 已是数组末尾)→ 插一条新的 user 轮
|
|
3636
|
+
* 标记 is_error:true —— 让模型知道这个工具**没跑成**,别把空白当"跑过了没输出"继续推。
|
|
3637
|
+
*
|
|
3638
|
+
* 前提:engine7 的 tool 结果永远紧跟它的 assistant 轮(provider 侧连续 tool 结果合并成一条
|
|
3639
|
+
* user 轮、ruleCompact 只整条删不搬家),所以不存在"结果在远处"的错位形态。真出现错位的
|
|
3640
|
+
* tool_use 也会被这里补上(下游那条远方的 tool_result 由第 2 步的孤儿判据处理)。
|
|
3641
|
+
*/
|
|
3642
|
+
fillMissingToolResults(formatted) {
|
|
3643
|
+
const out = [];
|
|
3644
|
+
let patched = 0;
|
|
3645
|
+
for (let i = 0; i < formatted.length; i++) {
|
|
3646
|
+
const msg2 = formatted[i];
|
|
3647
|
+
out.push(msg2);
|
|
3648
|
+
if (msg2.role !== "assistant" || !Array.isArray(msg2.content)) continue;
|
|
3649
|
+
const useIds = msg2.content.filter((b) => b.type === "tool_use" && b.id).map((b) => b.id);
|
|
3650
|
+
if (useIds.length === 0) continue;
|
|
3651
|
+
const next = formatted[i + 1];
|
|
3652
|
+
const isUserNext = next?.role === "user";
|
|
3653
|
+
const nextResults = isUserNext && Array.isArray(next.content) ? next.content.filter((b) => b.type === "tool_result") : [];
|
|
3654
|
+
const have = new Set(nextResults.map((b) => b.tool_use_id));
|
|
3655
|
+
const missing = useIds.filter((id) => !have.has(id));
|
|
3656
|
+
if (missing.length === 0) continue;
|
|
3657
|
+
const synthetic = missing.map((id) => ({
|
|
3658
|
+
type: "tool_result",
|
|
3659
|
+
tool_use_id: id,
|
|
3660
|
+
content: MISSING_TOOL_RESULT_TEXT,
|
|
3661
|
+
is_error: true
|
|
3662
|
+
}));
|
|
3663
|
+
if (isUserNext) {
|
|
3664
|
+
const rest = Array.isArray(next.content) ? next.content : next.content ? [{ type: "text", text: String(next.content) }] : [];
|
|
3665
|
+
next.content = [...synthetic, ...rest];
|
|
3666
|
+
} else {
|
|
3667
|
+
out.push({ role: "user", content: synthetic });
|
|
3668
|
+
}
|
|
3669
|
+
patched++;
|
|
3670
|
+
}
|
|
3671
|
+
return { messages: out, patched };
|
|
3672
|
+
}
|
|
3673
|
+
/**
|
|
3674
|
+
* assistant 轮的 thinking 块该不该回传、带不带签名。
|
|
3675
|
+
* - 有签名 → 回传(带签名),官方 Anthropic 端点只认这条
|
|
3676
|
+
* - 无签名 → requireThinking 的端点回传真思考原文(0923 实测这类端点不校验签名值,丢掉纯亏);
|
|
3677
|
+
* 否则不回传(0827 的老行为,防官方端点 400)
|
|
3678
|
+
* - 压根没有 reasoning_content → 没有块可回(占位由 backfillMissingThinking 兜)
|
|
3679
|
+
* requireThinking 由 formatMessages 按三态算好传进来,这里只管形状。
|
|
3680
|
+
*/
|
|
3681
|
+
thinkingBlockFor(m, requireThinking) {
|
|
3682
|
+
const rc = m.reasoning_content;
|
|
3683
|
+
if (!rc) return null;
|
|
3684
|
+
const sig = m.thinking_signature;
|
|
3685
|
+
if (sig) return { type: "thinking", thinking: rc, signature: sig };
|
|
3686
|
+
if (!requireThinking) return null;
|
|
3687
|
+
return { type: "thinking", thinking: rc, signature: PLACEHOLDER_THINKING_SIGNATURE };
|
|
3688
|
+
}
|
|
3689
|
+
/**
|
|
3690
|
+
* 给"带 tool_use 但整轮没有 thinking"的 assistant 轮补一个占位 thinking 块(0923)。
|
|
3691
|
+
*
|
|
3692
|
+
* 这种轮是引擎自己造出来的:query.ts 空响应重试会发 thinking:{type:'disabled'}(见该文件
|
|
3693
|
+
* "吃光输出预算 → retry with thinking off"),回来的轮只有 tool_use、没有 thinking。
|
|
3694
|
+
* 存进 jsonl 后,下一次请求把 thinking 开回来,上游就 400 must be passed back(实测复现)。
|
|
3695
|
+
*
|
|
3696
|
+
* 只补带 tool_use 的轮:纯文本 assistant 轮实测不触发该约束,少改一处就少一分风险。
|
|
3697
|
+
* **整段对话的第一个 assistant 轮也会被补**——实测首轮本来就是豁免的(不补也 200),
|
|
3698
|
+
* 但补上同样 200,为这一点加"是不是首轮"的判断不值得,统一补更好维护。
|
|
3699
|
+
* 用**赋值新数组**而不是 unshift——content 可能直接引用调用方的数组(下面的透传分支)。
|
|
3700
|
+
*/
|
|
3701
|
+
backfillMissingThinking(formatted) {
|
|
3702
|
+
let patched = 0;
|
|
3703
|
+
for (const msg2 of formatted) {
|
|
3704
|
+
if (msg2.role !== "assistant" || !Array.isArray(msg2.content)) continue;
|
|
3705
|
+
if (!msg2.content.some((b) => b.type === "tool_use")) continue;
|
|
3706
|
+
if (msg2.content.some((b) => b.type === "thinking" || b.type === "redacted_thinking")) continue;
|
|
3707
|
+
msg2.content = [
|
|
3708
|
+
{ type: "thinking", thinking: PLACEHOLDER_THINKING_TEXT, signature: PLACEHOLDER_THINKING_SIGNATURE },
|
|
3709
|
+
...msg2.content
|
|
3710
|
+
];
|
|
3711
|
+
patched++;
|
|
3712
|
+
}
|
|
3713
|
+
return patched;
|
|
3714
|
+
}
|
|
3198
3715
|
/** Anthropic streaming: event: content_block_start/delta/stop */
|
|
3199
3716
|
async *streamChat(params) {
|
|
3200
3717
|
const stableText = params.systemStable || params.systemPrompt || "";
|
|
3201
3718
|
const dynamicText = params.systemDynamic || "";
|
|
3719
|
+
const supportsThinking = !/haiku/i.test(params.model);
|
|
3720
|
+
const thinkingEnabled = !params.disableThinking && (this.config.thinking?.enabled ?? supportsThinking);
|
|
3721
|
+
const thinkingBudget = this.config.thinking?.budgetTokens || 8192;
|
|
3202
3722
|
const { messages: formatted } = this.formatMessages(
|
|
3203
3723
|
"",
|
|
3204
|
-
params.messages
|
|
3724
|
+
params.messages,
|
|
3725
|
+
{ thinkingEnabled, model: params.model }
|
|
3205
3726
|
);
|
|
3206
3727
|
const systemBlocks = [];
|
|
3207
3728
|
if (stableText) {
|
|
@@ -3210,9 +3731,6 @@ var init_anthropic_provider = __esm({
|
|
|
3210
3731
|
if (dynamicText) {
|
|
3211
3732
|
systemBlocks.push({ type: "text", text: dynamicText });
|
|
3212
3733
|
}
|
|
3213
|
-
const supportsThinking = !/haiku/i.test(params.model);
|
|
3214
|
-
const thinkingEnabled = !params.disableThinking && (this.config.thinking?.enabled ?? supportsThinking);
|
|
3215
|
-
const thinkingBudget = this.config.thinking?.budgetTokens || 8192;
|
|
3216
3734
|
const url = `${this.baseUrl}/v1/messages`;
|
|
3217
3735
|
const body = {
|
|
3218
3736
|
model: params.model,
|
|
@@ -3819,8 +4337,10 @@ function createProvider(config) {
|
|
|
3819
4337
|
apiKey: config.apiKey,
|
|
3820
4338
|
thinking: config.thinking,
|
|
3821
4339
|
proxy,
|
|
3822
|
-
wafSingleQuoteWorkaround: config.wafSingleQuoteWorkaround
|
|
4340
|
+
wafSingleQuoteWorkaround: config.wafSingleQuoteWorkaround,
|
|
3823
4341
|
// 0902 agentrouter WAF 引号规避
|
|
4342
|
+
requireThinkingBlocks: config.requireThinkingBlocks
|
|
4343
|
+
// 0923 DeepSeek 官方端点要块不要签名
|
|
3824
4344
|
});
|
|
3825
4345
|
case "gemini":
|
|
3826
4346
|
return new GeminiProvider({
|
|
@@ -5525,15 +6045,17 @@ function step2_compressOldTurns(messages, maxTokens, toolResultMode = "minimal")
|
|
|
5525
6045
|
if (isRecent) {
|
|
5526
6046
|
newMessages.push(...turn);
|
|
5527
6047
|
} else {
|
|
5528
|
-
const
|
|
5529
|
-
const
|
|
6048
|
+
const hasUserHead = turn[0].role === "user";
|
|
6049
|
+
const userMsg = hasUserHead ? turn[0] : null;
|
|
6050
|
+
const rest = hasUserHead ? turn.slice(1) : turn;
|
|
5530
6051
|
const origMsgCount = turn.length;
|
|
5531
6052
|
const origTokens = estimateMessageTokens(turn);
|
|
5532
|
-
newMessages.push(userMsg);
|
|
6053
|
+
if (userMsg) newMessages.push(userMsg);
|
|
5533
6054
|
if (toolResultMode === "inline") {
|
|
5534
6055
|
const folded = foldTurnInline(turn);
|
|
5535
|
-
newMessages.push(...folded.slice(1));
|
|
5536
|
-
|
|
6056
|
+
newMessages.push(...hasUserHead ? folded.slice(1) : folded);
|
|
6057
|
+
const pushed = hasUserHead ? folded.length - 1 : folded.length;
|
|
6058
|
+
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 ${(userMsg ? 1 : 0) + pushed} msgs (inline folded, mode=inline)`);
|
|
5537
6059
|
} else {
|
|
5538
6060
|
const kept = rest.filter((m) => m.role !== "tool").map((m) => {
|
|
5539
6061
|
if (m.role === "assistant" && m.tool_calls && m.tool_calls.length > 0) {
|
|
@@ -5543,7 +6065,8 @@ function step2_compressOldTurns(messages, maxTokens, toolResultMode = "minimal")
|
|
|
5543
6065
|
return m;
|
|
5544
6066
|
}).filter((m) => m !== null);
|
|
5545
6067
|
newMessages.push(...kept);
|
|
5546
|
-
|
|
6068
|
+
const keptDesc = userMsg ? `user+${kept.length} text` : `${kept.length} text (no user head)`;
|
|
6069
|
+
compactLog(TAG3, `Step 2 turn[${turnIdx}]: ${origMsgCount} msgs (${origTokens} tok) \u2192 ${(userMsg ? 1 : 0) + kept.length} msgs (${keptDesc}, tool stripped, mode=minimal)`);
|
|
5547
6070
|
}
|
|
5548
6071
|
compressedTurns++;
|
|
5549
6072
|
}
|
|
@@ -5685,7 +6208,8 @@ function stripImageBlocks(messages) {
|
|
|
5685
6208
|
}
|
|
5686
6209
|
function getEffectiveContextWindowSize(config) {
|
|
5687
6210
|
const contextWindow = config.contextWindow || DEFAULT_COMPACT_CONFIG.contextWindow;
|
|
5688
|
-
|
|
6211
|
+
const reservedOutput = config.maxOutputTokens ?? DEFAULT_COMPACT_CONFIG.maxOutputTokens;
|
|
6212
|
+
return contextWindow - reservedOutput;
|
|
5689
6213
|
}
|
|
5690
6214
|
function getAutoCompactThreshold(config) {
|
|
5691
6215
|
const effectiveWindow = getEffectiveContextWindowSize(config);
|
|
@@ -5825,7 +6349,7 @@ async function autoCompactIfNeeded(messages, config, tracking, provider, model,
|
|
|
5825
6349
|
};
|
|
5826
6350
|
}
|
|
5827
6351
|
}
|
|
5828
|
-
var TAG4, MAX_CONSECUTIVE_FAILURES,
|
|
6352
|
+
var TAG4, MAX_CONSECUTIVE_FAILURES, PRE_COMPACT_FLUSH_MESSAGE, PRE_COMPACT_FLUSH_MAX_TURNS;
|
|
5829
6353
|
var init_autoCompact = __esm({
|
|
5830
6354
|
"src/compact/autoCompact.ts"() {
|
|
5831
6355
|
"use strict";
|
|
@@ -5839,7 +6363,6 @@ var init_autoCompact = __esm({
|
|
|
5839
6363
|
init_compactLog();
|
|
5840
6364
|
TAG4 = "autoCompact";
|
|
5841
6365
|
MAX_CONSECUTIVE_FAILURES = 3;
|
|
5842
|
-
MAX_OUTPUT_TOKENS_FOR_SUMMARY = 2e4;
|
|
5843
6366
|
PRE_COMPACT_FLUSH_MESSAGE = "\u26A0\uFE0F Pre-compaction memory flush: \u4E0A\u4E0B\u6587\u5373\u5C06\u88AB\u538B\u7F29\u3002\n\n\u4F60\u5FC5\u987B\u7ACB\u5373\u6267\u884C\u4EE5\u4E0B\u64CD\u4F5C\uFF1A\n1. \u7528 write \u5DE5\u5177\u8986\u76D6\u66F4\u65B0 memory/working-buffer.md\uFF0C\u5199\u5165\u5F53\u524D\u6700\u65B0\u72B6\u6001\uFF08\u6B63\u5728\u505A\u4EC0\u4E48\u3001\u505A\u5230\u54EA\u4E86\u3001\u4E0B\u4E00\u6B65\u5177\u4F53\u52A8\u4F5C\uFF09\n2. \u5C06\u91CD\u8981\u4FE1\u606F\u8FFD\u52A0\u5230 memory/daily/YYYY-MM-DD.md\n\nworking-buffer.md \u662F\u6700\u9AD8\u4F18\u5148\u7EA7\u2014\u2014\u5B83\u4F1A\u88AB PostCompact hook \u81EA\u52A8\u6CE8\u5165\u5230\u538B\u7F29\u540E\u7684\u4E0A\u4E0B\u6587\u3002\u4E0D\u5199 = \u538B\u7F29\u540E\u5931\u5FC6\u3002";
|
|
5844
6367
|
PRE_COMPACT_FLUSH_MAX_TURNS = 2;
|
|
5845
6368
|
}
|
|
@@ -5860,18 +6383,88 @@ var init_compact2 = __esm({
|
|
|
5860
6383
|
}
|
|
5861
6384
|
});
|
|
5862
6385
|
|
|
5863
|
-
// src/
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
6386
|
+
// src/models/contextOverflow.ts
|
|
6387
|
+
function parseContextOverflowError(message) {
|
|
6388
|
+
if (!message) return void 0;
|
|
6389
|
+
for (const match of MATCHERS) {
|
|
6390
|
+
const info = match(message);
|
|
6391
|
+
if (!info) continue;
|
|
6392
|
+
if (!Number.isFinite(info.inputTokens) || !Number.isFinite(info.contextLimit)) continue;
|
|
6393
|
+
if (info.inputTokens <= 0 || info.contextLimit <= 0) continue;
|
|
6394
|
+
return info;
|
|
5869
6395
|
}
|
|
5870
|
-
return
|
|
6396
|
+
return void 0;
|
|
5871
6397
|
}
|
|
5872
|
-
function
|
|
5873
|
-
const
|
|
5874
|
-
|
|
6398
|
+
function computeAdjustedMaxTokens(info) {
|
|
6399
|
+
const available = info.contextLimit - info.inputTokens - CONTEXT_OVERFLOW_SAFETY_BUFFER;
|
|
6400
|
+
if (available < CONTEXT_OVERFLOW_FLOOR_TOKENS) return void 0;
|
|
6401
|
+
return available;
|
|
6402
|
+
}
|
|
6403
|
+
function fitMaxTokens(args) {
|
|
6404
|
+
const { window, estimatedInput, override } = args;
|
|
6405
|
+
const hasCap = typeof args.modelCap === "number" && args.modelCap > 0;
|
|
6406
|
+
const modelCap = hasCap ? args.modelCap : 0;
|
|
6407
|
+
if (override !== void 0 && override !== null && override > 0) {
|
|
6408
|
+
return hasCap ? Math.min(override, modelCap) : override;
|
|
6409
|
+
}
|
|
6410
|
+
if (!hasCap) return void 0;
|
|
6411
|
+
const fitted = window - estimatedInput - CONTEXT_OVERFLOW_SAFETY_BUFFER;
|
|
6412
|
+
if (fitted >= modelCap) return modelCap;
|
|
6413
|
+
return Math.max(fitted, CONTEXT_OVERFLOW_FLOOR_TOKENS);
|
|
6414
|
+
}
|
|
6415
|
+
var CONTEXT_OVERFLOW_SAFETY_BUFFER, CONTEXT_OVERFLOW_FLOOR_TOKENS, MATCHERS;
|
|
6416
|
+
var init_contextOverflow = __esm({
|
|
6417
|
+
"src/models/contextOverflow.ts"() {
|
|
6418
|
+
"use strict";
|
|
6419
|
+
CONTEXT_OVERFLOW_SAFETY_BUFFER = 1e3;
|
|
6420
|
+
CONTEXT_OVERFLOW_FLOOR_TOKENS = 3e3;
|
|
6421
|
+
MATCHERS = [
|
|
6422
|
+
// DeepSeek / vLLM / OpenAI 兼容:
|
|
6423
|
+
// "This model's maximum context length is 1048576 tokens. However, you requested 1048867
|
|
6424
|
+
// tokens (664867 in the messages, 384000 in the completion)."
|
|
6425
|
+
(m) => {
|
|
6426
|
+
const r = /maximum context length is\s*(\d+)\s*tokens[\s\S]{0,300}?\((\d+)\s*in the messages/i.exec(m);
|
|
6427
|
+
return r ? { contextLimit: Number(r[1]), inputTokens: Number(r[2]) } : void 0;
|
|
6428
|
+
},
|
|
6429
|
+
// OpenAI 老格式(只有实际用量,没有 requested 明细):
|
|
6430
|
+
// "This model's maximum context length is 8192 tokens. However your messages resulted in 9000 tokens."
|
|
6431
|
+
(m) => {
|
|
6432
|
+
const r = /maximum context length is\s*(\d+)\s*tokens[\s\S]{0,300}?resulted in\s*(\d+)\s*tokens/i.exec(m);
|
|
6433
|
+
return r ? { contextLimit: Number(r[1]), inputTokens: Number(r[2]) } : void 0;
|
|
6434
|
+
},
|
|
6435
|
+
// Anthropic 经典措辞:
|
|
6436
|
+
// "input length and `max_tokens` exceed context limit: 188059 + 20000 > 200000"
|
|
6437
|
+
(m) => {
|
|
6438
|
+
const r = /input length and\s+[`'"]?max_tokens[`'"]?\s+exceed context limit:\s*(\d+)\s*\+\s*(\d+)\s*>\s*(\d+)/i.exec(m);
|
|
6439
|
+
return r ? { inputTokens: Number(r[1]), contextLimit: Number(r[3]) } : void 0;
|
|
6440
|
+
},
|
|
6441
|
+
// Anthropic 新版措辞:
|
|
6442
|
+
// "prompt is too long: 210000 tokens > 200000 maximum"
|
|
6443
|
+
(m) => {
|
|
6444
|
+
const r = /prompt is too long:\s*(\d+)\s*tokens?\s*>\s*(\d+)\s*maximum/i.exec(m);
|
|
6445
|
+
return r ? { inputTokens: Number(r[1]), contextLimit: Number(r[2]) } : void 0;
|
|
6446
|
+
},
|
|
6447
|
+
// Gemini:"The input token count (1234) exceeds the maximum number of tokens allowed (1000)."
|
|
6448
|
+
(m) => {
|
|
6449
|
+
const r = /input token count\s*\((\d+)\)\s*exceeds the maximum number of tokens allowed\s*\((\d+)\)/i.exec(m);
|
|
6450
|
+
return r ? { inputTokens: Number(r[1]), contextLimit: Number(r[2]) } : void 0;
|
|
6451
|
+
}
|
|
6452
|
+
];
|
|
6453
|
+
}
|
|
6454
|
+
});
|
|
6455
|
+
|
|
6456
|
+
// src/utils/path.ts
|
|
6457
|
+
import * as path8 from "node:path";
|
|
6458
|
+
function fixMsysPath(p) {
|
|
6459
|
+
let fixed = p.includes("\\") ? p.replace(/\\/g, "/") : p;
|
|
6460
|
+
if (process.platform === "win32") {
|
|
6461
|
+
fixed = fixed.replace(/^\/([a-zA-Z])\//, (_, letter) => `${letter.toUpperCase()}:/`);
|
|
6462
|
+
}
|
|
6463
|
+
return fixed;
|
|
6464
|
+
}
|
|
6465
|
+
function resolvePath(p, workspace) {
|
|
6466
|
+
const fixed = fixMsysPath(p);
|
|
6467
|
+
const resolved = path8.isAbsolute(fixed) ? path8.normalize(fixed) : path8.resolve(workspace, fixed);
|
|
5875
6468
|
const normalized = path8.normalize(resolved);
|
|
5876
6469
|
const normalizedWorkspace = path8.normalize(workspace);
|
|
5877
6470
|
if (sandboxOff) return normalized;
|
|
@@ -7300,6 +7893,7 @@ var init_query = __esm({
|
|
|
7300
7893
|
init_registry();
|
|
7301
7894
|
init_types();
|
|
7302
7895
|
init_compact2();
|
|
7896
|
+
init_contextOverflow();
|
|
7303
7897
|
init_deferred();
|
|
7304
7898
|
init_constants();
|
|
7305
7899
|
init_deferred();
|
|
@@ -7410,6 +8004,27 @@ var init_query = __esm({
|
|
|
7410
8004
|
resolveOpt(v) {
|
|
7411
8005
|
return typeof v === "function" ? v() : v;
|
|
7412
8006
|
}
|
|
8007
|
+
/**
|
|
8008
|
+
* 本次请求实发的 max_tokens(0923 规则1:按剩余空间现算,不按模型上限全额要)
|
|
8009
|
+
*
|
|
8010
|
+
* 配置里的 maxTokens 是**模型上限**,不是"每次都要预留这么多"。provider 的闸门是
|
|
8011
|
+
* `输入 + max_tokens ≤ 窗口`,按上限全额要输出会把输入空间挤没 —— 小雯事故:
|
|
8012
|
+
* 1,048,576 − 384,000 后输入只剩 664,576,而压缩阈值按 90 万算,**阈值落在墙外面**,
|
|
8013
|
+
* 压缩永远触发不了,于是每条消息都 400、每次重试发的还是同一个值,自锁。
|
|
8014
|
+
*
|
|
8015
|
+
* 现算 `窗口 − 估算输入 − 安全垫`,夹在 [下限, 模型上限] 之间。
|
|
8016
|
+
* 估算本身偏保守(CJK 按 1 字 1 token + 4/3 pad,小雯实测高估 ~29%),所以算出来只会更小、
|
|
8017
|
+
* 不会更险;真算错了还有规则3 兜底(被拒后拿 API 报的真实数字重算,见 models/contextOverflow.ts)。
|
|
8018
|
+
*/
|
|
8019
|
+
resolveRequestMaxTokens(messages, override) {
|
|
8020
|
+
const cap = this.resolveOpt(this.options.maxTokens);
|
|
8021
|
+
return fitMaxTokens({
|
|
8022
|
+
modelCap: cap,
|
|
8023
|
+
window: this.compactConfig.contextWindow || DEFAULT_COMPACT_CONFIG.contextWindow,
|
|
8024
|
+
estimatedInput: estimateMessageTokens(messages) + (this.systemOverheadTokens ?? 0),
|
|
8025
|
+
override
|
|
8026
|
+
});
|
|
8027
|
+
}
|
|
7413
8028
|
/** 热加载:替换 provider 实例(doReloadConfig 重建 provider 链后调用) */
|
|
7414
8029
|
setProvider(provider) {
|
|
7415
8030
|
this.provider = provider;
|
|
@@ -7480,6 +8095,8 @@ var init_query = __esm({
|
|
|
7480
8095
|
let turnCount = 0;
|
|
7481
8096
|
let toolCalls = [];
|
|
7482
8097
|
let streamRetries = 0;
|
|
8098
|
+
let maxTokensOverride = null;
|
|
8099
|
+
let overflowAdjustments = 0;
|
|
7483
8100
|
let vcFirstToolAt = null;
|
|
7484
8101
|
let turnThinking = "";
|
|
7485
8102
|
let turnThinkingSig = "";
|
|
@@ -7613,7 +8230,7 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
7613
8230
|
systemStable: activeSystemStable,
|
|
7614
8231
|
systemDynamic: fullSystemDynamic,
|
|
7615
8232
|
tools: toolDefs.length > 0 ? toolDefs : void 0,
|
|
7616
|
-
maxTokens:
|
|
8233
|
+
maxTokens: this.resolveRequestMaxTokens(apiMessages, maxTokensOverride),
|
|
7617
8234
|
temperature: this.options.temperature,
|
|
7618
8235
|
signal: ac.signal,
|
|
7619
8236
|
disableThinking: this.options.disableThinking || !!perTurnEmotion
|
|
@@ -7679,6 +8296,23 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
7679
8296
|
}
|
|
7680
8297
|
if (streamError) {
|
|
7681
8298
|
const MAX_STREAM_RETRIES = 3;
|
|
8299
|
+
const overflow = parseContextOverflowError(streamError);
|
|
8300
|
+
if (overflow) {
|
|
8301
|
+
const adjusted = computeAdjustedMaxTokens(overflow);
|
|
8302
|
+
if (adjusted !== void 0 && overflowAdjustments < 3 && !ac.signal.aborted) {
|
|
8303
|
+
maxTokensOverride = maxTokensOverride !== null ? Math.min(maxTokensOverride, adjusted) : adjusted;
|
|
8304
|
+
overflowAdjustments++;
|
|
8305
|
+
console.log(`${this.logPrefix} Turn ${turnCount}: context overflow\uFF08API \u62A5 \u8F93\u5165=${overflow.inputTokens} / \u7A97\u53E3=${overflow.contextLimit}\uFF09\u2192 \u91CD\u7B97 max_tokens=${maxTokensOverride} \u540E\u91CD\u8BD5`);
|
|
8306
|
+
yield { type: "status", text: `\u26A0\uFE0F \u4E0A\u4E0B\u6587\u8D85\u9650\uFF0C\u6539\u7528 ${maxTokensOverride} \u8F93\u51FA\u9884\u7B97\u91CD\u8BD5...` };
|
|
8307
|
+
turnThinking = "";
|
|
8308
|
+
turnThinkingSig = "";
|
|
8309
|
+
turnResponsesReasoning = "";
|
|
8310
|
+
continue;
|
|
8311
|
+
}
|
|
8312
|
+
console.error(`${this.logPrefix} Turn ${turnCount}: context overflow \u4E14\u91CD\u7B97\u540E\u4E0D\u8DB3\uFF08\u8F93\u5165=${overflow.inputTokens} / \u7A97\u53E3=${overflow.contextLimit}\uFF09\u2192 \u653E\u5F03\u91CD\u8BD5`);
|
|
8313
|
+
yield { type: "error", error: `\u4E0A\u4E0B\u6587\u8D85\u51FA\u6A21\u578B\u7A97\u53E3\uFF08\u8F93\u5165 ${overflow.inputTokens} / \u7A97\u53E3 ${overflow.contextLimit}\uFF09\uFF0C\u9700\u8981\u538B\u7F29\u540E\u624D\u80FD\u7EE7\u7EED\u3002` };
|
|
8314
|
+
return;
|
|
8315
|
+
}
|
|
7682
8316
|
const delay = Math.min(1e3 * Math.pow(2, streamRetries), 16e3);
|
|
7683
8317
|
if (streamRetries < MAX_STREAM_RETRIES && !ac.signal.aborted) {
|
|
7684
8318
|
console.log(`${this.logPrefix} Turn ${turnCount}: stream retry ${streamRetries + 1}/${MAX_STREAM_RETRIES}, delay=${delay}ms`);
|
|
@@ -7707,7 +8341,7 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
7707
8341
|
systemStable: activeSystemStable,
|
|
7708
8342
|
systemDynamic: fullSystemDynamic,
|
|
7709
8343
|
tools: toolDefs.length > 0 ? toolDefs : void 0,
|
|
7710
|
-
maxTokens:
|
|
8344
|
+
maxTokens: this.resolveRequestMaxTokens(apiMessages, maxTokensOverride),
|
|
7711
8345
|
temperature: this.options.temperature,
|
|
7712
8346
|
signal: ac.signal,
|
|
7713
8347
|
...thinkingAteBudget ? { disableThinking: true } : {}
|
|
@@ -7831,7 +8465,7 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
7831
8465
|
for (const g of gatedCalls) {
|
|
7832
8466
|
yield { type: "tool_result", tool: g.call.function?.name, tool_call_id: g.call.id, summary: g.text.slice(0, 200), isError: true };
|
|
7833
8467
|
}
|
|
7834
|
-
yield { type: "history", messages: [
|
|
8468
|
+
yield { type: "history", messages: [gateAssistant, ...blockResults] };
|
|
7835
8469
|
continue;
|
|
7836
8470
|
}
|
|
7837
8471
|
const vcPlaceholderOn = context?.channel === "voice-chat" && (liveConfig.get("voiceChat.placeholder.enabled") ?? true);
|
|
@@ -7915,10 +8549,16 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
7915
8549
|
const interruptedResults = toolCalls.map(
|
|
7916
8550
|
(tc) => msg.tool(tc.id, "[interrupted by user /stop]", false)
|
|
7917
8551
|
);
|
|
8552
|
+
const interruptedAssistant = msg.assistant(finalText, toolCalls);
|
|
8553
|
+
if (turnThinking) {
|
|
8554
|
+
interruptedAssistant.reasoning_content = turnThinking;
|
|
8555
|
+
if (turnThinkingSig) interruptedAssistant.thinking_signature = turnThinkingSig;
|
|
8556
|
+
}
|
|
8557
|
+
if (turnResponsesReasoning) interruptedAssistant.responses_reasoning = turnResponsesReasoning;
|
|
7918
8558
|
yield {
|
|
7919
8559
|
type: "history",
|
|
7920
8560
|
messages: [
|
|
7921
|
-
|
|
8561
|
+
interruptedAssistant,
|
|
7922
8562
|
...interruptedResults
|
|
7923
8563
|
]
|
|
7924
8564
|
};
|
|
@@ -8001,6 +8641,19 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
8001
8641
|
import * as fs11 from "node:fs";
|
|
8002
8642
|
import * as path12 from "node:path";
|
|
8003
8643
|
import * as crypto2 from "node:crypto";
|
|
8644
|
+
function assistantWriteParamsFrom(m) {
|
|
8645
|
+
const anyM = m;
|
|
8646
|
+
const tc = anyM.tool_calls;
|
|
8647
|
+
return {
|
|
8648
|
+
text: typeof m.content === "string" ? m.content : "",
|
|
8649
|
+
// 三个通道各自独立判断。truthiness 即"有值"——跟 writer 只写真值 / reader 只认真值的约定一致
|
|
8650
|
+
// (空串等于没有,见本文件 ThinkingBlock 的落盘条件)。
|
|
8651
|
+
...anyM.reasoning_content ? { thinking: anyM.reasoning_content } : {},
|
|
8652
|
+
...anyM.thinking_signature ? { thinkingSignature: anyM.thinking_signature } : {},
|
|
8653
|
+
...anyM.responses_reasoning ? { responsesReasoning: anyM.responses_reasoning } : {},
|
|
8654
|
+
...tc?.length > 0 ? { toolCalls: tc } : {}
|
|
8655
|
+
};
|
|
8656
|
+
}
|
|
8004
8657
|
var SessionWriter;
|
|
8005
8658
|
var init_writer = __esm({
|
|
8006
8659
|
"src/session/writer.ts"() {
|
|
@@ -8874,12 +9527,19 @@ var init_reader = __esm({
|
|
|
8874
9527
|
// src/session/session-manager.ts
|
|
8875
9528
|
var session_manager_exports = {};
|
|
8876
9529
|
__export(session_manager_exports, {
|
|
8877
|
-
SessionManager: () => SessionManager
|
|
9530
|
+
SessionManager: () => SessionManager,
|
|
9531
|
+
alignRestoreHeadToTurnBoundary: () => alignRestoreHeadToTurnBoundary
|
|
8878
9532
|
});
|
|
8879
9533
|
import * as path14 from "node:path";
|
|
8880
9534
|
import * as fs13 from "node:fs";
|
|
8881
9535
|
import * as crypto3 from "node:crypto";
|
|
8882
|
-
|
|
9536
|
+
function alignRestoreHeadToTurnBoundary(messages, minKeep = RESTORE_MIN_KEEP_MESSAGES) {
|
|
9537
|
+
const isTurnBoundary = (m) => isAttachmentMessage(m) ? true : m.role === "user";
|
|
9538
|
+
const headIdx = messages.findIndex(isTurnBoundary);
|
|
9539
|
+
if (headIdx <= 0 || messages.length - headIdx <= minKeep) return { messages, dropped: 0 };
|
|
9540
|
+
return { messages: messages.slice(headIdx), dropped: headIdx };
|
|
9541
|
+
}
|
|
9542
|
+
var RESTORE_MAX_CHARS, CHARS_PER_TOKEN, RESTORE_MIN_KEEP_MESSAGES, SessionManager;
|
|
8883
9543
|
var init_session_manager = __esm({
|
|
8884
9544
|
"src/session/session-manager.ts"() {
|
|
8885
9545
|
"use strict";
|
|
@@ -8889,6 +9549,7 @@ var init_session_manager = __esm({
|
|
|
8889
9549
|
init_reader();
|
|
8890
9550
|
RESTORE_MAX_CHARS = 15e4;
|
|
8891
9551
|
CHARS_PER_TOKEN = 3;
|
|
9552
|
+
RESTORE_MIN_KEEP_MESSAGES = 10;
|
|
8892
9553
|
SessionManager = class {
|
|
8893
9554
|
sessionsDir;
|
|
8894
9555
|
indexPath;
|
|
@@ -9254,6 +9915,8 @@ var init_session_manager = __esm({
|
|
|
9254
9915
|
const a = msg.assistant(m.content, toolCalls);
|
|
9255
9916
|
if (m.timestamp) a.timestamp = m.timestamp;
|
|
9256
9917
|
if (m.responses_reasoning) a.responses_reasoning = m.responses_reasoning;
|
|
9918
|
+
if (m.reasoning_content) a.reasoning_content = m.reasoning_content;
|
|
9919
|
+
if (m.thinking_signature) a.thinking_signature = m.thinking_signature;
|
|
9257
9920
|
allMessages.push(a);
|
|
9258
9921
|
} else if (m.role === "tool") {
|
|
9259
9922
|
const t = msg.tool(m.tool_call_id || "", m.content);
|
|
@@ -9280,13 +9943,16 @@ var init_session_manager = __esm({
|
|
|
9280
9943
|
if (allMessages.length === 0) return [];
|
|
9281
9944
|
let messages = allMessages;
|
|
9282
9945
|
const maxTokens = Math.floor(RESTORE_MAX_CHARS / CHARS_PER_TOKEN);
|
|
9283
|
-
while (this.estimateTokens(messages) > maxTokens && messages.length >
|
|
9946
|
+
while (this.estimateTokens(messages) > maxTokens && messages.length > RESTORE_MIN_KEEP_MESSAGES) {
|
|
9284
9947
|
messages = messages.slice(1);
|
|
9285
9948
|
}
|
|
9949
|
+
const tokenDropped = allMessages.length - messages.length;
|
|
9950
|
+
const aligned = alignRestoreHeadToTurnBoundary(messages);
|
|
9951
|
+
messages = aligned.messages;
|
|
9952
|
+
const alignedNote = aligned.dropped > 0 ? ` (head-aligned: dropped ${aligned.dropped} more msg(s) to reach a turn boundary)` : "";
|
|
9286
9953
|
const totalTokens = this.estimateTokens(messages);
|
|
9287
|
-
const
|
|
9288
|
-
|
|
9289
|
-
console.log(`[session] Restored ${messages.length} messages (~${totalTokens} tokens)${truncNote} from ${files.length} file(s)`);
|
|
9954
|
+
const truncNote = tokenDropped > 0 ? ` (token-truncated: ${tokenDropped} older msgs dropped, max ${maxTokens} tok)` : "";
|
|
9955
|
+
console.log(`[session] Restored ${messages.length} messages (~${totalTokens} tokens)${truncNote}${alignedNote} from ${files.length} file(s)`);
|
|
9290
9956
|
return messages;
|
|
9291
9957
|
}
|
|
9292
9958
|
// === 启动时 rotate main session ===
|
|
@@ -11449,7 +12115,7 @@ async function acquireLock(inboxPath) {
|
|
|
11449
12115
|
for (let i = 0; i < LOCK_RETRIES; i++) {
|
|
11450
12116
|
if (await isLockStale(lockPath2)) {
|
|
11451
12117
|
try {
|
|
11452
|
-
await import("node:fs/promises").then((
|
|
12118
|
+
await import("node:fs/promises").then((fs33) => fs33.rm(lockPath2, { force: true }));
|
|
11453
12119
|
} catch {
|
|
11454
12120
|
}
|
|
11455
12121
|
}
|
|
@@ -11457,7 +12123,7 @@ async function acquireLock(inboxPath) {
|
|
|
11457
12123
|
await writeFile2(lockPath2, `${process.pid}-${Date.now()}`, { encoding: "utf-8", flag: "wx" });
|
|
11458
12124
|
return async () => {
|
|
11459
12125
|
try {
|
|
11460
|
-
await import("node:fs/promises").then((
|
|
12126
|
+
await import("node:fs/promises").then((fs33) => fs33.rm(lockPath2, { force: true }));
|
|
11461
12127
|
} catch {
|
|
11462
12128
|
}
|
|
11463
12129
|
};
|
|
@@ -12858,8 +13524,8 @@ Examples:
|
|
|
12858
13524
|
if (!to && !resolvedChannelId) {
|
|
12859
13525
|
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 };
|
|
12860
13526
|
}
|
|
12861
|
-
const
|
|
12862
|
-
if (!
|
|
13527
|
+
const fs33 = await import("node:fs");
|
|
13528
|
+
if (!fs33.existsSync(filePath)) {
|
|
12863
13529
|
return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6\u4E0D\u5B58\u5728 ${filePath}`, isError: true };
|
|
12864
13530
|
}
|
|
12865
13531
|
const toIds = to ? to.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
@@ -12887,7 +13553,7 @@ Examples:
|
|
|
12887
13553
|
md: "text/markdown"
|
|
12888
13554
|
};
|
|
12889
13555
|
const mimeType = mimeTypeMap[ext] || "application/octet-stream";
|
|
12890
|
-
const stat4 =
|
|
13556
|
+
const stat4 = fs33.statSync(filePath);
|
|
12891
13557
|
const sizeMB = stat4.size / 1024 / 1024;
|
|
12892
13558
|
if (sizeMB > 25) {
|
|
12893
13559
|
return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6 ${sizeMB.toFixed(1)}MB \u8D85\u8FC7 Discord 25MB \u9650\u5236`, isError: true };
|
|
@@ -13944,9 +14610,9 @@ function registerPortrait(relPath, name) {
|
|
|
13944
14610
|
const cur = cfg?.tools?.my_selfie?.references || cfg?.my_selfie?.references || [];
|
|
13945
14611
|
const next = Array.isArray(cur) ? cur.filter((r) => r?.name !== name) : [];
|
|
13946
14612
|
next.unshift({ name, path: relPath });
|
|
13947
|
-
const
|
|
13948
|
-
void liveConfig.set(
|
|
13949
|
-
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${
|
|
14613
|
+
const path35 = cfg?.tools?.my_selfie ? "tools.my_selfie.references" : "my_selfie.references";
|
|
14614
|
+
void liveConfig.set(path35, next);
|
|
14615
|
+
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${path35}[0] = ${relPath}`);
|
|
13950
14616
|
} catch (e) {
|
|
13951
14617
|
console.warn(`[my-selfie] \u5199\u5165 references \u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u672C\u6B21\u51FA\u56FE\uFF09: ${e.message}`);
|
|
13952
14618
|
}
|
|
@@ -17395,10 +18061,10 @@ function ensureLoaded(workspace, configIds) {
|
|
|
17395
18061
|
if (!state.blockedUserIds.includes(id)) state.blockedUserIds.push(id);
|
|
17396
18062
|
}
|
|
17397
18063
|
}
|
|
17398
|
-
const
|
|
18064
|
+
const path35 = join25(workspace, ".reply-blocklist.json");
|
|
17399
18065
|
try {
|
|
17400
|
-
if (existsSync20(
|
|
17401
|
-
const raw = readFileSync20(
|
|
18066
|
+
if (existsSync20(path35)) {
|
|
18067
|
+
const raw = readFileSync20(path35, "utf-8");
|
|
17402
18068
|
const parsed = JSON.parse(raw);
|
|
17403
18069
|
if (parsed.blockedUserIds) {
|
|
17404
18070
|
for (const id of parsed.blockedUserIds) {
|
|
@@ -17414,9 +18080,9 @@ function ensureLoaded(workspace, configIds) {
|
|
|
17414
18080
|
loaded = true;
|
|
17415
18081
|
}
|
|
17416
18082
|
function save(workspace) {
|
|
17417
|
-
const
|
|
18083
|
+
const path35 = join25(workspace, ".reply-blocklist.json");
|
|
17418
18084
|
try {
|
|
17419
|
-
writeFileSync15(
|
|
18085
|
+
writeFileSync15(path35, JSON.stringify(state, null, 2), "utf-8");
|
|
17420
18086
|
} catch (err) {
|
|
17421
18087
|
console.warn(`[reply-blocklist] Failed to save: ${err.message}`);
|
|
17422
18088
|
}
|
|
@@ -18803,7 +19469,9 @@ var init_system_filter = __esm({
|
|
|
18803
19469
|
var ingest_exports = {};
|
|
18804
19470
|
__export(ingest_exports, {
|
|
18805
19471
|
buildPayload: () => buildPayload,
|
|
18806
|
-
|
|
19472
|
+
filterMessages: () => filterMessages,
|
|
19473
|
+
pushConversation: () => pushConversation,
|
|
19474
|
+
toPayload: () => toPayload
|
|
18807
19475
|
});
|
|
18808
19476
|
import fs27 from "node:fs";
|
|
18809
19477
|
import path28 from "node:path";
|
|
@@ -18860,45 +19528,57 @@ function savePushTs(workspace, sessionId, ts) {
|
|
|
18860
19528
|
console.warn(`[everos-ingest] save pushTs failed: ${e?.message ?? e}`);
|
|
18861
19529
|
}
|
|
18862
19530
|
}
|
|
18863
|
-
function
|
|
19531
|
+
function contentToText(content) {
|
|
19532
|
+
if (typeof content === "string") return content;
|
|
19533
|
+
if (Array.isArray(content)) {
|
|
19534
|
+
const texts = content.filter((b) => b?.type === "text" && typeof b.text === "string").map((b) => b.text);
|
|
19535
|
+
return texts.length ? texts.join("\n") : "[content blocks]";
|
|
19536
|
+
}
|
|
19537
|
+
return "[content blocks]";
|
|
19538
|
+
}
|
|
19539
|
+
function filterMessages(messages, cfg) {
|
|
18864
19540
|
let lastUserWasSystem = false;
|
|
19541
|
+
const out = [];
|
|
19542
|
+
for (const m of messages) {
|
|
19543
|
+
const raw = contentToText(m.content);
|
|
19544
|
+
const meta = m.role === "user" ? parseMeta(raw) : null;
|
|
19545
|
+
let text = raw;
|
|
19546
|
+
let isSystemTurn = false;
|
|
19547
|
+
if (m.role === "user") {
|
|
19548
|
+
if (isSystemUserMessage(parseMeta(raw))) {
|
|
19549
|
+
text = "";
|
|
19550
|
+
isSystemTurn = true;
|
|
19551
|
+
} else text = stripInjectedContext(raw.replace(/^\[meta:[^\]]*\]\s*/, ""));
|
|
19552
|
+
lastUserWasSystem = isSystemTurn;
|
|
19553
|
+
}
|
|
19554
|
+
if (m.role === "user" && !text || m.role !== "user" && lastUserWasSystem) continue;
|
|
19555
|
+
const senderName = meta?.senderName ?? (m.role === "assistant" ? cfg.agentName : void 0) ?? m.role;
|
|
19556
|
+
const rawRole = m.role === "toolResult" ? "tool" : m.role;
|
|
19557
|
+
const role = rawRole === "user" ? "user" : "assistant";
|
|
19558
|
+
const tsMs = typeof m.timestamp === "number" ? m.timestamp : Date.parse(String(m.timestamp ?? "")) || Date.now();
|
|
19559
|
+
out.push({
|
|
19560
|
+
// owner 口径见 IngestConfig.ownerMode:'sender' 模式下 user 消息用真人 ID
|
|
19561
|
+
// (everos 的 owner fan-out 取的就是 user 的 sender_id → 每人一份记忆)
|
|
19562
|
+
sender_id: m.role === "user" && cfg.ownerMode === "sender" ? meta?.senderId || cfg.appId : cfg.appId,
|
|
19563
|
+
sender_name: senderName,
|
|
19564
|
+
role,
|
|
19565
|
+
timestamp: tsMs,
|
|
19566
|
+
content: text
|
|
19567
|
+
});
|
|
19568
|
+
}
|
|
19569
|
+
return out;
|
|
19570
|
+
}
|
|
19571
|
+
function toPayload(messages, cfg, sessionId) {
|
|
18865
19572
|
return {
|
|
18866
19573
|
session_id: `extract-${sessionId}`,
|
|
18867
19574
|
app_id: cfg.appId,
|
|
18868
19575
|
project_id: "default",
|
|
18869
|
-
|
|
18870
|
-
// 剥完为空的内部消息(心跳/nudge 注入)整条跳过。此前从未剥过,episode/foresight
|
|
18871
|
-
// 一直吃着 460 字里只有 26 字原话的拼装体。
|
|
18872
|
-
messages: messages.map((m) => {
|
|
18873
|
-
const raw = typeof m.content === "string" ? m.content : "[content blocks]";
|
|
18874
|
-
const meta = m.role === "user" ? parseMeta(raw) : null;
|
|
18875
|
-
let text = raw;
|
|
18876
|
-
let isSystemTurn = false;
|
|
18877
|
-
if (m.role === "user") {
|
|
18878
|
-
if (isSystemUserMessage(parseMeta(raw))) {
|
|
18879
|
-
text = "";
|
|
18880
|
-
isSystemTurn = true;
|
|
18881
|
-
} else text = stripInjectedContext(raw.replace(/^\[meta:[^\]]*\]\s*/, ""));
|
|
18882
|
-
lastUserWasSystem = isSystemTurn;
|
|
18883
|
-
}
|
|
18884
|
-
const senderName = meta?.senderName ?? (m.role === "assistant" ? cfg.agentName : void 0) ?? m.role;
|
|
18885
|
-
const rawRole = m.role === "toolResult" ? "tool" : m.role;
|
|
18886
|
-
const role = rawRole === "user" ? "user" : "assistant";
|
|
18887
|
-
const tsMs = typeof m.timestamp === "number" ? m.timestamp : Date.parse(String(m.timestamp ?? "")) || Date.now();
|
|
18888
|
-
return {
|
|
18889
|
-
// owner 口径见 IngestConfig.ownerMode:'sender' 模式下 user 消息用真人 ID
|
|
18890
|
-
// (everos 的 owner fan-out 取的就是 user 的 sender_id → 每人一份记忆)
|
|
18891
|
-
sender_id: m.role === "user" && cfg.ownerMode === "sender" ? meta?.senderId || cfg.appId : cfg.appId,
|
|
18892
|
-
sender_name: senderName,
|
|
18893
|
-
role,
|
|
18894
|
-
timestamp: tsMs,
|
|
18895
|
-
content: text,
|
|
18896
|
-
// 丢:系统指令本体 + 系统轮里的回应(与 mem7 同款,见上面状态机)
|
|
18897
|
-
_skip: m.role === "user" && !text || m.role !== "user" && lastUserWasSystem
|
|
18898
|
-
};
|
|
18899
|
-
}).filter((x) => !x._skip).map(({ _skip, ...rest }) => rest)
|
|
19576
|
+
messages
|
|
18900
19577
|
};
|
|
18901
19578
|
}
|
|
19579
|
+
function buildPayload(messages, cfg, sessionId) {
|
|
19580
|
+
return toPayload(filterMessages(messages, cfg), cfg, sessionId);
|
|
19581
|
+
}
|
|
18902
19582
|
async function fetchWithTimeout(url, options, timeoutMs) {
|
|
18903
19583
|
const controller = new AbortController();
|
|
18904
19584
|
let timer;
|
|
@@ -18962,17 +19642,24 @@ async function pushConversation(messages, sessionId, workspace) {
|
|
|
18962
19642
|
if (ms > maxTs) maxTs = ms;
|
|
18963
19643
|
}
|
|
18964
19644
|
if (toPush.length === 0) return;
|
|
18965
|
-
const
|
|
19645
|
+
const filtered = filterMessages(toPush, cfg);
|
|
19646
|
+
if (filtered.length === 0) {
|
|
19647
|
+
savePushTs(workspace, sessionId, maxTs);
|
|
19648
|
+
console.log("[everos-ingest] all filtered out (system turns only), advance pushTs only");
|
|
19649
|
+
return;
|
|
19650
|
+
}
|
|
19651
|
+
const total = filtered.length;
|
|
18966
19652
|
const chunkTotal = Math.ceil(total / PUSH_CHUNK);
|
|
18967
19653
|
let pushed = 0;
|
|
19654
|
+
let failedChunks = 0;
|
|
18968
19655
|
for (let i = 0; i < total; i += PUSH_CHUNK) {
|
|
18969
|
-
const chunk =
|
|
19656
|
+
const chunk = filtered.slice(i, i + PUSH_CHUNK);
|
|
18970
19657
|
const chunkMaxTs = chunk.reduce((mx, m) => {
|
|
18971
|
-
const ms = m.timestamp
|
|
18972
|
-
return
|
|
19658
|
+
const ms = m.timestamp ?? 0;
|
|
19659
|
+
return ms < mx ? mx : ms;
|
|
18973
19660
|
}, pushTs);
|
|
18974
19661
|
const chunkNo = Math.floor(i / PUSH_CHUNK) + 1;
|
|
18975
|
-
const payload =
|
|
19662
|
+
const payload = toPayload(chunk, cfg, sessionId);
|
|
18976
19663
|
console.log(`[everos-ingest] pushing chunk ${chunkNo}/${chunkTotal} (${chunk.length} msgs) to ${cfg.url} (timeout=${cfg.ingestTimeoutMs}ms)`);
|
|
18977
19664
|
try {
|
|
18978
19665
|
const resp = await fetchWithTimeout(`${cfg.url}/api/v1/memory/add`, {
|
|
@@ -18985,16 +19672,19 @@ async function pushConversation(messages, sessionId, workspace) {
|
|
|
18985
19672
|
pushed += chunk.length;
|
|
18986
19673
|
console.log(`[everos-ingest] \u2705 pushed chunk ${chunkNo}/${chunkTotal} (${chunk.length} msgs, pushTs\u2192${new Date(chunkMaxTs).toISOString().slice(11, 19)})`);
|
|
18987
19674
|
} else {
|
|
19675
|
+
failedChunks++;
|
|
18988
19676
|
savePushTs(workspace, sessionId, chunkMaxTs);
|
|
18989
19677
|
const errBody = await resp.text().catch(() => "?");
|
|
18990
19678
|
console.warn(`[everos-ingest] memory/add ${resp.status} on chunk ${chunkNo}, body=${errBody.slice(0, 300)}, skip & advance pushTs\u2192${new Date(chunkMaxTs).toISOString().slice(11, 19)}`);
|
|
18991
19679
|
}
|
|
18992
19680
|
} catch (e) {
|
|
19681
|
+
failedChunks++;
|
|
18993
19682
|
savePushTs(workspace, sessionId, chunkMaxTs);
|
|
18994
19683
|
console.warn(`[everos-ingest] chunk ${chunkNo} push failed (${e?.name || "error"}: ${e?.message || "unknown"}), skip & advance pushTs\u2192${new Date(chunkMaxTs).toISOString().slice(11, 19)}`);
|
|
18995
19684
|
}
|
|
18996
19685
|
}
|
|
18997
|
-
|
|
19686
|
+
if (failedChunks === 0) savePushTs(workspace, sessionId, maxTs);
|
|
19687
|
+
console.log(`[everos-ingest] done: ${pushed}/${total} msgs pushed${failedChunks ? ` (${failedChunks} chunk(s) FAILED & skipped \u2014 \u90A3\u4E9B\u6D88\u606F\u4E0D\u4F1A\u91CD\u63A8\uFF0C\u89C1\u51FD\u6570\u5934 \u26A0\uFE0F)` : ""}`);
|
|
18998
19688
|
} finally {
|
|
18999
19689
|
pushInProgress = false;
|
|
19000
19690
|
}
|
|
@@ -19275,17 +19965,46 @@ var init_filters = __esm({
|
|
|
19275
19965
|
});
|
|
19276
19966
|
|
|
19277
19967
|
// src/mem7/core/vector-store.ts
|
|
19278
|
-
import * as lancedb from "@lancedb/lancedb";
|
|
19279
19968
|
import { Schema, Field, Utf8, Float32, Int64, FixedSizeList, TimestampMicrosecond, List } from "apache-arrow";
|
|
19280
19969
|
import { mkdirSync as mkdirSync14 } from "node:fs";
|
|
19281
19970
|
import { join as join32 } from "node:path";
|
|
19282
|
-
|
|
19971
|
+
function lanceSpecifierFor(platform2, arch) {
|
|
19972
|
+
return platform2 === "darwin" && arch === "x64" ? "@engine7/lancedb-intel" : "@lancedb/lancedb";
|
|
19973
|
+
}
|
|
19974
|
+
function loadLance() {
|
|
19975
|
+
if (!lancePromise) {
|
|
19976
|
+
const spec = lanceSpecifierFor(process.platform, process.arch);
|
|
19977
|
+
lancePromise = import(spec).then(
|
|
19978
|
+
// 转发包是 CJS(module.exports = require(...)),命名导出可能落在 default 上
|
|
19979
|
+
(m) => m?.connect ? m : m?.default,
|
|
19980
|
+
(err) => {
|
|
19981
|
+
mem7VectorWarning = err?.message ? String(err.message) : String(err);
|
|
19982
|
+
console.warn(
|
|
19983
|
+
`[mem7] \u5411\u91CF\u7D22\u5F15\u4E0D\u53EF\u7528\uFF1A\u52A0\u8F7D ${spec} \u5931\u8D25 \u2014\u2014 ${mem7VectorWarning}
|
|
19984
|
+
mem7 \u964D\u7EA7\u505C\u7528\uFF0C\u5F15\u64CE\u5176\u4F59\u529F\u80FD\u4E0D\u53D7\u5F71\u54CD\u3002` + (isIntelMac ? "\n Intel Mac \u63D0\u793A\uFF1Ae7 \u4F9D\u8D56 @engine7/lancedb-intel\uFF08lancedb \u4E0D\u518D\u53D1\u5E03 darwin-x64 \u4E8C\u8FDB\u5236\uFF09\u3002\u82E5\u62A5\u300CCannot find native binding\u300D\uFF0C\u5220\u6389 node_modules \u91CD\u88C5\u4E00\u6B21\u5373\u53EF\u3002" : "")
|
|
19985
|
+
);
|
|
19986
|
+
return null;
|
|
19987
|
+
}
|
|
19988
|
+
);
|
|
19989
|
+
}
|
|
19990
|
+
return lancePromise;
|
|
19991
|
+
}
|
|
19992
|
+
var isIntelMac, lancePromise, mem7VectorWarning, Mem7VectorUnavailableError, VECTOR_DIM, BM25_FIELDS, RRF_K, warnedFts, vecField, commonFields, SCHEMAS, Mem7VectorStore;
|
|
19283
19993
|
var init_vector_store = __esm({
|
|
19284
19994
|
"src/mem7/core/vector-store.ts"() {
|
|
19285
19995
|
"use strict";
|
|
19286
19996
|
init_store2();
|
|
19287
19997
|
init_tokenizer();
|
|
19288
19998
|
init_filters();
|
|
19999
|
+
isIntelMac = process.platform === "darwin" && process.arch === "x64";
|
|
20000
|
+
lancePromise = null;
|
|
20001
|
+
mem7VectorWarning = null;
|
|
20002
|
+
Mem7VectorUnavailableError = class extends Error {
|
|
20003
|
+
constructor() {
|
|
20004
|
+
super(`mem7 \u5411\u91CF\u7D22\u5F15\u4E0D\u53EF\u7528\uFF08lancedb \u672A\u80FD\u52A0\u8F7D\uFF09${mem7VectorWarning ? `\uFF1A${mem7VectorWarning}` : ""}`);
|
|
20005
|
+
this.name = "Mem7VectorUnavailableError";
|
|
20006
|
+
}
|
|
20007
|
+
};
|
|
19289
20008
|
VECTOR_DIM = 1024;
|
|
19290
20009
|
BM25_FIELDS = {
|
|
19291
20010
|
foresight: ["foresight_tokens", "evidence_tokens"],
|
|
@@ -19348,8 +20067,10 @@ var init_vector_store = __esm({
|
|
|
19348
20067
|
}
|
|
19349
20068
|
async conn() {
|
|
19350
20069
|
if (!this.db) {
|
|
20070
|
+
const lance = await loadLance();
|
|
20071
|
+
if (!lance) throw new Mem7VectorUnavailableError();
|
|
19351
20072
|
mkdirSync14(this.dir, { recursive: true });
|
|
19352
|
-
this.db = await
|
|
20073
|
+
this.db = await lance.connect(this.dir);
|
|
19353
20074
|
}
|
|
19354
20075
|
return this.db;
|
|
19355
20076
|
}
|
|
@@ -19371,10 +20092,12 @@ var init_vector_store = __esm({
|
|
|
19371
20092
|
* 幂等:已建则跳过(索引缺失才建)。
|
|
19372
20093
|
*/
|
|
19373
20094
|
async ensureFtsIndexes(kind, tbl) {
|
|
20095
|
+
const lance = await loadLance();
|
|
20096
|
+
if (!lance) return;
|
|
19374
20097
|
for (const col of BM25_FIELDS[kind]) {
|
|
19375
20098
|
try {
|
|
19376
20099
|
await tbl.createIndex(col, {
|
|
19377
|
-
config:
|
|
20100
|
+
config: lance.Index.fts({
|
|
19378
20101
|
baseTokenizer: "whitespace",
|
|
19379
20102
|
removeStopWords: false,
|
|
19380
20103
|
lowercase: true,
|
|
@@ -24489,6 +25212,7 @@ var init_plugin = __esm({
|
|
|
24489
25212
|
get name() {
|
|
24490
25213
|
return current().name ?? "main";
|
|
24491
25214
|
},
|
|
25215
|
+
// 元组要跟 LLMProvider.formatMessages 的元数对齐(0923 加了第 3 个 opts),否则第 3 参被类型吃掉
|
|
24492
25216
|
formatMessages: (...args) => current().formatMessages(...args),
|
|
24493
25217
|
streamChat: (...args) => current().streamChat(args[0]),
|
|
24494
25218
|
chat: (...args) => {
|
|
@@ -25394,11 +26118,11 @@ async function readLastConsolidatedAt(memoryDir) {
|
|
|
25394
26118
|
}
|
|
25395
26119
|
}
|
|
25396
26120
|
async function tryAcquireConsolidationLock(memoryDir) {
|
|
25397
|
-
const
|
|
26121
|
+
const path35 = lockPath(memoryDir);
|
|
25398
26122
|
let mtimeMs;
|
|
25399
26123
|
let holderPid;
|
|
25400
26124
|
try {
|
|
25401
|
-
const [s, raw] = await Promise.all([stat3(
|
|
26125
|
+
const [s, raw] = await Promise.all([stat3(path35), readFile5(path35, "utf8")]);
|
|
25402
26126
|
mtimeMs = s.mtimeMs;
|
|
25403
26127
|
const parsed = parseInt(raw.trim(), 10);
|
|
25404
26128
|
holderPid = Number.isFinite(parsed) ? parsed : void 0;
|
|
@@ -25411,10 +26135,10 @@ async function tryAcquireConsolidationLock(memoryDir) {
|
|
|
25411
26135
|
}
|
|
25412
26136
|
}
|
|
25413
26137
|
await mkdir3(memoryDir, { recursive: true });
|
|
25414
|
-
await writeFile4(
|
|
26138
|
+
await writeFile4(path35, String(process.pid));
|
|
25415
26139
|
let verify;
|
|
25416
26140
|
try {
|
|
25417
|
-
verify = await readFile5(
|
|
26141
|
+
verify = await readFile5(path35, "utf8");
|
|
25418
26142
|
} catch {
|
|
25419
26143
|
return null;
|
|
25420
26144
|
}
|
|
@@ -25422,15 +26146,15 @@ async function tryAcquireConsolidationLock(memoryDir) {
|
|
|
25422
26146
|
return mtimeMs ?? 0;
|
|
25423
26147
|
}
|
|
25424
26148
|
async function rollbackConsolidationLock(memoryDir, priorMtime) {
|
|
25425
|
-
const
|
|
26149
|
+
const path35 = lockPath(memoryDir);
|
|
25426
26150
|
try {
|
|
25427
26151
|
if (priorMtime === 0) {
|
|
25428
|
-
await unlink(
|
|
26152
|
+
await unlink(path35);
|
|
25429
26153
|
return;
|
|
25430
26154
|
}
|
|
25431
|
-
await writeFile4(
|
|
26155
|
+
await writeFile4(path35, "");
|
|
25432
26156
|
const t = priorMtime / 1e3;
|
|
25433
|
-
await utimes(
|
|
26157
|
+
await utimes(path35, t, t);
|
|
25434
26158
|
} catch (e) {
|
|
25435
26159
|
console.log(`[autoDream] rollback failed: ${e.message} \u2014 next trigger delayed to minHours`);
|
|
25436
26160
|
}
|
|
@@ -25725,18 +26449,18 @@ function resetSessionStartInjection(sessionId) {
|
|
|
25725
26449
|
sessionStartDone.delete(sessionId);
|
|
25726
26450
|
}
|
|
25727
26451
|
function loadExternalChanRules(workspace) {
|
|
25728
|
-
const
|
|
25729
|
-
if (externalChanRulesCache && externalChanRulesCache.path ===
|
|
26452
|
+
const path35 = join45(workspace, "prompts", "external-chan-rules.md");
|
|
26453
|
+
if (externalChanRulesCache && externalChanRulesCache.path === path35) return externalChanRulesCache;
|
|
25730
26454
|
let content = "";
|
|
25731
|
-
if (existsSync28(
|
|
26455
|
+
if (existsSync28(path35)) {
|
|
25732
26456
|
try {
|
|
25733
|
-
content = readFileSync36(
|
|
26457
|
+
content = readFileSync36(path35, "utf-8").trim();
|
|
25734
26458
|
} catch (e) {
|
|
25735
26459
|
console.warn(`[external-chan-rules] Failed to load: ${e}`);
|
|
25736
26460
|
}
|
|
25737
26461
|
}
|
|
25738
|
-
externalChanRulesCache = { path:
|
|
25739
|
-
console.log(`[external-chan-rules] Loaded ${content.length} chars from ${
|
|
26462
|
+
externalChanRulesCache = { path: path35, content };
|
|
26463
|
+
console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path35}`);
|
|
25740
26464
|
return externalChanRulesCache;
|
|
25741
26465
|
}
|
|
25742
26466
|
function getExternalChanRulesBlock(inboundMeta, workspace) {
|
|
@@ -26093,6 +26817,8 @@ ${text}` : text });
|
|
|
26093
26817
|
let roundThinkingSig = "";
|
|
26094
26818
|
let roundResponsesReasoning = "";
|
|
26095
26819
|
let finalResponsesReasoning = "";
|
|
26820
|
+
let finalThinking = "";
|
|
26821
|
+
let finalThinkingSig = "";
|
|
26096
26822
|
const roundToolCalls = [];
|
|
26097
26823
|
const roundToolResults = [];
|
|
26098
26824
|
function flushRound(stopReason) {
|
|
@@ -26168,7 +26894,7 @@ ${text}` : text });
|
|
|
26168
26894
|
const recallP = deps.recallProvider;
|
|
26169
26895
|
const recallMode = topics?.recall?.mode || "llm";
|
|
26170
26896
|
let relevantMemories;
|
|
26171
|
-
const effectiveText = textForMemory.replace(/\[图片来自[^\]]*\]|路径:\S+/g, "").trim();
|
|
26897
|
+
const effectiveText = textForMemory.replace(/\[图片来自[^\]]*\]|路径:\S+/g, "").replace(/^\[inner-voice\]\s*/, "").trim();
|
|
26172
26898
|
if (!effectiveText) {
|
|
26173
26899
|
console.log("[handle-query] Memory recall skipped: image-only message (no effective text)");
|
|
26174
26900
|
relevantMemories = [];
|
|
@@ -26341,11 +27067,8 @@ ${text}` : text });
|
|
|
26341
27067
|
const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
26342
27068
|
writer.writeUserMessage(content);
|
|
26343
27069
|
} else if (role === "assistant") {
|
|
26344
|
-
const content = typeof m.content === "string" ? m.content : "";
|
|
26345
|
-
const toolCalls = m.tool_calls;
|
|
26346
27070
|
writer.writeAssistantMessage({
|
|
26347
|
-
|
|
26348
|
-
toolCalls: toolCalls?.length > 0 ? toolCalls : void 0,
|
|
27071
|
+
...assistantWriteParamsFrom(m),
|
|
26349
27072
|
api: providerApi,
|
|
26350
27073
|
provider: providerId,
|
|
26351
27074
|
model
|
|
@@ -26387,6 +27110,8 @@ ${text}` : text });
|
|
|
26387
27110
|
engine2.clearExternalAbort();
|
|
26388
27111
|
clearActiveQueryEngine(sessionId);
|
|
26389
27112
|
finalResponsesReasoning = roundResponsesReasoning;
|
|
27113
|
+
finalThinking = roundThinking;
|
|
27114
|
+
finalThinkingSig = roundThinkingSig;
|
|
26390
27115
|
if (roundText) {
|
|
26391
27116
|
try {
|
|
26392
27117
|
flushRound("endTurn");
|
|
@@ -26402,6 +27127,10 @@ ${text}` : text });
|
|
|
26402
27127
|
const prePushLen = history.length;
|
|
26403
27128
|
const finalAssistant = msg.assistant(roundText);
|
|
26404
27129
|
if (finalResponsesReasoning) finalAssistant.responses_reasoning = finalResponsesReasoning;
|
|
27130
|
+
if (finalThinking) {
|
|
27131
|
+
finalAssistant.reasoning_content = finalThinking;
|
|
27132
|
+
if (finalThinkingSig) finalAssistant.thinking_signature = finalThinkingSig;
|
|
27133
|
+
}
|
|
26405
27134
|
if (compacted) {
|
|
26406
27135
|
history.push(...toolHistoryEntries);
|
|
26407
27136
|
if (roundText) history.push(finalAssistant);
|
|
@@ -26572,6 +27301,7 @@ var init_handle_query = __esm({
|
|
|
26572
27301
|
init_types();
|
|
26573
27302
|
init_attachments();
|
|
26574
27303
|
init_active_query_engine();
|
|
27304
|
+
init_writer();
|
|
26575
27305
|
init_live();
|
|
26576
27306
|
init_features();
|
|
26577
27307
|
init_task_manager();
|
|
@@ -26747,8 +27477,8 @@ async function runQuery(args) {
|
|
|
26747
27477
|
if (!stateDir) {
|
|
26748
27478
|
const home = process.env.HOME || process.env.USERPROFILE || ".";
|
|
26749
27479
|
const cwd = process.cwd();
|
|
26750
|
-
const { existsSync:
|
|
26751
|
-
if (
|
|
27480
|
+
const { existsSync: existsSync36 } = await import("node:fs");
|
|
27481
|
+
if (existsSync36(join47(cwd, "configs"))) stateDir = cwd;
|
|
26752
27482
|
else {
|
|
26753
27483
|
try {
|
|
26754
27484
|
const pointer = JSON.parse(readFileSync37(join47(home, ".engine7.json"), "utf-8"));
|
|
@@ -27069,183 +27799,28 @@ var init_cli_ccrun = __esm({
|
|
|
27069
27799
|
}
|
|
27070
27800
|
});
|
|
27071
27801
|
|
|
27072
|
-
// src/cli-doctor.ts
|
|
27073
|
-
var cli_doctor_exports = {};
|
|
27074
|
-
__export(cli_doctor_exports, {
|
|
27075
|
-
resolveStateDirFromArgs: () => resolveStateDirFromArgs,
|
|
27076
|
-
runDoctor: () => runDoctor
|
|
27077
|
-
});
|
|
27078
|
-
import * as fs28 from "node:fs";
|
|
27079
|
-
import * as path30 from "node:path";
|
|
27080
|
-
import * as net from "node:net";
|
|
27081
|
-
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
27082
|
-
function readVersion() {
|
|
27083
|
-
const here = path30.dirname(fileURLToPath5(import.meta.url));
|
|
27084
|
-
for (const p of [path30.join(here, "..", "package.json"), path30.join(here, "..", "..", "package.json")]) {
|
|
27085
|
-
try {
|
|
27086
|
-
return JSON.parse(fs28.readFileSync(p, "utf8")).version || "unknown";
|
|
27087
|
-
} catch {
|
|
27088
|
-
}
|
|
27089
|
-
}
|
|
27090
|
-
return "unknown";
|
|
27091
|
-
}
|
|
27092
|
-
function channelHasCredentials(name, conf) {
|
|
27093
|
-
if (name === "feishu") return !!(conf?.appId && conf?.appSecret);
|
|
27094
|
-
if (name === "wechat") return !!(conf?.token && conf?.accountId);
|
|
27095
|
-
if (name === "discord") return !!conf?.accounts?.default?.token;
|
|
27096
|
-
return true;
|
|
27097
|
-
}
|
|
27098
|
-
function probePort(port, timeoutMs) {
|
|
27099
|
-
return new Promise((resolve12) => {
|
|
27100
|
-
const sock = net.connect({ port, host: "127.0.0.1", timeout: timeoutMs });
|
|
27101
|
-
sock.once("connect", () => {
|
|
27102
|
-
sock.destroy();
|
|
27103
|
-
resolve12(true);
|
|
27104
|
-
});
|
|
27105
|
-
sock.once("error", () => resolve12(false));
|
|
27106
|
-
sock.once("timeout", () => {
|
|
27107
|
-
sock.destroy();
|
|
27108
|
-
resolve12(false);
|
|
27109
|
-
});
|
|
27110
|
-
});
|
|
27111
|
-
}
|
|
27112
|
-
async function runDoctor(stateDir, opts) {
|
|
27113
|
-
const home = process.env.HOME || process.env.USERPROFILE || ".";
|
|
27114
|
-
const configPath = path30.join(stateDir, "configs", "main7.json");
|
|
27115
|
-
const report = {
|
|
27116
|
-
version: readVersion(),
|
|
27117
|
-
stateDir,
|
|
27118
|
-
configPath,
|
|
27119
|
-
configValid: false,
|
|
27120
|
-
channels: [],
|
|
27121
|
-
secrets: { file: "", exists: false, keys: [] },
|
|
27122
|
-
apiPort: 16890,
|
|
27123
|
-
engineAlive: false,
|
|
27124
|
-
vision: { ref: null, providerExists: null },
|
|
27125
|
-
missingEnvKeys: []
|
|
27126
|
-
};
|
|
27127
|
-
let config = null;
|
|
27128
|
-
try {
|
|
27129
|
-
config = JSON.parse(fs28.readFileSync(configPath, "utf8"));
|
|
27130
|
-
report.configValid = true;
|
|
27131
|
-
} catch (e) {
|
|
27132
|
-
report.configError = fs28.existsSync(configPath) ? `\u89E3\u6790\u5931\u8D25: ${e.message}` : "config \u4E0D\u5B58\u5728\uFF08\u5148 engine7 init\uFF09";
|
|
27133
|
-
}
|
|
27134
|
-
if (config?.channels) {
|
|
27135
|
-
for (const [name, conf] of Object.entries(config.channels)) {
|
|
27136
|
-
const c = conf;
|
|
27137
|
-
report.channels.push({ name, enabled: !!c.enabled, hasCredentials: channelHasCredentials(name, c) });
|
|
27138
|
-
}
|
|
27139
|
-
}
|
|
27140
|
-
const secretsDir = path30.join(home, ".engine7-secrets");
|
|
27141
|
-
const cfgBase = path30.basename(report.configPath, ".json") || "main7";
|
|
27142
|
-
const secretsCandidates = [
|
|
27143
|
-
path30.join(secretsDir, `${cfgBase}.env`),
|
|
27144
|
-
path30.join(stateDir, "configs", `.env.${cfgBase}`),
|
|
27145
|
-
path30.join(stateDir, "configs", ".env")
|
|
27146
|
-
];
|
|
27147
|
-
const secretsFile = secretsCandidates.find((p) => fs28.existsSync(p)) || secretsCandidates[0];
|
|
27148
|
-
report.secrets.file = secretsFile;
|
|
27149
|
-
if (fs28.existsSync(secretsFile)) {
|
|
27150
|
-
report.secrets.exists = true;
|
|
27151
|
-
for (const line of fs28.readFileSync(secretsFile, "utf8").split("\n")) {
|
|
27152
|
-
const eq = line.indexOf("=");
|
|
27153
|
-
if (eq > 0 && !line.startsWith("#")) report.secrets.keys.push(line.slice(0, eq).trim());
|
|
27154
|
-
}
|
|
27155
|
-
}
|
|
27156
|
-
const visionRef = config?.agents?.defaults?.model?.vision;
|
|
27157
|
-
if (typeof visionRef === "string" && visionRef) {
|
|
27158
|
-
const providerId = visionRef.includes("/") ? visionRef.slice(0, visionRef.indexOf("/")) : "";
|
|
27159
|
-
report.vision = {
|
|
27160
|
-
ref: visionRef,
|
|
27161
|
-
providerExists: !!providerId && !!config?.models?.providers?.[providerId]
|
|
27162
|
-
};
|
|
27163
|
-
}
|
|
27164
|
-
const missing = /* @__PURE__ */ new Set();
|
|
27165
|
-
const walk = (node) => {
|
|
27166
|
-
if (typeof node === "string") {
|
|
27167
|
-
if (node.startsWith("env:")) {
|
|
27168
|
-
const name = node.slice(4);
|
|
27169
|
-
if (/^[A-Z][A-Z0-9_]*$/.test(name) && process.env[name] === void 0 && !report.secrets.keys.includes(name)) {
|
|
27170
|
-
missing.add(name);
|
|
27171
|
-
}
|
|
27172
|
-
}
|
|
27173
|
-
return;
|
|
27174
|
-
}
|
|
27175
|
-
if (Array.isArray(node)) {
|
|
27176
|
-
node.forEach(walk);
|
|
27177
|
-
return;
|
|
27178
|
-
}
|
|
27179
|
-
if (node && typeof node === "object") {
|
|
27180
|
-
for (const k of Object.keys(node)) walk(node[k]);
|
|
27181
|
-
}
|
|
27182
|
-
};
|
|
27183
|
-
walk(config);
|
|
27184
|
-
report.missingEnvKeys = [...missing].sort();
|
|
27185
|
-
const stale = [];
|
|
27186
|
-
for (const rel of ["workspace/SESSION-STATE.md", "SESSION-STATE.md"]) {
|
|
27187
|
-
const p = path30.join(stateDir, rel);
|
|
27188
|
-
try {
|
|
27189
|
-
if (!fs28.existsSync(p)) continue;
|
|
27190
|
-
const head = fs28.readFileSync(p, "utf8").split("\n").slice(0, 10).join("\n");
|
|
27191
|
-
const m = head.match(/^#{1,3}\s*当前时间\s*$\n+\s*(\d{4}-\d{2}-\d{2})/m);
|
|
27192
|
-
if (!m) continue;
|
|
27193
|
-
const ageH = (Date.now() - (/* @__PURE__ */ new Date(`${m[1]}T00:00:00Z`)).getTime()) / 36e5;
|
|
27194
|
-
if (ageH > 48) stale.push(`${rel}: ${m[1]}`);
|
|
27195
|
-
} catch {
|
|
27196
|
-
}
|
|
27197
|
-
}
|
|
27198
|
-
report.staleTimeHeaders = stale;
|
|
27199
|
-
report.apiPort = config?.api?.port || 16890;
|
|
27200
|
-
report.engineAlive = await probePort(report.apiPort, opts?.probeTimeoutMs ?? 1500);
|
|
27201
|
-
return report;
|
|
27202
|
-
}
|
|
27203
|
-
function resolveStateDirFromArgs(args) {
|
|
27204
|
-
let stateDir = "";
|
|
27205
|
-
for (let i = 0; i < args.length; i++) {
|
|
27206
|
-
if (args[i] === "--state-dir" && args[i + 1]) stateDir = args[++i].trim();
|
|
27207
|
-
}
|
|
27208
|
-
if (stateDir) return path30.resolve(stateDir);
|
|
27209
|
-
const home = process.env.HOME || process.env.USERPROFILE || ".";
|
|
27210
|
-
const cwd = path30.resolve(process.cwd());
|
|
27211
|
-
if (fs28.existsSync(path30.join(cwd, "configs"))) return cwd;
|
|
27212
|
-
const pointer = path30.join(home, ".engine7.json");
|
|
27213
|
-
if (fs28.existsSync(pointer)) {
|
|
27214
|
-
try {
|
|
27215
|
-
const ptrStateDir = JSON.parse(fs28.readFileSync(pointer, "utf8")).stateDir;
|
|
27216
|
-
if (typeof ptrStateDir === "string" && ptrStateDir.trim()) return path30.resolve(ptrStateDir);
|
|
27217
|
-
} catch {
|
|
27218
|
-
}
|
|
27219
|
-
}
|
|
27220
|
-
return path30.join(home, ".engine7");
|
|
27221
|
-
}
|
|
27222
|
-
var init_cli_doctor = __esm({
|
|
27223
|
-
"src/cli-doctor.ts"() {
|
|
27224
|
-
"use strict";
|
|
27225
|
-
}
|
|
27226
|
-
});
|
|
27227
|
-
|
|
27228
27802
|
// src/provision-free.ts
|
|
27229
27803
|
var provision_free_exports = {};
|
|
27230
27804
|
__export(provision_free_exports, {
|
|
27805
|
+
FREE_TIER_ENV_KEY: () => FREE_TIER_ENV_KEY,
|
|
27231
27806
|
loadOrCreateInstallIdentity: () => loadOrCreateInstallIdentity,
|
|
27232
27807
|
provisionFreeToken: () => provisionFreeToken,
|
|
27233
27808
|
readInstallIdentity: () => readInstallIdentity,
|
|
27234
27809
|
writeIdentityContactEmail: () => writeIdentityContactEmail
|
|
27235
27810
|
});
|
|
27236
27811
|
import { randomBytes as randomBytes6, randomUUID as randomUUID9 } from "node:crypto";
|
|
27237
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
27238
|
-
import { dirname as
|
|
27239
|
-
import { fileURLToPath as
|
|
27812
|
+
import { chmodSync as chmodSync2, existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync38, writeFileSync as writeFileSync20 } from "node:fs";
|
|
27813
|
+
import { dirname as dirname11, join as join48 } from "node:path";
|
|
27814
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
27240
27815
|
function readContactEmailOf(parsed) {
|
|
27241
27816
|
return typeof parsed?.contactEmail === "string" ? parsed.contactEmail.trim() : "";
|
|
27242
27817
|
}
|
|
27243
27818
|
function loadOrCreateInstallIdentity(stateDir) {
|
|
27244
|
-
const file2 =
|
|
27245
|
-
if (
|
|
27819
|
+
const file2 = join48(stateDir, IDENTITY_FILE);
|
|
27820
|
+
if (existsSync30(file2)) {
|
|
27246
27821
|
let parsed;
|
|
27247
27822
|
try {
|
|
27248
|
-
parsed = JSON.parse(
|
|
27823
|
+
parsed = JSON.parse(readFileSync38(file2, "utf8"));
|
|
27249
27824
|
} catch (e) {
|
|
27250
27825
|
throw new Error(
|
|
27251
27826
|
`${IDENTITY_FILE} \u5B58\u5728\u4F46\u4E0D\u662F\u5408\u6CD5 JSON\uFF08${e.message}\uFF09\u2014\u2014\u5220\u6389\u5B83\u91CD\u8DD1\u4F1A\u9886\u4E00\u628A\u65B0\u4EE4\u724C\uFF0C\u65E7\u7684\u90A3\u628A\u53D6\u4E0D\u56DE\u6765`
|
|
@@ -27277,7 +27852,7 @@ function loadOrCreateInstallIdentity(stateDir) {
|
|
|
27277
27852
|
}
|
|
27278
27853
|
function readInstallIdentity(stateDir) {
|
|
27279
27854
|
try {
|
|
27280
|
-
const parsed = JSON.parse(
|
|
27855
|
+
const parsed = JSON.parse(readFileSync38(join48(stateDir, IDENTITY_FILE), "utf8"));
|
|
27281
27856
|
if (typeof parsed?.installId !== "string" || typeof parsed?.installSecret !== "string") return null;
|
|
27282
27857
|
const email = readContactEmailOf(parsed);
|
|
27283
27858
|
return {
|
|
@@ -27293,7 +27868,7 @@ function writeIdentityContactEmail(stateDir, email) {
|
|
|
27293
27868
|
const identity = loadOrCreateInstallIdentity(stateDir);
|
|
27294
27869
|
const trimmed = email.trim();
|
|
27295
27870
|
const next = { ...identity, contactEmail: trimmed };
|
|
27296
|
-
const file2 =
|
|
27871
|
+
const file2 = join48(stateDir, IDENTITY_FILE);
|
|
27297
27872
|
mkdirSync19(stateDir, { recursive: true });
|
|
27298
27873
|
writeFileSync20(file2, JSON.stringify(next, null, 2) + "\n", { mode: 384 });
|
|
27299
27874
|
try {
|
|
@@ -27303,7 +27878,7 @@ function writeIdentityContactEmail(stateDir, email) {
|
|
|
27303
27878
|
}
|
|
27304
27879
|
function readEngineVersion() {
|
|
27305
27880
|
try {
|
|
27306
|
-
return JSON.parse(
|
|
27881
|
+
return JSON.parse(readFileSync38(join48(dirname11(__filename), "..", "package.json"), "utf8")).version || "unknown";
|
|
27307
27882
|
} catch {
|
|
27308
27883
|
return "unknown";
|
|
27309
27884
|
}
|
|
@@ -27351,66 +27926,325 @@ var init_provision_free = __esm({
|
|
|
27351
27926
|
"src/provision-free.ts"() {
|
|
27352
27927
|
"use strict";
|
|
27353
27928
|
init_secrets_env();
|
|
27354
|
-
__filename =
|
|
27929
|
+
__filename = fileURLToPath5(import.meta.url);
|
|
27355
27930
|
IDENTITY_FILE = "install-identity.json";
|
|
27356
27931
|
FREE_TIER_ENV_KEY = "7YI_API_KEY";
|
|
27357
27932
|
}
|
|
27358
27933
|
});
|
|
27359
27934
|
|
|
27360
|
-
// src/
|
|
27361
|
-
var
|
|
27362
|
-
__export(
|
|
27363
|
-
|
|
27364
|
-
|
|
27935
|
+
// src/free-tier-apply.ts
|
|
27936
|
+
var free_tier_apply_exports = {};
|
|
27937
|
+
__export(free_tier_apply_exports, {
|
|
27938
|
+
FREE_TIER_PRIMARY: () => FREE_TIER_PRIMARY,
|
|
27939
|
+
FREE_TIER_PROVIDER_ID: () => FREE_TIER_PROVIDER_ID,
|
|
27940
|
+
applyFreeTier: () => applyFreeTier,
|
|
27941
|
+
applyFreeTierToConfig: () => applyFreeTierToConfig,
|
|
27942
|
+
isFreeTierConfig: () => isFreeTierConfig,
|
|
27943
|
+
resolveConfigPath: () => resolveConfigPath
|
|
27365
27944
|
});
|
|
27366
|
-
import * as
|
|
27367
|
-
import * as
|
|
27368
|
-
function
|
|
27369
|
-
|
|
27370
|
-
|
|
27371
|
-
|
|
27945
|
+
import * as fs28 from "node:fs";
|
|
27946
|
+
import * as path30 from "node:path";
|
|
27947
|
+
function isFreeTierConfig(cfg) {
|
|
27948
|
+
const hasProvider = !!cfg?.models?.providers?.[FREE_TIER_PROVIDER_ID];
|
|
27949
|
+
const primary = String(cfg?.agents?.defaults?.model?.primary || "");
|
|
27950
|
+
return hasProvider && primary.startsWith(`${FREE_TIER_PROVIDER_ID}/`);
|
|
27951
|
+
}
|
|
27952
|
+
function applyFreeTier(cfg, opts = {}) {
|
|
27953
|
+
const def = PROVIDER_DEFS[FREE_TIER_PROVIDER_ID];
|
|
27954
|
+
if (!def) throw new Error("\u5F15\u64CE\u91CC\u6CA1\u6709 7yi \u8FD9\u4E2A\u5185\u7F6E provider\u2014\u2014PROVIDER_DEFS \u88AB\u52A8\u8FC7\uFF1F");
|
|
27955
|
+
const baseUrl = (opts.baseUrl || "").trim() || def.baseUrl;
|
|
27956
|
+
const vision = resolveVisionModel(FREE_TIER_PROVIDER_ID, FREE_TIER_PRIMARY);
|
|
27957
|
+
cfg.models = cfg.models || {};
|
|
27958
|
+
cfg.models.providers = cfg.models.providers || {};
|
|
27959
|
+
cfg.models.providers[FREE_TIER_PROVIDER_ID] = {
|
|
27960
|
+
...def,
|
|
27961
|
+
// 深拷一份模型表:`...def` 是浅拷,别让写出来的 config 和 PROVIDER_DEFS 共享同一个数组
|
|
27962
|
+
models: (def.models || []).map((m) => ({ ...m })),
|
|
27963
|
+
apiKey: `env:${FREE_TIER_ENV_KEY}`,
|
|
27964
|
+
...baseUrl ? { baseUrl: String(baseUrl).trim() } : {}
|
|
27965
|
+
};
|
|
27966
|
+
cfg.agents = cfg.agents || {};
|
|
27967
|
+
cfg.agents.defaults = cfg.agents.defaults || {};
|
|
27968
|
+
cfg.agents.defaults.model = cfg.agents.defaults.model || {};
|
|
27969
|
+
cfg.agents.defaults.model.primary = FREE_TIER_PRIMARY;
|
|
27970
|
+
if (vision) cfg.agents.defaults.model.vision = vision;
|
|
27971
|
+
cfg.agents.defaults.features = cfg.agents.defaults.features || {};
|
|
27972
|
+
cfg.agents.defaults.features.selfie = true;
|
|
27973
|
+
cfg.agents.defaults.features.eyes = true;
|
|
27974
|
+
cfg.tools = cfg.tools || {};
|
|
27975
|
+
if (vision) cfg.tools.my_eyes = { ...cfg.tools.my_eyes || {}, model: vision };
|
|
27976
|
+
cfg.tools.my_selfie = { ...cfg.tools.my_selfie || {}, provider: "relay" };
|
|
27977
|
+
return cfg;
|
|
27978
|
+
}
|
|
27979
|
+
function resolveConfigPath(stateDir, configName) {
|
|
27980
|
+
const configsDir = path30.join(stateDir, "configs");
|
|
27981
|
+
if (configName) {
|
|
27982
|
+
const p = path30.join(configsDir, configName);
|
|
27983
|
+
return fs28.existsSync(p) ? p : "";
|
|
27984
|
+
}
|
|
27985
|
+
const main2 = path30.join(configsDir, "main7.json");
|
|
27986
|
+
if (fs28.existsSync(main2)) return main2;
|
|
27987
|
+
if (!fs28.existsSync(configsDir)) return "";
|
|
27988
|
+
for (const f of fs28.readdirSync(configsDir).filter((x) => x.endsWith(".json"))) {
|
|
27989
|
+
const full = path30.join(configsDir, f);
|
|
27372
27990
|
try {
|
|
27373
|
-
if (JSON.parse(
|
|
27991
|
+
if (JSON.parse(fs28.readFileSync(full, "utf8")).models?.providers) return full;
|
|
27374
27992
|
} catch {
|
|
27375
27993
|
}
|
|
27376
27994
|
}
|
|
27377
27995
|
return "";
|
|
27378
27996
|
}
|
|
27379
|
-
function
|
|
27380
|
-
|
|
27381
|
-
|
|
27382
|
-
|
|
27383
|
-
|
|
27997
|
+
function applyFreeTierToConfig(configPath, opts = {}) {
|
|
27998
|
+
let cfg;
|
|
27999
|
+
try {
|
|
28000
|
+
cfg = JSON.parse(fs28.readFileSync(configPath, "utf8"));
|
|
28001
|
+
} catch (e) {
|
|
28002
|
+
throw new Error(`\u8BFB\u53D6 config \u5931\u8D25: ${configPath}\uFF08${e?.message || e}\uFF09`);
|
|
27384
28003
|
}
|
|
27385
|
-
|
|
28004
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
28005
|
+
const backupPath = `${configPath}.bak-${stamp}`;
|
|
28006
|
+
fs28.copyFileSync(configPath, backupPath);
|
|
28007
|
+
applyFreeTier(cfg, opts);
|
|
28008
|
+
try {
|
|
28009
|
+
fs28.writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf8");
|
|
28010
|
+
} catch (e) {
|
|
28011
|
+
throw new Error(`\u5199\u5165 config \u5931\u8D25: ${configPath}\uFF08${e?.message || e}\uFF09\uFF1B\u539F\u6587\u4EF6\u672A\u6539\u52A8\uFF0C\u5907\u4EFD\u5728 ${backupPath}`);
|
|
28012
|
+
}
|
|
28013
|
+
return {
|
|
28014
|
+
configPath,
|
|
28015
|
+
backupPath,
|
|
28016
|
+
primary: cfg.agents.defaults.model.primary,
|
|
28017
|
+
vision: cfg.agents.defaults.model.vision ?? null
|
|
28018
|
+
};
|
|
27386
28019
|
}
|
|
27387
|
-
|
|
27388
|
-
|
|
27389
|
-
|
|
27390
|
-
|
|
27391
|
-
|
|
28020
|
+
var FREE_TIER_PROVIDER_ID, FREE_TIER_PRIMARY;
|
|
28021
|
+
var init_free_tier_apply = __esm({
|
|
28022
|
+
"src/free-tier-apply.ts"() {
|
|
28023
|
+
"use strict";
|
|
28024
|
+
init_cli_init_config();
|
|
28025
|
+
init_provision_free();
|
|
28026
|
+
FREE_TIER_PROVIDER_ID = "7yi";
|
|
28027
|
+
FREE_TIER_PRIMARY = "7yi/engine7-free";
|
|
27392
28028
|
}
|
|
27393
|
-
|
|
27394
|
-
|
|
27395
|
-
|
|
27396
|
-
|
|
27397
|
-
|
|
27398
|
-
|
|
27399
|
-
|
|
27400
|
-
|
|
27401
|
-
|
|
27402
|
-
|
|
27403
|
-
|
|
28029
|
+
});
|
|
28030
|
+
|
|
28031
|
+
// src/cli-doctor.ts
|
|
28032
|
+
var cli_doctor_exports = {};
|
|
28033
|
+
__export(cli_doctor_exports, {
|
|
28034
|
+
resolveStateDirFromArgs: () => resolveStateDirFromArgs,
|
|
28035
|
+
runDoctor: () => runDoctor
|
|
28036
|
+
});
|
|
28037
|
+
import * as fs29 from "node:fs";
|
|
28038
|
+
import * as path31 from "node:path";
|
|
28039
|
+
import * as net from "node:net";
|
|
28040
|
+
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
28041
|
+
function readVersion() {
|
|
28042
|
+
const here = path31.dirname(fileURLToPath6(import.meta.url));
|
|
28043
|
+
for (const p of [path31.join(here, "..", "package.json"), path31.join(here, "..", "..", "package.json")]) {
|
|
28044
|
+
try {
|
|
28045
|
+
return JSON.parse(fs29.readFileSync(p, "utf8")).version || "unknown";
|
|
28046
|
+
} catch {
|
|
27404
28047
|
}
|
|
27405
28048
|
}
|
|
27406
|
-
return "";
|
|
28049
|
+
return "unknown";
|
|
27407
28050
|
}
|
|
27408
|
-
|
|
27409
|
-
|
|
27410
|
-
|
|
27411
|
-
if (
|
|
27412
|
-
|
|
27413
|
-
|
|
28051
|
+
function channelHasCredentials(name, conf) {
|
|
28052
|
+
if (name === "feishu") return !!(conf?.appId && conf?.appSecret);
|
|
28053
|
+
if (name === "wechat") return !!(conf?.token && conf?.accountId);
|
|
28054
|
+
if (name === "discord") return !!conf?.accounts?.default?.token;
|
|
28055
|
+
return true;
|
|
28056
|
+
}
|
|
28057
|
+
function probePort(port, timeoutMs) {
|
|
28058
|
+
return new Promise((resolve12) => {
|
|
28059
|
+
const sock = net.connect({ port, host: "127.0.0.1", timeout: timeoutMs });
|
|
28060
|
+
sock.once("connect", () => {
|
|
28061
|
+
sock.destroy();
|
|
28062
|
+
resolve12(true);
|
|
28063
|
+
});
|
|
28064
|
+
sock.once("error", () => resolve12(false));
|
|
28065
|
+
sock.once("timeout", () => {
|
|
28066
|
+
sock.destroy();
|
|
28067
|
+
resolve12(false);
|
|
28068
|
+
});
|
|
28069
|
+
});
|
|
28070
|
+
}
|
|
28071
|
+
async function runDoctor(stateDir, opts) {
|
|
28072
|
+
const home = process.env.HOME || process.env.USERPROFILE || ".";
|
|
28073
|
+
const configPath = path31.join(stateDir, "configs", "main7.json");
|
|
28074
|
+
const report = {
|
|
28075
|
+
version: readVersion(),
|
|
28076
|
+
stateDir,
|
|
28077
|
+
configPath,
|
|
28078
|
+
configValid: false,
|
|
28079
|
+
channels: [],
|
|
28080
|
+
secrets: { file: "", exists: false, keys: [] },
|
|
28081
|
+
apiPort: 16890,
|
|
28082
|
+
engineAlive: false,
|
|
28083
|
+
vision: { ref: null, providerExists: null },
|
|
28084
|
+
missingEnvKeys: [],
|
|
28085
|
+
freeTier: { on: false, tokenPresent: false }
|
|
28086
|
+
};
|
|
28087
|
+
let config = null;
|
|
28088
|
+
try {
|
|
28089
|
+
config = JSON.parse(fs29.readFileSync(configPath, "utf8"));
|
|
28090
|
+
report.configValid = true;
|
|
28091
|
+
} catch (e) {
|
|
28092
|
+
report.configError = fs29.existsSync(configPath) ? `\u89E3\u6790\u5931\u8D25: ${e.message}` : "config \u4E0D\u5B58\u5728\uFF08\u5148 engine7 init\uFF09";
|
|
28093
|
+
}
|
|
28094
|
+
if (config?.channels) {
|
|
28095
|
+
for (const [name, conf] of Object.entries(config.channels)) {
|
|
28096
|
+
const c = conf;
|
|
28097
|
+
report.channels.push({ name, enabled: !!c.enabled, hasCredentials: channelHasCredentials(name, c) });
|
|
28098
|
+
}
|
|
28099
|
+
}
|
|
28100
|
+
const secretsDir = path31.join(home, ".engine7-secrets");
|
|
28101
|
+
const cfgBase = path31.basename(report.configPath, ".json") || "main7";
|
|
28102
|
+
const secretsCandidates = [
|
|
28103
|
+
path31.join(secretsDir, `${cfgBase}.env`),
|
|
28104
|
+
path31.join(stateDir, "configs", `.env.${cfgBase}`),
|
|
28105
|
+
path31.join(stateDir, "configs", ".env")
|
|
28106
|
+
];
|
|
28107
|
+
const secretsFile = secretsCandidates.find((p) => fs29.existsSync(p)) || secretsCandidates[0];
|
|
28108
|
+
report.secrets.file = secretsFile;
|
|
28109
|
+
if (fs29.existsSync(secretsFile)) {
|
|
28110
|
+
report.secrets.exists = true;
|
|
28111
|
+
for (const line of fs29.readFileSync(secretsFile, "utf8").split("\n")) {
|
|
28112
|
+
const eq = line.indexOf("=");
|
|
28113
|
+
if (eq > 0 && !line.startsWith("#")) report.secrets.keys.push(line.slice(0, eq).trim());
|
|
28114
|
+
}
|
|
28115
|
+
}
|
|
28116
|
+
report.freeTier = {
|
|
28117
|
+
on: isFreeTierConfig(config),
|
|
28118
|
+
tokenPresent: report.secrets.keys.includes(FREE_TIER_ENV_KEY)
|
|
28119
|
+
};
|
|
28120
|
+
const visionRef = config?.agents?.defaults?.model?.vision;
|
|
28121
|
+
if (typeof visionRef === "string" && visionRef) {
|
|
28122
|
+
const providerId = visionRef.includes("/") ? visionRef.slice(0, visionRef.indexOf("/")) : "";
|
|
28123
|
+
report.vision = {
|
|
28124
|
+
ref: visionRef,
|
|
28125
|
+
providerExists: !!providerId && !!config?.models?.providers?.[providerId]
|
|
28126
|
+
};
|
|
28127
|
+
}
|
|
28128
|
+
const missing = /* @__PURE__ */ new Set();
|
|
28129
|
+
const walk = (node) => {
|
|
28130
|
+
if (typeof node === "string") {
|
|
28131
|
+
if (node.startsWith("env:")) {
|
|
28132
|
+
const name = node.slice(4);
|
|
28133
|
+
if (/^[A-Z][A-Z0-9_]*$/.test(name) && process.env[name] === void 0 && !report.secrets.keys.includes(name)) {
|
|
28134
|
+
missing.add(name);
|
|
28135
|
+
}
|
|
28136
|
+
}
|
|
28137
|
+
return;
|
|
28138
|
+
}
|
|
28139
|
+
if (Array.isArray(node)) {
|
|
28140
|
+
node.forEach(walk);
|
|
28141
|
+
return;
|
|
28142
|
+
}
|
|
28143
|
+
if (node && typeof node === "object") {
|
|
28144
|
+
for (const k of Object.keys(node)) walk(node[k]);
|
|
28145
|
+
}
|
|
28146
|
+
};
|
|
28147
|
+
walk(config);
|
|
28148
|
+
report.missingEnvKeys = [...missing].sort();
|
|
28149
|
+
const stale = [];
|
|
28150
|
+
for (const rel of ["workspace/SESSION-STATE.md", "SESSION-STATE.md"]) {
|
|
28151
|
+
const p = path31.join(stateDir, rel);
|
|
28152
|
+
try {
|
|
28153
|
+
if (!fs29.existsSync(p)) continue;
|
|
28154
|
+
const head = fs29.readFileSync(p, "utf8").split("\n").slice(0, 10).join("\n");
|
|
28155
|
+
const m = head.match(/^#{1,3}\s*当前时间\s*$\n+\s*(\d{4}-\d{2}-\d{2})/m);
|
|
28156
|
+
if (!m) continue;
|
|
28157
|
+
const ageH = (Date.now() - (/* @__PURE__ */ new Date(`${m[1]}T00:00:00Z`)).getTime()) / 36e5;
|
|
28158
|
+
if (ageH > 48) stale.push(`${rel}: ${m[1]}`);
|
|
28159
|
+
} catch {
|
|
28160
|
+
}
|
|
28161
|
+
}
|
|
28162
|
+
report.staleTimeHeaders = stale;
|
|
28163
|
+
report.apiPort = config?.api?.port || 16890;
|
|
28164
|
+
report.engineAlive = await probePort(report.apiPort, opts?.probeTimeoutMs ?? 1500);
|
|
28165
|
+
return report;
|
|
28166
|
+
}
|
|
28167
|
+
function resolveStateDirFromArgs(args) {
|
|
28168
|
+
let stateDir = "";
|
|
28169
|
+
for (let i = 0; i < args.length; i++) {
|
|
28170
|
+
if (args[i] === "--state-dir" && args[i + 1]) stateDir = args[++i].trim();
|
|
28171
|
+
}
|
|
28172
|
+
if (stateDir) return path31.resolve(stateDir);
|
|
28173
|
+
const home = process.env.HOME || process.env.USERPROFILE || ".";
|
|
28174
|
+
const cwd = path31.resolve(process.cwd());
|
|
28175
|
+
if (fs29.existsSync(path31.join(cwd, "configs"))) return cwd;
|
|
28176
|
+
const pointer = path31.join(home, ".engine7.json");
|
|
28177
|
+
if (fs29.existsSync(pointer)) {
|
|
28178
|
+
try {
|
|
28179
|
+
const ptrStateDir = JSON.parse(fs29.readFileSync(pointer, "utf8")).stateDir;
|
|
28180
|
+
if (typeof ptrStateDir === "string" && ptrStateDir.trim()) return path31.resolve(ptrStateDir);
|
|
28181
|
+
} catch {
|
|
28182
|
+
}
|
|
28183
|
+
}
|
|
28184
|
+
return path31.join(home, ".engine7");
|
|
28185
|
+
}
|
|
28186
|
+
var init_cli_doctor = __esm({
|
|
28187
|
+
"src/cli-doctor.ts"() {
|
|
28188
|
+
"use strict";
|
|
28189
|
+
init_free_tier_apply();
|
|
28190
|
+
init_provision_free();
|
|
28191
|
+
}
|
|
28192
|
+
});
|
|
28193
|
+
|
|
28194
|
+
// src/cli-reconfig.ts
|
|
28195
|
+
var cli_reconfig_exports = {};
|
|
28196
|
+
__export(cli_reconfig_exports, {
|
|
28197
|
+
doReconfigFeishu: () => doReconfigFeishu,
|
|
28198
|
+
doReconfigWechat: () => doReconfigWechat
|
|
28199
|
+
});
|
|
28200
|
+
import * as fs30 from "node:fs";
|
|
28201
|
+
import * as path32 from "node:path";
|
|
28202
|
+
function scanForChannelsConfig(configsDir) {
|
|
28203
|
+
if (!fs30.existsSync(configsDir)) return "";
|
|
28204
|
+
for (const f of fs30.readdirSync(configsDir).filter((x) => x.endsWith(".json"))) {
|
|
28205
|
+
const full = path32.join(configsDir, f);
|
|
28206
|
+
try {
|
|
28207
|
+
if (JSON.parse(fs30.readFileSync(full, "utf8")).channels) return full;
|
|
28208
|
+
} catch {
|
|
28209
|
+
}
|
|
28210
|
+
}
|
|
28211
|
+
return "";
|
|
28212
|
+
}
|
|
28213
|
+
function findFeishuConfigFile(stateDir, configName) {
|
|
28214
|
+
const configsDir = path32.join(stateDir, "configs");
|
|
28215
|
+
if (configName) {
|
|
28216
|
+
const p = path32.join(configsDir, configName);
|
|
28217
|
+
if (fs30.existsSync(p)) return p;
|
|
28218
|
+
}
|
|
28219
|
+
return scanForChannelsConfig(configsDir);
|
|
28220
|
+
}
|
|
28221
|
+
function findWechatConfigFile(stateDir, configName) {
|
|
28222
|
+
const configsDir = path32.join(stateDir, "configs");
|
|
28223
|
+
if (configName) {
|
|
28224
|
+
const p = path32.join(configsDir, configName);
|
|
28225
|
+
return fs30.existsSync(p) ? p : "";
|
|
28226
|
+
}
|
|
28227
|
+
const scanned = scanForChannelsConfig(configsDir);
|
|
28228
|
+
if (scanned) return scanned;
|
|
28229
|
+
if (fs30.existsSync(configsDir)) {
|
|
28230
|
+
const candidates = fs30.readdirSync(configsDir).filter((f) => f.endsWith(".json"));
|
|
28231
|
+
if (candidates.length) {
|
|
28232
|
+
const first = path32.join(configsDir, candidates[0]);
|
|
28233
|
+
try {
|
|
28234
|
+
JSON.parse(fs30.readFileSync(first, "utf8"));
|
|
28235
|
+
return first;
|
|
28236
|
+
} catch {
|
|
28237
|
+
}
|
|
28238
|
+
}
|
|
28239
|
+
}
|
|
28240
|
+
return "";
|
|
28241
|
+
}
|
|
28242
|
+
async function doReconfigFeishu(opts) {
|
|
28243
|
+
const { stateDir, configName, events, emit: emit2, deps } = opts;
|
|
28244
|
+
const log = (msg2) => console.log(msg2);
|
|
28245
|
+
if (!events) {
|
|
28246
|
+
log("\u{1F527} \u98DE\u4E66\u626B\u7801\u63A5\u5165...");
|
|
28247
|
+
}
|
|
27414
28248
|
const result = await (deps?.feishuRegister || registerFeishuApp)({
|
|
27415
28249
|
onLog: (msg2) => {
|
|
27416
28250
|
if (!events) console.log(` ${msg2}`);
|
|
@@ -27436,11 +28270,11 @@ async function doReconfigFeishu(opts) {
|
|
|
27436
28270
|
return 2;
|
|
27437
28271
|
}
|
|
27438
28272
|
const configFile = findFeishuConfigFile(stateDir, configName);
|
|
27439
|
-
if (configFile &&
|
|
28273
|
+
if (configFile && fs30.existsSync(configFile)) {
|
|
27440
28274
|
if (events) emit2?.({ event: "progress", step: "write_config" });
|
|
27441
28275
|
let j;
|
|
27442
28276
|
try {
|
|
27443
|
-
j = JSON.parse(
|
|
28277
|
+
j = JSON.parse(fs30.readFileSync(configFile, "utf8"));
|
|
27444
28278
|
} catch (err) {
|
|
27445
28279
|
if (events) emit2?.({ event: "error", message: `\u8BFB\u53D6 config \u5931\u8D25: ${configFile}\uFF08${err?.message || err}\uFF09` });
|
|
27446
28280
|
else console.error(`\u274C \u8BFB\u53D6 config \u5931\u8D25: ${configFile}\uFF08${err?.message || err}\uFF09`);
|
|
@@ -27449,7 +28283,7 @@ async function doReconfigFeishu(opts) {
|
|
|
27449
28283
|
j.channels = j.channels || {};
|
|
27450
28284
|
j.channels.feishu = { enabled: true, appId: result.appId, appSecret: result.appSecret, connectionMode: "websocket", dmPolicy: "pairing", groupPolicy: "open" };
|
|
27451
28285
|
try {
|
|
27452
|
-
|
|
28286
|
+
fs30.writeFileSync(configFile, JSON.stringify(j, null, 2), "utf8");
|
|
27453
28287
|
} catch (err) {
|
|
27454
28288
|
if (events) emit2?.({ event: "error", message: `\u5199\u5165 config \u5931\u8D25: ${configFile}\uFF08${err?.message || err}\uFF09` });
|
|
27455
28289
|
else console.error(`\u274C \u5199\u5165 config \u5931\u8D25: ${configFile}\uFF08${err?.message || err}\uFF09`);
|
|
@@ -27482,7 +28316,7 @@ async function doReconfigWechat(opts) {
|
|
|
27482
28316
|
return 2;
|
|
27483
28317
|
}
|
|
27484
28318
|
const configFile = findWechatConfigFile(stateDir, configName);
|
|
27485
|
-
if (!configFile || !
|
|
28319
|
+
if (!configFile || !fs30.existsSync(configFile)) {
|
|
27486
28320
|
console.log("\n\u26A0\uFE0F \u6CA1\u627E\u5230 config \u6587\u4EF6\uFF0C\u8BF7\u624B\u52A8\u628A\u4EE5\u4E0B\u6BB5\u52A0\u8FDB\u4F60\u7684 config \u7684 channels \u91CC\uFF1A");
|
|
27487
28321
|
console.log(JSON.stringify({ wechat: { enabled: true, token: cred.token, accountId: cred.accountId, dmPolicy: "pairing", group: { policy: "disabled" } } }, null, 2));
|
|
27488
28322
|
if (events) emit2?.({ event: "done", channel: "wechat", detail: cred.accountId, restart_required: true });
|
|
@@ -27491,7 +28325,7 @@ async function doReconfigWechat(opts) {
|
|
|
27491
28325
|
if (events) emit2?.({ event: "progress", step: "write_config" });
|
|
27492
28326
|
let j;
|
|
27493
28327
|
try {
|
|
27494
|
-
j = JSON.parse(
|
|
28328
|
+
j = JSON.parse(fs30.readFileSync(configFile, "utf8"));
|
|
27495
28329
|
} catch (err) {
|
|
27496
28330
|
if (events) emit2?.({ event: "error", message: `\u8BFB\u53D6 config \u5931\u8D25: ${configFile}\uFF08${err?.message || err}\uFF09` });
|
|
27497
28331
|
else console.error(`\u274C \u8BFB\u53D6 config \u5931\u8D25: ${configFile}\uFF08${err?.message || err}\uFF09`);
|
|
@@ -27500,7 +28334,7 @@ async function doReconfigWechat(opts) {
|
|
|
27500
28334
|
j.channels = j.channels || {};
|
|
27501
28335
|
j.channels.wechat = { enabled: true, token: cred.token, accountId: cred.accountId, dmPolicy: "pairing", group: { policy: "disabled" } };
|
|
27502
28336
|
try {
|
|
27503
|
-
|
|
28337
|
+
fs30.writeFileSync(configFile, JSON.stringify(j, null, 2), "utf8");
|
|
27504
28338
|
} catch (err) {
|
|
27505
28339
|
if (events) emit2?.({ event: "error", message: `\u5199\u5165 config \u5931\u8D25: ${configFile}\uFF08${err?.message || err}\uFF09` });
|
|
27506
28340
|
else console.error(`\u274C \u5199\u5165 config \u5931\u8D25: ${configFile}\uFF08${err?.message || err}\uFF09`);
|
|
@@ -27535,20 +28369,20 @@ __export(cli_travel_exports, {
|
|
|
27535
28369
|
resolveArchiveForTest: () => resolveArchiveForTest,
|
|
27536
28370
|
saveTravelConfig: () => saveTravelConfig
|
|
27537
28371
|
});
|
|
27538
|
-
import * as
|
|
27539
|
-
import * as
|
|
28372
|
+
import * as path33 from "node:path";
|
|
28373
|
+
import * as fs31 from "node:fs";
|
|
27540
28374
|
import * as os5 from "node:os";
|
|
27541
28375
|
import { execSync as execSync2 } from "node:child_process";
|
|
27542
28376
|
function loadTravelConfig() {
|
|
27543
28377
|
try {
|
|
27544
|
-
if (!
|
|
27545
|
-
return JSON.parse(
|
|
28378
|
+
if (!fs31.existsSync(CONFIG_PATH)) return null;
|
|
28379
|
+
return JSON.parse(fs31.readFileSync(CONFIG_PATH, "utf-8"));
|
|
27546
28380
|
} catch {
|
|
27547
28381
|
return null;
|
|
27548
28382
|
}
|
|
27549
28383
|
}
|
|
27550
28384
|
function saveTravelConfig(cfg) {
|
|
27551
|
-
|
|
28385
|
+
fs31.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2));
|
|
27552
28386
|
}
|
|
27553
28387
|
function sanitizeContent(content, dirs) {
|
|
27554
28388
|
let result = content;
|
|
@@ -27573,7 +28407,7 @@ function restoreContent(content, dirs) {
|
|
|
27573
28407
|
return result;
|
|
27574
28408
|
}
|
|
27575
28409
|
function isTextFile(filePath) {
|
|
27576
|
-
const ext =
|
|
28410
|
+
const ext = path33.extname(filePath).toLowerCase();
|
|
27577
28411
|
return TEXT_EXTENSIONS.has(ext);
|
|
27578
28412
|
}
|
|
27579
28413
|
function shouldExclude(name) {
|
|
@@ -27588,14 +28422,14 @@ function shouldExclude(name) {
|
|
|
27588
28422
|
}
|
|
27589
28423
|
function collectFiles(rootDir, includeSet, optionalSet) {
|
|
27590
28424
|
const files = [];
|
|
27591
|
-
const entries2 =
|
|
28425
|
+
const entries2 = fs31.readdirSync(rootDir, { withFileTypes: true });
|
|
27592
28426
|
for (const entry of entries2) {
|
|
27593
28427
|
if (shouldExclude(entry.name)) continue;
|
|
27594
|
-
const fullPath =
|
|
27595
|
-
const realPath =
|
|
28428
|
+
const fullPath = path33.join(rootDir, entry.name);
|
|
28429
|
+
const realPath = fs31.realpathSync(fullPath);
|
|
27596
28430
|
if (realPath !== fullPath) {
|
|
27597
28431
|
try {
|
|
27598
|
-
const lstat =
|
|
28432
|
+
const lstat = fs31.lstatSync(fullPath);
|
|
27599
28433
|
if (lstat.isSymbolicLink()) {
|
|
27600
28434
|
continue;
|
|
27601
28435
|
}
|
|
@@ -27616,12 +28450,12 @@ function collectFiles(rootDir, includeSet, optionalSet) {
|
|
|
27616
28450
|
function collectAllFiles(dir) {
|
|
27617
28451
|
const files = [];
|
|
27618
28452
|
try {
|
|
27619
|
-
const entries2 =
|
|
28453
|
+
const entries2 = fs31.readdirSync(dir, { withFileTypes: true });
|
|
27620
28454
|
for (const entry of entries2) {
|
|
27621
28455
|
if (shouldExclude(entry.name)) continue;
|
|
27622
|
-
const fullPath =
|
|
28456
|
+
const fullPath = path33.join(dir, entry.name);
|
|
27623
28457
|
try {
|
|
27624
|
-
if (
|
|
28458
|
+
if (fs31.lstatSync(fullPath).isSymbolicLink()) continue;
|
|
27625
28459
|
} catch {
|
|
27626
28460
|
}
|
|
27627
28461
|
if (entry.isDirectory()) {
|
|
@@ -27636,16 +28470,16 @@ function collectAllFiles(dir) {
|
|
|
27636
28470
|
}
|
|
27637
28471
|
function readMainEngineUuid(agentsDir) {
|
|
27638
28472
|
try {
|
|
27639
|
-
const platformMapPath =
|
|
27640
|
-
const indexMapPath =
|
|
27641
|
-
if (!
|
|
27642
|
-
const platformMap = JSON.parse(
|
|
28473
|
+
const platformMapPath = path33.join(agentsDir, "main", "sessions", "platform-map.json");
|
|
28474
|
+
const indexMapPath = path33.join(agentsDir, "main", "sessions", "session-index.json");
|
|
28475
|
+
if (!fs31.existsSync(platformMapPath) || !fs31.existsSync(indexMapPath)) return null;
|
|
28476
|
+
const platformMap = JSON.parse(fs31.readFileSync(platformMapPath, "utf-8"));
|
|
27643
28477
|
const mainPlatformId = platformMap["scope:main"] || platformMap["main"];
|
|
27644
28478
|
if (!mainPlatformId) return null;
|
|
27645
|
-
const indexMap = JSON.parse(
|
|
28479
|
+
const indexMap = JSON.parse(fs31.readFileSync(indexMapPath, "utf-8"));
|
|
27646
28480
|
const entry = indexMap[mainPlatformId];
|
|
27647
28481
|
if (!entry || !entry.file) return null;
|
|
27648
|
-
const basename10 =
|
|
28482
|
+
const basename10 = path33.basename(entry.file).replace(/\.jsonl$/, "");
|
|
27649
28483
|
return basename10;
|
|
27650
28484
|
} catch {
|
|
27651
28485
|
return null;
|
|
@@ -27653,13 +28487,13 @@ function readMainEngineUuid(agentsDir) {
|
|
|
27653
28487
|
}
|
|
27654
28488
|
function collectRecentSessions(agentsDir, _days = 7) {
|
|
27655
28489
|
const files = [];
|
|
27656
|
-
if (!
|
|
28490
|
+
if (!fs31.existsSync(agentsDir)) return files;
|
|
27657
28491
|
const sessionGroups = /* @__PURE__ */ new Map();
|
|
27658
28492
|
function scanSessionsDir(dir) {
|
|
27659
28493
|
try {
|
|
27660
|
-
const entries2 =
|
|
28494
|
+
const entries2 = fs31.readdirSync(dir, { withFileTypes: true });
|
|
27661
28495
|
for (const entry of entries2) {
|
|
27662
|
-
const fullPath =
|
|
28496
|
+
const fullPath = path33.join(dir, entry.name);
|
|
27663
28497
|
if (entry.isDirectory()) {
|
|
27664
28498
|
scanSessionsDir(fullPath);
|
|
27665
28499
|
continue;
|
|
@@ -27683,7 +28517,7 @@ function collectRecentSessions(agentsDir, _days = 7) {
|
|
|
27683
28517
|
if (entry.name.endsWith(".jsonl")) {
|
|
27684
28518
|
group.jsonl = fullPath;
|
|
27685
28519
|
} else {
|
|
27686
|
-
const stat4 =
|
|
28520
|
+
const stat4 = fs31.statSync(fullPath);
|
|
27687
28521
|
group.archived.push({ file: fullPath, mtime: stat4.mtimeMs });
|
|
27688
28522
|
}
|
|
27689
28523
|
}
|
|
@@ -27702,21 +28536,21 @@ function collectRecentSessions(agentsDir, _days = 7) {
|
|
|
27702
28536
|
}
|
|
27703
28537
|
function collectExportFileSet(stateDir, workspace) {
|
|
27704
28538
|
const wsFiles = collectFiles(workspace, WORKSPACE_INCLUDE, WORKSPACE_OPTIONAL);
|
|
27705
|
-
const sessionFiles = collectRecentSessions(
|
|
27706
|
-
const configsDir =
|
|
28539
|
+
const sessionFiles = collectRecentSessions(path33.join(stateDir, "agents"), 7);
|
|
28540
|
+
const configsDir = path33.join(stateDir, "configs");
|
|
27707
28541
|
const configFiles = [];
|
|
27708
|
-
if (
|
|
28542
|
+
if (fs31.existsSync(configsDir)) {
|
|
27709
28543
|
for (const f of collectAllFiles(configsDir)) {
|
|
27710
|
-
if (shouldExclude(
|
|
28544
|
+
if (shouldExclude(path33.basename(f))) continue;
|
|
27711
28545
|
configFiles.push(f);
|
|
27712
28546
|
}
|
|
27713
28547
|
}
|
|
27714
28548
|
const EVEROS_SKIP = /* @__PURE__ */ new Set([".tmp", ".lock", "import.log", "import_progress.json"]);
|
|
27715
|
-
const everosDir =
|
|
28549
|
+
const everosDir = path33.join(stateDir, ".everos");
|
|
27716
28550
|
const everosFiles = [];
|
|
27717
|
-
if (
|
|
28551
|
+
if (fs31.existsSync(everosDir)) {
|
|
27718
28552
|
for (const f of collectAllFiles(everosDir)) {
|
|
27719
|
-
if (EVEROS_SKIP.has(
|
|
28553
|
+
if (EVEROS_SKIP.has(path33.basename(f))) continue;
|
|
27720
28554
|
everosFiles.push(f);
|
|
27721
28555
|
}
|
|
27722
28556
|
}
|
|
@@ -27724,13 +28558,13 @@ function collectExportFileSet(stateDir, workspace) {
|
|
|
27724
28558
|
}
|
|
27725
28559
|
async function doExport(opts) {
|
|
27726
28560
|
const { agentName, stateDir, note, dryRun: dryRun2 } = opts;
|
|
27727
|
-
const workspace = (opts.workspace ||
|
|
27728
|
-
const engineHome =
|
|
28561
|
+
const workspace = (opts.workspace || path33.join(stateDir, "workspace")).replace(/[/\\]+$/, "");
|
|
28562
|
+
const engineHome = path33.join(os5.homedir(), ".engine7").replace(/[/\\]+$/, "");
|
|
27729
28563
|
console.log(`\u{1F4E6} engine7 export`);
|
|
27730
28564
|
console.log(` agent: ${agentName}`);
|
|
27731
28565
|
console.log(` state: ${stateDir}`);
|
|
27732
28566
|
console.log(` workspace: ${workspace}`);
|
|
27733
|
-
if (!
|
|
28567
|
+
if (!fs31.existsSync(workspace)) {
|
|
27734
28568
|
console.error(`\u274C workspace \u4E0D\u5B58\u5728: ${workspace}`);
|
|
27735
28569
|
process.exit(1);
|
|
27736
28570
|
}
|
|
@@ -27739,16 +28573,16 @@ async function doExport(opts) {
|
|
|
27739
28573
|
console.log(` workspace \u6587\u4EF6: ${wsFiles.length}`);
|
|
27740
28574
|
console.log(` session jsonl: ${sessionFiles.length} (\u6700\u8FD17\u5929)`);
|
|
27741
28575
|
console.log(` configs: ${configFiles.length}`);
|
|
27742
|
-
console.log(` everos: ${everosFiles.length} (${(everosFiles.reduce((s, f) => s +
|
|
28576
|
+
console.log(` everos: ${everosFiles.length} (${(everosFiles.reduce((s, f) => s + fs31.statSync(f).size, 0) / 1024 / 1024).toFixed(1)} MB)`);
|
|
27743
28577
|
if (dryRun2) {
|
|
27744
28578
|
console.log(`
|
|
27745
28579
|
[DRY RUN] \u4F1A\u6253\u5305\u4EE5\u4E0B\u6587\u4EF6:`);
|
|
27746
|
-
wsFiles.slice(0, 20).forEach((f) => console.log(` ${
|
|
28580
|
+
wsFiles.slice(0, 20).forEach((f) => console.log(` ${path33.relative(workspace, f)}`));
|
|
27747
28581
|
if (wsFiles.length > 20) console.log(` ... \u8FD8\u6709 ${wsFiles.length - 20} \u4E2A\u6587\u4EF6`);
|
|
27748
28582
|
if (everosFiles.length > 0) {
|
|
27749
28583
|
console.log(`
|
|
27750
28584
|
[DRY RUN] everos (${everosFiles.length} \u6587\u4EF6):`);
|
|
27751
|
-
everosFiles.slice(0, 10).forEach((f) => console.log(` ${
|
|
28585
|
+
everosFiles.slice(0, 10).forEach((f) => console.log(` ${path33.relative(stateDir, f)}`));
|
|
27752
28586
|
if (everosFiles.length > 10) console.log(` ... \u8FD8\u6709 ${everosFiles.length - 10} \u4E2A\u6587\u4EF6`);
|
|
27753
28587
|
}
|
|
27754
28588
|
console.log(`
|
|
@@ -27756,46 +28590,46 @@ async function doExport(opts) {
|
|
|
27756
28590
|
return;
|
|
27757
28591
|
}
|
|
27758
28592
|
const version = formatLog().replace(/[-: ]/g, "").slice(0, 12).replace(/(d{8})(d{4})/, "$1-$2");
|
|
27759
|
-
const tmpDir =
|
|
27760
|
-
|
|
27761
|
-
const stagingDir =
|
|
27762
|
-
|
|
28593
|
+
const tmpDir = path33.join(os5.tmpdir(), `engine7-export-${agentName}-${version}`);
|
|
28594
|
+
fs31.mkdirSync(tmpDir, { recursive: true });
|
|
28595
|
+
const stagingDir = path33.join(tmpDir, agentName);
|
|
28596
|
+
fs31.mkdirSync(stagingDir, { recursive: true });
|
|
27763
28597
|
let fileCount = 0;
|
|
27764
28598
|
let totalSize = 0;
|
|
27765
28599
|
let copyFail = 0;
|
|
27766
28600
|
const copyOne = (srcFile, relRoot, destRoot) => {
|
|
27767
|
-
const relPath =
|
|
27768
|
-
const destFile =
|
|
27769
|
-
|
|
28601
|
+
const relPath = path33.relative(relRoot, srcFile);
|
|
28602
|
+
const destFile = path33.join(destRoot, relPath);
|
|
28603
|
+
fs31.mkdirSync(path33.dirname(destFile), { recursive: true });
|
|
27770
28604
|
try {
|
|
27771
28605
|
if (isTextFile(srcFile)) {
|
|
27772
|
-
const content =
|
|
27773
|
-
|
|
28606
|
+
const content = fs31.readFileSync(srcFile, "utf-8");
|
|
28607
|
+
fs31.writeFileSync(destFile, sanitizeContent(content, dirs));
|
|
27774
28608
|
} else {
|
|
27775
|
-
|
|
28609
|
+
fs31.copyFileSync(srcFile, destFile);
|
|
27776
28610
|
}
|
|
27777
28611
|
fileCount++;
|
|
27778
|
-
totalSize +=
|
|
28612
|
+
totalSize += fs31.statSync(srcFile).size;
|
|
27779
28613
|
} catch (e) {
|
|
27780
28614
|
copyFail++;
|
|
27781
28615
|
console.warn(` \u26A0\uFE0F \u62F7\u8D1D\u5931\u8D25: ${relPath} (${e.code || e.message})`);
|
|
27782
28616
|
}
|
|
27783
28617
|
};
|
|
27784
|
-
for (const srcFile of wsFiles) copyOne(srcFile, workspace,
|
|
28618
|
+
for (const srcFile of wsFiles) copyOne(srcFile, workspace, path33.join(stagingDir, "workspace"));
|
|
27785
28619
|
for (const srcFile of sessionFiles) {
|
|
27786
|
-
const relPath =
|
|
27787
|
-
const destFile =
|
|
27788
|
-
|
|
28620
|
+
const relPath = path33.relative(stateDir, srcFile);
|
|
28621
|
+
const destFile = path33.join(stagingDir, relPath);
|
|
28622
|
+
fs31.mkdirSync(path33.dirname(destFile), { recursive: true });
|
|
27789
28623
|
try {
|
|
27790
|
-
let content =
|
|
28624
|
+
let content = fs31.readFileSync(srcFile, "utf-8");
|
|
27791
28625
|
content = sanitizeContent(content, dirs);
|
|
27792
|
-
const baseName =
|
|
28626
|
+
const baseName = path33.basename(srcFile);
|
|
27793
28627
|
if (baseName === "session-index.json" || baseName === "platform-map.json") {
|
|
27794
28628
|
content = content.split("\\\\").join("/");
|
|
27795
28629
|
}
|
|
27796
|
-
|
|
28630
|
+
fs31.writeFileSync(destFile, content);
|
|
27797
28631
|
fileCount++;
|
|
27798
|
-
totalSize +=
|
|
28632
|
+
totalSize += fs31.statSync(srcFile).size;
|
|
27799
28633
|
} catch (e) {
|
|
27800
28634
|
copyFail++;
|
|
27801
28635
|
console.warn(` \u26A0\uFE0F \u62F7\u8D1D\u5931\u8D25: ${relPath} (${e.code || e.message})`);
|
|
@@ -27817,13 +28651,13 @@ async function doExport(opts) {
|
|
|
27817
28651
|
totalSize,
|
|
27818
28652
|
note
|
|
27819
28653
|
};
|
|
27820
|
-
|
|
27821
|
-
const archiveFile =
|
|
28654
|
+
fs31.writeFileSync(path33.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
28655
|
+
const archiveFile = path33.join(tmpDir, `${agentName}-${version}.tar.gz`);
|
|
27822
28656
|
console.log(`
|
|
27823
28657
|
\u{1F5DC}\uFE0F \u6253\u5305\u4E2D...`);
|
|
27824
28658
|
const tarExe = process.platform === "win32" ? "C:\\Windows\\System32\\tar.exe" : "tar";
|
|
27825
28659
|
execSync2(`"${tarExe}" -czf "${archiveFile}" -C "${tmpDir}" ${agentName}`, { stdio: "pipe" });
|
|
27826
|
-
const archiveSize =
|
|
28660
|
+
const archiveSize = fs31.statSync(archiveFile).size;
|
|
27827
28661
|
console.log(`\u2705 \u6253\u5305\u5B8C\u6210: ${archiveFile} (${(archiveSize / 1024 / 1024).toFixed(1)} MB, ${fileCount} \u6587\u4EF6${copyFail ? `, \u5931\u8D25 ${copyFail}` : ""})`);
|
|
27828
28662
|
const cfg = loadTravelConfig();
|
|
27829
28663
|
if (!cfg || !cfg.githubToken) {
|
|
@@ -27849,12 +28683,12 @@ function parseImportArgs(args) {
|
|
|
27849
28683
|
else if (args[i] === "--dry-run") dryRun2 = true;
|
|
27850
28684
|
}
|
|
27851
28685
|
if (!stateDir) throw new Error("import \u9700\u8981 --state-dir <path>");
|
|
27852
|
-
if (!agentName) agentName =
|
|
28686
|
+
if (!agentName) agentName = path33.basename(stateDir);
|
|
27853
28687
|
return { stateDir, agentName, version, archiveFile, dryRun: dryRun2 };
|
|
27854
28688
|
}
|
|
27855
28689
|
async function resolveArchiveForTest(opts) {
|
|
27856
28690
|
if (opts.archiveFile) {
|
|
27857
|
-
if (!
|
|
28691
|
+
if (!fs31.existsSync(opts.archiveFile)) {
|
|
27858
28692
|
throw new Error(`\u672C\u5730\u5305\u4E0D\u5B58\u5728: ${opts.archiveFile}`);
|
|
27859
28693
|
}
|
|
27860
28694
|
return opts.archiveFile;
|
|
@@ -27866,8 +28700,8 @@ async function doImport(opts) {
|
|
|
27866
28700
|
console.log(`\u{1F4E5} engine7 import`);
|
|
27867
28701
|
console.log(` agent: ${agentName}`);
|
|
27868
28702
|
console.log(` target: ${stateDir}`);
|
|
27869
|
-
const tmpDir =
|
|
27870
|
-
|
|
28703
|
+
const tmpDir = path33.join(os5.tmpdir(), `engine7-import-${agentName}-${Date.now()}`);
|
|
28704
|
+
fs31.mkdirSync(tmpDir, { recursive: true });
|
|
27871
28705
|
const resolved = await resolveArchiveForTest({
|
|
27872
28706
|
archiveFile: archiveFile || "",
|
|
27873
28707
|
tmpDir,
|
|
@@ -27892,99 +28726,99 @@ async function doImport(opts) {
|
|
|
27892
28726
|
console.log(`\u{1F4C2} \u89E3\u5305\u4E2D...`);
|
|
27893
28727
|
const tarExe = process.platform === "win32" ? "C:\\Windows\\System32\\tar.exe" : "tar";
|
|
27894
28728
|
execSync2(`"${tarExe}" -xzf "${finalArchive}" -C "${tmpDir}"`, { stdio: "pipe" });
|
|
27895
|
-
const stagingDir =
|
|
27896
|
-
const manifestPath =
|
|
27897
|
-
if (!
|
|
28729
|
+
const stagingDir = path33.join(tmpDir, agentName);
|
|
28730
|
+
const manifestPath = path33.join(stagingDir, "manifest.json");
|
|
28731
|
+
if (!fs31.existsSync(manifestPath)) {
|
|
27898
28732
|
console.error(`\u274C manifest.json \u4E0D\u5B58\u5728\uFF0C\u6587\u4EF6\u53EF\u80FD\u635F\u574F`);
|
|
27899
28733
|
process.exit(1);
|
|
27900
28734
|
}
|
|
27901
|
-
const manifest = JSON.parse(
|
|
27902
|
-
const workspace =
|
|
27903
|
-
const engineHome =
|
|
28735
|
+
const manifest = JSON.parse(fs31.readFileSync(manifestPath, "utf-8"));
|
|
28736
|
+
const workspace = path33.join(stateDir, "workspace").replace(/\\/g, "/").replace(/[/\\]+$/, "");
|
|
28737
|
+
const engineHome = path33.join(os5.homedir(), ".engine7").replace(/\\/g, "/").replace(/[/\\]+$/, "");
|
|
27904
28738
|
const dirs = { workspace, stateDir: stateDir.replace(/\\/g, "/").replace(/[/\\]+$/, ""), engineHome };
|
|
27905
28739
|
console.log(` \u7248\u672C: ${manifest.version}`);
|
|
27906
28740
|
console.log(` \u521B\u5EFA: ${manifest.createdAt}`);
|
|
27907
28741
|
console.log(` \u6587\u4EF6\u6570: ${manifest.fileCount}`);
|
|
27908
|
-
const wsStaging =
|
|
28742
|
+
const wsStaging = path33.join(stagingDir, "workspace");
|
|
27909
28743
|
let restoredCount = 0;
|
|
27910
28744
|
let skipCount = 0;
|
|
27911
|
-
if (
|
|
28745
|
+
if (fs31.existsSync(wsStaging)) {
|
|
27912
28746
|
const allFiles = collectAllFiles(wsStaging);
|
|
27913
28747
|
for (const srcFile of allFiles) {
|
|
27914
|
-
const relPath =
|
|
27915
|
-
const destFile =
|
|
27916
|
-
|
|
28748
|
+
const relPath = path33.relative(wsStaging, srcFile);
|
|
28749
|
+
const destFile = path33.join(workspace, relPath);
|
|
28750
|
+
fs31.mkdirSync(path33.dirname(destFile), { recursive: true });
|
|
27917
28751
|
try {
|
|
27918
28752
|
if (isTextFile(srcFile)) {
|
|
27919
|
-
const content =
|
|
27920
|
-
|
|
28753
|
+
const content = fs31.readFileSync(srcFile, "utf-8");
|
|
28754
|
+
fs31.writeFileSync(destFile, restoreContent(content, dirs));
|
|
27921
28755
|
} else {
|
|
27922
|
-
|
|
28756
|
+
fs31.copyFileSync(srcFile, destFile);
|
|
27923
28757
|
}
|
|
27924
28758
|
restoredCount++;
|
|
27925
28759
|
} catch (e) {
|
|
27926
28760
|
skipCount++;
|
|
27927
|
-
console.log(` \u26A0\uFE0F \u8DF3\u8FC7: ${
|
|
28761
|
+
console.log(` \u26A0\uFE0F \u8DF3\u8FC7: ${path33.relative(wsStaging, srcFile)} (${e.code || e.message})`);
|
|
27928
28762
|
}
|
|
27929
28763
|
}
|
|
27930
28764
|
}
|
|
27931
|
-
const agentsStaging =
|
|
27932
|
-
if (
|
|
28765
|
+
const agentsStaging = path33.join(stagingDir, "agents");
|
|
28766
|
+
if (fs31.existsSync(agentsStaging)) {
|
|
27933
28767
|
const sessionFiles = collectAllFiles(agentsStaging);
|
|
27934
28768
|
for (const srcFile of sessionFiles) {
|
|
27935
|
-
const relPath =
|
|
27936
|
-
const destFile =
|
|
27937
|
-
|
|
27938
|
-
const content =
|
|
27939
|
-
|
|
28769
|
+
const relPath = path33.relative(stagingDir, srcFile);
|
|
28770
|
+
const destFile = path33.join(stateDir, relPath);
|
|
28771
|
+
fs31.mkdirSync(path33.dirname(destFile), { recursive: true });
|
|
28772
|
+
const content = fs31.readFileSync(srcFile, "utf-8");
|
|
28773
|
+
fs31.writeFileSync(destFile, restoreContent(content, dirs));
|
|
27940
28774
|
restoredCount++;
|
|
27941
28775
|
}
|
|
27942
28776
|
}
|
|
27943
|
-
const configsStaging =
|
|
27944
|
-
if (
|
|
28777
|
+
const configsStaging = path33.join(stagingDir, "configs");
|
|
28778
|
+
if (fs31.existsSync(configsStaging)) {
|
|
27945
28779
|
const cfgFiles = collectAllFiles(configsStaging);
|
|
27946
28780
|
for (const srcFile of cfgFiles) {
|
|
27947
|
-
const relPath =
|
|
27948
|
-
const destFile =
|
|
27949
|
-
|
|
28781
|
+
const relPath = path33.relative(stagingDir, srcFile);
|
|
28782
|
+
const destFile = path33.join(stateDir, relPath);
|
|
28783
|
+
fs31.mkdirSync(path33.dirname(destFile), { recursive: true });
|
|
27950
28784
|
if (isTextFile(srcFile)) {
|
|
27951
|
-
const content =
|
|
27952
|
-
|
|
28785
|
+
const content = fs31.readFileSync(srcFile, "utf-8");
|
|
28786
|
+
fs31.writeFileSync(destFile, restoreContent(content, dirs));
|
|
27953
28787
|
} else {
|
|
27954
|
-
|
|
28788
|
+
fs31.copyFileSync(srcFile, destFile);
|
|
27955
28789
|
}
|
|
27956
28790
|
restoredCount++;
|
|
27957
28791
|
}
|
|
27958
28792
|
}
|
|
27959
|
-
const everosStaging =
|
|
27960
|
-
if (
|
|
28793
|
+
const everosStaging = path33.join(stagingDir, ".everos");
|
|
28794
|
+
if (fs31.existsSync(everosStaging)) {
|
|
27961
28795
|
const everosFiles = collectAllFiles(everosStaging);
|
|
27962
28796
|
for (const srcFile of everosFiles) {
|
|
27963
|
-
const relPath =
|
|
27964
|
-
const destFile =
|
|
27965
|
-
|
|
28797
|
+
const relPath = path33.relative(stagingDir, srcFile);
|
|
28798
|
+
const destFile = path33.join(stateDir, relPath);
|
|
28799
|
+
fs31.mkdirSync(path33.dirname(destFile), { recursive: true });
|
|
27966
28800
|
try {
|
|
27967
28801
|
if (isTextFile(srcFile)) {
|
|
27968
|
-
const content =
|
|
27969
|
-
|
|
28802
|
+
const content = fs31.readFileSync(srcFile, "utf-8");
|
|
28803
|
+
fs31.writeFileSync(destFile, restoreContent(content, dirs));
|
|
27970
28804
|
} else {
|
|
27971
|
-
|
|
28805
|
+
fs31.copyFileSync(srcFile, destFile);
|
|
27972
28806
|
}
|
|
27973
28807
|
restoredCount++;
|
|
27974
28808
|
} catch (e) {
|
|
27975
28809
|
skipCount++;
|
|
27976
|
-
console.log(` \u26A0\uFE0F \u8DF3\u8FC7: ${
|
|
28810
|
+
console.log(` \u26A0\uFE0F \u8DF3\u8FC7: ${path33.relative(everosStaging, srcFile)} (${e.code || e.message})`);
|
|
27977
28811
|
}
|
|
27978
28812
|
}
|
|
27979
28813
|
console.log(` everos: ${everosFiles.length} \u6587\u4EF6\u5DF2\u6062\u590D`);
|
|
27980
28814
|
}
|
|
27981
28815
|
console.log(`\u2705 \u6062\u590D\u5B8C\u6210: ${restoredCount} \u6587\u4EF6 \u2192 ${stateDir}${skipCount ? ` (\u8DF3\u8FC7 ${skipCount})` : ""}`);
|
|
27982
28816
|
const platformConfigName = process.platform === "darwin" ? "xiaoke-mac.json" : "xiaoke-win.json";
|
|
27983
|
-
const restoredConfigPath =
|
|
27984
|
-
const configToUse =
|
|
28817
|
+
const restoredConfigPath = path33.join(stateDir, "configs", platformConfigName);
|
|
28818
|
+
const configToUse = fs31.existsSync(restoredConfigPath) ? restoredConfigPath : path33.join(stateDir, "configs", "xiaoke.json");
|
|
27985
28819
|
console.log(`
|
|
27986
28820
|
\u{1F4A1} \u4E0B\u4E00\u6B65: engine7 start --config "${configToUse}"`);
|
|
27987
|
-
|
|
28821
|
+
fs31.rmSync(tmpDir, { recursive: true, force: true });
|
|
27988
28822
|
}
|
|
27989
28823
|
async function uploadToGitHub(cfg, archiveFile, agentName, version, manifest) {
|
|
27990
28824
|
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
@@ -28007,618 +28841,205 @@ async function uploadToGitHub(cfg, archiveFile, agentName, version, manifest) {
|
|
|
28007
28841
|
prerelease: false,
|
|
28008
28842
|
make_latest: "true"
|
|
28009
28843
|
})
|
|
28010
|
-
});
|
|
28011
|
-
if (!createRes.ok) {
|
|
28012
|
-
const err = await createRes.text();
|
|
28013
|
-
throw new Error(`\u521B\u5EFA release \u5931\u8D25: ${createRes.status} ${err}`);
|
|
28014
|
-
}
|
|
28015
|
-
const release2 = await createRes.json();
|
|
28016
|
-
const uploadUrl = release2.upload_url.replace("{?name,label}", "");
|
|
28017
|
-
const fileBuffer = fs30.readFileSync(archiveFile);
|
|
28018
|
-
const fileName = path32.basename(archiveFile);
|
|
28019
|
-
const uploadRes = await fetch(`${uploadUrl}?name=${fileName}`, {
|
|
28020
|
-
method: "POST",
|
|
28021
|
-
headers: {
|
|
28022
|
-
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
28023
|
-
"Accept": "application/vnd.github+json",
|
|
28024
|
-
"Content-Type": "application/gzip",
|
|
28025
|
-
"Content-Length": String(fileBuffer.length)
|
|
28026
|
-
},
|
|
28027
|
-
body: fileBuffer
|
|
28028
|
-
});
|
|
28029
|
-
if (!uploadRes.ok) {
|
|
28030
|
-
const err = await uploadRes.text();
|
|
28031
|
-
throw new Error(`\u4E0A\u4F20 asset \u5931\u8D25: ${uploadRes.status} ${err}`);
|
|
28032
|
-
}
|
|
28033
|
-
console.log(`\u2705 \u4E0A\u4F20\u6210\u529F!`);
|
|
28034
|
-
console.log(` release: ${release2.html_url}`);
|
|
28035
|
-
} catch (e) {
|
|
28036
|
-
console.error(`\u274C GitHub \u4E0A\u4F20\u5931\u8D25: ${e.message}`);
|
|
28037
|
-
console.error(` archive \u4ECD\u5728: ${archiveFile}`);
|
|
28038
|
-
throw e;
|
|
28039
|
-
}
|
|
28040
|
-
try {
|
|
28041
|
-
fs30.rmSync(path32.dirname(archiveFile), { recursive: true, force: true });
|
|
28042
|
-
} catch {
|
|
28043
|
-
}
|
|
28044
|
-
}
|
|
28045
|
-
async function downloadFromGitHub(cfg, agentName, version, destDir) {
|
|
28046
|
-
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
28047
|
-
const tag = version || await getLatestReleaseTag(cfg, agentName);
|
|
28048
|
-
console.log(`\u2B07\uFE0F \u4E0B\u8F7D: ${repo} release ${tag}`);
|
|
28049
|
-
const res = await fetch(`https://api.github.com/repos/${repo}/releases/tags/${tag}`, {
|
|
28050
|
-
headers: {
|
|
28051
|
-
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
28052
|
-
"Accept": "application/vnd.github+json"
|
|
28053
|
-
}
|
|
28054
|
-
});
|
|
28055
|
-
if (!res.ok) {
|
|
28056
|
-
throw new Error(`\u83B7\u53D6 release \u5931\u8D25: ${res.status}`);
|
|
28057
|
-
}
|
|
28058
|
-
const release2 = await res.json();
|
|
28059
|
-
const asset = release2.assets?.find((a) => a.name.endsWith(".tar.gz") || a.name.endsWith(".zip"));
|
|
28060
|
-
if (!asset) {
|
|
28061
|
-
throw new Error(`release ${tag} \u6CA1\u6709 tar.gz/zip asset`);
|
|
28062
|
-
}
|
|
28063
|
-
const downloadRes = await fetch(asset.url, {
|
|
28064
|
-
headers: {
|
|
28065
|
-
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
28066
|
-
"Accept": "application/octet-stream"
|
|
28067
|
-
}
|
|
28068
|
-
});
|
|
28069
|
-
if (!downloadRes.ok) {
|
|
28070
|
-
throw new Error(`\u4E0B\u8F7D asset \u5931\u8D25: ${downloadRes.status}`);
|
|
28071
|
-
}
|
|
28072
|
-
const buffer = Buffer.from(await downloadRes.arrayBuffer());
|
|
28073
|
-
const archiveFile = path32.join(destDir, asset.name);
|
|
28074
|
-
fs30.writeFileSync(archiveFile, buffer);
|
|
28075
|
-
return archiveFile;
|
|
28076
|
-
}
|
|
28077
|
-
async function getLatestReleaseTag(cfg, agentName) {
|
|
28078
|
-
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
28079
|
-
const res = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=30`, {
|
|
28080
|
-
headers: {
|
|
28081
|
-
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
28082
|
-
"Accept": "application/vnd.github+json"
|
|
28083
|
-
}
|
|
28084
|
-
});
|
|
28085
|
-
if (!res.ok) {
|
|
28086
|
-
throw new Error(`\u83B7\u53D6 release \u5217\u8868\u5931\u8D25: ${res.status}`);
|
|
28087
|
-
}
|
|
28088
|
-
const releases = await res.json();
|
|
28089
|
-
const match = releases.find((r) => r.tag_name?.startsWith(`${agentName}-`));
|
|
28090
|
-
if (!match) {
|
|
28091
|
-
throw new Error(`\u6CA1\u6709\u627E\u5230 ${agentName} \u7684 release`);
|
|
28092
|
-
}
|
|
28093
|
-
return match.tag_name;
|
|
28094
|
-
}
|
|
28095
|
-
var CONFIG_PATH, PATH_PLACEHOLDERS, WORKSPACE_INCLUDE, WORKSPACE_OPTIONAL, WORKSPACE_EXCLUDE, TEXT_EXTENSIONS;
|
|
28096
|
-
var init_cli_travel = __esm({
|
|
28097
|
-
"src/cli-travel.ts"() {
|
|
28098
|
-
"use strict";
|
|
28099
|
-
init_clock();
|
|
28100
|
-
CONFIG_PATH = path32.join(os5.homedir(), ".engine7-travel.json");
|
|
28101
|
-
PATH_PLACEHOLDERS = [
|
|
28102
|
-
{ placeholder: "{{WORKSPACE}}", getOriginal: (d) => d.workspace },
|
|
28103
|
-
{ placeholder: "{{ENGINE_HOME}}", getOriginal: (d) => d.engineHome },
|
|
28104
|
-
{ placeholder: "{{STATE_DIR}}", getOriginal: (d) => d.stateDir }
|
|
28105
|
-
];
|
|
28106
|
-
WORKSPACE_INCLUDE = /* @__PURE__ */ new Set([
|
|
28107
|
-
// 核心文件
|
|
28108
|
-
"AGENTS.md",
|
|
28109
|
-
"SOUL.md",
|
|
28110
|
-
"MEMORY.md",
|
|
28111
|
-
"USER.md",
|
|
28112
|
-
"HEARTBEAT.md",
|
|
28113
|
-
"INDEX.md",
|
|
28114
|
-
"SESSION-STATE.md",
|
|
28115
|
-
// 核心目录
|
|
28116
|
-
"prompts",
|
|
28117
|
-
"topics",
|
|
28118
|
-
"memory",
|
|
28119
|
-
"inner-voice",
|
|
28120
|
-
"docs",
|
|
28121
|
-
"skills",
|
|
28122
|
-
"voice-chat",
|
|
28123
|
-
"scripts",
|
|
28124
|
-
"selfie",
|
|
28125
|
-
"moodboard",
|
|
28126
|
-
// 状态文件 + 目录
|
|
28127
|
-
".calendar",
|
|
28128
|
-
"nudge-state.json"
|
|
28129
|
-
]);
|
|
28130
|
-
WORKSPACE_OPTIONAL = /* @__PURE__ */ new Set([
|
|
28131
|
-
"images",
|
|
28132
|
-
"tools"
|
|
28133
|
-
]);
|
|
28134
|
-
WORKSPACE_EXCLUDE = /* @__PURE__ */ new Set([
|
|
28135
|
-
"livestream",
|
|
28136
|
-
"content-library",
|
|
28137
|
-
"tmp",
|
|
28138
|
-
".git",
|
|
28139
|
-
"node_modules",
|
|
28140
|
-
"memory_runs",
|
|
28141
|
-
"workspace",
|
|
28142
|
-
"prompt-archive",
|
|
28143
|
-
"aim-archive",
|
|
28144
|
-
"test-agent",
|
|
28145
|
-
"nul",
|
|
28146
|
-
"*.bak*",
|
|
28147
|
-
"*.bak-*",
|
|
28148
|
-
"~$*"
|
|
28149
|
-
]);
|
|
28150
|
-
TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
28151
|
-
".md",
|
|
28152
|
-
".json",
|
|
28153
|
-
".txt",
|
|
28154
|
-
".js",
|
|
28155
|
-
".ts",
|
|
28156
|
-
".mjs",
|
|
28157
|
-
".py",
|
|
28158
|
-
".yaml",
|
|
28159
|
-
".yml",
|
|
28160
|
-
".cmd",
|
|
28161
|
-
".bat",
|
|
28162
|
-
".sh",
|
|
28163
|
-
".csv",
|
|
28164
|
-
".html",
|
|
28165
|
-
".toml",
|
|
28166
|
-
".list"
|
|
28167
|
-
// .everos 的 everos.toml/config.toml/ome.toml/everos-env.list
|
|
28168
|
-
]);
|
|
28169
|
-
}
|
|
28170
|
-
});
|
|
28171
|
-
|
|
28172
|
-
// src/cli-init.ts
|
|
28173
|
-
import * as path33 from "node:path";
|
|
28174
|
-
import * as fs31 from "node:fs";
|
|
28175
|
-
|
|
28176
|
-
// src/cli-service.ts
|
|
28177
|
-
function parseServiceArgs(args) {
|
|
28178
|
-
const action = args[0] || "";
|
|
28179
|
-
let configPath = "";
|
|
28180
|
-
let cwd = process.cwd();
|
|
28181
|
-
let nodeExe = "";
|
|
28182
|
-
for (let i = 1; i < args.length; i++) {
|
|
28183
|
-
if (args[i] === "--config" && args[i + 1]) configPath = args[++i];
|
|
28184
|
-
else if (args[i] === "--cwd" && args[i + 1]) cwd = args[++i];
|
|
28185
|
-
else if (args[i] === "--node-exe" && args[i + 1]) nodeExe = args[++i];
|
|
28186
|
-
}
|
|
28187
|
-
return { action, configPath, cwd, nodeExe };
|
|
28188
|
-
}
|
|
28189
|
-
function buildServiceWrapper(opts) {
|
|
28190
|
-
const nodeExe = opts.nodeExe || process.execPath;
|
|
28191
|
-
return `@echo off\r
|
|
28192
|
-
cd /d "${opts.cwd}"\r
|
|
28193
|
-
"${nodeExe}" "${opts.cliMjs}" start --config "${opts.configPath}"\r
|
|
28194
|
-
`;
|
|
28195
|
-
}
|
|
28196
|
-
|
|
28197
|
-
// src/cli-init.ts
|
|
28198
|
-
init_feishu_quick_register();
|
|
28199
|
-
init_wechat();
|
|
28200
|
-
init_qr_render();
|
|
28201
|
-
init_cli_events();
|
|
28202
|
-
import * as readline3 from "node:readline";
|
|
28203
|
-
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
28204
|
-
|
|
28205
|
-
// src/cli-init-answers.ts
|
|
28206
|
-
init_clock();
|
|
28207
|
-
import * as fs2 from "node:fs";
|
|
28208
|
-
|
|
28209
|
-
// src/cli-init-config.ts
|
|
28210
|
-
import * as path2 from "node:path";
|
|
28211
|
-
var SCHEMA_VERSION = 1;
|
|
28212
|
-
var FREE_TIER_RELAY_URL = "https://www.twinsun.top:8866";
|
|
28213
|
-
function isFreeTier(v) {
|
|
28214
|
-
return v.freeTier === true || v.primaryProvider === "7yi";
|
|
28215
|
-
}
|
|
28216
|
-
function resolveRelayUrl(relayArg, env = process.env) {
|
|
28217
|
-
return (relayArg || "").trim() || (env.E7_RELAY_URL || "").trim() || FREE_TIER_RELAY_URL;
|
|
28218
|
-
}
|
|
28219
|
-
var DEFAULT_PRIMARY_MODEL = {
|
|
28220
|
-
dashscope: "dashscope/qwen3.8-max",
|
|
28221
|
-
minimax: "minimax/MiniMax-M3",
|
|
28222
|
-
zhipu: "zhipu/glm-5.3",
|
|
28223
|
-
deepseek: "deepseek/deepseek-v4-pro",
|
|
28224
|
-
"7yi": "7yi/engine7-free"
|
|
28225
|
-
};
|
|
28226
|
-
var VISION_CAPABLE = {
|
|
28227
|
-
// 0917:原先标 true,但同一个文件里 qwen3.8-max 的 input 是 ['text'],安装器目录
|
|
28228
|
-
// 也明确标 vision=false(只有 Flash 叫"多模态")。矛盾直接导致**默认 CLI 装机路径**
|
|
28229
|
-
// 重演 P0-2 的症状:vision 复用主模型 → 该型号 modelInputs=['text'] → 引擎把历史和
|
|
28230
|
-
// 当前消息里的图全剥成 "[image]"。改成 false 后 resolveVisionModel 会落到
|
|
28231
|
-
// qwen3.8-flash(input 真含 image)。
|
|
28232
|
-
"dashscope/qwen3.8-max": false,
|
|
28233
|
-
"dashscope/qwen3.8-flash": true,
|
|
28234
|
-
"minimax/MiniMax-M3": true,
|
|
28235
|
-
// 0918 移除 'minimax/MiniMax-M2.7'(内置表同时去掉该型号)
|
|
28236
|
-
"zhipu/glm-5v-turbo": true,
|
|
28237
|
-
"zhipu/glm-5.3": false,
|
|
28238
|
-
// 0917:DeepSeek 全线按支持图片处理。依据(官方更新日志 + Vision 文档):
|
|
28239
|
-
// - `deepseek-flash`(V4.1 Flash)原生多模态,官方 Vision 文档明确收图
|
|
28240
|
-
// https://api-docs.deepseek.com/guides/vision
|
|
28241
|
-
// - `deepseek-v4-flash` / `deepseek-v4-flash-vision-exp`:退役名,请求由 V4.1 Flash
|
|
28242
|
-
// 承接(临时兼容路由,官方未给移除日期)→ 同样收图
|
|
28243
|
-
// - `deepseek-v4-pro`:官方旗舰已停,9/14 起请求同样由 V4.1 Flash 承接;新的
|
|
28244
|
-
// deepseek-pro 发布后大概率也走多模态(flash 都支持了,pro 不会倒退)
|
|
28245
|
-
// 原先两个都标 false:v4-pro 标 false 会让今天用它的 agent **静默丢图**,
|
|
28246
|
-
// deepseek-flash 标 false 还会触发 P0-2 的幽灵引用兜底。
|
|
28247
|
-
// 声明多一分的代价是上游响亮报错,少一分的代价是无声丢图——选前者。
|
|
28248
|
-
"deepseek/deepseek-flash": true,
|
|
28249
|
-
"deepseek/deepseek-v4-flash": true,
|
|
28250
|
-
"deepseek/deepseek-v4-pro": true,
|
|
28251
|
-
// 免费档(0919):engine7-free 只收文本,engine7-vis-free 才带 image。
|
|
28252
|
-
// 标错会让主模型被当成能看图 → 图发给一个 input=['text'] 的型号 → 静默丢图。
|
|
28253
|
-
"7yi/engine7-free": false,
|
|
28254
|
-
"7yi/engine7-vis-free": true
|
|
28255
|
-
};
|
|
28256
|
-
var PROVIDER_DEFS = {
|
|
28257
|
-
dashscope: {
|
|
28258
|
-
// 双通道(0916):按量付费(sk-)走 compatible-mode;Token Plan 订阅(sk-sp-)走专属域名,
|
|
28259
|
-
// 两边密钥/端点不可互换——用错就"订阅了还按量计费"。计划选择见 dashscopePlan。
|
|
28260
|
-
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
28261
|
-
api: "openai-completions",
|
|
28262
|
-
models: [
|
|
28263
|
-
{ id: "qwen3.8-max", name: "Qwen 3.8 Max", reasoning: true, input: ["text"], contextWindow: 1e6, maxTokens: 65536 },
|
|
28264
|
-
{ id: "qwen3.8-flash", name: "Qwen 3.8 Flash\uFF08\u591A\u6A21\u6001\uFF09", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 65536 }
|
|
28265
|
-
]
|
|
28266
|
-
},
|
|
28267
|
-
minimax: {
|
|
28268
|
-
baseUrl: "https://api.minimaxi.com/anthropic",
|
|
28269
|
-
api: "anthropic",
|
|
28270
|
-
models: [
|
|
28271
|
-
// 0918 去掉 MiniMax-M2.7:有 M3 之后没人用了(翀哥)
|
|
28272
|
-
{ id: "MiniMax-M3", name: "MiniMax M3", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 64e3 }
|
|
28273
|
-
]
|
|
28274
|
-
},
|
|
28275
|
-
zhipu: {
|
|
28276
|
-
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
28277
|
-
api: "openai-completions",
|
|
28278
|
-
models: [
|
|
28279
|
-
{ id: "glm-5.3", name: "GLM-5.3 (\u6700\u65B0\u4E3B\u529B)", reasoning: true, input: ["text"], contextWindow: 1e6, maxTokens: 128e3 },
|
|
28280
|
-
{ id: "glm-5v-turbo", name: "GLM-5V-Turbo (\u89C6\u89C9)", reasoning: true, input: ["text", "image"], contextWindow: 2e5, maxTokens: 128e3 }
|
|
28281
|
-
]
|
|
28282
|
-
},
|
|
28283
|
-
deepseek: {
|
|
28284
|
-
baseUrl: "https://api.deepseek.com/anthropic",
|
|
28285
|
-
api: "anthropic",
|
|
28286
|
-
thinking: { enabled: true, budgetTokens: 8192 },
|
|
28287
|
-
models: [
|
|
28288
|
-
// 0916 改名:deepseek-v4-flash → deepseek-flash(旧名仍可调,请求由 V4.1 Flash 承接)
|
|
28289
|
-
// 0917 补 image:V4.1 Flash 原生多模态(见 VISION_CAPABLE 注释)
|
|
28290
|
-
{ id: "deepseek-flash", name: "DeepSeek Flash", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 384e3 },
|
|
28291
|
-
// 0917 补 image:官方旗舰已停、9/14 起请求由 V4.1 Flash 承接,实际收图
|
|
28292
|
-
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 384e3 }
|
|
28293
|
-
]
|
|
28294
|
-
},
|
|
28295
|
-
"7yi": {
|
|
28296
|
-
// 引擎免费档的中转站。baseUrl 是兜底值——真正的权威值来自领号响应
|
|
28297
|
-
// (服务端按请求 Host 自报地址),由安装器作为 customBaseUrl 下发。
|
|
28298
|
-
// 这样挪机器、换域名只改服务端,不用重发安装包。
|
|
28299
|
-
//
|
|
28300
|
-
// ⚠️ 0921 修:这里原本写的是 `https://www.twinsun.top/v1`(443),而 **443 上不是
|
|
28301
|
-
// 中转站**(实测回的是另一个服务的 FastAPI 404)。安装器那条路每次都会下发
|
|
28302
|
-
// customBaseUrl 把它盖掉,所以正式装机撞不到;但**终端向导选 7yi 时不问 baseUrl**,
|
|
28303
|
-
// 直接落到这个兜底值——于是命令行用户拿到的是「号领到了、引擎起来了、一发消息就废」。
|
|
28304
|
-
// 用常量拼,别再手写一遍(漏端口就是这个坑)。
|
|
28305
|
-
baseUrl: `${FREE_TIER_RELAY_URL}/v1`,
|
|
28306
|
-
api: "anthropic",
|
|
28307
|
-
models: [
|
|
28308
|
-
// ⚠️ contextWindow **不能照抄 1000000**。
|
|
28309
|
-
//
|
|
28310
|
-
// engine7-free 是虚拟模型,真实上限取决于当时哪一档在接。写满了会让压缩阈值
|
|
28311
|
-
// 高到永不触发——实测病例(0919 小柯):声明 1M → 阈值 906137,而她的上下文
|
|
28312
|
-
// 已滚到 191080 仍不压缩,最后那条 191k 的请求把 SenseNova 的 TPM 打爆
|
|
28313
|
-
// (429 inference exceeds tpm/rpm limit),整条链退到付费的 MiniMax。
|
|
28314
|
-
//
|
|
28315
|
-
// 实测数据点(0919,SenseNova 那一档):
|
|
28316
|
-
// 114K token → 成功(10s,真实输出)
|
|
28317
|
-
// 191K token → 失败(tpm/rpm 429)
|
|
28318
|
-
// 分界在 114K~191K 之间,中点无数据。
|
|
28319
|
-
//
|
|
28320
|
-
// 阈值与 contextWindow 是比例关系:实测 1,000,000 → 906,137(×0.906)。
|
|
28321
|
-
// 取 150000 → 阈值约 136K,在成功线上方留 22K 余量,又不去赌无数据区。
|
|
28322
|
-
// 等中点实测出来再放宽。
|
|
28323
|
-
{ id: "engine7-free", name: "Engine7 \u514D\u8D39\uFF08\u6587\u672C\uFF09", reasoning: true, input: ["text"], contextWindow: 15e4, maxTokens: 64e3 },
|
|
28324
|
-
// input 带 image:实测 Claude 格式的图片块经 /v1/messages 能被上游正确识别
|
|
28325
|
-
{ id: "engine7-vis-free", name: "Engine7 \u514D\u8D39\uFF08\u89C6\u89C9\uFF09", reasoning: true, input: ["text", "image"], contextWindow: 15e4, maxTokens: 64e3 }
|
|
28326
|
-
// 注意:engine7-img-free 不在这里。它在 chat 端点是 404(不是 chat 模型),
|
|
28327
|
-
// 生图由 my_selfie 自己拼 /v1/images/edits,relayModel 默认值已经是它。
|
|
28328
|
-
]
|
|
28329
|
-
}
|
|
28330
|
-
};
|
|
28331
|
-
var MODEL_CHOICES = Object.fromEntries(
|
|
28332
|
-
Object.entries(PROVIDER_DEFS).map(([pid, def]) => [
|
|
28333
|
-
pid,
|
|
28334
|
-
def.models.map((m) => `${pid}/${m.id}`)
|
|
28335
|
-
])
|
|
28336
|
-
);
|
|
28337
|
-
var CUSTOM_DEFAULT_INPUT = ["text", "image"];
|
|
28338
|
-
function modelIdOf(ref) {
|
|
28339
|
-
const i = ref.indexOf("/");
|
|
28340
|
-
return i >= 0 ? ref.slice(i + 1) : ref;
|
|
28341
|
-
}
|
|
28342
|
-
function menuProviderOf(providerId) {
|
|
28343
|
-
return PROVIDER_DEFS[providerId] ? providerId : "custom";
|
|
28344
|
-
}
|
|
28345
|
-
function resolveProviderTarget(input) {
|
|
28346
|
-
const isCustom = !PROVIDER_DEFS[input.menuProvider];
|
|
28347
|
-
const providerId = isCustom ? (input.customProviderId || "").trim() || "custom" : input.menuProvider;
|
|
28348
|
-
const rawRef = (input.modelRef || "").trim();
|
|
28349
|
-
const modelId = (rawRef.includes("/") ? modelIdOf(rawRef) : rawRef) || (input.modelId || "").trim();
|
|
28350
|
-
return { providerId, modelId, modelRef: modelId ? `${providerId}/${modelId}` : "" };
|
|
28351
|
-
}
|
|
28352
|
-
function visionOf(ref, catalog) {
|
|
28353
|
-
const hit = catalog?.find((m) => m.id === modelIdOf(ref));
|
|
28354
|
-
if (hit && typeof hit.vision === "boolean") return hit.vision;
|
|
28355
|
-
return VISION_CAPABLE[ref];
|
|
28356
|
-
}
|
|
28357
|
-
function builtinVisionModels(providerId) {
|
|
28358
|
-
const def = PROVIDER_DEFS[providerId];
|
|
28359
|
-
if (!def) return [];
|
|
28360
|
-
return def.models.filter((m) => (m.input || []).includes("image")).map((m) => `${providerId}/${m.id}`);
|
|
28361
|
-
}
|
|
28362
|
-
function resolveVisionModel(providerId, primaryModel, catalog) {
|
|
28363
|
-
if (visionOf(primaryModel, catalog) !== false) return primaryModel;
|
|
28364
|
-
const candidates = catalog?.length ? catalog.filter((m) => m.vision === true).map((m) => `${providerId}/${m.id}`) : builtinVisionModels(providerId);
|
|
28365
|
-
return candidates[0] || "";
|
|
28366
|
-
}
|
|
28367
|
-
var TEMPLATE_VALUES = {
|
|
28368
|
-
stateDir: "{{STATE_DIR}}",
|
|
28369
|
-
timezone: "{{TIMEZONE}}",
|
|
28370
|
-
agentName: "{{AGENT_NAME}}",
|
|
28371
|
-
persona: "assistant",
|
|
28372
|
-
chatMode: "auto",
|
|
28373
|
-
primaryProvider: "dashscope",
|
|
28374
|
-
zhipuPlan: "",
|
|
28375
|
-
dashscopePlan: "",
|
|
28376
|
-
providerModels: null,
|
|
28377
|
-
// 走内置模型表,模板里展示内置形状
|
|
28378
|
-
primaryApiKey: "",
|
|
28379
|
-
// generateConfig 只写 env: 占位符,明文不落 config
|
|
28380
|
-
primaryModel: "dashscope/qwen3.8-max",
|
|
28381
|
-
// 派生而非手写:qwen3.8-max 不支持图片,vision 落到 flash。手写就会再制造一次
|
|
28382
|
-
// "vision 指向一个 input 里没有 image 的型号"(见 VISION_CAPABLE 注释)。
|
|
28383
|
-
visionModel: resolveVisionModel("dashscope", "dashscope/qwen3.8-max"),
|
|
28384
|
-
discordEnabled: false,
|
|
28385
|
-
discordToken: "",
|
|
28386
|
-
discordUserId: "",
|
|
28387
|
-
feishuEnabled: true,
|
|
28388
|
-
feishuAppId: "{{FEISHU_APP_ID}}",
|
|
28389
|
-
feishuAppSecret: "{{FEISHU_APP_SECRET}}",
|
|
28390
|
-
feishuOpenId: "",
|
|
28391
|
-
wechatEnabled: false,
|
|
28392
|
-
// 微信块是条件写入的,模板只展示通用形状
|
|
28393
|
-
wechatToken: "",
|
|
28394
|
-
wechatAccountId: "",
|
|
28395
|
-
customBaseUrl: "",
|
|
28396
|
-
customModelId: "",
|
|
28397
|
-
customApi: "anthropic",
|
|
28398
|
-
customProviderId: "",
|
|
28399
|
-
// 用 env: 而不是 {{}}:{{}} 是脚手架占位符({{STATE_DIR}} 那种,由工具替换),
|
|
28400
|
-
// 而这一格真实 init 写的就是个 key。写成 {{TAVILY_API_KEY}} 会误导人以为有人会替换它。
|
|
28401
|
-
// env:TAVILY_API_KEY 是引擎真认的形式(loader 会解析),照抄也能跑通。
|
|
28402
|
-
tavilyKey: "env:TAVILY_API_KEY",
|
|
28403
|
-
apiPort: 16990
|
|
28404
|
-
};
|
|
28405
|
-
function generateConfig(v) {
|
|
28406
|
-
const workspace = path2.join(v.stateDir, "workspace").replace(/\\/g, "/");
|
|
28407
|
-
const config = {
|
|
28408
|
-
schemaVersion: SCHEMA_VERSION,
|
|
28409
|
-
// 全引擎统一时区(design §3.1)。真实 init 走 getDefaultValues() 探测本机,
|
|
28410
|
-
// 模板走 {{TIMEZONE}} 占位符。
|
|
28411
|
-
timezone: v.timezone,
|
|
28412
|
-
stateDir: v.stateDir.replace(/\\/g, "/"),
|
|
28413
|
-
mediaDir: `${v.stateDir.replace(/\\/g, "/")}/media/inbound`,
|
|
28414
|
-
models: {
|
|
28415
|
-
providers: {}
|
|
28416
|
-
},
|
|
28417
|
-
// 0917:my_eyes(看图工具)读的是 tools.my_eyes.model,不是 tools.vision——
|
|
28418
|
-
// 后者是模板里遗留的死键(全仓零读者),已删。visionModel 解析不出就不写这个块。
|
|
28419
|
-
tools: {
|
|
28420
|
-
...v.tavilyKey ? { tavily: { apiKey: v.tavilyKey } } : {},
|
|
28421
|
-
...v.visionModel ? { my_eyes: { model: v.visionModel } } : {},
|
|
28422
|
-
// 自拍(§4.3):免费档必须显式写 provider:'relay'。不写的话 getProvider()
|
|
28423
|
-
// 会落回默认值 'fal',而免费装机没有 fal key——第一次自拍就失败。
|
|
28424
|
-
// 非免费档只在用户真给了形象时才写这个块,且不替他决定 provider。
|
|
28425
|
-
...isFreeTier(v) || v.selfiePhotoRelPath || v.selfiePortraitDesc ? {
|
|
28426
|
-
my_selfie: {
|
|
28427
|
-
...isFreeTier(v) ? { provider: "relay" } : {},
|
|
28428
|
-
...v.selfiePhotoRelPath ? { references: [{ name: "portrait", path: v.selfiePhotoRelPath }] } : {},
|
|
28429
|
-
// 只在没有照片时才写描述——两个都给的话以照片为准,写进去会让人以为
|
|
28430
|
-
// 描述也生效了(冷启读的就是这个键)
|
|
28431
|
-
...!v.selfiePhotoRelPath && v.selfiePortraitDesc ? { portraitDesc: v.selfiePortraitDesc } : {}
|
|
28432
|
-
}
|
|
28433
|
-
} : {}
|
|
28434
|
-
},
|
|
28435
|
-
session: { dmScope: "main", groupScope: "main" },
|
|
28436
|
-
sandbox: { mode: "off" },
|
|
28437
|
-
display: {
|
|
28438
|
-
thinking: { enabled: false, emoji: "\u{1F4AD}", maxLen: 300 },
|
|
28439
|
-
toolUse: { enabled: true, emoji: "\u{1F527}", displayMode: "raw", bashDisplayMode: "both" },
|
|
28440
|
-
toolResult: { enabled: false },
|
|
28441
|
-
reactions: { enabled: true, start: "\u{1F440}", done: "\u2705", error: "\u274C" },
|
|
28442
|
-
preview: { enabled: true, agentName: v.agentName, color: "orange" },
|
|
28443
|
-
typing: { enabled: true }
|
|
28444
|
-
},
|
|
28445
|
-
agents: {
|
|
28446
|
-
defaults: {
|
|
28447
|
-
workspace,
|
|
28448
|
-
name: v.agentName,
|
|
28449
|
-
// 人设落 config 是为了**复跑能读回来**(loadExistingValues)。引擎运行时不读它
|
|
28450
|
-
// ——SOUL.md 已经按人设渲染好了。不写的话复跑 init 会退回 'assistant',
|
|
28451
|
-
// 而 execute() 用模板重写 SOUL.md,用户选的"情感陪伴"重装一次就变回通用助手。
|
|
28452
|
-
persona: v.persona,
|
|
28453
|
-
model: {
|
|
28454
|
-
primary: v.primaryModel,
|
|
28455
|
-
// 条件写入:vision 引用解析不出时省略键(loader 按 if (visionRef) 处理缺失)
|
|
28456
|
-
...v.visionModel ? { vision: v.visionModel } : {}
|
|
28457
|
-
},
|
|
28458
|
-
features: {
|
|
28459
|
-
filesystem: true,
|
|
28460
|
-
shell: true,
|
|
28461
|
-
webSearch: true,
|
|
28462
|
-
webFetch: true,
|
|
28463
|
-
memory: true,
|
|
28464
|
-
topics: true,
|
|
28465
|
-
"topic-extract": true,
|
|
28466
|
-
"topic-recall": true,
|
|
28467
|
-
cron: true,
|
|
28468
|
-
agent: true,
|
|
28469
|
-
agentTeams: true,
|
|
28470
|
-
processOutput: "minimal",
|
|
28471
|
-
interrupt: "command",
|
|
28472
|
-
debounceMs: 5e3,
|
|
28473
|
-
// 免费档的装机目标是「能对话、能看图、能自拍」,这两个 flag 默认 false
|
|
28474
|
-
// (src/config/features.ts)。显式打开,它们是声明这两件事的地方。
|
|
28475
|
-
...isFreeTier(v) ? { selfie: true, eyes: true } : {}
|
|
28476
|
-
}
|
|
28477
|
-
}
|
|
28478
|
-
},
|
|
28479
|
-
channels: {
|
|
28480
|
-
discord: {
|
|
28481
|
-
enabled: v.discordEnabled,
|
|
28482
|
-
dmPolicy: "pairing",
|
|
28483
|
-
groupPolicy: "open",
|
|
28484
|
-
accounts: { default: { token: v.discordToken } },
|
|
28485
|
-
replyToMode: "all"
|
|
28486
|
-
},
|
|
28487
|
-
feishu: {
|
|
28488
|
-
enabled: v.feishuEnabled,
|
|
28489
|
-
appId: v.feishuAppId,
|
|
28490
|
-
appSecret: v.feishuAppSecret,
|
|
28491
|
-
connectionMode: "websocket",
|
|
28492
|
-
dmPolicy: "pairing",
|
|
28493
|
-
groupPolicy: "open"
|
|
28494
|
-
},
|
|
28495
|
-
...v.wechatEnabled && v.wechatToken ? {
|
|
28496
|
-
wechat: {
|
|
28497
|
-
enabled: true,
|
|
28498
|
-
token: v.wechatToken,
|
|
28499
|
-
accountId: v.wechatAccountId,
|
|
28500
|
-
dmPolicy: "pairing",
|
|
28501
|
-
group: { policy: "disabled" }
|
|
28502
|
-
}
|
|
28503
|
-
} : {},
|
|
28504
|
-
// 0920 翀哥拍板:emotion 通道默认开启(含 liveSystemPrompt 加载 EMOTION.md)
|
|
28505
|
-
// 即使没装 EMOTION.md,buildLightStablePrompt 也会硬编码"想 TA 就发"那段兜底
|
|
28506
|
-
emotion: {
|
|
28507
|
-
enabled: true,
|
|
28508
|
-
liveSystemPrompt: {
|
|
28509
|
-
files: ["SOUL.md", "EMOTION.md"]
|
|
28510
|
-
}
|
|
28511
|
-
}
|
|
28512
|
-
},
|
|
28513
|
-
api: { port: v.apiPort },
|
|
28514
|
-
// 0909 npm 包默认开:inner-voice + proactive 是开箱即用的核心体验
|
|
28515
|
-
// (不写 model → 都兜底主模型;proactive 领域/persona 可用 workspace/prompts/ 覆盖)
|
|
28516
|
-
innerVoice: { enabled: true },
|
|
28517
|
-
proactive: { enabled: true },
|
|
28518
|
-
prompt: {
|
|
28519
|
-
mode: "custom",
|
|
28520
|
-
staticFiles: ["AGENTS.md", "USER.md", "MEMORY.md", "memory/distill-output.md"],
|
|
28521
|
-
order: ["soul", "AGENTS.md", "prompts/contacts.md", "using-tools", "USER.md", "MEMORY.md", "memory/distill-output.md", "auto-memory-instructions"]
|
|
28522
|
-
},
|
|
28523
|
-
compaction: {
|
|
28524
|
-
enabled: true,
|
|
28525
|
-
bufferTokens: 23e3,
|
|
28526
|
-
maxOutputTokens: 16384,
|
|
28527
|
-
minReductionRatio: 0.3,
|
|
28528
|
-
ruleBased: { enabled: true, essentialFields: [] },
|
|
28529
|
-
memoryFlush: { enabled: true, forceFlushTranscriptBytes: "2.0mb" }
|
|
28530
|
-
},
|
|
28531
|
-
skills: {
|
|
28532
|
-
enabled: true,
|
|
28533
|
-
path: "skills"
|
|
28534
|
-
}
|
|
28535
|
-
};
|
|
28536
|
-
if (!PROVIDER_DEFS[v.primaryProvider]) {
|
|
28537
|
-
const models = v.existingModels?.length ? [...v.existingModels] : [];
|
|
28538
|
-
const customModelId = v.customModelId || models[0]?.id || modelIdOf(v.primaryModel);
|
|
28539
|
-
if (!customModelId) {
|
|
28540
|
-
throw new Error(
|
|
28541
|
-
`\u81EA\u5B9A\u4E49 provider "${v.primaryProvider}" \u6CA1\u6709\u6A21\u578B id\u2014\u2014init \u751F\u6210\u4E0D\u51FA\u53EF\u7528\u7684 config\u3002\u8BF7\u5728\u5411\u5BFC\u91CC\u586B\u300C\u6A21\u578B ID\u300D\uFF0C\u6216\u8865\u4E0A configs/main7.json \u91CC models.providers.${v.primaryProvider}.models[0].id\u3002`
|
|
28542
|
-
);
|
|
28844
|
+
});
|
|
28845
|
+
if (!createRes.ok) {
|
|
28846
|
+
const err = await createRes.text();
|
|
28847
|
+
throw new Error(`\u521B\u5EFA release \u5931\u8D25: ${createRes.status} ${err}`);
|
|
28543
28848
|
}
|
|
28544
|
-
|
|
28545
|
-
|
|
28849
|
+
const release2 = await createRes.json();
|
|
28850
|
+
const uploadUrl = release2.upload_url.replace("{?name,label}", "");
|
|
28851
|
+
const fileBuffer = fs31.readFileSync(archiveFile);
|
|
28852
|
+
const fileName = path33.basename(archiveFile);
|
|
28853
|
+
const uploadRes = await fetch(`${uploadUrl}?name=${fileName}`, {
|
|
28854
|
+
method: "POST",
|
|
28855
|
+
headers: {
|
|
28856
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
28857
|
+
"Accept": "application/vnd.github+json",
|
|
28858
|
+
"Content-Type": "application/gzip",
|
|
28859
|
+
"Content-Length": String(fileBuffer.length)
|
|
28860
|
+
},
|
|
28861
|
+
body: fileBuffer
|
|
28862
|
+
});
|
|
28863
|
+
if (!uploadRes.ok) {
|
|
28864
|
+
const err = await uploadRes.text();
|
|
28865
|
+
throw new Error(`\u4E0A\u4F20 asset \u5931\u8D25: ${uploadRes.status} ${err}`);
|
|
28546
28866
|
}
|
|
28547
|
-
|
|
28548
|
-
|
|
28549
|
-
|
|
28550
|
-
|
|
28551
|
-
|
|
28552
|
-
|
|
28553
|
-
]
|
|
28554
|
-
};
|
|
28867
|
+
console.log(`\u2705 \u4E0A\u4F20\u6210\u529F!`);
|
|
28868
|
+
console.log(` release: ${release2.html_url}`);
|
|
28869
|
+
} catch (e) {
|
|
28870
|
+
console.error(`\u274C GitHub \u4E0A\u4F20\u5931\u8D25: ${e.message}`);
|
|
28871
|
+
console.error(` archive \u4ECD\u5728: ${archiveFile}`);
|
|
28872
|
+
throw e;
|
|
28555
28873
|
}
|
|
28556
|
-
|
|
28557
|
-
|
|
28558
|
-
|
|
28559
|
-
|
|
28560
|
-
|
|
28561
|
-
|
|
28562
|
-
|
|
28563
|
-
|
|
28564
|
-
|
|
28565
|
-
|
|
28566
|
-
|
|
28567
|
-
|
|
28568
|
-
|
|
28569
|
-
id: m.id,
|
|
28570
|
-
name: m.name || m.id,
|
|
28571
|
-
reasoning: m.reasoning ?? true,
|
|
28572
|
-
// 没标 vision 的条目按「不支持」写 input(目录是权威),但 visionOf 那边对
|
|
28573
|
-
// 「没标」仍按未知处理——若用户手输的型号恰好是主模型,vision 仍会宽复用它
|
|
28574
|
-
input: m.vision ? ["text", "image"] : ["text"],
|
|
28575
|
-
contextWindow: m.contextWindow ?? 2e5,
|
|
28576
|
-
maxTokens: m.maxTokens ?? 8192
|
|
28577
|
-
}));
|
|
28874
|
+
try {
|
|
28875
|
+
fs31.rmSync(path33.dirname(archiveFile), { recursive: true, force: true });
|
|
28876
|
+
} catch {
|
|
28877
|
+
}
|
|
28878
|
+
}
|
|
28879
|
+
async function downloadFromGitHub(cfg, agentName, version, destDir) {
|
|
28880
|
+
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
28881
|
+
const tag = version || await getLatestReleaseTag(cfg, agentName);
|
|
28882
|
+
console.log(`\u2B07\uFE0F \u4E0B\u8F7D: ${repo} release ${tag}`);
|
|
28883
|
+
const res = await fetch(`https://api.github.com/repos/${repo}/releases/tags/${tag}`, {
|
|
28884
|
+
headers: {
|
|
28885
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
28886
|
+
"Accept": "application/vnd.github+json"
|
|
28578
28887
|
}
|
|
28579
|
-
|
|
28580
|
-
|
|
28581
|
-
|
|
28582
|
-
|
|
28888
|
+
});
|
|
28889
|
+
if (!res.ok) {
|
|
28890
|
+
throw new Error(`\u83B7\u53D6 release \u5931\u8D25: ${res.status}`);
|
|
28891
|
+
}
|
|
28892
|
+
const release2 = await res.json();
|
|
28893
|
+
const asset = release2.assets?.find((a) => a.name.endsWith(".tar.gz") || a.name.endsWith(".zip"));
|
|
28894
|
+
if (!asset) {
|
|
28895
|
+
throw new Error(`release ${tag} \u6CA1\u6709 tar.gz/zip asset`);
|
|
28896
|
+
}
|
|
28897
|
+
const downloadRes = await fetch(asset.url, {
|
|
28898
|
+
headers: {
|
|
28899
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
28900
|
+
"Accept": "application/octet-stream"
|
|
28583
28901
|
}
|
|
28584
|
-
|
|
28585
|
-
|
|
28586
|
-
|
|
28587
|
-
...def.baseUrl ? { baseUrl: def.baseUrl.trim() } : {}
|
|
28588
|
-
};
|
|
28902
|
+
});
|
|
28903
|
+
if (!downloadRes.ok) {
|
|
28904
|
+
throw new Error(`\u4E0B\u8F7D asset \u5931\u8D25: ${downloadRes.status}`);
|
|
28589
28905
|
}
|
|
28590
|
-
const
|
|
28591
|
-
|
|
28592
|
-
|
|
28906
|
+
const buffer = Buffer.from(await downloadRes.arrayBuffer());
|
|
28907
|
+
const archiveFile = path33.join(destDir, asset.name);
|
|
28908
|
+
fs31.writeFileSync(archiveFile, buffer);
|
|
28909
|
+
return archiveFile;
|
|
28910
|
+
}
|
|
28911
|
+
async function getLatestReleaseTag(cfg, agentName) {
|
|
28912
|
+
const repo = `${cfg.githubOwner}/${cfg.repoName}`;
|
|
28913
|
+
const res = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=30`, {
|
|
28914
|
+
headers: {
|
|
28915
|
+
"Authorization": `Bearer ${cfg.githubToken}`,
|
|
28916
|
+
"Accept": "application/vnd.github+json"
|
|
28917
|
+
}
|
|
28918
|
+
});
|
|
28919
|
+
if (!res.ok) {
|
|
28920
|
+
throw new Error(`\u83B7\u53D6 release \u5217\u8868\u5931\u8D25: ${res.status}`);
|
|
28593
28921
|
}
|
|
28594
|
-
const
|
|
28595
|
-
const
|
|
28596
|
-
if (!
|
|
28597
|
-
throw new Error(
|
|
28598
|
-
`\u751F\u6210\u7684 config \u4E0D\u81EA\u6D3D\uFF1Aagents.defaults.model.primary = "${primaryRef}"\uFF0C\u4F46 models.providers \u91CC\u53EA\u6709 ${providerKeys.join(", ")}\u2014\u2014\u5F15\u64CE\u542F\u52A8\u4F1A\u76F4\u63A5\u629B Provider not found\u3002\u8FD9\u662F init \u7684 bug\uFF0C\u8BF7\u8FDE\u540C\u8FD9\u6761\u6D88\u606F\u4E00\u8D77\u53CD\u9988\u3002`
|
|
28599
|
-
);
|
|
28922
|
+
const releases = await res.json();
|
|
28923
|
+
const match = releases.find((r) => r.tag_name?.startsWith(`${agentName}-`));
|
|
28924
|
+
if (!match) {
|
|
28925
|
+
throw new Error(`\u6CA1\u6709\u627E\u5230 ${agentName} \u7684 release`);
|
|
28600
28926
|
}
|
|
28601
|
-
|
|
28602
|
-
|
|
28603
|
-
|
|
28604
|
-
|
|
28605
|
-
|
|
28606
|
-
|
|
28927
|
+
return match.tag_name;
|
|
28928
|
+
}
|
|
28929
|
+
var CONFIG_PATH, PATH_PLACEHOLDERS, WORKSPACE_INCLUDE, WORKSPACE_OPTIONAL, WORKSPACE_EXCLUDE, TEXT_EXTENSIONS;
|
|
28930
|
+
var init_cli_travel = __esm({
|
|
28931
|
+
"src/cli-travel.ts"() {
|
|
28932
|
+
"use strict";
|
|
28933
|
+
init_clock();
|
|
28934
|
+
CONFIG_PATH = path33.join(os5.homedir(), ".engine7-travel.json");
|
|
28935
|
+
PATH_PLACEHOLDERS = [
|
|
28936
|
+
{ placeholder: "{{WORKSPACE}}", getOriginal: (d) => d.workspace },
|
|
28937
|
+
{ placeholder: "{{ENGINE_HOME}}", getOriginal: (d) => d.engineHome },
|
|
28938
|
+
{ placeholder: "{{STATE_DIR}}", getOriginal: (d) => d.stateDir }
|
|
28939
|
+
];
|
|
28940
|
+
WORKSPACE_INCLUDE = /* @__PURE__ */ new Set([
|
|
28941
|
+
// 核心文件
|
|
28942
|
+
"AGENTS.md",
|
|
28943
|
+
"SOUL.md",
|
|
28944
|
+
"MEMORY.md",
|
|
28945
|
+
"USER.md",
|
|
28946
|
+
"HEARTBEAT.md",
|
|
28947
|
+
"INDEX.md",
|
|
28948
|
+
"SESSION-STATE.md",
|
|
28949
|
+
// 核心目录
|
|
28950
|
+
"prompts",
|
|
28951
|
+
"topics",
|
|
28952
|
+
"memory",
|
|
28953
|
+
"inner-voice",
|
|
28954
|
+
"docs",
|
|
28955
|
+
"skills",
|
|
28956
|
+
"voice-chat",
|
|
28957
|
+
"scripts",
|
|
28958
|
+
"selfie",
|
|
28959
|
+
"moodboard",
|
|
28960
|
+
// 状态文件 + 目录
|
|
28961
|
+
".calendar",
|
|
28962
|
+
"nudge-state.json"
|
|
28963
|
+
]);
|
|
28964
|
+
WORKSPACE_OPTIONAL = /* @__PURE__ */ new Set([
|
|
28965
|
+
"images",
|
|
28966
|
+
"tools"
|
|
28967
|
+
]);
|
|
28968
|
+
WORKSPACE_EXCLUDE = /* @__PURE__ */ new Set([
|
|
28969
|
+
"livestream",
|
|
28970
|
+
"content-library",
|
|
28971
|
+
"tmp",
|
|
28972
|
+
".git",
|
|
28973
|
+
"node_modules",
|
|
28974
|
+
"memory_runs",
|
|
28975
|
+
"workspace",
|
|
28976
|
+
"prompt-archive",
|
|
28977
|
+
"aim-archive",
|
|
28978
|
+
"test-agent",
|
|
28979
|
+
"nul",
|
|
28980
|
+
"*.bak*",
|
|
28981
|
+
"*.bak-*",
|
|
28982
|
+
"~$*"
|
|
28983
|
+
]);
|
|
28984
|
+
TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
28985
|
+
".md",
|
|
28986
|
+
".json",
|
|
28987
|
+
".txt",
|
|
28988
|
+
".js",
|
|
28989
|
+
".ts",
|
|
28990
|
+
".mjs",
|
|
28991
|
+
".py",
|
|
28992
|
+
".yaml",
|
|
28993
|
+
".yml",
|
|
28994
|
+
".cmd",
|
|
28995
|
+
".bat",
|
|
28996
|
+
".sh",
|
|
28997
|
+
".csv",
|
|
28998
|
+
".html",
|
|
28999
|
+
".toml",
|
|
29000
|
+
".list"
|
|
29001
|
+
// .everos 的 everos.toml/config.toml/ome.toml/everos-env.list
|
|
29002
|
+
]);
|
|
28607
29003
|
}
|
|
28608
|
-
|
|
28609
|
-
|
|
28610
|
-
|
|
28611
|
-
|
|
28612
|
-
|
|
28613
|
-
|
|
28614
|
-
|
|
28615
|
-
|
|
28616
|
-
|
|
29004
|
+
});
|
|
29005
|
+
|
|
29006
|
+
// src/cli-init.ts
|
|
29007
|
+
import * as path34 from "node:path";
|
|
29008
|
+
import * as fs32 from "node:fs";
|
|
29009
|
+
|
|
29010
|
+
// src/cli-service.ts
|
|
29011
|
+
function parseServiceArgs(args) {
|
|
29012
|
+
const action = args[0] || "";
|
|
29013
|
+
let configPath = "";
|
|
29014
|
+
let cwd = process.cwd();
|
|
29015
|
+
let nodeExe = "";
|
|
29016
|
+
for (let i = 1; i < args.length; i++) {
|
|
29017
|
+
if (args[i] === "--config" && args[i + 1]) configPath = args[++i];
|
|
29018
|
+
else if (args[i] === "--cwd" && args[i + 1]) cwd = args[++i];
|
|
29019
|
+
else if (args[i] === "--node-exe" && args[i + 1]) nodeExe = args[++i];
|
|
28617
29020
|
}
|
|
28618
|
-
return
|
|
29021
|
+
return { action, configPath, cwd, nodeExe };
|
|
29022
|
+
}
|
|
29023
|
+
function buildServiceWrapper(opts) {
|
|
29024
|
+
const nodeExe = opts.nodeExe || process.execPath;
|
|
29025
|
+
return `@echo off\r
|
|
29026
|
+
cd /d "${opts.cwd}"\r
|
|
29027
|
+
"${nodeExe}" "${opts.cliMjs}" start --config "${opts.configPath}"\r
|
|
29028
|
+
`;
|
|
28619
29029
|
}
|
|
28620
29030
|
|
|
29031
|
+
// src/cli-init.ts
|
|
29032
|
+
init_feishu_quick_register();
|
|
29033
|
+
init_wechat();
|
|
29034
|
+
init_qr_render();
|
|
29035
|
+
init_cli_events();
|
|
29036
|
+
import * as readline3 from "node:readline";
|
|
29037
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
29038
|
+
|
|
28621
29039
|
// src/cli-init-answers.ts
|
|
29040
|
+
init_clock();
|
|
29041
|
+
init_cli_init_config();
|
|
29042
|
+
import * as fs2 from "node:fs";
|
|
28622
29043
|
var VALID_PROVIDERS = ["dashscope", "minimax", "zhipu", "deepseek", "7yi", "custom"];
|
|
28623
29044
|
var VALID_CHANNEL_MODES = ["skip", "qr", "manual"];
|
|
28624
29045
|
var VALID_PERSONAS = ["assistant", "companion", "butler", "friend"];
|
|
@@ -28738,6 +29159,7 @@ function buildConfigValuesFromAnswers(stateDir, a, existing = {}) {
|
|
|
28738
29159
|
// src/cli-init.ts
|
|
28739
29160
|
init_secrets_env();
|
|
28740
29161
|
init_clock();
|
|
29162
|
+
init_cli_init_config();
|
|
28741
29163
|
var defaultWarningListeners = process.listeners("warning");
|
|
28742
29164
|
process.removeAllListeners("warning");
|
|
28743
29165
|
process.on("warning", (warning) => {
|
|
@@ -28750,7 +29172,7 @@ process.on("warning", (warning) => {
|
|
|
28750
29172
|
});
|
|
28751
29173
|
var headlessEmit = null;
|
|
28752
29174
|
var __filename2 = fileURLToPath7(import.meta.url);
|
|
28753
|
-
var __dirname =
|
|
29175
|
+
var __dirname = path34.dirname(__filename2);
|
|
28754
29176
|
function parseArgs() {
|
|
28755
29177
|
const args = process.argv.slice(2);
|
|
28756
29178
|
let stateDir = "";
|
|
@@ -28777,10 +29199,10 @@ function parseArgs() {
|
|
|
28777
29199
|
}
|
|
28778
29200
|
}
|
|
28779
29201
|
if (!stateDir) {
|
|
28780
|
-
stateDir =
|
|
29202
|
+
stateDir = path34.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
28781
29203
|
console.log(` \u4F7F\u7528\u9ED8\u8BA4\u76EE\u5F55: ${stateDir}`);
|
|
28782
29204
|
}
|
|
28783
|
-
stateDir =
|
|
29205
|
+
stateDir = path34.resolve(stateDir);
|
|
28784
29206
|
return { stateDir, quick, dryRun: dryRun2, force, answersPath };
|
|
28785
29207
|
}
|
|
28786
29208
|
function printHelp() {
|
|
@@ -28895,16 +29317,16 @@ function getDefaultValues(stateDir) {
|
|
|
28895
29317
|
};
|
|
28896
29318
|
}
|
|
28897
29319
|
function loadExistingValues(stateDir) {
|
|
28898
|
-
const configPath =
|
|
29320
|
+
const configPath = path34.join(stateDir, "configs", "main7.json");
|
|
28899
29321
|
try {
|
|
28900
|
-
if (!
|
|
28901
|
-
const j = JSON.parse(
|
|
29322
|
+
if (!fs32.existsSync(configPath)) return {};
|
|
29323
|
+
const j = JSON.parse(fs32.readFileSync(configPath, "utf8"));
|
|
28902
29324
|
const out = {};
|
|
28903
29325
|
if (typeof j.timezone === "string" && j.timezone) out.timezone = j.timezone;
|
|
28904
29326
|
if (j.agents?.defaults?.name) out.agentName = j.agents.defaults.name;
|
|
28905
29327
|
if (j.agents?.defaults?.persona) out.persona = j.agents.defaults.persona;
|
|
28906
29328
|
try {
|
|
28907
|
-
const wm =
|
|
29329
|
+
const wm = fs32.readFileSync(path34.join(stateDir, "workspace", ".work-mode"), "utf8").trim();
|
|
28908
29330
|
if (wm === "on") out.chatMode = "work";
|
|
28909
29331
|
else if (wm === "off") out.chatMode = "emotion";
|
|
28910
29332
|
} catch {
|
|
@@ -28958,15 +29380,15 @@ function loadExistingValues(stateDir) {
|
|
|
28958
29380
|
}
|
|
28959
29381
|
function readSecretsValue(stateDir, envName) {
|
|
28960
29382
|
try {
|
|
28961
|
-
const secretsDir =
|
|
29383
|
+
const secretsDir = path34.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7-secrets");
|
|
28962
29384
|
const cfgBase = "main7";
|
|
28963
29385
|
const candidates = [
|
|
28964
|
-
|
|
28965
|
-
|
|
29386
|
+
path34.join(secretsDir, `${cfgBase}.env`),
|
|
29387
|
+
path34.join(stateDir, "configs", `.env.${cfgBase}`)
|
|
28966
29388
|
];
|
|
28967
29389
|
for (const p of candidates) {
|
|
28968
|
-
if (
|
|
28969
|
-
for (const line of
|
|
29390
|
+
if (fs32.existsSync(p)) {
|
|
29391
|
+
for (const line of fs32.readFileSync(p, "utf8").split("\n")) {
|
|
28970
29392
|
const eq = line.indexOf("=");
|
|
28971
29393
|
if (eq > 0 && line.slice(0, eq).trim() === envName) {
|
|
28972
29394
|
return line.slice(eq + 1).trim();
|
|
@@ -29199,29 +29621,29 @@ async function configureWechat(rl, v) {
|
|
|
29199
29621
|
}
|
|
29200
29622
|
function buildDirTree(v) {
|
|
29201
29623
|
const d = v.stateDir;
|
|
29202
|
-
const w =
|
|
29624
|
+
const w = path34.join(d, "workspace");
|
|
29203
29625
|
return [
|
|
29204
29626
|
// 目录
|
|
29205
|
-
{ path:
|
|
29206
|
-
{ path:
|
|
29207
|
-
{ path:
|
|
29208
|
-
{ path:
|
|
29209
|
-
{ path:
|
|
29210
|
-
{ path:
|
|
29211
|
-
{ path:
|
|
29212
|
-
{ path:
|
|
29213
|
-
{ path:
|
|
29214
|
-
{ path:
|
|
29215
|
-
{ path:
|
|
29627
|
+
{ path: path34.join(d, "configs"), type: "dir" },
|
|
29628
|
+
{ path: path34.join(d, "state", "agents", "main", "sessions"), type: "dir" },
|
|
29629
|
+
{ path: path34.join(w, "prompts"), type: "dir" },
|
|
29630
|
+
{ path: path34.join(w, "memory", "daily"), type: "dir" },
|
|
29631
|
+
{ path: path34.join(w, "docs", "research"), type: "dir" },
|
|
29632
|
+
{ path: path34.join(w, "docs", "todo"), type: "dir" },
|
|
29633
|
+
{ path: path34.join(w, "docs", "decisions"), type: "dir" },
|
|
29634
|
+
{ path: path34.join(w, "docs", "knowledge"), type: "dir" },
|
|
29635
|
+
{ path: path34.join(w, "docs", "sop"), type: "dir" },
|
|
29636
|
+
{ path: path34.join(d, "logs"), type: "dir" },
|
|
29637
|
+
{ path: path34.join(d, "media", "inbound"), type: "dir" },
|
|
29216
29638
|
// workspace 文件
|
|
29217
29639
|
{
|
|
29218
|
-
path:
|
|
29640
|
+
path: path34.join(w, "SESSION-STATE.md"),
|
|
29219
29641
|
type: "file",
|
|
29220
29642
|
content: readTemplate("workspace/SESSION-STATE.md")
|
|
29221
29643
|
},
|
|
29222
|
-
{ path:
|
|
29644
|
+
{ path: path34.join(w, "HEARTBEAT.md"), type: "file", content: readTemplate("workspace/HEARTBEAT.md") },
|
|
29223
29645
|
{
|
|
29224
|
-
path:
|
|
29646
|
+
path: path34.join(w, "SOUL.md"),
|
|
29225
29647
|
type: "file",
|
|
29226
29648
|
// 0917:SOUL 按人设取模板(templates/workspace/souls/<persona>.md),
|
|
29227
29649
|
// 单一人设模板缺失时回退通用骨架(workspace/SOUL.md)
|
|
@@ -29233,12 +29655,12 @@ function buildDirTree(v) {
|
|
|
29233
29655
|
}
|
|
29234
29656
|
})()
|
|
29235
29657
|
},
|
|
29236
|
-
{ path:
|
|
29658
|
+
{ path: path34.join(w, "AGENTS.md"), type: "file", content: readTemplate("workspace/AGENTS.md") },
|
|
29237
29659
|
{
|
|
29238
29660
|
// 0920 翀哥拍板:EMOTION.md 模板按人设装(templates/workspace/emotion/<persona>.md)
|
|
29239
29661
|
// 冷启动默认带"想 TA 了就直接发(msg_send)"那段(写在每份模板 §一)
|
|
29240
29662
|
// 单一人设模板缺失时回退通用骨架(emotion/_default.md)
|
|
29241
|
-
path:
|
|
29663
|
+
path: path34.join(w, "EMOTION.md"),
|
|
29242
29664
|
type: "file",
|
|
29243
29665
|
content: (() => {
|
|
29244
29666
|
try {
|
|
@@ -29249,30 +29671,30 @@ function buildDirTree(v) {
|
|
|
29249
29671
|
})()
|
|
29250
29672
|
},
|
|
29251
29673
|
{
|
|
29252
|
-
path:
|
|
29674
|
+
path: path34.join(w, "prompts", "contacts.md"),
|
|
29253
29675
|
type: "file",
|
|
29254
29676
|
content: readTemplate("workspace/prompts/contacts.md").replace("{{DISCORD_USER_ID}}", v.discordUserId || "YOUR_DISCORD_ID").replace("{{FEISHU_OPEN_ID}}", v.feishuOpenId || "YOUR_FEISHU_OPEN_ID")
|
|
29255
29677
|
},
|
|
29256
|
-
{ path:
|
|
29678
|
+
{ path: path34.join(w, "prompts", "auto-memory-instructions.md"), type: "file", content: readTemplate("workspace/prompts/auto-memory-instructions.md") },
|
|
29257
29679
|
{
|
|
29258
29680
|
// 0917:inner-voice 定制 prompt 按人设装(templates/workspace/inner-voice/<persona>.md)
|
|
29259
|
-
path:
|
|
29681
|
+
path: path34.join(w, "prompts", "my-inner-voice.md"),
|
|
29260
29682
|
type: "file",
|
|
29261
29683
|
content: readTemplate(`workspace/inner-voice/${v.persona}.md`).replace(/\{\{AGENT_NAME\}\}/g, v.agentName)
|
|
29262
29684
|
},
|
|
29263
|
-
{ path:
|
|
29264
|
-
{ path:
|
|
29685
|
+
{ path: path34.join(w, "MEMORY.md"), type: "file", content: "# MEMORY.md \u2014 \u8BB0\u5FC6\u6587\u4EF6\u7D22\u5F15\n\n> \u6700\u540E\u66F4\u65B0\uFF1A\u521D\u59CB\u5316\n" },
|
|
29686
|
+
{ path: path34.join(w, "USER.md"), type: "file", content: "# USER.md \u2014 \u7528\u6237\u4FE1\u606F\n\n\uFF08\u5728\u8FD9\u91CC\u8BB0\u5F55\u7528\u6237\u7684\u504F\u597D\u3001\u80CC\u666F\u7B49\uFF09\n" },
|
|
29265
29687
|
// 情绪状态机(0917):脚本 + hook 接线 + 人设情绪表(用户在 init 选的 persona)
|
|
29266
29688
|
//
|
|
29267
29689
|
// 0920 起装的是 **.mjs 版**(原来是 .py,见下面 hook 命令那段)。老装机的 workspace
|
|
29268
29690
|
// 里那份 .py 不动它——没被引用就不会跑,删了反而无据可查。
|
|
29269
29691
|
{
|
|
29270
|
-
path:
|
|
29692
|
+
path: path34.join(w, "scripts", "emotion_state_machine.mjs"),
|
|
29271
29693
|
type: "file",
|
|
29272
29694
|
content: readTemplate("workspace/scripts/emotion_state_machine.mjs")
|
|
29273
29695
|
},
|
|
29274
29696
|
{
|
|
29275
|
-
path:
|
|
29697
|
+
path: path34.join(w, ".hooks.json"),
|
|
29276
29698
|
type: "file",
|
|
29277
29699
|
content: JSON.stringify({
|
|
29278
29700
|
hooks: {
|
|
@@ -29287,26 +29709,26 @@ function buildDirTree(v) {
|
|
|
29287
29709
|
// (hook 的 env 是 {...process.env} 继承的),所以它就是个 node。
|
|
29288
29710
|
// 与 `service install --node-exe` 同一个路子。
|
|
29289
29711
|
// 路径两侧都加引号:stateDir 可能带空格(C:\Users\Foo Bar\...)。
|
|
29290
|
-
{ hooks: [{ type: "command", command: '"${E7_NODE}" "' +
|
|
29712
|
+
{ hooks: [{ type: "command", command: '"${E7_NODE}" "' + path34.join(w, "scripts", "emotion_state_machine.mjs") + '"' }] }
|
|
29291
29713
|
]
|
|
29292
29714
|
}
|
|
29293
29715
|
}, null, 2) + "\n"
|
|
29294
29716
|
},
|
|
29295
29717
|
{
|
|
29296
|
-
path:
|
|
29718
|
+
path: path34.join(w, ".emotion_states.json"),
|
|
29297
29719
|
type: "file",
|
|
29298
29720
|
content: readTemplate(`workspace/emotion-states/${v.persona}.json`)
|
|
29299
29721
|
},
|
|
29300
29722
|
// 模式档位(0920):与 /work 命令写的是同一个文件,读端只认 on/off,
|
|
29301
29723
|
// 没有这个键就是 auto。auto 刻意不写——写个 "auto" 进去反而多一种除了这里没人认得的状态。
|
|
29302
29724
|
...v.chatMode === "work" || v.chatMode === "emotion" ? [{
|
|
29303
|
-
path:
|
|
29725
|
+
path: path34.join(w, ".work-mode"),
|
|
29304
29726
|
type: "file",
|
|
29305
29727
|
content: v.chatMode === "work" ? "on" : "off"
|
|
29306
29728
|
}] : [],
|
|
29307
29729
|
// package.json — 让 agent 目录成为独立 npm 项目根,防止 npm hoisting
|
|
29308
29730
|
{
|
|
29309
|
-
path:
|
|
29731
|
+
path: path34.join(d, "package.json"),
|
|
29310
29732
|
type: "file",
|
|
29311
29733
|
content: JSON.stringify({
|
|
29312
29734
|
name: v.agentName.toLowerCase().replace(/[^a-z0-9-]/g, "-") || "agent",
|
|
@@ -29317,7 +29739,7 @@ function buildDirTree(v) {
|
|
|
29317
29739
|
},
|
|
29318
29740
|
// 启动脚本(根据 OS 生成)
|
|
29319
29741
|
...process.platform === "win32" ? [{
|
|
29320
|
-
path:
|
|
29742
|
+
path: path34.join(d, "start.cmd"),
|
|
29321
29743
|
type: "file",
|
|
29322
29744
|
content: `@echo off
|
|
29323
29745
|
rem Engine 7 startup script
|
|
@@ -29349,7 +29771,7 @@ if %ERRORLEVEL% NEQ 0 (
|
|
|
29349
29771
|
)
|
|
29350
29772
|
`
|
|
29351
29773
|
}] : [{
|
|
29352
|
-
path:
|
|
29774
|
+
path: path34.join(d, "start.sh"),
|
|
29353
29775
|
type: "file",
|
|
29354
29776
|
content: `#!/bin/bash
|
|
29355
29777
|
# Engine 7 startup script
|
|
@@ -29382,24 +29804,24 @@ node "$ENGINE7_BIN" --engine-config configs/main7.json
|
|
|
29382
29804
|
}
|
|
29383
29805
|
function readTemplate(relativePath) {
|
|
29384
29806
|
const candidates = [
|
|
29385
|
-
|
|
29386
|
-
|
|
29387
|
-
|
|
29807
|
+
path34.join(__dirname, "..", "templates", relativePath),
|
|
29808
|
+
path34.join(__dirname, "..", "..", "templates", relativePath),
|
|
29809
|
+
path34.join(process.cwd(), "templates", relativePath)
|
|
29388
29810
|
];
|
|
29389
29811
|
for (const p of candidates) {
|
|
29390
|
-
if (
|
|
29391
|
-
return
|
|
29812
|
+
if (fs32.existsSync(p)) {
|
|
29813
|
+
return fs32.readFileSync(p, "utf-8");
|
|
29392
29814
|
}
|
|
29393
29815
|
}
|
|
29394
29816
|
throw new Error(`\u6A21\u677F\u6587\u4EF6\u4E0D\u5B58\u5728: ${relativePath}\uFF08\u627E\u4E86: ${candidates.join(", ")}\uFF09`);
|
|
29395
29817
|
}
|
|
29396
29818
|
function looksLikeAppInstall(dir) {
|
|
29397
|
-
return ["", "install"].some((sub) =>
|
|
29819
|
+
return ["", "install"].some((sub) => fs32.existsSync(path34.join(dir, sub, "Engine7.exe")) && fs32.existsSync(path34.join(dir, sub, "resources", "engine7", "dist", "cli.mjs")));
|
|
29398
29820
|
}
|
|
29399
29821
|
var PHOTO_EXTS = [".png", ".jpg", ".jpeg", ".webp"];
|
|
29400
29822
|
function hasOnlyEngineBookkeeping(dir) {
|
|
29401
29823
|
try {
|
|
29402
|
-
const entries2 =
|
|
29824
|
+
const entries2 = fs32.readdirSync(dir);
|
|
29403
29825
|
return entries2.length > 0 && entries2.every((e) => e === "install-identity.json");
|
|
29404
29826
|
} catch {
|
|
29405
29827
|
return false;
|
|
@@ -29415,11 +29837,11 @@ function dryRun(entries2) {
|
|
|
29415
29837
|
function execute(entries2) {
|
|
29416
29838
|
for (const e of entries2) {
|
|
29417
29839
|
if (e.type === "dir") {
|
|
29418
|
-
|
|
29840
|
+
fs32.mkdirSync(e.path, { recursive: true });
|
|
29419
29841
|
console.log(` \u{1F4C1} ${e.path}`);
|
|
29420
29842
|
} else {
|
|
29421
|
-
|
|
29422
|
-
|
|
29843
|
+
fs32.mkdirSync(path34.dirname(e.path), { recursive: true });
|
|
29844
|
+
fs32.writeFileSync(e.path, e.content || "", "utf-8");
|
|
29423
29845
|
console.log(` \u{1F4C4} ${e.path}`);
|
|
29424
29846
|
}
|
|
29425
29847
|
}
|
|
@@ -29453,8 +29875,8 @@ async function main() {
|
|
|
29453
29875
|
if (subcommand === "--version" || subcommand === "-v") {
|
|
29454
29876
|
let ver = "unknown";
|
|
29455
29877
|
try {
|
|
29456
|
-
const pkgPath =
|
|
29457
|
-
ver = JSON.parse(
|
|
29878
|
+
const pkgPath = path34.join(path34.dirname(__filename2), "..", "package.json");
|
|
29879
|
+
ver = JSON.parse(fs32.readFileSync(pkgPath, "utf8")).version || "unknown";
|
|
29458
29880
|
} catch {
|
|
29459
29881
|
}
|
|
29460
29882
|
console.log(`engine7 ${ver}`);
|
|
@@ -29479,14 +29901,14 @@ async function main() {
|
|
|
29479
29901
|
relay = resolveRelayUrl(relay, process.env);
|
|
29480
29902
|
if (!relayFromArg) console.error(` \u4E2D\u8F6C\u7AD9: ${relay}\uFF08\u8981\u6362\u5730\u5740\u7528 --relay <\u5730\u5740>\uFF09`);
|
|
29481
29903
|
const { resolveStateDirFromArgs: resolveStateDirFromArgs2 } = await Promise.resolve().then(() => (init_cli_doctor(), cli_doctor_exports));
|
|
29482
|
-
stateDir = stateDir ?
|
|
29904
|
+
stateDir = stateDir ? path34.resolve(stateDir) : resolveStateDirFromArgs2(args.slice(1));
|
|
29483
29905
|
try {
|
|
29484
29906
|
const { provisionFreeToken: provisionFreeToken2 } = await Promise.resolve().then(() => (init_provision_free(), provision_free_exports));
|
|
29485
29907
|
const result = await provisionFreeToken2({
|
|
29486
29908
|
relayBaseUrl: relay,
|
|
29487
29909
|
stateDir,
|
|
29488
29910
|
// 和 init 写 config 的路径一致(cli-init.ts 里 configPath 就是这么拼的)
|
|
29489
|
-
configPath:
|
|
29911
|
+
configPath: path34.join(stateDir, "configs", "main7.json")
|
|
29490
29912
|
});
|
|
29491
29913
|
console.log(JSON.stringify(result, null, 2));
|
|
29492
29914
|
process.exit(0);
|
|
@@ -29503,24 +29925,28 @@ async function main() {
|
|
|
29503
29925
|
let stateDir = "";
|
|
29504
29926
|
let configName = "";
|
|
29505
29927
|
let events = false;
|
|
29928
|
+
let free = false;
|
|
29929
|
+
let relayBaseUrl = "";
|
|
29506
29930
|
for (let i = 1; i < args.length; i++) {
|
|
29507
29931
|
if (args[i] === "--state-dir" && args[i + 1]) stateDir = args[++i].trim();
|
|
29508
29932
|
else if (args[i] === "--config" && args[i + 1]) configName = args[++i].trim();
|
|
29509
29933
|
else if (args[i] === "--events") events = true;
|
|
29934
|
+
else if (args[i] === "--free") free = true;
|
|
29935
|
+
else if (args[i] === "--base-url" && args[i + 1]) relayBaseUrl = args[++i].trim();
|
|
29510
29936
|
}
|
|
29511
29937
|
if (!stateDir) {
|
|
29512
|
-
const cwd =
|
|
29513
|
-
if (
|
|
29938
|
+
const cwd = path34.resolve(process.cwd());
|
|
29939
|
+
if (fs32.existsSync(path34.join(cwd, "configs"))) {
|
|
29514
29940
|
stateDir = cwd;
|
|
29515
29941
|
} else {
|
|
29516
|
-
const pointer =
|
|
29517
|
-
if (
|
|
29942
|
+
const pointer = path34.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7.json");
|
|
29943
|
+
if (fs32.existsSync(pointer)) {
|
|
29518
29944
|
try {
|
|
29519
|
-
stateDir = JSON.parse(
|
|
29945
|
+
stateDir = JSON.parse(fs32.readFileSync(pointer, "utf8")).stateDir || "";
|
|
29520
29946
|
} catch {
|
|
29521
29947
|
}
|
|
29522
29948
|
}
|
|
29523
|
-
if (!stateDir) stateDir =
|
|
29949
|
+
if (!stateDir) stateDir = path34.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
29524
29950
|
}
|
|
29525
29951
|
}
|
|
29526
29952
|
if (!target && !isLegacyAddchannel) {
|
|
@@ -29532,7 +29958,7 @@ async function main() {
|
|
|
29532
29958
|
console.log(" feishu \u98DE\u4E66\u626B\u7801\u63A5\u5165\uFF08OAuth\uFF0C30\u79D2\uFF09");
|
|
29533
29959
|
console.log(" discord Discord bot \u914D\u7F6E");
|
|
29534
29960
|
console.log(" \u6A21\u578B\u7C7B:");
|
|
29535
|
-
console.log(" llm
|
|
29961
|
+
console.log(" llm --free \u5207\u5230 7yi \u514D\u8D39\u6863\uFF08\u4E0D\u7528\u586B Key\uFF1B\u8001\u7528\u6237\u4ECE\u65E7\u914D\u7F6E\u5347\u4E0A\u6765\u7684\u90A3\u6761\u8DEF\uFF09");
|
|
29536
29962
|
console.log(" \u5176\u4ED6:");
|
|
29537
29963
|
console.log(" channels \u67E5\u770B\u5F53\u524D\u901A\u9053\u72B6\u6001");
|
|
29538
29964
|
console.log("");
|
|
@@ -29540,12 +29966,12 @@ async function main() {
|
|
|
29540
29966
|
process.exit(0);
|
|
29541
29967
|
}
|
|
29542
29968
|
if (channel === "channels") {
|
|
29543
|
-
const configsDir =
|
|
29544
|
-
if (
|
|
29545
|
-
const files =
|
|
29969
|
+
const configsDir = path34.join(stateDir, "configs");
|
|
29970
|
+
if (fs32.existsSync(configsDir)) {
|
|
29971
|
+
const files = fs32.readdirSync(configsDir).filter((f) => f.endsWith(".json"));
|
|
29546
29972
|
for (const f of files) {
|
|
29547
29973
|
try {
|
|
29548
|
-
const j = JSON.parse(
|
|
29974
|
+
const j = JSON.parse(fs32.readFileSync(path34.join(configsDir, f), "utf8"));
|
|
29549
29975
|
if (!j.channels) continue;
|
|
29550
29976
|
console.log(`
|
|
29551
29977
|
\u{1F4C4} ${f}:`);
|
|
@@ -29562,8 +29988,51 @@ async function main() {
|
|
|
29562
29988
|
process.exit(0);
|
|
29563
29989
|
}
|
|
29564
29990
|
if (channel === "llm") {
|
|
29565
|
-
|
|
29566
|
-
|
|
29991
|
+
let fail2 = function(message) {
|
|
29992
|
+
if (emit2) emit2({ event: "error", message });
|
|
29993
|
+
else console.error(`
|
|
29994
|
+
\u274C ${message}`);
|
|
29995
|
+
process.exit(1);
|
|
29996
|
+
};
|
|
29997
|
+
var fail = fail2;
|
|
29998
|
+
const { applyFreeTierToConfig: applyFreeTierToConfig2, resolveConfigPath: resolveConfigPath2 } = await Promise.resolve().then(() => (init_free_tier_apply(), free_tier_apply_exports));
|
|
29999
|
+
const { FREE_TIER_ENV_KEY: FREE_TIER_ENV_KEY2 } = await Promise.resolve().then(() => (init_provision_free(), provision_free_exports));
|
|
30000
|
+
const emit2 = events ? createEmitter() : void 0;
|
|
30001
|
+
if (events) redirectConsoleToStderr();
|
|
30002
|
+
if (!free) {
|
|
30003
|
+
if (emit2) fail2("reconfig llm \u9700\u8981 --free\uFF08\u5207\u5230 7yi \u514D\u8D39\u6863\uFF09");
|
|
30004
|
+
console.log("\u{1F527} LLM \u91CD\u914D");
|
|
30005
|
+
console.log(" \u5F53\u524D\u652F\u6301: engine7 reconfig llm --free \u5207\u5230 7yi \u514D\u8D39\u6863\uFF08\u4E0D\u7528\u586B Key\uFF09");
|
|
30006
|
+
console.log(" \u6362\u5230\u522B\u7684\u5382\u5546\u4ECD\u8D70\u5B89\u88C5\u7A0B\u5E8F / \u547D\u4EE4\u884C init\uFF08\u901A\u7528\u6362\u5382\u5546 v7.2 \u8BA1\u5212\u505A\uFF09");
|
|
30007
|
+
process.exit(0);
|
|
30008
|
+
}
|
|
30009
|
+
const configFile = resolveConfigPath2(stateDir, configName);
|
|
30010
|
+
if (!configFile) {
|
|
30011
|
+
fail2(`\u6CA1\u627E\u5230\u53EF\u6539\u7684 config\uFF1A\u5728 ${path34.join(stateDir, "configs")} \u4E0B\u65E2\u6CA1\u6709 main7.json\uFF0C\u4E5F\u6CA1\u6709\u542B models.providers \u7684 json\u3002\u7528 --config <\u6587\u4EF6\u540D> \u6307\u5B9A\u3002`);
|
|
30012
|
+
}
|
|
30013
|
+
if (!readSecretsValue(stateDir, FREE_TIER_ENV_KEY2)) {
|
|
30014
|
+
fail2(`\u514D\u8D39\u6863\u7F3A\u5C11\u4EE4\u724C\uFF1Asecrets \u91CC\u6CA1\u6709 ${FREE_TIER_ENV_KEY2}\u3002\u5148\u9886\u53F7\uFF1A
|
|
30015
|
+
engine7 provision-free --relay <\u4E2D\u8F6C\u7AD9\u5730\u5740> --state-dir ${stateDir}`);
|
|
30016
|
+
}
|
|
30017
|
+
if (emit2) emit2({ event: "progress", step: "write_config" });
|
|
30018
|
+
let applied;
|
|
30019
|
+
try {
|
|
30020
|
+
applied = applyFreeTierToConfig2(configFile, { baseUrl: relayBaseUrl });
|
|
30021
|
+
} catch (e) {
|
|
30022
|
+
fail2(e?.message || String(e));
|
|
30023
|
+
}
|
|
30024
|
+
if (emit2) {
|
|
30025
|
+
emit2({ event: "done", channel: "llm", detail: applied.primary, restart_required: true });
|
|
30026
|
+
} else {
|
|
30027
|
+
console.log(`
|
|
30028
|
+
\u2705 \u5DF2\u5207\u5230 7yi \u514D\u8D39\u6863: ${applied.configPath}`);
|
|
30029
|
+
console.log(` \u4E3B\u6A21\u578B ${applied.primary}${applied.vision ? `\uFF0C\u89C6\u89C9 ${applied.vision}` : ""}`);
|
|
30030
|
+
console.log(` \u539F\u914D\u7F6E\u5DF2\u5907\u4EFD: ${applied.backupPath}`);
|
|
30031
|
+
console.log("\n\u26A0\uFE0F \u5FC5\u987B\u91CD\u542F\u5F15\u64CE\u624D\u751F\u6548\u2014\u2014secrets \u7684\u76D1\u89C6\u5668\u53EA\u5728\u300C\u542F\u52A8\u65F6\u90A3\u4E2A\u6587\u4EF6\u5DF2\u5B58\u5728\u300D\u65F6\u624D\u5EFA");
|
|
30032
|
+
console.log(" \uFF08src/reload/reload.ts \u7684 secrets-watch\uFF09\uFF0C\u521A\u5199\u5B8C\u7684\u4EE4\u724C\u70ED\u52A0\u8F7D\u63A5\u4E0D\u5230\uFF1B");
|
|
30033
|
+
console.log(" \u4E0D\u91CD\u542F\u5F15\u64CE\u4F1A\u628A\u5B57\u9762\u91CF env:7YI_API_KEY \u5F53 key \u53D1\u51FA\u53BB \u2192 401\u3002");
|
|
30034
|
+
console.log(" engine7 restart");
|
|
30035
|
+
}
|
|
29567
30036
|
process.exit(0);
|
|
29568
30037
|
}
|
|
29569
30038
|
if (channel === "discord") {
|
|
@@ -29606,8 +30075,8 @@ async function main() {
|
|
|
29606
30075
|
else if (args[i] === "--note" && args[i + 1]) note = args[++i];
|
|
29607
30076
|
else if (args[i] === "--dry-run") dryRun2 = true;
|
|
29608
30077
|
}
|
|
29609
|
-
if (!exportStateDir) exportStateDir =
|
|
29610
|
-
if (!agentName) agentName =
|
|
30078
|
+
if (!exportStateDir) exportStateDir = path34.resolve(process.cwd());
|
|
30079
|
+
if (!agentName) agentName = path34.basename(exportStateDir);
|
|
29611
30080
|
await doExport2({ agentName, stateDir: exportStateDir, note, dryRun: dryRun2 });
|
|
29612
30081
|
process.exit(0);
|
|
29613
30082
|
}
|
|
@@ -29629,7 +30098,7 @@ async function main() {
|
|
|
29629
30098
|
console.error("\u274C import \u9700\u8981 --state-dir <path>");
|
|
29630
30099
|
process.exit(1);
|
|
29631
30100
|
}
|
|
29632
|
-
if (!agentName) agentName =
|
|
30101
|
+
if (!agentName) agentName = path34.basename(importStateDir);
|
|
29633
30102
|
await doImport2({ agentName, stateDir: importStateDir, version, archiveFile: archiveFile || void 0, dryRun: dryRun2 });
|
|
29634
30103
|
process.exit(0);
|
|
29635
30104
|
}
|
|
@@ -29654,18 +30123,18 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
29654
30123
|
}
|
|
29655
30124
|
}
|
|
29656
30125
|
if (!configPath2) {
|
|
29657
|
-
const defaultCfg =
|
|
29658
|
-
const homeCfg =
|
|
29659
|
-
if (
|
|
30126
|
+
const defaultCfg = path34.join("configs", "main7.json");
|
|
30127
|
+
const homeCfg = path34.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7", "configs", "main7.json");
|
|
30128
|
+
if (fs32.existsSync(defaultCfg)) {
|
|
29660
30129
|
configPath2 = defaultCfg;
|
|
29661
|
-
} else if (
|
|
30130
|
+
} else if (fs32.existsSync(homeCfg)) {
|
|
29662
30131
|
configPath2 = homeCfg;
|
|
29663
30132
|
console.log(` \u4F7F\u7528\u9ED8\u8BA4 config: ${homeCfg}`);
|
|
29664
30133
|
} else {
|
|
29665
30134
|
const ptr = readHomePointer();
|
|
29666
30135
|
if (ptr?.stateDir) {
|
|
29667
|
-
const ptrCfg =
|
|
29668
|
-
if (
|
|
30136
|
+
const ptrCfg = path34.join(ptr.stateDir, "configs", "main7.json");
|
|
30137
|
+
if (fs32.existsSync(ptrCfg)) {
|
|
29669
30138
|
configPath2 = ptrCfg;
|
|
29670
30139
|
console.log(` \u4F7F\u7528 config: ${ptrCfg}`);
|
|
29671
30140
|
}
|
|
@@ -29680,19 +30149,19 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
29680
30149
|
}
|
|
29681
30150
|
const { execSync: execSync3, spawn: spawn5 } = await import("node:child_process");
|
|
29682
30151
|
let enginePath;
|
|
29683
|
-
const localPath =
|
|
29684
|
-
if (
|
|
30152
|
+
const localPath = path34.join("node_modules", "engine7", "dist", "main.mjs");
|
|
30153
|
+
if (fs32.existsSync(localPath)) {
|
|
29685
30154
|
enginePath = localPath;
|
|
29686
30155
|
} else {
|
|
29687
|
-
const cliPath =
|
|
29688
|
-
const distDir =
|
|
29689
|
-
const candidate =
|
|
29690
|
-
if (
|
|
30156
|
+
const cliPath = path34.resolve(process.argv[1]);
|
|
30157
|
+
const distDir = path34.dirname(cliPath);
|
|
30158
|
+
const candidate = path34.join(distDir, "main.mjs");
|
|
30159
|
+
if (fs32.existsSync(candidate)) {
|
|
29691
30160
|
enginePath = candidate;
|
|
29692
30161
|
} else {
|
|
29693
30162
|
try {
|
|
29694
30163
|
const globalRoot = execSync3("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
29695
|
-
enginePath =
|
|
30164
|
+
enginePath = path34.join(globalRoot, "engine7", "dist", "main.mjs");
|
|
29696
30165
|
} catch {
|
|
29697
30166
|
console.error("\u274C \u627E\u4E0D\u5230 engine7 main.mjs");
|
|
29698
30167
|
process.exit(1);
|
|
@@ -29702,7 +30171,7 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
29702
30171
|
console.log(label);
|
|
29703
30172
|
console.log(` config: ${configPath2}`);
|
|
29704
30173
|
console.log(` engine: ${enginePath}`);
|
|
29705
|
-
const configName =
|
|
30174
|
+
const configName = path34.basename(configPath2);
|
|
29706
30175
|
const myPid = process.pid;
|
|
29707
30176
|
try {
|
|
29708
30177
|
if (process.platform === "win32") {
|
|
@@ -29720,16 +30189,16 @@ engine7 start \u2014 \u542F\u52A8 Engine
|
|
|
29720
30189
|
}
|
|
29721
30190
|
console.log("");
|
|
29722
30191
|
const envForChild = { ...process.env };
|
|
29723
|
-
const secretsDir =
|
|
29724
|
-
const cfgBase =
|
|
30192
|
+
const secretsDir = path34.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7-secrets");
|
|
30193
|
+
const cfgBase = path34.basename(configPath2, ".json");
|
|
29725
30194
|
const secretCandidates = [
|
|
29726
|
-
|
|
29727
|
-
|
|
29728
|
-
|
|
30195
|
+
path34.join(secretsDir, `${cfgBase}.env`),
|
|
30196
|
+
path34.join(path34.dirname(configPath2), `.env.${cfgBase}`),
|
|
30197
|
+
path34.join(path34.dirname(configPath2), ".env")
|
|
29729
30198
|
];
|
|
29730
30199
|
for (const secretPath of secretCandidates) {
|
|
29731
|
-
if (
|
|
29732
|
-
const content =
|
|
30200
|
+
if (fs32.existsSync(secretPath)) {
|
|
30201
|
+
const content = fs32.readFileSync(secretPath, "utf-8");
|
|
29733
30202
|
for (const line of content.split("\n")) {
|
|
29734
30203
|
const trimmed = line.trim();
|
|
29735
30204
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -29804,14 +30273,14 @@ engine7 session \u2014 \u4F1A\u8BDD\u7BA1\u7406\u5DE5\u5177
|
|
|
29804
30273
|
console.error(`\u274C \u672A\u77E5\u6A21\u5F0F: ${mode}\uFF08\u53EF\u9009: archive / drop-last / strip-images\uFF09`);
|
|
29805
30274
|
process.exit(1);
|
|
29806
30275
|
}
|
|
29807
|
-
const configRaw = JSON.parse(
|
|
30276
|
+
const configRaw = JSON.parse(fs32.readFileSync(configPath2, "utf-8"));
|
|
29808
30277
|
const stateDir = configRaw.stateDir;
|
|
29809
30278
|
if (!stateDir) {
|
|
29810
30279
|
console.error("\u274C config \u91CC\u6CA1\u6709 stateDir");
|
|
29811
30280
|
process.exit(1);
|
|
29812
30281
|
}
|
|
29813
|
-
const sessionsDir =
|
|
29814
|
-
if (!
|
|
30282
|
+
const sessionsDir = path34.join(stateDir, "agents", "main", "sessions");
|
|
30283
|
+
if (!fs32.existsSync(sessionsDir)) {
|
|
29815
30284
|
console.error(`\u274C sessions \u76EE\u5F55\u4E0D\u5B58\u5728: ${sessionsDir}`);
|
|
29816
30285
|
process.exit(1);
|
|
29817
30286
|
}
|
|
@@ -29819,37 +30288,37 @@ engine7 session \u2014 \u4F1A\u8BDD\u7BA1\u7406\u5DE5\u5177
|
|
|
29819
30288
|
console.log(` config: ${configPath2}`);
|
|
29820
30289
|
console.log(` mode: ${mode}`);
|
|
29821
30290
|
if (mode === "drop-last") console.log(` n: ${dropN}`);
|
|
29822
|
-
const platformMapPath =
|
|
29823
|
-
const indexPath =
|
|
30291
|
+
const platformMapPath = path34.join(sessionsDir, "platform-map.json");
|
|
30292
|
+
const indexPath = path34.join(sessionsDir, "session-index.json");
|
|
29824
30293
|
let sessionFileUUID = null;
|
|
29825
|
-
if (
|
|
29826
|
-
const index = JSON.parse(
|
|
30294
|
+
if (fs32.existsSync(indexPath)) {
|
|
30295
|
+
const index = JSON.parse(fs32.readFileSync(indexPath, "utf-8"));
|
|
29827
30296
|
const entries2 = Object.values(index);
|
|
29828
30297
|
if (entries2.length > 0) {
|
|
29829
30298
|
const fileVal = entries2[0].file || "";
|
|
29830
|
-
sessionFileUUID =
|
|
30299
|
+
sessionFileUUID = path34.isAbsolute(fileVal) ? path34.basename(fileVal, ".jsonl") : fileVal.replace(".jsonl", "");
|
|
29831
30300
|
}
|
|
29832
30301
|
}
|
|
29833
30302
|
if (!sessionFileUUID) {
|
|
29834
|
-
const jsonlFiles =
|
|
30303
|
+
const jsonlFiles = fs32.readdirSync(sessionsDir).filter((f) => f.endsWith(".jsonl") && !f.includes(".archived.") && !f.includes(".compaction.")).map((f) => ({ name: f, mtime: fs32.statSync(path34.join(sessionsDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
29835
30304
|
if (jsonlFiles.length === 0) {
|
|
29836
30305
|
console.error("\u274C \u627E\u4E0D\u5230\u6D3B\u8DC3\u7684 session JSONL \u6587\u4EF6");
|
|
29837
30306
|
process.exit(1);
|
|
29838
30307
|
}
|
|
29839
30308
|
sessionFileUUID = jsonlFiles[0].name.replace(".jsonl", "");
|
|
29840
30309
|
}
|
|
29841
|
-
let jsonlPath =
|
|
29842
|
-
if (!
|
|
29843
|
-
const jsonlFiles =
|
|
30310
|
+
let jsonlPath = path34.join(sessionsDir, `${sessionFileUUID}.jsonl`);
|
|
30311
|
+
if (!fs32.existsSync(jsonlPath)) {
|
|
30312
|
+
const jsonlFiles = fs32.readdirSync(sessionsDir).filter((f) => f.endsWith(".jsonl") && !f.includes(".archived.") && !f.includes(".compaction.")).map((f) => ({ name: f, mtime: fs32.statSync(path34.join(sessionsDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
29844
30313
|
if (jsonlFiles.length === 0) {
|
|
29845
30314
|
console.error(`\u274C JSONL \u6587\u4EF6\u4E0D\u5B58\u5728: ${jsonlPath}`);
|
|
29846
30315
|
process.exit(1);
|
|
29847
30316
|
}
|
|
29848
30317
|
sessionFileUUID = jsonlFiles[0].name.replace(".jsonl", "");
|
|
29849
|
-
jsonlPath =
|
|
30318
|
+
jsonlPath = path34.join(sessionsDir, `${sessionFileUUID}.jsonl`);
|
|
29850
30319
|
}
|
|
29851
30320
|
console.log(` file: ${jsonlPath}`);
|
|
29852
|
-
const configName =
|
|
30321
|
+
const configName = path34.basename(configPath2);
|
|
29853
30322
|
if (autoRestart) {
|
|
29854
30323
|
try {
|
|
29855
30324
|
const myPid = process.pid;
|
|
@@ -29865,15 +30334,15 @@ engine7 session \u2014 \u4F1A\u8BDD\u7BA1\u7406\u5DE5\u5177
|
|
|
29865
30334
|
}
|
|
29866
30335
|
}
|
|
29867
30336
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
29868
|
-
const { readFileSync:
|
|
30337
|
+
const { readFileSync: readFileSync44, writeFileSync: writeFileSync25, renameSync: renameSync5 } = fs32;
|
|
29869
30338
|
if (mode === "archive") {
|
|
29870
30339
|
const archiveName = `${sessionFileUUID}.jsonl.archived.${timestamp}`;
|
|
29871
|
-
renameSync5(jsonlPath,
|
|
30340
|
+
renameSync5(jsonlPath, path34.join(sessionsDir, archiveName));
|
|
29872
30341
|
console.log(`
|
|
29873
30342
|
\u2705 \u5F52\u6863\u5B8C\u6210: ${archiveName}`);
|
|
29874
30343
|
console.log(` \u91CD\u542F\u540E\u5C06\u4ECE\u7A7A\u767D\u4F1A\u8BDD\u5F00\u59CB`);
|
|
29875
30344
|
} else if (mode === "drop-last") {
|
|
29876
|
-
const lines =
|
|
30345
|
+
const lines = readFileSync44(jsonlPath, "utf-8").trim().split("\n");
|
|
29877
30346
|
const parsed = lines.map((l) => {
|
|
29878
30347
|
try {
|
|
29879
30348
|
return JSON.parse(l);
|
|
@@ -29892,12 +30361,12 @@ engine7 session \u2014 \u4F1A\u8BDD\u7BA1\u7406\u5DE5\u5177
|
|
|
29892
30361
|
}
|
|
29893
30362
|
const kept = parsed.slice(0, cutIndex);
|
|
29894
30363
|
const newContent = kept.map((p) => JSON.stringify(p)).join("\n") + "\n";
|
|
29895
|
-
|
|
30364
|
+
writeFileSync25(jsonlPath, newContent);
|
|
29896
30365
|
console.log(`
|
|
29897
30366
|
\u2705 \u780D\u6389\u6700\u8FD1 ${parsed.length - cutIndex} \u884C\uFF08${dropN} \u8F6E\uFF09`);
|
|
29898
30367
|
console.log(` \u4FDD\u7559 ${kept.length} \u884C`);
|
|
29899
30368
|
} else if (mode === "strip-images") {
|
|
29900
|
-
const lines =
|
|
30369
|
+
const lines = readFileSync44(jsonlPath, "utf-8").trim().split("\n");
|
|
29901
30370
|
let stripped = 0;
|
|
29902
30371
|
const newLines = lines.map((line) => {
|
|
29903
30372
|
try {
|
|
@@ -29938,17 +30407,17 @@ engine7 session \u2014 \u4F1A\u8BDD\u7BA1\u7406\u5DE5\u5177
|
|
|
29938
30407
|
return line;
|
|
29939
30408
|
}
|
|
29940
30409
|
});
|
|
29941
|
-
|
|
30410
|
+
writeFileSync25(jsonlPath, newLines.join("\n") + "\n");
|
|
29942
30411
|
console.log(`
|
|
29943
30412
|
\u2705 \u6458\u9664 ${stripped} \u884C\u56FE\u7247\u76F8\u5173\u5185\u5BB9`);
|
|
29944
30413
|
}
|
|
29945
30414
|
if (autoRestart) {
|
|
29946
|
-
const realCliPath =
|
|
29947
|
-
const cliDir =
|
|
29948
|
-
const pkgRoot =
|
|
29949
|
-
let enginePath =
|
|
29950
|
-
if (!
|
|
29951
|
-
enginePath =
|
|
30415
|
+
const realCliPath = fs32.realpathSync(process.argv[1]);
|
|
30416
|
+
const cliDir = path34.dirname(realCliPath);
|
|
30417
|
+
const pkgRoot = path34.resolve(cliDir, "..");
|
|
30418
|
+
let enginePath = path34.join(pkgRoot, "dist", "main.mjs");
|
|
30419
|
+
if (!fs32.existsSync(enginePath)) {
|
|
30420
|
+
enginePath = path34.join(cliDir, "main.mjs");
|
|
29952
30421
|
}
|
|
29953
30422
|
console.log(`
|
|
29954
30423
|
\u{1F680} \u62C9\u8D77 engine...`);
|
|
@@ -29988,18 +30457,18 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
29988
30457
|
let configPath2 = cfg;
|
|
29989
30458
|
let cwd = svcCwd;
|
|
29990
30459
|
if (!configPath2) {
|
|
29991
|
-
const defaultCfg =
|
|
29992
|
-
const homeCfg =
|
|
29993
|
-
if (
|
|
30460
|
+
const defaultCfg = path34.join(cwd, "configs", "main7.json");
|
|
30461
|
+
const homeCfg = path34.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7", "configs", "main7.json");
|
|
30462
|
+
if (fs32.existsSync(defaultCfg)) {
|
|
29994
30463
|
configPath2 = defaultCfg;
|
|
29995
|
-
} else if (
|
|
30464
|
+
} else if (fs32.existsSync(homeCfg)) {
|
|
29996
30465
|
configPath2 = homeCfg;
|
|
29997
|
-
cwd =
|
|
30466
|
+
cwd = path34.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
29998
30467
|
} else {
|
|
29999
30468
|
const ptr = readHomePointer();
|
|
30000
30469
|
if (ptr?.stateDir) {
|
|
30001
|
-
const ptrCfg =
|
|
30002
|
-
if (
|
|
30470
|
+
const ptrCfg = path34.join(ptr.stateDir, "configs", "main7.json");
|
|
30471
|
+
if (fs32.existsSync(ptrCfg)) {
|
|
30003
30472
|
configPath2 = ptrCfg;
|
|
30004
30473
|
cwd = ptr.stateDir;
|
|
30005
30474
|
}
|
|
@@ -30017,27 +30486,27 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
30017
30486
|
if (action === "install") {
|
|
30018
30487
|
if (process.platform === "win32") {
|
|
30019
30488
|
const taskName = "Engine7";
|
|
30020
|
-
const wrapperPath =
|
|
30489
|
+
const wrapperPath = path34.join(cwd, "engine7-start.cmd");
|
|
30021
30490
|
const wrapperContent = buildServiceWrapper({
|
|
30022
30491
|
cwd,
|
|
30023
30492
|
nodeExe: nodeExe || void 0,
|
|
30024
|
-
cliMjs:
|
|
30025
|
-
configPath:
|
|
30493
|
+
cliMjs: path34.resolve(engine7Bin),
|
|
30494
|
+
configPath: path34.resolve(configPath2)
|
|
30026
30495
|
});
|
|
30027
|
-
|
|
30028
|
-
const startupDir =
|
|
30029
|
-
process.env.APPDATA ||
|
|
30496
|
+
fs32.writeFileSync(wrapperPath, wrapperContent);
|
|
30497
|
+
const startupDir = path34.join(
|
|
30498
|
+
process.env.APPDATA || path34.join(process.env.USERPROFILE || "", "AppData", "Roaming"),
|
|
30030
30499
|
"Microsoft",
|
|
30031
30500
|
"Windows",
|
|
30032
30501
|
"Start Menu",
|
|
30033
30502
|
"Programs",
|
|
30034
30503
|
"Startup"
|
|
30035
30504
|
);
|
|
30036
|
-
|
|
30037
|
-
const vbsPath =
|
|
30505
|
+
fs32.mkdirSync(startupDir, { recursive: true });
|
|
30506
|
+
const vbsPath = path34.join(startupDir, "Engine7.vbs");
|
|
30038
30507
|
const nodeExePath = nodeExe || process.execPath;
|
|
30039
|
-
const cliMjsPath =
|
|
30040
|
-
const absCfg =
|
|
30508
|
+
const cliMjsPath = path34.resolve(engine7Bin);
|
|
30509
|
+
const absCfg = path34.resolve(configPath2);
|
|
30041
30510
|
const vbs = [
|
|
30042
30511
|
"' Engine 7 \u5F00\u673A\u81EA\u542F\uFF08\u542F\u52A8\u6587\u4EF6\u5939\uFF0C\u514D\u7BA1\u7406\u5458\uFF09",
|
|
30043
30512
|
'Set fso = CreateObject("Scripting.FileSystemObject")',
|
|
@@ -30053,15 +30522,15 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
30053
30522
|
's.Run """' + nodeExePath + '"" ""' + cliMjsPath + '"" start --config ""' + absCfg + '""", 0, False',
|
|
30054
30523
|
""
|
|
30055
30524
|
].join(String.fromCharCode(13) + String.fromCharCode(10));
|
|
30056
|
-
|
|
30525
|
+
fs32.writeFileSync(vbsPath, vbs);
|
|
30057
30526
|
console.log(`\u2705 \u5F00\u673A\u81EA\u542F\u52A8\u5DF2\u6CE8\u518C\uFF08\u542F\u52A8\u6587\u4EF6\u5939: ${vbsPath}\uFF09`);
|
|
30058
30527
|
} else if (process.platform === "darwin") {
|
|
30059
30528
|
const label = "com.engine7.agent";
|
|
30060
|
-
const plistDir =
|
|
30061
|
-
|
|
30062
|
-
const plistPath =
|
|
30063
|
-
const localCli =
|
|
30064
|
-
const cliMjs =
|
|
30529
|
+
const plistDir = path34.join(process.env.HOME, "Library", "LaunchAgents");
|
|
30530
|
+
fs32.mkdirSync(plistDir, { recursive: true });
|
|
30531
|
+
const plistPath = path34.join(plistDir, `${label}.plist`);
|
|
30532
|
+
const localCli = path34.join("node_modules", "engine7", "dist", "cli.mjs");
|
|
30533
|
+
const cliMjs = fs32.existsSync(localCli) ? path34.resolve(localCli) : path34.join(path34.dirname(path34.dirname(engine7Bin)), "lib", "node_modules", "engine7", "dist", "cli.mjs");
|
|
30065
30534
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
30066
30535
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
30067
30536
|
<plist version="1.0">
|
|
@@ -30073,14 +30542,14 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
30073
30542
|
<string>${cliMjs}</string>
|
|
30074
30543
|
<string>start</string>
|
|
30075
30544
|
<string>--config</string>
|
|
30076
|
-
<string>${
|
|
30545
|
+
<string>${path34.resolve(configPath2)}</string>
|
|
30077
30546
|
</array>
|
|
30078
30547
|
<key>WorkingDirectory</key><string>${cwd}</string>
|
|
30079
30548
|
<key>RunAtLoad</key><true/>
|
|
30080
30549
|
<key>KeepAlive</key><true/>
|
|
30081
30550
|
</dict>
|
|
30082
30551
|
</plist>`;
|
|
30083
|
-
|
|
30552
|
+
fs32.writeFileSync(plistPath, plist);
|
|
30084
30553
|
try {
|
|
30085
30554
|
execSync3(`launchctl load "${plistPath}"`, { stdio: "inherit" });
|
|
30086
30555
|
console.log(`\u2705 Mac launchd \u670D\u52A1\u5DF2\u521B\u5EFA\uFF08\u5F00\u673A\u81EA\u542F\u52A8\uFF09`);
|
|
@@ -30091,9 +30560,9 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
30091
30560
|
}
|
|
30092
30561
|
} else {
|
|
30093
30562
|
const svcName = "engine7";
|
|
30094
|
-
const svcDir =
|
|
30095
|
-
|
|
30096
|
-
const svcPath =
|
|
30563
|
+
const svcDir = path34.join(process.env.HOME, ".config", "systemd", "user");
|
|
30564
|
+
fs32.mkdirSync(svcDir, { recursive: true });
|
|
30565
|
+
const svcPath = path34.join(svcDir, `${svcName}.service`);
|
|
30097
30566
|
const svc = `[Unit]
|
|
30098
30567
|
Description=Engine 7 Agent
|
|
30099
30568
|
After=network.target
|
|
@@ -30101,13 +30570,13 @@ After=network.target
|
|
|
30101
30570
|
[Service]
|
|
30102
30571
|
Type=simple
|
|
30103
30572
|
WorkingDirectory=${cwd}
|
|
30104
|
-
ExecStart=${process.execPath} ${engine7Bin} start --config ${
|
|
30573
|
+
ExecStart=${process.execPath} ${engine7Bin} start --config ${path34.resolve(configPath2)}
|
|
30105
30574
|
Restart=on-failure
|
|
30106
30575
|
RestartSec=10
|
|
30107
30576
|
|
|
30108
30577
|
[Install]
|
|
30109
30578
|
WantedBy=default.target`;
|
|
30110
|
-
|
|
30579
|
+
fs32.writeFileSync(svcPath, svc);
|
|
30111
30580
|
try {
|
|
30112
30581
|
execSync3(`systemctl --user daemon-reload`, { stdio: "inherit" });
|
|
30113
30582
|
execSync3(`systemctl --user enable ${svcName}`, { stdio: "inherit" });
|
|
@@ -30120,8 +30589,8 @@ WantedBy=default.target`;
|
|
|
30120
30589
|
}
|
|
30121
30590
|
} else if (action === "uninstall") {
|
|
30122
30591
|
if (process.platform === "win32") {
|
|
30123
|
-
const vbsPath =
|
|
30124
|
-
process.env.APPDATA ||
|
|
30592
|
+
const vbsPath = path34.join(
|
|
30593
|
+
process.env.APPDATA || path34.join(process.env.USERPROFILE || "", "AppData", "Roaming"),
|
|
30125
30594
|
"Microsoft",
|
|
30126
30595
|
"Windows",
|
|
30127
30596
|
"Start Menu",
|
|
@@ -30133,20 +30602,20 @@ WantedBy=default.target`;
|
|
|
30133
30602
|
execSync3(`schtasks /delete /tn "Engine7" /f`, { stdio: "ignore" });
|
|
30134
30603
|
} catch {
|
|
30135
30604
|
}
|
|
30136
|
-
if (
|
|
30137
|
-
|
|
30605
|
+
if (fs32.existsSync(vbsPath)) {
|
|
30606
|
+
fs32.rmSync(vbsPath, { force: true });
|
|
30138
30607
|
console.log(`\u2705 \u5F00\u673A\u81EA\u542F\u52A8\u5DF2\u79FB\u9664\uFF08${vbsPath}\uFF09`);
|
|
30139
30608
|
} else {
|
|
30140
30609
|
console.log("\uFF08\u5F00\u673A\u81EA\u542F\u52A8\u672C\u6765\u5C31\u6CA1\u6CE8\u518C\uFF09");
|
|
30141
30610
|
}
|
|
30142
30611
|
} else if (process.platform === "darwin") {
|
|
30143
|
-
const plistPath =
|
|
30612
|
+
const plistPath = path34.join(process.env.HOME, "Library", "LaunchAgents", "com.engine7.agent.plist");
|
|
30144
30613
|
try {
|
|
30145
30614
|
execSync3(`launchctl unload "${plistPath}"`, { stdio: "inherit" });
|
|
30146
30615
|
} catch {
|
|
30147
30616
|
}
|
|
30148
30617
|
try {
|
|
30149
|
-
|
|
30618
|
+
fs32.unlinkSync(plistPath);
|
|
30150
30619
|
} catch {
|
|
30151
30620
|
}
|
|
30152
30621
|
console.log(`\u2705 Mac launchd \u670D\u52A1\u5DF2\u5220\u9664`);
|
|
@@ -30155,9 +30624,9 @@ WantedBy=default.target`;
|
|
|
30155
30624
|
execSync3(`systemctl --user disable engine7`, { stdio: "inherit" });
|
|
30156
30625
|
} catch {
|
|
30157
30626
|
}
|
|
30158
|
-
const svcPath =
|
|
30627
|
+
const svcPath = path34.join(process.env.HOME, ".config", "systemd", "user", "engine7.service");
|
|
30159
30628
|
try {
|
|
30160
|
-
|
|
30629
|
+
fs32.unlinkSync(svcPath);
|
|
30161
30630
|
} catch {
|
|
30162
30631
|
}
|
|
30163
30632
|
try {
|
|
@@ -30168,8 +30637,8 @@ WantedBy=default.target`;
|
|
|
30168
30637
|
}
|
|
30169
30638
|
} else if (action === "status") {
|
|
30170
30639
|
if (process.platform === "win32") {
|
|
30171
|
-
const vbsPath =
|
|
30172
|
-
process.env.APPDATA ||
|
|
30640
|
+
const vbsPath = path34.join(
|
|
30641
|
+
process.env.APPDATA || path34.join(process.env.USERPROFILE || "", "AppData", "Roaming"),
|
|
30173
30642
|
"Microsoft",
|
|
30174
30643
|
"Windows",
|
|
30175
30644
|
"Start Menu",
|
|
@@ -30177,10 +30646,10 @@ WantedBy=default.target`;
|
|
|
30177
30646
|
"Startup",
|
|
30178
30647
|
"Engine7.vbs"
|
|
30179
30648
|
);
|
|
30180
|
-
console.log(
|
|
30649
|
+
console.log(fs32.existsSync(vbsPath) ? `\u2705 \u5DF2\u6CE8\u518C\uFF08\u542F\u52A8\u6587\u4EF6\u5939\uFF09: ${vbsPath}` : "\u274C \u672A\u6CE8\u518C");
|
|
30181
30650
|
} else if (process.platform === "darwin") {
|
|
30182
|
-
const plistPath =
|
|
30183
|
-
if (
|
|
30651
|
+
const plistPath = path34.join(process.env.HOME, "Library", "LaunchAgents", "com.engine7.agent.plist");
|
|
30652
|
+
if (fs32.existsSync(plistPath)) {
|
|
30184
30653
|
console.log("\u2705 \u5DF2\u5B89\u88C5\uFF08launchd\uFF09");
|
|
30185
30654
|
} else {
|
|
30186
30655
|
console.log("\u274C \u672A\u5B89\u88C5");
|
|
@@ -30213,8 +30682,8 @@ WantedBy=default.target`;
|
|
|
30213
30682
|
console.log(`
|
|
30214
30683
|
\u{1F680} engine7 init`);
|
|
30215
30684
|
console.log(` state-dir: ${opts.stateDir}`);
|
|
30216
|
-
if (
|
|
30217
|
-
const files =
|
|
30685
|
+
if (fs32.existsSync(opts.stateDir) && !opts.dryRun) {
|
|
30686
|
+
const files = fs32.readdirSync(opts.stateDir);
|
|
30218
30687
|
if (files.length > 0) {
|
|
30219
30688
|
if (opts.force) {
|
|
30220
30689
|
if (looksLikeAppInstall(opts.stateDir)) {
|
|
@@ -30227,10 +30696,10 @@ WantedBy=default.target`;
|
|
|
30227
30696
|
}
|
|
30228
30697
|
console.log(`
|
|
30229
30698
|
\u26A0\uFE0F --force: \u6E05\u7A7A\u5DF2\u6709\u76EE\u5F55 ${opts.stateDir}`);
|
|
30230
|
-
|
|
30699
|
+
fs32.rmSync(opts.stateDir, { recursive: true, force: true });
|
|
30231
30700
|
} else {
|
|
30232
|
-
const existingConfig =
|
|
30233
|
-
if (
|
|
30701
|
+
const existingConfig = path34.join(opts.stateDir, "configs", "main7.json");
|
|
30702
|
+
if (fs32.existsSync(existingConfig)) {
|
|
30234
30703
|
console.log(`
|
|
30235
30704
|
\u267B\uFE0F \u68C0\u6D4B\u5230\u5DF2\u6709\u914D\u7F6E: ${existingConfig}`);
|
|
30236
30705
|
console.log(` \u2192 \u590D\u8DD1\u6A21\u5F0F\uFF1A\u663E\u793A\u73B0\u6709\u914D\u7F6E\uFF0C\u56DE\u8F66\u4FDD\u7559\uFF0C\u6539\u4E86\u624D\u751F\u6548\uFF08\u4E0D\u4F1A\u6E05\u7A7A\u76EE\u5F55\uFF09`);
|
|
@@ -30311,12 +30780,12 @@ WantedBy=default.target`;
|
|
|
30311
30780
|
let photoSrc = "";
|
|
30312
30781
|
if (answers?.selfie?.photoPath) {
|
|
30313
30782
|
const { dimsOf: dimsOf2 } = await Promise.resolve().then(() => (init_my_selfie(), my_selfie_exports));
|
|
30314
|
-
const src =
|
|
30315
|
-
const ext =
|
|
30783
|
+
const src = path34.resolve(answers.selfie.photoPath.trim());
|
|
30784
|
+
const ext = path34.extname(src).toLowerCase();
|
|
30316
30785
|
if (!PHOTO_EXTS.includes(ext)) {
|
|
30317
30786
|
throw new Error(`\u7167\u7247\u683C\u5F0F\u4E0D\u652F\u6301\uFF1A${ext || "(\u6CA1\u6709\u540E\u7F00)"}\u3002\u8BF7\u7528 PNG / JPEG / WebP\u3002`);
|
|
30318
30787
|
}
|
|
30319
|
-
if (!
|
|
30788
|
+
if (!fs32.existsSync(src)) {
|
|
30320
30789
|
throw new Error(`\u7167\u7247\u4E0D\u5B58\u5728\uFF1A${src}`);
|
|
30321
30790
|
}
|
|
30322
30791
|
const dims = dimsOf2(src);
|
|
@@ -30327,7 +30796,7 @@ WantedBy=default.target`;
|
|
|
30327
30796
|
photoSrc = src;
|
|
30328
30797
|
}
|
|
30329
30798
|
const config = generateConfig(values);
|
|
30330
|
-
const configPath =
|
|
30799
|
+
const configPath = path34.join(opts.stateDir, "configs", "main7.json");
|
|
30331
30800
|
const tree = buildDirTree(values);
|
|
30332
30801
|
if (headlessEmit) headlessEmit({ event: "progress", step: "write_config" });
|
|
30333
30802
|
tree.push({
|
|
@@ -30339,22 +30808,22 @@ WantedBy=default.target`;
|
|
|
30339
30808
|
dryRun(tree);
|
|
30340
30809
|
const skillsSrc = findTemplateDir("skills");
|
|
30341
30810
|
if (skillsSrc) {
|
|
30342
|
-
const skills =
|
|
30811
|
+
const skills = fs32.readdirSync(skillsSrc);
|
|
30343
30812
|
for (const s of skills) {
|
|
30344
|
-
console.log(` \u{1F4C1} ${
|
|
30813
|
+
console.log(` \u{1F4C1} ${path34.join(opts.stateDir, "workspace", "skills", s)}`);
|
|
30345
30814
|
}
|
|
30346
30815
|
}
|
|
30347
30816
|
} else {
|
|
30348
30817
|
if (values.chatMode === "auto") {
|
|
30349
30818
|
try {
|
|
30350
|
-
|
|
30819
|
+
fs32.unlinkSync(path34.join(opts.stateDir, "workspace", ".work-mode"));
|
|
30351
30820
|
} catch {
|
|
30352
30821
|
}
|
|
30353
30822
|
}
|
|
30354
30823
|
if (photoSrc && values.selfiePhotoRelPath) {
|
|
30355
|
-
const dest =
|
|
30356
|
-
|
|
30357
|
-
|
|
30824
|
+
const dest = path34.join(opts.stateDir, "workspace", values.selfiePhotoRelPath);
|
|
30825
|
+
fs32.mkdirSync(path34.dirname(dest), { recursive: true });
|
|
30826
|
+
fs32.copyFileSync(photoSrc, dest);
|
|
30358
30827
|
console.log(` \u{1F5BC} \u5F62\u8C61: ${dest}`);
|
|
30359
30828
|
}
|
|
30360
30829
|
if (headlessEmit) headlessEmit({ event: "progress", step: "create_dirs" });
|
|
@@ -30370,58 +30839,58 @@ WantedBy=default.target`;
|
|
|
30370
30839
|
if (headlessEmit) headlessEmit({ event: "progress", step: "copy_skills" });
|
|
30371
30840
|
const skillsSrc = findTemplateDir("skills");
|
|
30372
30841
|
if (skillsSrc) {
|
|
30373
|
-
const skillsDest =
|
|
30374
|
-
|
|
30375
|
-
const skills =
|
|
30842
|
+
const skillsDest = path34.join(opts.stateDir, "workspace", "skills");
|
|
30843
|
+
fs32.mkdirSync(skillsDest, { recursive: true });
|
|
30844
|
+
const skills = fs32.readdirSync(skillsSrc);
|
|
30376
30845
|
for (const s of skills) {
|
|
30377
|
-
copyDirRecursive(
|
|
30846
|
+
copyDirRecursive(path34.join(skillsSrc, s), path34.join(skillsDest, s));
|
|
30378
30847
|
console.log(` \u{1F4E6} skill: ${s}`);
|
|
30379
30848
|
}
|
|
30380
30849
|
}
|
|
30381
30850
|
console.log(`
|
|
30382
30851
|
\u2705 \u521D\u59CB\u5316\u5B8C\u6210\uFF01
|
|
30383
30852
|
`);
|
|
30384
|
-
const homePointer =
|
|
30853
|
+
const homePointer = path34.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7.json");
|
|
30385
30854
|
const pointerData = { stateDir: opts.stateDir };
|
|
30386
|
-
|
|
30855
|
+
fs32.writeFileSync(homePointer, JSON.stringify(pointerData, null, 2) + "\n");
|
|
30387
30856
|
console.log(` \u{1F4C4} ${homePointer}\uFF08\u4ECE\u4EFB\u610F\u76EE\u5F55\u90FD\u80FD\u627E\u5230\u6B64 agent\uFF09`);
|
|
30388
30857
|
console.log(`\u4E0B\u4E00\u6B65\uFF1A`);
|
|
30389
30858
|
console.log(` 1. \u68C0\u67E5\u5E76\u4FEE\u6539 ${opts.stateDir}/configs/main7.json`);
|
|
30390
30859
|
console.log(` 2. \u542F\u52A8 Engine: engine7 start\uFF08\u5728\u4EFB\u610F\u76EE\u5F55\u90FD\u884C\uFF09`);
|
|
30391
30860
|
console.log(` 3. \u5F00\u673A\u81EA\u542F: engine7 service install\uFF08\u5728\u4EFB\u610F\u76EE\u5F55\u90FD\u884C\uFF09`);
|
|
30392
|
-
console.log(` 4. \u67E5\u770B workspace: ${
|
|
30861
|
+
console.log(` 4. \u67E5\u770B workspace: ${path34.join(opts.stateDir, "workspace")}`);
|
|
30393
30862
|
if (headlessEmit) {
|
|
30394
30863
|
headlessEmit({ event: "done", stateDir: opts.stateDir.replace(/\\/g, "/"), configPath: configPath.replace(/\\/g, "/") });
|
|
30395
30864
|
}
|
|
30396
30865
|
}
|
|
30397
30866
|
}
|
|
30398
30867
|
function copyDirRecursive(src, dest) {
|
|
30399
|
-
|
|
30400
|
-
for (const entry of
|
|
30401
|
-
const srcPath =
|
|
30402
|
-
const destPath =
|
|
30868
|
+
fs32.mkdirSync(dest, { recursive: true });
|
|
30869
|
+
for (const entry of fs32.readdirSync(src, { withFileTypes: true })) {
|
|
30870
|
+
const srcPath = path34.join(src, entry.name);
|
|
30871
|
+
const destPath = path34.join(dest, entry.name);
|
|
30403
30872
|
if (entry.isDirectory()) {
|
|
30404
30873
|
copyDirRecursive(srcPath, destPath);
|
|
30405
30874
|
} else {
|
|
30406
|
-
|
|
30875
|
+
fs32.copyFileSync(srcPath, destPath);
|
|
30407
30876
|
}
|
|
30408
30877
|
}
|
|
30409
30878
|
}
|
|
30410
30879
|
function findTemplateDir(name) {
|
|
30411
30880
|
const candidates = [
|
|
30412
|
-
|
|
30413
|
-
|
|
30414
|
-
|
|
30881
|
+
path34.join(__dirname, "..", "templates", name),
|
|
30882
|
+
path34.join(__dirname, "..", "..", "templates", name),
|
|
30883
|
+
path34.join(process.cwd(), "templates", name)
|
|
30415
30884
|
];
|
|
30416
30885
|
for (const p of candidates) {
|
|
30417
|
-
if (
|
|
30886
|
+
if (fs32.existsSync(p)) return p;
|
|
30418
30887
|
}
|
|
30419
30888
|
return null;
|
|
30420
30889
|
}
|
|
30421
30890
|
function readHomePointer() {
|
|
30422
|
-
const ptrPath =
|
|
30891
|
+
const ptrPath = path34.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7.json");
|
|
30423
30892
|
try {
|
|
30424
|
-
return JSON.parse(
|
|
30893
|
+
return JSON.parse(fs32.readFileSync(ptrPath, "utf-8"));
|
|
30425
30894
|
} catch {
|
|
30426
30895
|
return null;
|
|
30427
30896
|
}
|