engine7 7.1.119 → 7.1.120

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 CHANGED
@@ -395,6 +395,17 @@ var init_live = __esm({
395
395
  Object.assign(this.current, newConfig);
396
396
  }
397
397
  /** 改活树上的值;文件承载路径同步持久化(read-modify-write 回 config 文件) */
398
+ /**
399
+ * 改活树 + 落盘。
400
+ *
401
+ * 返回值 = **落盘**是否成功(活树无论成败都已同步——它是先改的)。绝大多数调用点照旧
402
+ * `void liveConfig.set(...)` 不理它;但**要打成功日志的调用点必须 await 这个布尔值**。
403
+ *
404
+ * 为什么把这个信号交出去(1001 审查捞回):本方法本来是 fire-and-forget,落盘失败只在
405
+ * `persistToFile` 里 warn 一行;而启动早段的调用点(两个免费档 fixup)紧接着打了句
406
+ * "免费档补齐 xxx" 的成功日志 ⇒ 配置**没写进文件**却被读成"写好了":进程内生效、托盘
407
+ * 读文件还是旧值、下次启动重放同一句——正是本仓最恨的假信号。
408
+ */
398
409
  async set(dotPath, val) {
399
410
  if (!this.current) throw new Error("[liveConfig] not initialized \u2014 call liveConfig.init() first");
400
411
  const keys = dotPath.split(".");
@@ -404,15 +415,15 @@ var init_live = __esm({
404
415
  obj = obj[keys[i]];
405
416
  }
406
417
  obj[keys[keys.length - 1]] = val;
407
- await this.persistToFile(dotPath, val);
418
+ return this.persistToFile(dotPath, val);
408
419
  }
409
- /** 把改动写回 config 文件对应段(找不到文件路径则只内存生效) */
420
+ /** 把改动写回 config 文件对应段(找不到文件路径则只内存生效)。返回是否真的落盘。 */
410
421
  async persistToFile(dotPath, val) {
411
422
  const fs44 = await import("node:fs");
412
423
  const cfgPath = this.current?._configFilePath;
413
424
  if (!cfgPath || !fs44.existsSync(cfgPath)) {
414
425
  console.warn(`[liveConfig] set: no config file path, in-memory only (${dotPath})`);
415
- return;
426
+ return false;
416
427
  }
417
428
  try {
418
429
  const raw = JSON.parse(fs44.readFileSync(cfgPath, "utf-8"));
@@ -426,8 +437,10 @@ var init_live = __esm({
426
437
  fs44.writeFileSync(cfgPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
427
438
  console.log(`[liveConfig] persisted ${dotPath} = ${JSON.stringify(val)} to ${cfgPath}`);
428
439
  markSelfWrite();
440
+ return true;
429
441
  } catch (e) {
430
442
  console.warn(`[liveConfig] persist failed (${dotPath}): ${e.message}`);
443
+ return false;
431
444
  }
432
445
  }
433
446
  /** 是否已初始化 */
@@ -1549,6 +1562,16 @@ function secretRef(envKey, value) {
1549
1562
  function isFreeTier(v) {
1550
1563
  return v.freeTier === true || v.primaryProvider === "7yi";
1551
1564
  }
1565
+ function freeTierVoiceConfig(existing) {
1566
+ const mv = existing && typeof existing === "object" ? existing : {};
1567
+ return {
1568
+ ...mv,
1569
+ provider: "relay",
1570
+ relayProviderId: FREE_TIER_PROVIDER_ID,
1571
+ relayModel: "engine7-tts",
1572
+ voice: mv.voice || DEFAULT_FREE_VOICE
1573
+ };
1574
+ }
1552
1575
  function resolveRelayUrl(relayArg, env = process.env) {
1553
1576
  return (relayArg || "").trim() || (env.E7_RELAY_URL || "").trim() || FREE_TIER_RELAY_URL;
1554
1577
  }
@@ -1612,9 +1635,21 @@ function generateConfig(v) {
1612
1635
  },
1613
1636
  // 0917:my_eyes(看图工具)读的是 tools.my_eyes.model,不是 tools.vision——
1614
1637
  // 后者是模板里遗留的死键(全仓零读者),已删。visionModel 解析不出就不写这个块。
1638
+ //
1639
+ // 1001 发语音条(§7.4):免费档必须把这块写全。此前唯一会写 `tools.my_voice` 的是
1640
+ // `applyFreeTier`,而它只从「切换到免费档」那张卡触发——配置已经是免费档时卡片不显示
1641
+ // ⇒ **新装机永远没有这块**:托盘「合成引擎」显示 edge-tts(面板在 provider 缺失时
1642
+ // 默认选它),引擎 `pickProvider` 也落 edge,而 edge 要本机 Python + `edge_tts`
1643
+ // (安装包一个都不带)⇒ 用户第一次说"发条语音"就报错。郭叔 1001 装机实测撞到。
1644
+ // 形状走 `freeTierVoiceConfig()`(唯一源,与 applyFreeTier / 启动补齐同一份)。
1615
1645
  tools: {
1616
1646
  ...v.tavilyKey ? { tavily: { apiKey: v.tavilyKey } } : {},
1617
1647
  ...v.visionModel ? { my_eyes: { model: v.visionModel } } : {},
1648
+ // ⚠️ 判据是 **provider 真的是 7yi**,不是 `isFreeTier(v)`:后者会因 `freeTier:true`
1649
+ // 单独为真(answers 手写坏组合时),而那种 config 里**根本没有 7yi provider 块**,
1650
+ // 写进去就是个悬空引用(第一次发语音报"免费池未配置",把用户指向错误的排查方向)。
1651
+ // 与下面 vision/video 的收口闸同一条纪律:**引用必须落在本次真实写出的 provider 里**。
1652
+ ...v.primaryProvider === FREE_TIER_PROVIDER_ID ? { my_voice: freeTierVoiceConfig() } : {},
1618
1653
  // 自拍(§4.3):免费档必须显式写 provider:'relay'。不写的话 getProvider()
1619
1654
  // 会落回默认值 'fal',而免费装机没有 fal key——第一次自拍就失败。
1620
1655
  // 非免费档只在用户真给了形象时才写这个块,且不替他决定 provider。
@@ -1837,13 +1872,15 @@ function generateConfig(v) {
1837
1872
  }
1838
1873
  return config;
1839
1874
  }
1840
- var SCHEMA_VERSION, FREE_TIER_RELAY_URL, DEFAULT_PRIMARY_MODEL, VISION_CAPABLE, PROVIDER_DEFS, MODEL_CHOICES, CUSTOM_DEFAULT_INPUT, TEMPLATE_VALUES;
1875
+ var SCHEMA_VERSION, FREE_TIER_RELAY_URL, FREE_TIER_PROVIDER_ID, DEFAULT_FREE_VOICE, DEFAULT_PRIMARY_MODEL, VISION_CAPABLE, PROVIDER_DEFS, MODEL_CHOICES, CUSTOM_DEFAULT_INPUT, TEMPLATE_VALUES;
1841
1876
  var init_cli_init_config = __esm({
1842
1877
  "src/cli-init-config.ts"() {
1843
1878
  "use strict";
1844
1879
  init_registry();
1845
1880
  SCHEMA_VERSION = 1;
1846
1881
  FREE_TIER_RELAY_URL = "https://www.twinsun.top:8866";
1882
+ FREE_TIER_PROVIDER_ID = "7yi";
1883
+ DEFAULT_FREE_VOICE = "Chinese (Mandarin)_Warm_Girl";
1847
1884
  DEFAULT_PRIMARY_MODEL = {
1848
1885
  dashscope: "dashscope/qwen3.8-max",
1849
1886
  minimax: "minimax/MiniMax-M3",
@@ -15575,7 +15612,7 @@ var init_providers = __esm({
15575
15612
  if (r.warning) console.warn(`[my-voice] ${r.warning}`);
15576
15613
  if (!r.voice) {
15577
15614
  throw new TtsConfigError(
15578
- "relay TTS \u7F3A\u97F3\u8272\uFF1Atools.my_voice.voice \u662F\u7A7A\u7684\uFF0C\u4E0A\u6E38\u4F1A\u62D2\u3002\u514D\u8D39\u6863\u88C5\u673A\u5E94\u5DF2\u7531 applyFreeTier \u586B\u597D\u9ED8\u8BA4\u97F3\u8272\u3002"
15615
+ "relay TTS \u7F3A\u97F3\u8272\uFF1Atools.my_voice.voice \u662F\u7A7A\u7684\uFF0C\u4E0A\u6E38\u4F1A\u62D2\u3002\u514D\u8D39\u6863\u88C5\u673A\u5E94\u5DF2\u81EA\u52A8\u586B\u597D\u9ED8\u8BA4\u97F3\u8272\uFF08\u65B0\u88C5\u673A generateConfig / \u8001\u88C5\u673A\u542F\u52A8\u8865\u9F50 / \u5207\u6362\u514D\u8D39\u6863 applyFreeTier \u4E09\u6761\u8DEF\uFF0C\u89C1 cli-init-config.ts \u7684 freeTierVoiceConfig\uFF09\u3002"
15579
15616
  );
15580
15617
  }
15581
15618
  try {
@@ -15824,7 +15861,7 @@ var init_my_voice = __esm({
15824
15861
  description: "TTS engine. relay = 7yi \u4E2D\u8F6C\u7AD9\uFF08\u514D\u8D39\u6863\u9ED8\u8BA4\uFF09\uFF0Ccosyvoice = \u767E\u70BC API\uFF0Cgptsovits = \u672C\u5730\u514B\u9686\uFF0Cqwen3-3060 = \u672C\u5730 GGML\uFF0Cedge = \u515C\u5E95\u3002"
15825
15862
  },
15826
15863
  channel: { type: "string", enum: ["weixin", "feishu", "discord"], description: "Target channel. Default: current channel." },
15827
- to: { type: "string", description: "Recipient ID. \u5FAE\u4FE1\u683C\u5F0F o\u5F00\u5934@im.wechat\u7ED3\u5C3E\uFF0C\u98DE\u4E66\u683C\u5F0F ou_\u5F00\u5934 open_id\u3002\u4E0D\u786E\u5B9A\u5C31\u5148 read prompts/contacts.md \u67E5\u6536\u4EF6\u4EBA ID\u3002multaca \u4EFB\u52A1\u91CC\u4E0D\u9700\u8981\u586B to\u2014\u2014\u8BED\u97F3\u4F1A\u6302\u5230\u5F53\u524D issue \u8BC4\u8BBA\uFF0C\u586B issueId \u6307\u5B9A\u76EE\u6807 issue\u3002" },
15864
+ to: { type: "string", description: 'Recipient ID, ONLY when sending to someone else. \u5FAE\u4FE1\u683C\u5F0F o\u5F00\u5934@im.wechat\u7ED3\u5C3E\uFF0C\u98DE\u4E66\u683C\u5F0F ou_\u5F00\u5934 open_id\uFF1B\u53D1\u7ED9\u522B\u4EBA\u524D read prompts/contacts.md \u67E5\u6536\u4EF6\u4EBA ID\u3002**\u53D1\u7ED9\u5F53\u524D\u5BF9\u8BDD\u7684\u4EBA\uFF08"\u53D1\u7ED9\u6211/\u53D1\u6761\u8BED\u97F3"\uFF09\u4E0D\u8981\u586B to**\u2014\u2014\u7559\u7A7A\u5373\u53D1\u5F53\u524D\u4F1A\u8BDD\uFF0C\u8FD9\u662F\u9ED8\u8BA4\u4E5F\u662F\u66F4\u7A33\u7684\u8DEF\uFF08\u90E8\u5206\u79DF\u6237\u4F1A\u62D2"\u6309\u7528\u6237 ID \u76F4\u53D1"\uFF0C\u89C1 1001 \u6848\u4F8B\uFF09\u3002multaca \u4EFB\u52A1\u91CC\u4E0D\u9700\u8981\u586B to\u2014\u2014\u8BED\u97F3\u4F1A\u6302\u5230\u5F53\u524D issue \u8BC4\u8BBA\uFF0C\u586B issueId \u6307\u5B9A\u76EE\u6807 issue\u3002' },
15828
15865
  issueId: { type: "string", description: 'multaca headless \u4E13\u7528\uFF1A\u76EE\u6807 issue ID\uFF08\u4EFB\u52A1 prompt \u91CC\u7684 "Your assigned issue ID"\uFF09\u3002\u666E\u901A\u98DE\u4E66/\u5FAE\u4FE1\u573A\u666F\u4E0D\u8981\u586B\u3002' },
15829
15866
  caption: { type: "string", description: "Optional text to accompany the voice message." }
15830
15867
  },
@@ -15936,7 +15973,8 @@ var init_my_voice = __esm({
15936
15973
  }
15937
15974
  return { content: `Voice sent! (${actualEngine}, ${sizeKB.toFixed(0)}KB, ${resolvedChannel})` };
15938
15975
  } catch (err) {
15939
- return { content: `Voice send failed: ${err.message}`, isError: true };
15976
+ const toHint = args.to ? '\u3002\u26A0\uFE0F \u8FD9\u6B21\u5E26\u7684\u662F to=\u6536\u4EF6\u4EBA ID\uFF1A\u82E5\u6536\u4EF6\u4EBA\u5C31\u662F**\u5F53\u524D\u5BF9\u8BDD\u7684\u4EBA**\uFF0C\u53BB\u6389 to \u91CD\u8BD5\u2014\u2014\u5F15\u64CE\u4F1A\u53D1\u7ED9\u5F53\u524D\u4F1A\u8BDD\uFF0C\u90E8\u5206\u79DF\u6237\u4F1A\u62D2"\u6309\u7528\u6237 ID \u76F4\u53D1"\uFF08\u98DE\u4E66 230101\uFF09' : "";
15977
+ return { content: `Voice send failed: ${err.message}${toHint}`, isError: true };
15940
15978
  }
15941
15979
  },
15942
15980
  isConcurrencySafe: () => false,
@@ -29835,15 +29873,7 @@ function applyFreeTier(cfg, opts = {}) {
29835
29873
  const mv = cfg.tools.my_voice || null;
29836
29874
  const existingProvider = typeof mv?.provider === "string" ? mv.provider : "";
29837
29875
  if (!mv || existingProvider === "relay") {
29838
- cfg.tools.my_voice = {
29839
- ...mv || {},
29840
- provider: "relay",
29841
- relayProviderId: FREE_TIER_PROVIDER_ID,
29842
- // '7yi'
29843
- relayModel: "engine7-tts",
29844
- // 有块就不夺 ⇒ 走到这里时 mv 要么不存在、要么 voice 就是 relay 的;非 relay 的块已跳过
29845
- voice: mv?.voice || DEFAULT_FREE_VOICE
29846
- };
29876
+ cfg.tools.my_voice = freeTierVoiceConfig(mv);
29847
29877
  }
29848
29878
  return cfg;
29849
29879
  }
@@ -29889,15 +29919,14 @@ function applyFreeTierToConfig(configPath, opts = {}) {
29889
29919
  video: cfg.agents.defaults.model.video ?? null
29890
29920
  };
29891
29921
  }
29892
- var FREE_TIER_PROVIDER_ID, FREE_TIER_PRIMARY, DEFAULT_FREE_VOICE;
29922
+ var FREE_TIER_PRIMARY;
29893
29923
  var init_free_tier_apply = __esm({
29894
29924
  "src/free-tier-apply.ts"() {
29895
29925
  "use strict";
29896
29926
  init_cli_init_config();
29897
29927
  init_provision_free();
29898
- FREE_TIER_PROVIDER_ID = "7yi";
29928
+ init_cli_init_config();
29899
29929
  FREE_TIER_PRIMARY = "7yi/engine7-free";
29900
- DEFAULT_FREE_VOICE = "Chinese (Mandarin)_Warm_Girl";
29901
29930
  }
29902
29931
  });
29903
29932