codebee 0.1.66 → 0.1.68
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 +543 -520
- package/LICENSE +21 -21
- package/README.md +11 -7
- package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +88 -0
- package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +126 -0
- package/app/.playwright-cli/page-2026-09-23T08-29-44-203Z.png +0 -0
- package/app/.playwright-cli/page-2026-09-23T08-35-42-046Z.yml +210 -0
- package/app/.playwright-cli/page-2026-09-23T08-36-52-858Z.png +0 -0
- package/app/.playwright-cli/page-2026-09-23T08-37-33-938Z.png +0 -0
- package/app/.playwright-cli/run-detail-after.png +0 -0
- package/app/core/acceptance.py +100 -0
- package/app/core/aiflavor.py +197 -197
- package/app/core/artifacts.py +148 -148
- package/app/core/assets.py +110 -0
- package/app/core/attachments.py +623 -532
- package/app/core/automation.py +758 -758
- package/app/core/backup.py +470 -470
- package/app/core/beekeeper.py +183 -183
- package/app/core/board.py +209 -209
- package/app/core/bookmeta.py +930 -796
- package/app/core/bookmeta_catalog.py +180 -180
- package/app/core/branching.py +137 -137
- package/app/core/builtin_agent.py +1234 -1234
- package/app/core/capability.py +144 -144
- package/app/core/catalog.py +42 -0
- package/app/core/chaptersafe.py +32 -32
- package/app/core/cleanup.py +303 -303
- package/app/core/compaction.py +201 -201
- package/app/core/covergen.py +356 -356
- package/app/core/defectretro.py +43 -0
- package/app/core/diagnostics.py +114 -114
- package/app/core/dispatch.py +173 -173
- package/app/core/dispatch_log.py +128 -113
- package/app/core/env_scrub.py +85 -85
- package/app/core/error_codes.py +23 -23
- package/app/core/errorlog.py +137 -137
- package/app/core/evaluation.py +174 -174
- package/app/core/failure_notify.py +290 -290
- package/app/core/flows.py +48 -2
- package/app/core/goal_service.py +167 -167
- package/app/core/health.py +374 -374
- package/app/core/history.py +38 -38
- package/app/core/hooks.py +162 -162
- package/app/core/jobs.py +46 -26
- package/app/core/knowledge.py +57 -3
- package/app/core/manager.py +2122 -2115
- package/app/core/market.py +362 -362
- package/app/core/market_remote.py +124 -26
- package/app/core/mocks.py +64 -64
- package/app/core/modelhub.py +51 -3
- package/app/core/notify.py +106 -106
- package/app/core/operations.py +228 -0
- package/app/core/paihang.py +138 -109
- package/app/core/paths.py +2 -2
- package/app/core/pipeline.py +221 -12
- package/app/core/planner.py +6 -1
- package/app/core/portguard.py +111 -111
- package/app/core/preview.py +293 -293
- package/app/core/publish/__init__.py +7 -7
- package/app/core/publish/auto.py +367 -367
- package/app/core/publish/browser.py +539 -533
- package/app/core/publish/fanqie.py +159 -128
- package/app/core/publish/flow.py +440 -432
- package/app/core/publish/flows-fanqie-calibrated.json +332 -332
- package/app/core/publish/flows-qimao-calibrated.json +7 -3
- package/app/core/publish/ledger.py +203 -198
- package/app/core/publish/manager.py +692 -623
- package/app/core/publish/qimao.py +141 -141
- package/app/core/publish/ws.py +139 -139
- package/app/core/redact.py +38 -38
- package/app/core/repeat_guard.py +129 -129
- package/app/core/revisions.py +51 -0
- package/app/core/router.py +398 -398
- package/app/core/runner.py +2067 -2050
- package/app/core/selfupdate.py +384 -384
- package/app/core/session_log.py +161 -161
- package/app/core/sessions.py +312 -312
- package/app/core/settings.py +149 -149
- package/app/core/settings_schema.py +262 -262
- package/app/core/share_page.py +114 -114
- package/app/core/skill_scan.py +122 -114
- package/app/core/skillpacks/fanqie-novel.md +80 -80
- package/app/core/skillpacks/market/character-bible.md +66 -66
- package/app/core/skillpacks/market/code-risk-checklist.md +58 -58
- package/app/core/skillpacks/market/git-workflow.md +57 -57
- package/app/core/skillpacks/market/release-notes.md +72 -72
- package/app/core/skillpacks/market/weekly-report.md +71 -71
- package/app/core/skillpacks/market/worldview-consistency.md +70 -70
- package/app/core/skills.py +109 -2
- package/app/core/step_runner.py +132 -132
- package/app/core/store.py +278 -41
- package/app/core/task_compile.py +199 -199
- package/app/core/telemetry.py +292 -292
- package/app/core/tlsctx.py +64 -64
- package/app/core/token_meter.py +147 -147
- package/app/core/tracing.py +144 -0
- package/app/core/upstream.py +41 -41
- package/app/core/usage.py +862 -858
- package/app/core/volumes.py +389 -355
- package/app/core/wxdigest.py +858 -858
- package/app/core/zentao.py +2465 -2447
- package/app/main.py +2981 -2838
- package/app/pick_dialog.py +110 -110
- package/app/ui/app.js +14399 -13994
- package/app/ui/artdiff.js +74 -0
- package/app/ui/board.html +162 -162
- package/app/ui/board.js +305 -305
- package/app/ui/i18n.js +2770 -2716
- package/app/ui/icons/bee.svg +79 -79
- package/app/ui/index.html +6 -0
- package/app/ui/manifest.json +16 -16
- package/app/ui/qrcode.js +2297 -2297
- package/app/ui/style.css +96 -24
- package/bin/tutti.js +147 -147
- package/package.json +46 -46
- package/tools/wx_reader.py +261 -261
package/app/core/planner.py
CHANGED
|
@@ -133,11 +133,15 @@ CODE_PLAN_PROMPT = """你是技术负责人。请先判定任务难度,再按
|
|
|
133
133
|
子任务拆分:easy 一律只拆 1 个子任务——一个子任务把目标完整做完并自检通过即收尾,
|
|
134
134
|
禁止拆出独立的核查/联调/收尾子任务;只有 hard 才允许多个子任务(至多 __N__ 个),
|
|
135
135
|
粒度要可独立验证,且最后一个子任务必须包含整体联调/收尾。
|
|
136
|
+
hard 也不做任务没要的防御性扩展子任务(缓存/重试/监控/通用抽象层)——
|
|
137
|
+
发现真实风险写进对应子任务的 detail 里备注,由人决定要不要做。
|
|
136
138
|
|
|
137
139
|
## 先探索再计划(重要,借鉴 OpenSpec explore)
|
|
138
140
|
拆解之前先用你的读文件/搜索工具**实际查看工作目录**,找到目标相关的真实文件与函数,再据此拆解。
|
|
139
141
|
detail 必须点名真实存在的文件路径;files 列出该子任务会改动的文件(新建的写目标路径)。
|
|
140
142
|
**没探索过就不要凭想象编路径**——计划的可执行性取决于它对真实代码库的贴合度。
|
|
143
|
+
detail 的清晰度按「初级工程师测试」校验:假设执行者没有项目上下文、不做隐含判断,
|
|
144
|
+
只读 detail 就能照做不跑偏(借鉴 superpowers)。
|
|
141
145
|
若工作目录为空(全新项目),files 写计划新建的文件路径。
|
|
142
146
|
|
|
143
147
|
## 开发目标
|
|
@@ -248,7 +252,8 @@ def _norm_chapters(data, n, vol_per=0, vol_start=1, vol_plan=None):
|
|
|
248
252
|
# 分卷:只收本批真正用到的卷的卷名/卷弧光(模型可能多写或乱写卷号,一律按
|
|
249
253
|
# 本批章号范围过滤)。卷边界不在这里——它由 volumes.build_plan 从章号推导。
|
|
250
254
|
if vol_plan:
|
|
251
|
-
named = volumes.norm_volumes(data.get("volumes"), vol_per, vol_start, n
|
|
255
|
+
named = volumes.norm_volumes(data.get("volumes"), vol_per, vol_start, n,
|
|
256
|
+
plan=vol_plan)
|
|
252
257
|
if named:
|
|
253
258
|
res["volumes"] = named
|
|
254
259
|
return res
|
package/app/core/portguard.py
CHANGED
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""启动端口清场(用户拍板 2026-09-21):端口被占时先清场再启动。
|
|
3
|
-
|
|
4
|
-
- **自家旧实例**(命令行含本包 main.py 完整路径,或 npm 包内 app/main.py)
|
|
5
|
-
→ 杀树(TerminateProcess 直杀)——升级/重启最常见的占用者就是没退干净
|
|
6
|
-
的 CodeBee 自己;控制台进程对温和信号(WM_CLOSE)无反应,温和关不掉
|
|
7
|
-
正是用户「旧进程太难杀」的痛点,所以自家实例直接强杀。
|
|
8
|
-
- **别人的进程** → 只报告占用者,不发送任何信号;启动流程不得替用户
|
|
9
|
-
关闭可能正在开发中的服务。
|
|
10
|
-
- 系统/自身进程拒关(portscan 内置护栏)。
|
|
11
|
-
"""
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
import logging
|
|
15
|
-
import os
|
|
16
|
-
import shlex
|
|
17
|
-
import subprocess
|
|
18
|
-
|
|
19
|
-
from . import portscan, runner
|
|
20
|
-
|
|
21
|
-
log = logging.getLogger(__name__)
|
|
22
|
-
|
|
23
|
-
PS_EXE = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def proc_cmdline(pid):
|
|
27
|
-
"""读进程命令行;失败返回空串。仅用于确认旧 CodeBee 实例身份。"""
|
|
28
|
-
try:
|
|
29
|
-
pid = int(pid)
|
|
30
|
-
except (TypeError, ValueError):
|
|
31
|
-
return ""
|
|
32
|
-
if pid <= 0:
|
|
33
|
-
return ""
|
|
34
|
-
if os.name != "nt":
|
|
35
|
-
try:
|
|
36
|
-
with open("/proc/%d/cmdline" % pid, "rb") as fh:
|
|
37
|
-
return fh.read().decode("utf-8", "replace").replace("\x00", " ").strip()
|
|
38
|
-
except Exception:
|
|
39
|
-
return ""
|
|
40
|
-
try:
|
|
41
|
-
r = subprocess.run(
|
|
42
|
-
[PS_EXE, "-NoProfile", "-Command",
|
|
43
|
-
"(Get-CimInstance Win32_Process -Filter 'ProcessId = %d').CommandLine" % pid],
|
|
44
|
-
capture_output=True, timeout=10)
|
|
45
|
-
return r.stdout.decode("utf-8", "replace").strip() if r.returncode == 0 else ""
|
|
46
|
-
except Exception:
|
|
47
|
-
return ""
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def _command_tokens(cmdline):
|
|
51
|
-
try:
|
|
52
|
-
return [token.strip().strip('"').strip("'")
|
|
53
|
-
for token in shlex.split(str(cmdline or ""), posix=False)]
|
|
54
|
-
except (TypeError, ValueError):
|
|
55
|
-
return []
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def is_own_instance(cmdline, main_script, port=None):
|
|
59
|
-
"""按启动器、脚本独立参数和端口精确确认 CodeBee 实例。"""
|
|
60
|
-
tokens = _command_tokens(cmdline)
|
|
61
|
-
if len(tokens) < 2:
|
|
62
|
-
return False
|
|
63
|
-
launcher = os.path.splitext(os.path.basename(tokens[0]))[0].lower()
|
|
64
|
-
if launcher not in ("python", "python3", "py"):
|
|
65
|
-
return False
|
|
66
|
-
script = ""
|
|
67
|
-
for token in tokens[1:]:
|
|
68
|
-
if token.startswith("-"):
|
|
69
|
-
continue
|
|
70
|
-
script = token
|
|
71
|
-
break
|
|
72
|
-
if not script or not os.path.isabs(script):
|
|
73
|
-
return False
|
|
74
|
-
actual = os.path.normcase(os.path.normpath(os.path.abspath(script)))
|
|
75
|
-
expected = os.path.normcase(os.path.normpath(os.path.abspath(str(main_script))))
|
|
76
|
-
packaged = actual.replace("\\", "/").lower().endswith(
|
|
77
|
-
"/node_modules/codebee/app/main.py")
|
|
78
|
-
if actual != expected and not packaged:
|
|
79
|
-
return False
|
|
80
|
-
if port is None:
|
|
81
|
-
return True
|
|
82
|
-
try:
|
|
83
|
-
wanted = str(int(port))
|
|
84
|
-
except (TypeError, ValueError):
|
|
85
|
-
return False
|
|
86
|
-
explicit_port = None
|
|
87
|
-
for i, token in enumerate(tokens):
|
|
88
|
-
if token == "--port" and i + 1 < len(tokens):
|
|
89
|
-
explicit_port = tokens[i + 1]
|
|
90
|
-
elif token.startswith("--port="):
|
|
91
|
-
explicit_port = token.split("=", 1)[1]
|
|
92
|
-
# argparse 默认端口可不出现在旧实例参数中;非默认端口必须显式相符。
|
|
93
|
-
return ((explicit_port == wanted) if explicit_port is not None
|
|
94
|
-
else wanted == "8765")
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def clear_stale_port(port, main_script):
|
|
98
|
-
"""清掉占用端口的进程。返回 (是否清掉, 人话说明)。"""
|
|
99
|
-
holders = [p for p in portscan.listening_ports() if p.get("port") == int(port)]
|
|
100
|
-
if not holders:
|
|
101
|
-
return True, ""
|
|
102
|
-
pid = int(holders[0].get("pid") or 0)
|
|
103
|
-
if pid <= 4 or pid == os.getpid():
|
|
104
|
-
return False, "占用者是系统进程/自身,拒绝清理"
|
|
105
|
-
if is_own_instance(proc_cmdline(pid), main_script, port=port):
|
|
106
|
-
try:
|
|
107
|
-
runner._kill_tree(pid)
|
|
108
|
-
return True, "已结束旧实例 PID %d" % pid
|
|
109
|
-
except Exception as e:
|
|
110
|
-
return False, "旧实例 PID %d 清理失败 %s" % (pid, str(e)[:80])
|
|
111
|
-
return False, "占用者非 CodeBee(PID %d),未自动关闭" % pid
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""启动端口清场(用户拍板 2026-09-21):端口被占时先清场再启动。
|
|
3
|
+
|
|
4
|
+
- **自家旧实例**(命令行含本包 main.py 完整路径,或 npm 包内 app/main.py)
|
|
5
|
+
→ 杀树(TerminateProcess 直杀)——升级/重启最常见的占用者就是没退干净
|
|
6
|
+
的 CodeBee 自己;控制台进程对温和信号(WM_CLOSE)无反应,温和关不掉
|
|
7
|
+
正是用户「旧进程太难杀」的痛点,所以自家实例直接强杀。
|
|
8
|
+
- **别人的进程** → 只报告占用者,不发送任何信号;启动流程不得替用户
|
|
9
|
+
关闭可能正在开发中的服务。
|
|
10
|
+
- 系统/自身进程拒关(portscan 内置护栏)。
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
import shlex
|
|
17
|
+
import subprocess
|
|
18
|
+
|
|
19
|
+
from . import portscan, runner
|
|
20
|
+
|
|
21
|
+
log = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
PS_EXE = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def proc_cmdline(pid):
|
|
27
|
+
"""读进程命令行;失败返回空串。仅用于确认旧 CodeBee 实例身份。"""
|
|
28
|
+
try:
|
|
29
|
+
pid = int(pid)
|
|
30
|
+
except (TypeError, ValueError):
|
|
31
|
+
return ""
|
|
32
|
+
if pid <= 0:
|
|
33
|
+
return ""
|
|
34
|
+
if os.name != "nt":
|
|
35
|
+
try:
|
|
36
|
+
with open("/proc/%d/cmdline" % pid, "rb") as fh:
|
|
37
|
+
return fh.read().decode("utf-8", "replace").replace("\x00", " ").strip()
|
|
38
|
+
except Exception:
|
|
39
|
+
return ""
|
|
40
|
+
try:
|
|
41
|
+
r = subprocess.run(
|
|
42
|
+
[PS_EXE, "-NoProfile", "-Command",
|
|
43
|
+
"(Get-CimInstance Win32_Process -Filter 'ProcessId = %d').CommandLine" % pid],
|
|
44
|
+
capture_output=True, timeout=10)
|
|
45
|
+
return r.stdout.decode("utf-8", "replace").strip() if r.returncode == 0 else ""
|
|
46
|
+
except Exception:
|
|
47
|
+
return ""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _command_tokens(cmdline):
|
|
51
|
+
try:
|
|
52
|
+
return [token.strip().strip('"').strip("'")
|
|
53
|
+
for token in shlex.split(str(cmdline or ""), posix=False)]
|
|
54
|
+
except (TypeError, ValueError):
|
|
55
|
+
return []
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def is_own_instance(cmdline, main_script, port=None):
|
|
59
|
+
"""按启动器、脚本独立参数和端口精确确认 CodeBee 实例。"""
|
|
60
|
+
tokens = _command_tokens(cmdline)
|
|
61
|
+
if len(tokens) < 2:
|
|
62
|
+
return False
|
|
63
|
+
launcher = os.path.splitext(os.path.basename(tokens[0]))[0].lower()
|
|
64
|
+
if launcher not in ("python", "python3", "py"):
|
|
65
|
+
return False
|
|
66
|
+
script = ""
|
|
67
|
+
for token in tokens[1:]:
|
|
68
|
+
if token.startswith("-"):
|
|
69
|
+
continue
|
|
70
|
+
script = token
|
|
71
|
+
break
|
|
72
|
+
if not script or not os.path.isabs(script):
|
|
73
|
+
return False
|
|
74
|
+
actual = os.path.normcase(os.path.normpath(os.path.abspath(script)))
|
|
75
|
+
expected = os.path.normcase(os.path.normpath(os.path.abspath(str(main_script))))
|
|
76
|
+
packaged = actual.replace("\\", "/").lower().endswith(
|
|
77
|
+
"/node_modules/codebee/app/main.py")
|
|
78
|
+
if actual != expected and not packaged:
|
|
79
|
+
return False
|
|
80
|
+
if port is None:
|
|
81
|
+
return True
|
|
82
|
+
try:
|
|
83
|
+
wanted = str(int(port))
|
|
84
|
+
except (TypeError, ValueError):
|
|
85
|
+
return False
|
|
86
|
+
explicit_port = None
|
|
87
|
+
for i, token in enumerate(tokens):
|
|
88
|
+
if token == "--port" and i + 1 < len(tokens):
|
|
89
|
+
explicit_port = tokens[i + 1]
|
|
90
|
+
elif token.startswith("--port="):
|
|
91
|
+
explicit_port = token.split("=", 1)[1]
|
|
92
|
+
# argparse 默认端口可不出现在旧实例参数中;非默认端口必须显式相符。
|
|
93
|
+
return ((explicit_port == wanted) if explicit_port is not None
|
|
94
|
+
else wanted == "8765")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def clear_stale_port(port, main_script):
|
|
98
|
+
"""清掉占用端口的进程。返回 (是否清掉, 人话说明)。"""
|
|
99
|
+
holders = [p for p in portscan.listening_ports() if p.get("port") == int(port)]
|
|
100
|
+
if not holders:
|
|
101
|
+
return True, ""
|
|
102
|
+
pid = int(holders[0].get("pid") or 0)
|
|
103
|
+
if pid <= 4 or pid == os.getpid():
|
|
104
|
+
return False, "占用者是系统进程/自身,拒绝清理"
|
|
105
|
+
if is_own_instance(proc_cmdline(pid), main_script, port=port):
|
|
106
|
+
try:
|
|
107
|
+
runner._kill_tree(pid)
|
|
108
|
+
return True, "已结束旧实例 PID %d" % pid
|
|
109
|
+
except Exception as e:
|
|
110
|
+
return False, "旧实例 PID %d 清理失败 %s" % (pid, str(e)[:80])
|
|
111
|
+
return False, "占用者非 CodeBee(PID %d),未自动关闭" % pid
|