codebee 0.1.103 → 0.1.105

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.
Files changed (128) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -21
  3. package/README.md +5 -3
  4. package/app/core/acceptance.py +100 -100
  5. package/app/core/acp_client.py +457 -457
  6. package/app/core/aiflavor.py +197 -197
  7. package/app/core/analytics.py +38 -38
  8. package/app/core/artifacts.py +148 -148
  9. package/app/core/assets.py +110 -110
  10. package/app/core/attachments.py +623 -623
  11. package/app/core/audit.py +30 -30
  12. package/app/core/backend_profiles.py +188 -188
  13. package/app/core/backup.py +470 -470
  14. package/app/core/beekeeper.py +183 -183
  15. package/app/core/benchstore.py +102 -102
  16. package/app/core/bookmeta.py +1036 -1036
  17. package/app/core/bookmeta_catalog.py +180 -180
  18. package/app/core/branching.py +137 -137
  19. package/app/core/capability.py +144 -144
  20. package/app/core/chaptersafe.py +32 -32
  21. package/app/core/checkpoints.py +48 -5
  22. package/app/core/cleanup.py +303 -303
  23. package/app/core/command_auth.py +302 -302
  24. package/app/core/compaction.py +201 -201
  25. package/app/core/contracts.py +255 -255
  26. package/app/core/covergen.py +356 -356
  27. package/app/core/defectretro.py +43 -43
  28. package/app/core/diagnostics.py +114 -114
  29. package/app/core/dispatch_log.py +128 -128
  30. package/app/core/env_scrub.py +85 -85
  31. package/app/core/errorlog.py +137 -137
  32. package/app/core/eval_matrix.py +91 -91
  33. package/app/core/evaluation.py +174 -174
  34. package/app/core/failure_notify.py +290 -290
  35. package/app/core/flow_graph.py +33 -33
  36. package/app/core/goal_service.py +167 -167
  37. package/app/core/health.py +389 -389
  38. package/app/core/history.py +38 -38
  39. package/app/core/hooks.py +162 -162
  40. package/app/core/knowledge_pipeline.py +86 -86
  41. package/app/core/manager.py +2386 -2385
  42. package/app/core/mocks.py +64 -64
  43. package/app/core/notify.py +201 -201
  44. package/app/core/novel_quality.py +285 -181
  45. package/app/core/openapi.py +246 -217
  46. package/app/core/operations.py +228 -228
  47. package/app/core/paihang.py +145 -145
  48. package/app/core/pipeline.py +81 -11
  49. package/app/core/planner.py +7 -0
  50. package/app/core/portguard.py +111 -111
  51. package/app/core/presence.py +77 -77
  52. package/app/core/preview.py +293 -293
  53. package/app/core/project_memory.py +252 -206
  54. package/app/core/prompt_guard.py +78 -78
  55. package/app/core/publish/__init__.py +7 -7
  56. package/app/core/publish/auto.py +659 -659
  57. package/app/core/publish/chaprows.py +138 -138
  58. package/app/core/publish/flows-fanqie-calibrated.json +442 -442
  59. package/app/core/publish/manager.py +1185 -1185
  60. package/app/core/publish/qimao.py +193 -193
  61. package/app/core/publish/volumes.py +108 -108
  62. package/app/core/publish/ws.py +139 -139
  63. package/app/core/quality_gate.py +201 -201
  64. package/app/core/redact.py +38 -38
  65. package/app/core/registry.py +35 -0
  66. package/app/core/repeat_guard.py +129 -129
  67. package/app/core/retrieval.py +137 -137
  68. package/app/core/review_requests.py +119 -0
  69. package/app/core/runner.py +5 -1
  70. package/app/core/sandbox_runtime.py +95 -95
  71. package/app/core/selfupdate.py +384 -384
  72. package/app/core/session_log.py +161 -161
  73. package/app/core/sessions.py +312 -312
  74. package/app/core/settings.py +215 -215
  75. package/app/core/settings_schema.py +282 -282
  76. package/app/core/share_page.py +114 -114
  77. package/app/core/skill_scan.py +139 -139
  78. package/app/core/skillpacks/market/character-bible.md +66 -66
  79. package/app/core/skillpacks/market/code-risk-checklist.md +58 -58
  80. package/app/core/skillpacks/market/git-workflow.md +57 -57
  81. package/app/core/skillpacks/market/release-notes.md +72 -72
  82. package/app/core/skillpacks/market/weekly-report.md +71 -71
  83. package/app/core/skillpacks/market/worldview-consistency.md +70 -70
  84. package/app/core/step_runner.py +132 -132
  85. package/app/core/story_tracking.py +275 -275
  86. package/app/core/task_compile.py +199 -199
  87. package/app/core/telemetry.py +292 -292
  88. package/app/core/tlsctx.py +64 -64
  89. package/app/core/token_meter.py +147 -147
  90. package/app/core/tool_outputs.py +107 -107
  91. package/app/core/tracing.py +207 -207
  92. package/app/core/upstream.py +41 -41
  93. package/app/core/usage.py +1038 -1038
  94. package/app/core/volumes.py +389 -389
  95. package/app/core/webhooks.py +89 -89
  96. package/app/core/wxdigest.py +865 -865
  97. package/app/core/zentao.py +2692 -2692
  98. package/app/main.py +43 -0
  99. package/app/mcp_server.py +244 -244
  100. package/app/pick_dialog.py +110 -110
  101. package/app/ui/app.js +21 -2
  102. package/app/ui/artdiff.js +74 -74
  103. package/app/ui/hive3d.js +54 -31
  104. package/app/ui/i18n.js +32 -1
  105. package/app/ui/icons/bee.svg +79 -79
  106. package/app/ui/index.html +1 -0
  107. package/app/ui/manifest.json +16 -16
  108. package/app/ui/qrcode.js +2297 -2297
  109. package/bin/tutti.js +147 -147
  110. package/package.json +1 -1
  111. package/tools/wx_reader.py +261 -261
  112. package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +0 -88
  113. package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +0 -126
  114. package/app/.playwright-cli/page-2026-09-23T08-29-44-203Z.png +0 -0
  115. package/app/.playwright-cli/page-2026-09-23T08-35-42-046Z.yml +0 -210
  116. package/app/.playwright-cli/page-2026-09-23T08-36-52-858Z.png +0 -0
  117. package/app/.playwright-cli/page-2026-09-23T08-37-33-938Z.png +0 -0
  118. package/app/.playwright-cli/run-detail-after.png +0 -0
  119. package/app/output/_createbook_body.json +0 -1
  120. package/app/output/_probe_fq_click.py +0 -50
  121. package/app/output/_probe_fq_form.py +0 -42
  122. package/app/output/_probe_fq_modal.py +0 -55
  123. package/app/output/_probe_fq_points.py +0 -71
  124. package/app/output/_probe_fq_submit.py +0 -34
  125. package/app/output/_probe_fq_watch.py +0 -62
  126. package/app/output/workbench-clean-preview.jpg +0 -0
  127. package/app/output/workbench-reference-render.png +0 -0
  128. package/app/output/workbench-shell.png +0 -0
@@ -1,138 +1,138 @@
1
- # -*- coding: utf-8 -*-
2
- """章节管理页的行收集与分桶(校准的数据面,manager 只做编排)。
3
-
4
- 从 manager.py 拆出(manager 超 1200 行行数基线,2026-10-08):收集器只吃
5
- page + 平台 CONFIG,不依赖任何内部模块——放 L0 层无依赖。
6
- 状态关键词口径与台账一致:已发布 / 待审核·审核中·排队 / 未通过·驳回。
7
- """
8
- from __future__ import annotations
9
-
10
- import re
11
- import time
12
-
13
- _STATUS_PUB = "已发布"
14
- _STATUS_REVIEW = ("待审核", "审核中", "排队")
15
- _STATUS_BAD = ("未通过", "驳回")
16
-
17
- _CH_NO_RE = re.compile(r"第\s*(\d+)\s*章")
18
-
19
-
20
- def bucket_chapter_rows(rows):
21
- """章节管理页的行文本 → {total, published, review, rejected, published_nos}。
22
-
23
- 行里含状态关键词才计入对应桶;total=识别出的章节数据行数。
24
- published_nos=已发布行里解析出的章号(第N章,阿拉伯数字)——本地台账
25
- 只记得 CodeBee 自己发的章,用户手工补交的只有平台知道,校准把它带
26
- 回来供幂等/待发清单对账。"""
27
- out = {"total": 0, "published": 0, "review": 0, "rejected": 0,
28
- "published_nos": []}
29
- nos = set()
30
- for t in rows or []:
31
- t = str(t)
32
- if not t:
33
- continue
34
- if "章节名称" in t: # 表头兜底(正常到不了这)
35
- continue
36
- out["total"] += 1
37
- if any(k in t for k in _STATUS_BAD):
38
- out["rejected"] += 1
39
- elif _STATUS_PUB in t:
40
- out["published"] += 1
41
- m = _CH_NO_RE.search(t)
42
- if m:
43
- nos.add(int(m.group(1)))
44
- elif any(k in t for k in _STATUS_REVIEW):
45
- out["review"] += 1
46
- out["published_nos"] = sorted(nos)
47
- return out
48
-
49
-
50
- def collect_manage_rows(page, mod):
51
- """读章节管理页的全部行:跨页翻页收齐(番茄表每页 15 行,只数当前页
52
- 会把 30 章已发报成 15——2026-10-08 校准少计实案)。平台没配翻页 JS
53
- (七猫)或翻页无进展时按单页收。行文本去重保序,重复页不会翻倍。"""
54
- js_rows = mod.CONFIG.get("count_rows_js")
55
- next_js = mod.CONFIG.get("pager_next_js")
56
- settle = float(mod.CONFIG.get("count_page_settle") or 1.2)
57
- rows, seen = [], set()
58
- for _ in range(int(mod.CONFIG.get("count_max_pages") or 20)):
59
- fresh = 0
60
- for t in (page.call(js_rows) or []):
61
- t = str(t)
62
- if t and t not in seen:
63
- seen.add(t)
64
- rows.append(t)
65
- fresh += 1
66
- if not next_js or fresh == 0:
67
- break
68
- r = page.call(next_js)
69
- if not isinstance(r, dict) or r.get("done"):
70
- break
71
- time.sleep(settle)
72
- return rows
73
-
74
-
75
- def collect_all_volumes(page, mod):
76
- """跨分卷收行:章节管理页带分卷筛选(番茄「第一卷:…」下拉,无「全部」
77
- 项),列表只显示选中卷的章节——卷二发布后只数卷一会继续少计。弹层在
78
- click 后下一帧才渲染,open→读/点之间必须隔 sleep(同步 JS 一口气查必
79
- 落空,2026-10-08 真机实案)。收完恢复进入时的卷;一卷都没选成时按原
80
- 筛选兜底收一次,不让分卷自动化失败拖死整次校准。
81
-
82
- 返回 (rows, vols):vols=读到的平台分卷显示名清单(读不到为 None),
83
- 供 books.json 缓存平台卷表。"""
84
- vj = mod.CONFIG.get("volume_js") or {}
85
- if not all(vj.get(k) for k in ("open", "options", "pick")):
86
- return collect_manage_rows(page, mod), None
87
- settle = float(mod.CONFIG.get("count_page_settle") or 1.2)
88
-
89
- def open_popup():
90
- try:
91
- page.call(vj["open"])
92
- except Exception:
93
- pass
94
- time.sleep(0.8)
95
-
96
- orig = ""
97
- try:
98
- orig = str(page.call(vj.get("current")) or "").strip()
99
- except Exception:
100
- pass
101
- open_popup()
102
- try:
103
- vols = page.call(vj["options"]) or []
104
- except Exception:
105
- vols = []
106
- if not isinstance(vols, list) or len(vols) < 2:
107
- return collect_manage_rows(page, mod), None # 单卷:当前筛选就是全量
108
- rows, seen, picked_any, misses = [], set(), False, 0
109
- for v in vols:
110
- v = str(v or "").strip()
111
- if not v:
112
- continue
113
- open_popup()
114
- try:
115
- r = page.call(vj["pick"], v)
116
- except Exception:
117
- r = None
118
- if not isinstance(r, dict) or not r.get("ok"):
119
- misses += 1
120
- if misses >= 3:
121
- break # 弹层压根点不动:别把选项清单烧完
122
- continue
123
- misses = 0
124
- picked_any = True
125
- time.sleep(settle)
126
- for t in collect_manage_rows(page, mod):
127
- if t not in seen:
128
- seen.add(t)
129
- rows.append(t)
130
- if orig: # 还原进入时的卷筛选
131
- open_popup()
132
- try:
133
- page.call(vj["pick"], orig)
134
- except Exception:
135
- pass
136
- if not picked_any:
137
- return collect_manage_rows(page, mod), list(vols)
138
- return rows, [str(v) for v in vols if str(v or "").strip()]
1
+ # -*- coding: utf-8 -*-
2
+ """章节管理页的行收集与分桶(校准的数据面,manager 只做编排)。
3
+
4
+ 从 manager.py 拆出(manager 超 1200 行行数基线,2026-10-08):收集器只吃
5
+ page + 平台 CONFIG,不依赖任何内部模块——放 L0 层无依赖。
6
+ 状态关键词口径与台账一致:已发布 / 待审核·审核中·排队 / 未通过·驳回。
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import re
11
+ import time
12
+
13
+ _STATUS_PUB = "已发布"
14
+ _STATUS_REVIEW = ("待审核", "审核中", "排队")
15
+ _STATUS_BAD = ("未通过", "驳回")
16
+
17
+ _CH_NO_RE = re.compile(r"第\s*(\d+)\s*章")
18
+
19
+
20
+ def bucket_chapter_rows(rows):
21
+ """章节管理页的行文本 → {total, published, review, rejected, published_nos}。
22
+
23
+ 行里含状态关键词才计入对应桶;total=识别出的章节数据行数。
24
+ published_nos=已发布行里解析出的章号(第N章,阿拉伯数字)——本地台账
25
+ 只记得 CodeBee 自己发的章,用户手工补交的只有平台知道,校准把它带
26
+ 回来供幂等/待发清单对账。"""
27
+ out = {"total": 0, "published": 0, "review": 0, "rejected": 0,
28
+ "published_nos": []}
29
+ nos = set()
30
+ for t in rows or []:
31
+ t = str(t)
32
+ if not t:
33
+ continue
34
+ if "章节名称" in t: # 表头兜底(正常到不了这)
35
+ continue
36
+ out["total"] += 1
37
+ if any(k in t for k in _STATUS_BAD):
38
+ out["rejected"] += 1
39
+ elif _STATUS_PUB in t:
40
+ out["published"] += 1
41
+ m = _CH_NO_RE.search(t)
42
+ if m:
43
+ nos.add(int(m.group(1)))
44
+ elif any(k in t for k in _STATUS_REVIEW):
45
+ out["review"] += 1
46
+ out["published_nos"] = sorted(nos)
47
+ return out
48
+
49
+
50
+ def collect_manage_rows(page, mod):
51
+ """读章节管理页的全部行:跨页翻页收齐(番茄表每页 15 行,只数当前页
52
+ 会把 30 章已发报成 15——2026-10-08 校准少计实案)。平台没配翻页 JS
53
+ (七猫)或翻页无进展时按单页收。行文本去重保序,重复页不会翻倍。"""
54
+ js_rows = mod.CONFIG.get("count_rows_js")
55
+ next_js = mod.CONFIG.get("pager_next_js")
56
+ settle = float(mod.CONFIG.get("count_page_settle") or 1.2)
57
+ rows, seen = [], set()
58
+ for _ in range(int(mod.CONFIG.get("count_max_pages") or 20)):
59
+ fresh = 0
60
+ for t in (page.call(js_rows) or []):
61
+ t = str(t)
62
+ if t and t not in seen:
63
+ seen.add(t)
64
+ rows.append(t)
65
+ fresh += 1
66
+ if not next_js or fresh == 0:
67
+ break
68
+ r = page.call(next_js)
69
+ if not isinstance(r, dict) or r.get("done"):
70
+ break
71
+ time.sleep(settle)
72
+ return rows
73
+
74
+
75
+ def collect_all_volumes(page, mod):
76
+ """跨分卷收行:章节管理页带分卷筛选(番茄「第一卷:…」下拉,无「全部」
77
+ 项),列表只显示选中卷的章节——卷二发布后只数卷一会继续少计。弹层在
78
+ click 后下一帧才渲染,open→读/点之间必须隔 sleep(同步 JS 一口气查必
79
+ 落空,2026-10-08 真机实案)。收完恢复进入时的卷;一卷都没选成时按原
80
+ 筛选兜底收一次,不让分卷自动化失败拖死整次校准。
81
+
82
+ 返回 (rows, vols):vols=读到的平台分卷显示名清单(读不到为 None),
83
+ 供 books.json 缓存平台卷表。"""
84
+ vj = mod.CONFIG.get("volume_js") or {}
85
+ if not all(vj.get(k) for k in ("open", "options", "pick")):
86
+ return collect_manage_rows(page, mod), None
87
+ settle = float(mod.CONFIG.get("count_page_settle") or 1.2)
88
+
89
+ def open_popup():
90
+ try:
91
+ page.call(vj["open"])
92
+ except Exception:
93
+ pass
94
+ time.sleep(0.8)
95
+
96
+ orig = ""
97
+ try:
98
+ orig = str(page.call(vj.get("current")) or "").strip()
99
+ except Exception:
100
+ pass
101
+ open_popup()
102
+ try:
103
+ vols = page.call(vj["options"]) or []
104
+ except Exception:
105
+ vols = []
106
+ if not isinstance(vols, list) or len(vols) < 2:
107
+ return collect_manage_rows(page, mod), None # 单卷:当前筛选就是全量
108
+ rows, seen, picked_any, misses = [], set(), False, 0
109
+ for v in vols:
110
+ v = str(v or "").strip()
111
+ if not v:
112
+ continue
113
+ open_popup()
114
+ try:
115
+ r = page.call(vj["pick"], v)
116
+ except Exception:
117
+ r = None
118
+ if not isinstance(r, dict) or not r.get("ok"):
119
+ misses += 1
120
+ if misses >= 3:
121
+ break # 弹层压根点不动:别把选项清单烧完
122
+ continue
123
+ misses = 0
124
+ picked_any = True
125
+ time.sleep(settle)
126
+ for t in collect_manage_rows(page, mod):
127
+ if t not in seen:
128
+ seen.add(t)
129
+ rows.append(t)
130
+ if orig: # 还原进入时的卷筛选
131
+ open_popup()
132
+ try:
133
+ page.call(vj["pick"], orig)
134
+ except Exception:
135
+ pass
136
+ if not picked_any:
137
+ return collect_manage_rows(page, mod), list(vols)
138
+ return rows, [str(v) for v in vols if str(v or "").strip()]