codebee 0.1.33 → 0.1.34
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/CHANGELOG.md +29 -19
- package/README.md +14 -0
- package/app/core/prefs.py +20 -11
- package/app/core/wxdigest.py +12 -4
- package/app/ui/app.js +38 -2
- package/app/ui/i18n.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,28 +4,38 @@ CodeBee 的用户可感知变更记录。发布新版时:最上面加一节,
|
|
|
4
4
|
`<!-- relnotes:start -->…<!-- relnotes:end -->` 段(那段会被 `npm view` 的
|
|
5
5
|
README 元数据带回,供老版本在「发现新版本」时展示新版更新内容)。
|
|
6
6
|
|
|
7
|
-
## 未发布
|
|
8
|
-
|
|
9
|
-
## v0.1.
|
|
10
|
-
|
|
11
|
-
###
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
- 修复 Anthropic 兼容接口误收到 OpenAI `reasoning_effort` 字段;修复语言切换覆盖端口标题导致桌宠重复开窗;过滤 CLI 历史和评审中间文件,成果页只展示可交付产物。
|
|
21
|
-
|
|
22
|
-
## v0.1.32(2026-09-21)
|
|
7
|
+
## 未发布
|
|
8
|
+
|
|
9
|
+
## v0.1.34(2026-09-21)
|
|
10
|
+
|
|
11
|
+
### 🛠 问题修复
|
|
12
|
+
|
|
13
|
+
- 手机端操作被「其他设备正在控制」挡住时,现在会弹出「接管并重试」确认框——点一下就接管控制权并自动重做刚才的操作,不再连点全是失败。
|
|
14
|
+
- 微信群列表拉取失败时把读取器的真实报错带出来(依赖缺失/解释器位数不对/微信未登录一眼可辨),不再只说「无有效输出」。
|
|
15
|
+
- 连载「章节评审」现在在侧栏进入的任务级详情也会显示,不再只有打开某一次运行时才能看到。
|
|
16
|
+
- 新建任务会一并记住上次的编排模式与思考程度;非法历史值会丢弃,不会污染表单。
|
|
17
|
+
- 修复扫描 Python 后尚未保存时被后台轮询冲回旧路径;现在点选或自动选中的候选都会保持到用户保存。
|
|
18
|
+
|
|
19
|
+
## v0.1.33(2026-09-21)
|
|
23
20
|
|
|
24
21
|
### ✨ 新功能
|
|
25
22
|
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
- 创建任务支持快速/自动/专家/手动四档编排,以及独立的自动/快速/标准/深度思考程度;所有预置类型按风险动态决定规划、评审、修复和换将步骤。
|
|
24
|
+
- 对话默认使用系统推荐,也可仅为本次对话指定厂商或模型;指定渠道不可用时明确失败,不修改全局 CLI 绑定。
|
|
25
|
+
- 创建前显示同类历史或流程基线 ETA/P90,运行中显示剩余时间与超出预估状态;满载时不排队,异常退出的运行自动收口为失败。
|
|
26
|
+
|
|
27
|
+
### 🛠 问题修复
|
|
28
|
+
|
|
29
|
+
- 修复 Windows GBK 控制台因特殊符号导致服务启动崩溃;Aider 启动前增加 Git 仓库可读性诊断,避免损坏仓库长时间卡住。
|
|
30
|
+
- 修复 Anthropic 兼容接口误收到 OpenAI `reasoning_effort` 字段;修复语言切换覆盖端口标题导致桌宠重复开窗;过滤 CLI 历史和评审中间文件,成果页只展示可交付产物。
|
|
31
|
+
|
|
32
|
+
## v0.1.32(2026-09-21)
|
|
33
|
+
|
|
34
|
+
### ✨ 新功能
|
|
35
|
+
|
|
36
|
+
- 连载任务详情新增「章节评审」卡片:每章的维度评分(低分标红)、达标状态、字数与修订轮数一目了然;每章按大纲逐项核对的判定结果(已完成/未完成/待核实)以红绿灰行直接展示——哪章哪个要点没写到一眼可查。
|
|
37
|
+
|
|
38
|
+
## v0.1.31(2026-09-21)
|
|
29
39
|
|
|
30
40
|
### ✨ 新功能
|
|
31
41
|
|
package/README.md
CHANGED
|
@@ -21,6 +21,20 @@ Kimi Code、MiMo Code、Grok Build、Pi、DeepSeek Harness……),提供
|
|
|
21
21
|
不会把你的任务内容交给任何第三方。
|
|
22
22
|
|
|
23
23
|
<!-- relnotes:start -->
|
|
24
|
+
### 🛠 问题修复 · Fixes
|
|
25
|
+
|
|
26
|
+
- 手机端操作被「其他设备正在控制」挡住时,弹出「接管并重试」——一键接管并自动重做刚才的操作。
|
|
27
|
+
- When a mobile action is blocked by another device in control, a "Take over & retry" prompt appears — one tap retakes control and redoes the action.
|
|
28
|
+
|
|
29
|
+
- 微信群列表拉取失败时会显示读取器的真实报错(依赖缺失/位数不对/微信未登录一眼可辨)。
|
|
30
|
+
- WeChat group-list failures now surface the reader's real error (missing dependency, wrong bitness, or WeChat not logged in).
|
|
31
|
+
|
|
32
|
+
- 连载章节评审在任务级详情与单次运行详情都可见;新建任务会记住上次的编排模式和思考程度。
|
|
33
|
+
- Chapter reviews now appear in both task and run details; new tasks also remember the last orchestration mode and reasoning level.
|
|
34
|
+
|
|
35
|
+
- 扫描 Python 后即使保存前遇到后台刷新,刚选中的路径也不会再被冲掉。
|
|
36
|
+
- A Python selected by Scan now survives background refreshes even before settings are saved.
|
|
37
|
+
|
|
24
38
|
### ✨ 新功能 · New
|
|
25
39
|
|
|
26
40
|
- 创建任务支持快速/自动/专家/手动编排与独立思考程度;代码、写作、翻译、邮件、汇报、调研等全类型按风险动态选择步骤。
|
package/app/core/prefs.py
CHANGED
|
@@ -14,11 +14,15 @@ from . import paths
|
|
|
14
14
|
|
|
15
15
|
_LOCK = threading.Lock()
|
|
16
16
|
|
|
17
|
-
SAVE_KEYS = ("type", "rounds", "threshold", "best_of", "chapters",
|
|
18
|
-
"words_per_chapter", "variants")
|
|
19
|
-
_LIMITS = {"rounds": (1, 5), "threshold": (1.0, 10.0), "best_of": (1, 3),
|
|
20
|
-
"chapters": (1, 2000), "words_per_chapter": (200, 20000),
|
|
21
|
-
"variants": (1, 3)}
|
|
17
|
+
SAVE_KEYS = ("type", "rounds", "threshold", "best_of", "chapters",
|
|
18
|
+
"words_per_chapter", "variants", "mode", "thinking")
|
|
19
|
+
_LIMITS = {"rounds": (1, 5), "threshold": (1.0, 10.0), "best_of": (1, 3),
|
|
20
|
+
"chapters": (1, 2000), "words_per_chapter": (200, 20000),
|
|
21
|
+
"variants": (1, 3)}
|
|
22
|
+
_CHOICES = {
|
|
23
|
+
"mode": ("auto", "fast", "expert", "manual"),
|
|
24
|
+
"thinking": ("auto", "low", "standard", "high"),
|
|
25
|
+
}
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
def _file():
|
|
@@ -47,12 +51,17 @@ def record(payload):
|
|
|
47
51
|
v = payload.get(key)
|
|
48
52
|
if v is None:
|
|
49
53
|
continue
|
|
50
|
-
if key == "type":
|
|
51
|
-
v = str(v)[:32]
|
|
52
|
-
if v:
|
|
53
|
-
cur["type"] = v
|
|
54
|
-
continue
|
|
55
|
-
|
|
54
|
+
if key == "type":
|
|
55
|
+
v = str(v)[:32]
|
|
56
|
+
if v:
|
|
57
|
+
cur["type"] = v
|
|
58
|
+
continue
|
|
59
|
+
if key in _CHOICES:
|
|
60
|
+
v = str(v).strip().lower()
|
|
61
|
+
if v in _CHOICES[key]:
|
|
62
|
+
cur[key] = v
|
|
63
|
+
continue
|
|
64
|
+
c = _clamp(key, v)
|
|
56
65
|
if c is not None:
|
|
57
66
|
cur[key] = c
|
|
58
67
|
tmp = _file().with_suffix(".tmp")
|
package/app/core/wxdigest.py
CHANGED
|
@@ -131,17 +131,25 @@ def refresh_groups():
|
|
|
131
131
|
_DIR.mkdir(parents=True, exist_ok=True)
|
|
132
132
|
rf = _DIR / "groups_cache.json"
|
|
133
133
|
cmd = [rp, str(READER_SCRIPT), "--list-groups", "--result", str(rf)]
|
|
134
|
+
stderr = b""
|
|
134
135
|
try:
|
|
135
|
-
subprocess.run(cmd, timeout=READER_TIMEOUT, capture_output=True,
|
|
136
|
-
|
|
136
|
+
r = subprocess.run(cmd, timeout=READER_TIMEOUT, capture_output=True,
|
|
137
|
+
creationflags=CREATE_NO_WINDOW if os.name == "nt" else 0)
|
|
138
|
+
stderr = r.stderr or b""
|
|
137
139
|
except subprocess.TimeoutExpired:
|
|
138
140
|
raise RuntimeError("读取群列表超时(%ds)" % READER_TIMEOUT)
|
|
139
141
|
try:
|
|
140
142
|
res = json.loads(rf.read_text(encoding="utf-8"))
|
|
141
143
|
except Exception:
|
|
142
|
-
|
|
144
|
+
# 带上 sidecar stderr 尾部:依赖缺失/解释器位数不对/脚本异常的真因
|
|
145
|
+
# 不再被「无有效输出」一句吞掉(用户实测排障反馈)
|
|
146
|
+
detail = (stderr or b"").decode("utf-8", "replace").strip()[-300:]
|
|
147
|
+
raise RuntimeError("群列表无有效输出(检查 reader_python 环境)%s"
|
|
148
|
+
% (":" + detail if detail else ""))
|
|
143
149
|
if not res.get("ok"):
|
|
144
|
-
|
|
150
|
+
errs = ";".join(str(x) for x in (res.get("errors") or [])) or \
|
|
151
|
+
str(res.get("error") or "未知错误")
|
|
152
|
+
raise RuntimeError("读取群列表失败:%s" % errs[:300])
|
|
145
153
|
groups = res.get("groups") or []
|
|
146
154
|
with _LOCK:
|
|
147
155
|
_STATE["groups_cache"] = groups
|
package/app/ui/app.js
CHANGED
|
@@ -10,7 +10,7 @@ function codebeeDocumentTitle(lang) {
|
|
|
10
10
|
document.title = codebeeDocumentTitle((localStorage.getItem("orch.lang") || "zh").toLowerCase());
|
|
11
11
|
|
|
12
12
|
const $ = (id) => document.getElementById(id);
|
|
13
|
-
const S = { state: null, catalog: null, catSig: "", providers: null, bindings: null, modelsSig: "", bindSig: "", tab: "tasks", detailRunId: null, pollTimer: null, showArchived: false, /* 会话内开关:每次加载默认隐藏已归档、图标不选中(不持久化,见 btn-side-arch) */ selProvs: {}, selModels: {}, selRuns: {}, bindSel: {}, catalogChecking: false, updateCheckAt: 0, control: null, sseLive: false, es: null, flows: null, orch: null, skills: null, orchSig: "", settings: null, sessionAgents: new Set(), atts: [], gitInfo: null, gitWb: null, gitWbKey: "", gitWbAt: 0, gitWbBusy: false, creatingTask: false, inspKey: null, inspData: null, inspSig: "", inspAt: 0, inspTab: "git", inspAutoSig: "", rdTab: null, rdTabSig: "", rdTabPin: false, _rdCtx: {}, lastPrefs: null };
|
|
13
|
+
const S = { state: null, catalog: null, catSig: "", providers: null, bindings: null, modelsSig: "", bindSig: "", tab: "tasks", detailRunId: null, pollTimer: null, showArchived: false, /* 会话内开关:每次加载默认隐藏已归档、图标不选中(不持久化,见 btn-side-arch) */ selProvs: {}, selModels: {}, selRuns: {}, bindSel: {}, catalogChecking: false, updateCheckAt: 0, control: null, sseLive: false, es: null, flows: null, orch: null, skills: null, orchSig: "", settings: null, sessionAgents: new Set(), atts: [], gitInfo: null, gitWb: null, gitWbKey: "", gitWbAt: 0, gitWbBusy: false, creatingTask: false, inspKey: null, inspData: null, inspSig: "", inspAt: 0, inspTab: "git", inspAutoSig: "", rdTab: null, rdTabSig: "", rdTabPin: false, _rdCtx: {}, lastPrefs: null, prefsApplied: false };
|
|
14
14
|
|
|
15
15
|
/* ---------------------------------------------------------- 任务类型(流程) */
|
|
16
16
|
async function loadFlows() {
|
|
@@ -65,10 +65,22 @@ function renderTypeOptions() {
|
|
|
65
65
|
// 首屏默认落在「直接执行」(快档位:单 CLI 直达,无拆解/评审);
|
|
66
66
|
// 老数据或该流程被删时保持首项,不硬造一个不存在的值
|
|
67
67
|
else if (flowById("direct")) sel.value = "direct";
|
|
68
|
+
applyTaskPreferenceDefaults();
|
|
68
69
|
renderTypeMenu();
|
|
69
70
|
onTypeChange();
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
function applyTaskPreferenceDefaults() {
|
|
74
|
+
if (S.prefsApplied || !S.lastPrefs) return;
|
|
75
|
+
const lp = S.lastPrefs;
|
|
76
|
+
const mode = $("f-mode"), thinking = $("f-thinking");
|
|
77
|
+
if (mode && ["auto", "fast", "expert", "manual"].includes(lp.mode)) mode.value = lp.mode;
|
|
78
|
+
if (thinking && ["auto", "low", "standard", "high"].includes(lp.thinking)) thinking.value = lp.thinking;
|
|
79
|
+
const manual = $("f-manual-only");
|
|
80
|
+
if (manual && mode) manual.classList.toggle("hidden", mode.value !== "manual");
|
|
81
|
+
S.prefsApplied = true;
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
/* 类型选择的可见层:带黑白图标的按钮 + 弹出菜单。原生 <option> 渲染不了 SVG,
|
|
73
85
|
* 所以隐藏 select 只当值真源(既有联动/测试全部照旧),外观全靠这层。 */
|
|
74
86
|
function renderTypeMenu() {
|
|
@@ -590,6 +602,7 @@ async function api(path, opts) {
|
|
|
590
602
|
delete fetchOpts.busy;
|
|
591
603
|
delete fetchOpts.busyElement;
|
|
592
604
|
delete fetchOpts.operation;
|
|
605
|
+
delete fetchOpts._ctrlRetry;
|
|
593
606
|
try {
|
|
594
607
|
let res;
|
|
595
608
|
try {
|
|
@@ -602,7 +615,27 @@ async function api(path, opts) {
|
|
|
602
615
|
if (res.status === 401) { showTokenGate(t("令牌不正确或已更换,请重新输入")); throw new Error(t("需要访问令牌")); }
|
|
603
616
|
let data = null;
|
|
604
617
|
try { data = await res.json(); } catch (e) { /* ignore */ }
|
|
605
|
-
if (res.status === 423 && data && data.control)
|
|
618
|
+
if (res.status === 423 && data && data.control) {
|
|
619
|
+
setControl(data.control);
|
|
620
|
+
// 手机端接管闭环(用户实测反馈:423 连点全失败不知道怎么办):
|
|
621
|
+
// 他人持有时弹「接管并重试」确认,确认后抢夺控制权并自动重试一次。
|
|
622
|
+
// uiConfirm 弹窗互斥防堆叠;重试标记防递归;GET 轮询不会走到这里。
|
|
623
|
+
const holder = data.control.holder || "";
|
|
624
|
+
if (!opts._ctrlRetry && !opts.silent && !api._ctrlPrompting && holder) {
|
|
625
|
+
api._ctrlPrompting = true;
|
|
626
|
+
try {
|
|
627
|
+
if (await uiConfirm(t("「{0}」正在控制,接管后自动重试「{1}」?",
|
|
628
|
+
holder, operationLabel(path, { ...opts, operation: true }) || t("提交操作")),
|
|
629
|
+
{ ok: t("接管并重试") })) {
|
|
630
|
+
await ctrlAction("acquire", true);
|
|
631
|
+
api._ctrlPrompting = false;
|
|
632
|
+
return api(path, Object.assign({}, opts, { _ctrlRetry: true }));
|
|
633
|
+
}
|
|
634
|
+
} finally {
|
|
635
|
+
api._ctrlPrompting = false;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
606
639
|
if (!res.ok) throw new Error((data && data.error) || ("HTTP " + res.status));
|
|
607
640
|
operationFinish(operationToken, "done");
|
|
608
641
|
return data;
|
|
@@ -3672,6 +3705,7 @@ function drawTaskDetail(key, runs) {
|
|
|
3672
3705
|
(latest.error ? '<span class="stat err">' + errTag(latest.error) + esc(latest.error.slice(0, 200)) + "</span>" : "");
|
|
3673
3706
|
$("rd-plan").classList.add("hidden");
|
|
3674
3707
|
renderRouting(latest);
|
|
3708
|
+
renderChapterScores(latest);
|
|
3675
3709
|
let html = "";
|
|
3676
3710
|
ordered.forEach((r, i) => {
|
|
3677
3711
|
const runNo = runs.length - i;
|
|
@@ -11733,6 +11767,7 @@ function beePyRender(list) {
|
|
|
11733
11767
|
b.addEventListener("click", () => {
|
|
11734
11768
|
const p = list[Number(b.dataset.i)];
|
|
11735
11769
|
$("bee-reader-py").value = p.path;
|
|
11770
|
+
Bee.pyDirty = true;
|
|
11736
11771
|
box.querySelectorAll(".bee-py-item").forEach((x) => x.classList.remove("on"));
|
|
11737
11772
|
b.classList.add("on");
|
|
11738
11773
|
beePyInstallVisibility(p);
|
|
@@ -11744,6 +11779,7 @@ function beePyRender(list) {
|
|
|
11744
11779
|
list.find((p) => p.bits >= 64);
|
|
11745
11780
|
if (hit) {
|
|
11746
11781
|
$("bee-reader-py").value = hit.path;
|
|
11782
|
+
if (hit.path !== cur) Bee.pyDirty = true;
|
|
11747
11783
|
beePyInstallVisibility(hit);
|
|
11748
11784
|
const btn = box.querySelector('.bee-py-item[data-i="' + list.indexOf(hit) + '"]');
|
|
11749
11785
|
if (btn) btn.classList.add("on");
|
package/app/ui/i18n.js
CHANGED
|
@@ -409,6 +409,8 @@
|
|
|
409
409
|
"%(失败 ": "% (failed ",
|
|
410
410
|
"「": "\"",
|
|
411
411
|
"」将变为只读。": "\" will become read-only.",
|
|
412
|
+
"「{0}」正在控制,接管后自动重试「{1}」?": "\"{0}\" is in control. Take over and retry \"{1}\"?",
|
|
413
|
+
"接管并重试": "Take over & retry",
|
|
412
414
|
"」恢复为内置默认配置?": "\" restored to built-in defaults?",
|
|
413
415
|
"{0} 正在控制(点击接管)": "{0} is in control (click to take over)",
|
|
414
416
|
"」?已有任务不受影响。": "\"? Existing tasks are not affected.",
|
package/package.json
CHANGED