codebee 0.1.97 → 0.1.99

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/app/ui/app.js CHANGED
@@ -812,8 +812,14 @@ function onTypeChange() {
812
812
  }
813
813
 
814
814
  function directProviders() {
815
- /* 停用厂商不列出(用户拍板 2026-09-21「停用的不弄出来」);只列启用且有 key 的 */
816
- return (S.providers || []).filter((p) => p.enabled !== false && p.api_key);
815
+ /* 停用厂商不列出(用户拍板 2026-09-21「停用的不弄出来」);只列启用且有 key 的。
816
+ * 模型名单已抓取但启用数为零的也不列——「随厂商推荐」没有可落的模型。 */
817
+ return (S.providers || []).filter((p) => {
818
+ if (p.enabled === false || !p.api_key) return false;
819
+ const ms = p.models;
820
+ if (Array.isArray(ms) && ms.length && !ms.some((m) => m.enabled !== false && !m.hidden)) return false;
821
+ return true;
822
+ });
817
823
  }
818
824
 
819
825
  function renderDirectModelPicker() {
@@ -826,7 +832,8 @@ function renderDirectModelPicker() {
826
832
  if (prev && provs.some((p) => p.id === prev)) ps.value = prev;
827
833
  const p = provs.find((x) => x.id === ps.value);
828
834
  const oldModel = ms.value;
829
- const names = p ? (p.models || []).filter((m) => !m.hidden)
835
+ // 只列启用模型(停用/已删除的与后端 resolve 同口径;已选模型被停用时自然回落「随厂商推荐」)
836
+ const names = p ? (p.models || []).filter((m) => !m.hidden && m.enabled !== false)
830
837
  .map((m) => m.name).filter(Boolean) : [];
831
838
  ms.innerHTML = '<option value="">' + t("随厂商推荐") + '</option>' + names.map((name) =>
832
839
  '<option value="' + esc(name) + '">' + esc(name) + "</option>").join("");
@@ -6166,7 +6173,7 @@ function pbBlock(task, platform) {
6166
6173
  const run = (S.pubAuto && S.pubAuto.running &&
6167
6174
  S.pubAuto.running.platform === platform) ? S.pubAuto.running : null;
6168
6175
  if (run && run.status === "running") {
6169
- btns += '<span class="pb-book">' + esc(t("自动发布中 ") + run.done + "/" + run.total) + "</span>";
6176
+ btns += '<span class="pb-book">' + esc((run.as_draft ? t("存草稿中 ") : t("自动发布中 ")) + run.done + "/" + run.total) + "</span>";
6170
6177
  } else if (au.pending > 0) {
6171
6178
  // 门禁不再禁用按钮:点了走 pbApiWithQualityOverride 的复审/强制对话,
6172
6179
  // 死按钮只会让用户无路可走(2026-10-08 实案:guard false 全灰点不了)
@@ -6174,6 +6181,12 @@ function pbBlock(task, platform) {
6174
6181
  ' title="' + esc(au.guard_ok ? t("按章号顺序逐章填稿(人工模式每点一次填一章,浏览器里提交后再点发下一章)") : t("护栏提示:") + (au.guard_reason || "")) + '"' +
6175
6182
  ' onclick="pbPublishAll(\'' + esc(task.id) + "', '" + platform + '\')">' +
6176
6183
  t("发布全部待发") + t("(") + au.pending + t(")") + "</button>";
6184
+ // 全部发草稿(2026-10-09):质量闸拦发布不拦草稿——逐章自动填稿点
6185
+ // 「存草稿」连跑到清空,提交发布由用户到平台草稿箱手工完成
6186
+ btns += ' <button class="ghost" ' + (busy || au.draft_ok === false ? "disabled" : "") +
6187
+ ' title="' + esc(au.draft_ok !== false ? t("逐章自动填稿并点「存草稿」:内容落平台草稿箱不上线,不受质量门禁拦截,无需逐章人工确认;完成后到平台检查并提交发布") : t("护栏提示:") + (au.draft_reason || "")) + '"' +
6188
+ ' onclick="pbDraftAll(\'' + esc(task.id) + "', '" + platform + '\')">' +
6189
+ t("全部存草稿") + t("(") + au.pending + t(")") + "</button>";
6177
6190
  }
6178
6191
  // 批量选择:待发章号 chips 按卷分组(卷计划来自服务端 items[].volume),
6179
6192
  // 「发布所选」人工模式 / 「直发所选」逐章自动提交(护栏照常生效)。
@@ -6222,10 +6235,23 @@ function pbBlock(task, platform) {
6222
6235
  (au.guard_ok ? "" : ";" + t("护栏提示:") + (au.guard_reason || ""))) + '"' +
6223
6236
  ' onclick="pbPublishSelected(\'' + esc(task.id) + "','" + platform + '\',true)">' +
6224
6237
  t("直发所选") + t("(") + sel.size + t(")") + "</button>" +
6238
+ '<button class="ghost" ' + (busy || !sel.size || au.draft_ok === false ? "disabled" : "") +
6239
+ ' title="' + esc(t("所选章节逐章自动存草稿(不上线、不受质量门禁拦截),完成后到平台检查并提交发布") +
6240
+ (au.draft_ok !== false ? "" : ";" + t("护栏提示:") + (au.draft_reason || ""))) + '"' +
6241
+ ' onclick="pbDraftSelected(\'' + esc(task.id) + "','" + platform + '\')">' +
6242
+ t("存草稿所选") + t("(") + sel.size + t(")") + "</button>" +
6225
6243
  "</div></div>";
6226
6244
  }
6227
- if (au.pending > 0 && !au.guard_ok) {
6228
- btns += '<div class="pb-err">' + esc(au.guard_reason || t("护栏拦截")) + "</div>";
6245
+ if (au.pending > 0 && au.draft_ok === false) {
6246
+ // 硬护栏(建书未确认/每日上限/连败退避):存草稿也走不了,红色示警
6247
+ btns += '<div class="pb-err">' + esc(au.draft_reason || au.guard_reason || t("护栏拦截")) + "</div>";
6248
+ } else if (au.pending > 0 && !au.guard_ok && au.quality_blockers && au.quality_blockers.length) {
6249
+ // 纯质量闸拦截:不给死路——提示先存草稿(2026-10-09 全部发草稿通路)
6250
+ btns += '<div class="pb-hint">' + esc(t("质量闸未过:") + au.quality_blockers.join(t(";")) +
6251
+ t("——可先「全部存草稿」落平台(不上线),正式发布前需复审达标或强制放行")) + "</div>";
6252
+ }
6253
+ if (au.drafted > 0) {
6254
+ btns += '<div class="pb-hint">' + esc(t("已存草稿 {0} 章(未发布):到平台章节管理/草稿箱逐章检查后提交发布", au.drafted)) + "</div>";
6229
6255
  }
6230
6256
  if (run && run.status === "manual_pause") {
6231
6257
  // 人工模式一轮只填一章:等用户在浏览器提交后再发起(连发会导航离开
@@ -6580,6 +6606,38 @@ window.pbPublishAll = async function (taskId, platform) { const au = (((S.pubAu
6580
6606
  S._pbSig = ""; pbKick();
6581
6607
  };
6582
6608
 
6609
+ /* 全部发草稿(2026-10-09):逐章自动填稿点「存草稿」连跑到清空,不上线、
6610
+ * 不受质量闸拦、无需人工确认;提交发布由用户到平台草稿箱手工完成。 */
6611
+ window.pbDraftAll = async function (taskId, platform) {
6612
+ const au = (((S.pubAuto && S.pubAuto.books) || [])
6613
+ .find((b) => b.platform === platform)) || {};
6614
+ if (!au.pending) return;
6615
+ if (!(await uiConfirm(t("将对全部 {0} 章待发稿逐章自动填稿并点「存草稿」:内容落平台草稿箱不上线,不受质量门禁拦截,无需逐章人工确认(章间隔约 1 分钟防风控)。跑完后请到平台章节管理/草稿箱逐章检查,再手工提交发布。继续?", au.pending),
6616
+ { ok: t("开始存草稿") }))) return;
6617
+ try {
6618
+ await api("/api/publish/task/" + encodeURIComponent(taskId) + "/publish-all",
6619
+ { method: "POST", body: JSON.stringify({ platform, as_draft: true }) });
6620
+ toast(t("存草稿已启动:逐章自动填稿并保存草稿,进度见发布台"));
6621
+ } catch (e) { toast(t("存草稿启动失败:") + e.message, true); }
6622
+ S._pbSig = ""; pbKick();
6623
+ };
6624
+
6625
+ window.pbDraftSelected = async function (taskId, platform) {
6626
+ const key = taskId + ":" + platform;
6627
+ const ent = (S.pbSel || {})[key];
6628
+ const chapters = ent ? [...ent.sel].sort((a, b) => a - b) : [];
6629
+ if (!chapters.length) return;
6630
+ if (!(await uiConfirm(t("将对所选 {0} 章(第 {1} 章—第 {2} 章)逐章自动填稿并点「存草稿」(不上线、不受质量门禁拦截),完成后到平台章节管理/草稿箱检查并提交发布。继续?",
6631
+ chapters.length, chapters[0], chapters[chapters.length - 1]),
6632
+ { ok: t("开始存草稿") }))) return;
6633
+ try {
6634
+ await api("/api/publish/task/" + encodeURIComponent(taskId) + "/publish-all",
6635
+ { method: "POST", body: JSON.stringify({ platform, chapters, as_draft: true }) });
6636
+ toast(t("存草稿已启动:逐章自动填稿并保存草稿,进度见发布台"));
6637
+ } catch (e) { toast(t("存草稿启动失败:") + e.message, true); }
6638
+ S._pbSig = ""; pbKick();
6639
+ };
6640
+
6583
6641
  function pbKick() {
6584
6642
  const key = detailSideTaskKey();
6585
6643
  if (!key) return;
@@ -9781,7 +9839,8 @@ function rdModelMenuRender() {
9781
9839
  t("管理模型…") + "</span></span></button></div>";
9782
9840
  } else {
9783
9841
  const p = directProviders().find((x) => x.id === rdMenuProv);
9784
- const names = p ? (p.models || []).filter((m) => !m.hidden)
9842
+ // 只列启用模型:停用的选了也会被运行时拒(builtin_agent.resolve 对账启用名单)
9843
+ const names = p ? (p.models || []).filter((m) => !m.hidden && m.enabled !== false)
9785
9844
  .map((m) => m.name).filter(Boolean) : [];
9786
9845
  menu.innerHTML =
9787
9846
  '<button type="button" class="type-item" data-back="1"><span class="ti-body"><span class="ti-name">‹ ' +
@@ -10486,6 +10545,14 @@ function apnSyncActive() {
10486
10545
  /* ---------------------------------------------------------- 智能体目录:模型选择 */
10487
10546
  const MAX_ORCH_MODELS = 3; // 与后端 modelhub.MAX_BIND_MODELS、runner 降级链保持一致
10488
10547
 
10548
+ /* 供应商有无可用密钥:keys 结构以「有启用且未冷却」为准;老数据单 KEY 看
10549
+ * api_key 镜像(与后端 _chain_keys / resolve_binding 的「无可用密钥」同口径) */
10550
+ function provKeyOk(p) {
10551
+ const ks = (p || {}).keys || [];
10552
+ if (ks.length) return ks.some((k) => k.enabled !== false && !k.cooling);
10553
+ return !!(p || {}).api_key;
10554
+ }
10555
+
10489
10556
  /* 供应商 → 已启用模型(按优先级),用于分组下拉与多选面板 */
10490
10557
  function modelGroups() {
10491
10558
  const out = [];
@@ -10496,7 +10563,7 @@ function modelGroups() {
10496
10563
  .sort((a, b) => (a.priority || 0) - (b.priority || 0))
10497
10564
  .map((m) => m.name).filter(Boolean);
10498
10565
  if (ms.length) out.push({ id: p.id, name: p.name, models: ms,
10499
- protocol: p.protocol || "", caps: p.wire_caps || {} });
10566
+ protocol: p.protocol || "", caps: p.wire_caps || {}, keyOk: provKeyOk(p) });
10500
10567
  }
10501
10568
  return out;
10502
10569
  }
@@ -10556,9 +10623,10 @@ function bindAllowedProtocols(kind) {
10556
10623
  function modelSelectHtml(id, current, kind) {
10557
10624
  const cur = fmtModel(current);
10558
10625
  const allow = bindAllowedProtocols(kind);
10626
+ // 无可用密钥的供应商不进下拉:默认模型配上去解析时也会被跳过(死配置)
10559
10627
  const groups = modelGroups().filter((g) => {
10560
10628
  const p = (S.providers || []).find((x) => x.id === g.id);
10561
- return provAdaptedProto(p, allow, kind);
10629
+ return g.keyOk !== false && provAdaptedProto(p, allow, kind);
10562
10630
  });
10563
10631
  let opts = '<option value="">' + t("(未设置)") + '</option>';
10564
10632
  if (cur && !groups.some((g) => g.models.includes(cur))) {
@@ -10685,7 +10753,7 @@ function chainProvUsable(p, allow, kind) {
10685
10753
  if (!p || p.enabled === false) return false;
10686
10754
  if (!provAdaptedProto(p, allow, kind)) return false;
10687
10755
  const ks = p.keys || [];
10688
- if (!ks.length) return true; // 老数据单 KEY(api_key 镜像):无 keys 结构视为可用
10756
+ if (!ks.length) return !!p.api_key; // 老数据单 KEY(api_key 镜像);两者皆无=没配密钥,不推荐
10689
10757
  return ks.some((k) => k.enabled !== false && !k.cooling);
10690
10758
  }
10691
10759
 
@@ -10847,16 +10915,19 @@ function bindPanel(c) {
10847
10915
  ? t("注入该厂商凭据") : t("已适配 · 注入该厂商凭据"))) + '</span></div>' +
10848
10916
  g.models.map((m) => {
10849
10917
  // 本供应商注入本 CLI 实际走的 wire(与后端 _entry_endpoint 同规则);
10850
- // 无命中 = 勾了也会被解析层整条跳过,标死防止白配
10918
+ // 无命中 = 勾了也会被解析层整条跳过,标死防止白配。
10919
+ // 无可用密钥同理:勾了也是死条目(解析层「无可用密钥」跳过)
10851
10920
  const wire = provAdaptedProto(g, allow, c.orch_kind);
10852
- const dead = !wire;
10921
+ const dead = !wire || g.keyOk === false;
10853
10922
  const has = st.chain.some((x) => x.p === g.id && x.m === m);
10854
10923
  return '<label class="oitem' + (dead ? " dim" : "") + '"><input type="checkbox" value="' + esc(m) + '" data-p="' + esc(g.id) + '"' +
10855
10924
  (has ? " checked" : "") + (dead ? " disabled" : "") +
10856
10925
  " onchange=\"bindPick('" + esc(c.id) + "', this, this.checked)\">" + esc(m) +
10857
10926
  (modelHasImage(g.id, m)
10858
10927
  ? ' <span class="tag ok" title="' + esc(t("支持图片输入")) + '">' + t("图") + "</span>" : "") +
10859
- (dead ? ' <span class="hint warn">' + esc(t("无可用 wire,解析时跳过")) + "</span>" : "") +
10928
+ (dead ? ' <span class="hint warn">' + esc(!wire
10929
+ ? t("无可用 wire,解析时跳过")
10930
+ : t("无可用密钥,解析时跳过")) + "</span>" : "") +
10860
10931
  "</label>";
10861
10932
  }).join("") +
10862
10933
  "</div>").join("") + "</div>";
@@ -11995,7 +12066,8 @@ function renderAuModelPicker(pid, model) {
11995
12066
  ps.value = provs.some((p) => p.id === keepP) ? keepP : "";
11996
12067
  const fillModels = (keepM) => {
11997
12068
  const p = provs.find((x) => x.id === ps.value);
11998
- const names = p ? (p.models || []).filter((m) => !m.hidden)
12069
+ // 只列启用模型:停用的不允许在定时任务里选(运行时同样会拒)
12070
+ const names = p ? (p.models || []).filter((m) => !m.hidden && m.enabled !== false)
11999
12071
  .map((m) => m.name).filter(Boolean) : [];
12000
12072
  ms.innerHTML = '<option value="">' + t("随厂商推荐") + "</option>" + names.map((name) =>
12001
12073
  '<option value="' + esc(name) + '">' + esc(name) + "</option>").join("");
package/app/ui/board.css CHANGED
@@ -207,7 +207,11 @@ button { font: inherit; cursor: pointer; }
207
207
  }
208
208
  .k-lab { font-size: 11px; color: var(--t3); letter-spacing: .8px; }
209
209
  .k-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
210
- .k-num { font: 700 27px/1 var(--mono); }
210
+ /* k-num 不许换行:卡高 78px 固定 + overflow hidden,换行必裁字(「2246.7万」案);
211
+ 放不下由 fitKpis() 降档,仍放不下才在末档省略号 */
212
+ .k-num { font: 700 27px/1 var(--mono); white-space: nowrap; }
213
+ .k-num.sm { font-size: 21px; }
214
+ .k-num.xs { font-size: 16px; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
211
215
  .k-sub { font: 11px var(--mono); color: var(--t2); white-space: nowrap; }
212
216
  .k-run .k-num { color: var(--live-hi); }
213
217
  .k-queue .k-num { color: var(--data); }
package/app/ui/board.js CHANGED
@@ -42,6 +42,20 @@ function parseTs(s) {
42
42
  const t = Date.parse(String(s || "").replace(" ", "T"));
43
43
  return isNaN(t) ? 0 : t;
44
44
  }
45
+ /* 指标条长数值自适应:行宽放不下就逐档降字号(27→21→16px)。
46
+ 判据用 k-row 的 scrollWidth:num/sub 都 nowrap 不收缩,超宽才会外溢。 */
47
+ function fitKpis() {
48
+ document.querySelectorAll("#kpis .k-row").forEach((row) => {
49
+ const num = row.querySelector(".k-num");
50
+ if (!num) return;
51
+ num.classList.remove("sm", "xs");
52
+ if (row.scrollWidth <= row.clientWidth + 1) return;
53
+ num.classList.add("sm");
54
+ if (row.scrollWidth <= row.clientWidth + 1) return;
55
+ num.classList.remove("sm");
56
+ num.classList.add("xs");
57
+ });
58
+ }
45
59
  function esc(s) {
46
60
  return String(s == null ? "" : s).replace(/[&<>"']/g,
47
61
  (c) => ({"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;"}[c]));
@@ -167,6 +181,7 @@ function render(d) {
167
181
 
168
182
  $("#k-tokens").textContent = fmtTokens(usage.tokens);
169
183
  $("#k-use-sub").textContent = fmtCost(usage.cost_usd) + " · " + (Number(usage.calls) || 0) + " " + t("次调用");
184
+ fitKpis();
170
185
 
171
186
  // 顶栏态势:整屏只在这里说一次「有没有活」
172
187
  const st = $("#hd-state");
@@ -493,6 +508,7 @@ document.addEventListener("keydown", (e) => {
493
508
  if (e.key === "T" || e.key === "t") $("#sw-mode").click();
494
509
  if (e.key === "S" || e.key === "s") $("#sw-skin").click();
495
510
  });
511
+ window.addEventListener("resize", fitKpis);
496
512
 
497
513
  /* ---------------------------------------------------------------- 时钟与耗时每秒跳 */
498
514
  setInterval(() => {
package/app/ui/i18n.js CHANGED
@@ -824,6 +824,7 @@
824
824
  "openai 面仅 chat 形态(codex 只讲 responses)": "openai face is chat-only (codex speaks responses only)",
825
825
  "openai 面仅 chat 形态(codex 只讲 responses),解析时跳过": "openai face is chat-only (codex speaks responses only) — skipped when resolving",
826
826
  "无可用 wire,解析时跳过": "no usable wire — skipped when resolving",
827
+ "无可用密钥,解析时跳过": "no usable key — skipped when resolving",
827
828
  "该 CLI 只接受特定 wire 协议的供应商,当前没有匹配项——先到「模型接入」页导入对应协议的网关。": "This CLI only accepts providers on a specific wire protocol and none match — import a compatible gateway on the Models page first.",
828
829
  "%1 个供应商 wire 协议不匹配已隐藏(降级只在同协议网关间进行)。": "%1 provider(s) hidden due to wire-protocol mismatch — fallback stays within the same protocol.",
829
830
  "已适配(%1 wire)": "adapted (%1 wire)",
@@ -1407,7 +1408,7 @@
1407
1408
  "每套皮肤都是一份完整配色,并且各带「日间 / 夜间」两版,明暗切换不会丢皮肤。选中的皮肤立即生效并记在这台设备上(手机与电脑各自独立)。": "Each skin is a complete palette, with day / night variants — switching brightness never loses your skin. The selected skin applies instantly and is remembered on this device (phone and computer are independent).",
1408
1409
  "系统自己的经验沉淀:": "The system's own distilled experience:",
1409
1410
  "这台设备第一次连接,请输入访问令牌": "First connection from this device — enter the access token",
1410
- "(如七猫签约标准,自动注入小说类任务的规划与评审提示词) 与": " (e.g. the Qimao signing standard, auto-injected into planning & review prompts for novel tasks)",
1411
+ "(如七猫签约标准,自动注入同类任务的规划与评审提示词) 与": " (e.g. the Qimao signing standard, auto-injected into planning & review prompts for matching tasks)",
1411
1412
  "(每次任务结束由编排者复盘本次评审暴露的问题,去重沉淀,下次同类任务自动带上)——越跑越好,无需人工维护。": " (after each task the orchestrator reviews the problems this review exposed, dedupes and distills them, and re-attaches them to the next similar task) — it gets better with every run, no manual upkeep.",
1412
1413
  "条": " entries",
1413
1414
  "(暂无,跑一次任务后自动生成)": " (none yet — generated after the first run)",
@@ -2600,6 +2601,30 @@
2600
2601
  "自动发布失败:": "Auto-publish failed: ",
2601
2602
  "已填好一章,请在浏览器确认提交": "One chapter is filled — please review and submit in the browser",
2602
2603
  "护栏拦截": "Blocked by guardrail",
2604
+ "全部存草稿": "Save all as drafts",
2605
+ "存草稿所选": "Draft selected",
2606
+ "逐章自动填稿并点「存草稿」:内容落平台草稿箱不上线,不受质量门禁拦截,无需逐章人工确认;完成后到平台检查并提交发布": "Fills each chapter and clicks \"Save draft\": content lands in the platform's draft box without going live, bypasses the quality gate, no per-chapter confirmation; review and submit on the platform when done",
2607
+ "所选章节逐章自动存草稿(不上线、不受质量门禁拦截),完成后到平台检查并提交发布": "Saves the selected chapters as drafts one by one (not live, quality gate not applied); review and submit on the platform when done",
2608
+ "存草稿中 ": "Saving drafts ",
2609
+ "已存草稿 {0} 章(未发布):到平台章节管理/草稿箱逐章检查后提交发布": "{0} chapters saved as drafts (not published): review each in the platform's chapter management/draft box, then submit",
2610
+ "质量闸未过:": "Quality gate not passed: ",
2611
+ "——可先「全部存草稿」落平台(不上线),正式发布前需复审达标或强制放行": " — you can \"Save all as drafts\" first (not live); publishing requires a passing re-review or a forced override",
2612
+ "将对全部 {0} 章待发稿逐章自动填稿并点「存草稿」:内容落平台草稿箱不上线,不受质量门禁拦截,无需逐章人工确认(章间隔约 1 分钟防风控)。跑完后请到平台章节管理/草稿箱逐章检查,再手工提交发布。继续?": "Will fill and save every pending chapter ({0} total) as a draft: content lands in the platform's draft box without going live, quality gate not applied, no per-chapter confirmation (~1 min pacing between chapters). Review each draft on the platform and submit manually when done. Continue?",
2613
+ "将对所选 {0} 章(第 {1} 章—第 {2} 章)逐章自动填稿并点「存草稿」(不上线、不受质量门禁拦截),完成后到平台章节管理/草稿箱检查并提交发布。继续?": "Will fill and save the selected {0} chapters (ch. {1}–ch. {2}) as drafts (not live, quality gate not applied); review and submit on the platform when done. Continue?",
2614
+ "开始存草稿": "Start saving drafts",
2615
+ "存草稿已启动:逐章自动填稿并保存草稿,进度见发布台": "Draft saving started: chapters are filled and saved one by one — watch progress in the publish panel",
2616
+ "存草稿启动失败:": "Failed to start draft saving: ",
2617
+ "发布所选": "Publish selected",
2618
+ "直发所选": "Auto-publish selected",
2619
+ "开始直发": "Start auto-publish",
2620
+ "清空": "Clear",
2621
+ "待发 ": "Pending ",
2622
+ " 章(按卷计划自动分卷)": " chapters (volumes auto-assigned per plan)",
2623
+ "直发已启动,进度见发布台": "Auto-publish started — track progress in the publish panel",
2624
+ "所选章节逐章自动提交(无人值守),护栏每日上限/连败退避照常生效": "Selected chapters submit automatically one by one (unattended); guards (daily cap / failure backoff) stay active",
2625
+ "将对所选 {0} 章(第 {1} 章—第 {2} 章)逐章自动填稿并提交发布,无需人工确认;护栏(每日上限/连续失败暂停)生效。确定直发?": "Will fill and submit the selected {0} chapters (ch. {1}–ch. {2}) one by one automatically, no manual confirmation; guards (daily cap / consecutive-failure pause) stay active. Start auto-publish?",
2626
+ "将按章号顺序逐章填所选 {0} 章(第 {1} 章—第 {2} 章)。人工模式每轮填一章并停在表单页,由你在浏览器里确认提交。分卷会按卷计划自动选/建。继续?": "Will fill the selected {0} chapters (ch. {1}–ch. {2}) in order. Manual mode fills one chapter per round and stops at the form page — you confirm each submission in the browser. Volumes are auto-picked/created per plan. Continue?",
2627
+ "护栏提示:": "Guardrail notice: ",
2603
2628
 
2604
2629
  // —— 报告区 / 状态 / 杂项 ——
2605
2630
  "将于 {0} 自动续跑": "Auto-resumes at {0}",
package/app/ui/index.html CHANGED
@@ -957,7 +957,7 @@
957
957
  <div class="panel wide">
958
958
  <div class="panel-head"><h2 data-i18n="经验库">经验库</h2>
959
959
  <button class="hhelp" data-help-topic="features" title="这是什么?点开看帮助" data-i18n-title="这是什么?点开看帮助" aria-label="帮助" data-i18n-aria="帮助"><svg class="ico" aria-hidden="true"><use href="#i-help"></use></svg></button></div>
960
- <p class="hint"><span data-i18n="系统自己的经验沉淀:">系统自己的经验沉淀:</span><b data-i18n="内置规范包">内置规范包</b><span data-i18n="(如七猫签约标准,自动注入小说类任务的规划与评审提示词) 与">(如七猫签约标准,自动注入小说类任务的规划与评审提示词)
960
+ <p class="hint"><span data-i18n="系统自己的经验沉淀:">系统自己的经验沉淀:</span><b data-i18n="内置规范包">内置规范包</b><span data-i18n="(如七猫签约标准,自动注入同类任务的规划与评审提示词) 与">(如七猫签约标准,自动注入同类任务的规划与评审提示词)
961
961
  与</span><b data-i18n="运行中自动学到的教训">运行中自动学到的教训</b><span data-i18n="(每次任务结束由编排者复盘本次评审暴露的问题,去重沉淀,下次同类任务自动带上)——越跑越好,无需人工维护。">(每次任务结束由编排者复盘本次评审暴露的问题,去重沉淀,下次同类任务自动带上)——越跑越好,无需人工维护。</span></p>
962
962
  <h3 data-i18n="内置规范包" class="sec-title">内置规范包</h3>
963
963
  <div id="skill-packs" class="cards"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebee",
3
- "version": "0.1.97",
3
+ "version": "0.1.99",
4
4
  "description": "CodeBee · 多智能体编排台 — 统一编排本机 AI 编码 CLI(Codex / Claude Code / QwenCode / OpenCode / Aider…):目标→拆解→路由→执行→验证→跨厂商评审→修复→报告。纯 Python 标准库实现,本地 Web UI。",
5
5
  "license": "MIT",
6
6
  "bin": {