codebee 0.1.95 → 0.1.96
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 +725 -720
- package/LICENSE +21 -21
- package/README.md +515 -509
- package/app/core/acceptance.py +100 -100
- package/app/core/agent_context.py +100 -0
- package/app/core/aiflavor.py +197 -197
- package/app/core/analytics.py +38 -38
- package/app/core/artifacts.py +148 -148
- package/app/core/assets.py +110 -110
- package/app/core/attachments.py +623 -623
- package/app/core/audit.py +30 -30
- package/app/core/backup.py +470 -470
- package/app/core/beekeeper.py +183 -183
- package/app/core/benchstore.py +102 -95
- package/app/core/board.py +209 -209
- package/app/core/bookmeta.py +1036 -1036
- package/app/core/bookmeta_catalog.py +180 -180
- package/app/core/branching.py +137 -137
- package/app/core/builtin_agent.py +1680 -1680
- package/app/core/capability.py +144 -144
- package/app/core/chaptersafe.py +32 -32
- package/app/core/checkpoints.py +143 -0
- package/app/core/cleanup.py +303 -303
- package/app/core/compaction.py +201 -201
- package/app/core/contracts.py +255 -255
- package/app/core/covergen.py +356 -356
- package/app/core/defectretro.py +43 -43
- package/app/core/diagnostics.py +114 -114
- package/app/core/dispatch.py +198 -191
- package/app/core/dispatch_log.py +128 -128
- package/app/core/env_scrub.py +85 -85
- package/app/core/errorlog.py +137 -137
- package/app/core/eval_matrix.py +82 -0
- package/app/core/evalbench.py +184 -91
- package/app/core/evaluation.py +174 -174
- package/app/core/failure_notify.py +290 -290
- package/app/core/flow_graph.py +33 -0
- package/app/core/goal_service.py +167 -167
- package/app/core/health.py +389 -389
- package/app/core/history.py +38 -38
- package/app/core/hooks.py +162 -162
- package/app/core/jobs.py +25 -4
- package/app/core/knowledge_pipeline.py +86 -0
- package/app/core/manager.py +2232 -2232
- package/app/core/mocks.py +64 -64
- package/app/core/notify.py +201 -201
- package/app/core/novel_quality.py +181 -189
- package/app/core/openapi.py +177 -154
- package/app/core/operations.py +228 -228
- package/app/core/paihang.py +145 -145
- package/app/core/pipeline.py +43 -19
- package/app/core/planner.py +17 -11
- package/app/core/policy.py +52 -0
- package/app/core/portguard.py +111 -111
- package/app/core/presence.py +77 -77
- package/app/core/preview.py +293 -293
- package/app/core/prompt_guard.py +78 -78
- package/app/core/publish/__init__.py +7 -7
- package/app/core/publish/auto.py +26 -3
- package/app/core/publish/browser.py +655 -655
- package/app/core/publish/chaprows.py +138 -0
- package/app/core/publish/fanqie.py +210 -165
- package/app/core/publish/flow.py +753 -640
- package/app/core/publish/flows-fanqie-calibrated.json +349 -336
- package/app/core/publish/ledger.py +13 -1
- package/app/core/publish/manager.py +1150 -1118
- package/app/core/publish/qimao.py +176 -176
- package/app/core/publish/volumes.py +108 -0
- package/app/core/publish/ws.py +139 -139
- package/app/core/quality_gate.py +194 -194
- package/app/core/redact.py +38 -38
- package/app/core/repeat_guard.py +129 -129
- package/app/core/retrieval.py +137 -137
- package/app/core/router.py +398 -398
- package/app/core/runner.py +2238 -2238
- 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 +20 -0
- package/app/core/settings_schema.py +270 -270
- package/app/core/share_page.py +114 -114
- package/app/core/skill_scan.py +139 -139
- package/app/core/skillpacks/fanqie-novel.md +8 -6
- 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/skillpacks/qimao-signing.md +14 -11
- package/app/core/skills.py +32 -21
- package/app/core/step_runner.py +132 -132
- package/app/core/store.py +9 -0
- package/app/core/story_tracking.py +275 -275
- 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 +207 -144
- package/app/core/upstream.py +41 -41
- package/app/core/usage.py +1038 -1038
- package/app/core/volumes.py +389 -389
- package/app/core/webhooks.py +89 -0
- package/app/core/wxdigest.py +858 -858
- package/app/core/zentao.py +2692 -2692
- package/app/main.py +104 -9
- package/app/mcp_server.py +244 -244
- package/app/pick_dialog.py +110 -110
- package/app/ui/app.js +286 -18
- package/app/ui/artdiff.js +74 -74
- package/app/ui/assets/community-qr.png +0 -0
- package/app/ui/assets/donate-alipay.jpg +0 -0
- package/app/ui/assets/donate-qr.jpg +0 -0
- package/app/ui/board.html +162 -162
- package/app/ui/board.js +305 -305
- package/app/ui/i18n.js +33 -0
- package/app/ui/icons/bee.svg +79 -79
- package/app/ui/index.html +32 -3
- package/app/ui/manifest.json +16 -16
- package/app/ui/qrcode.js +2297 -2297
- package/app/ui/style.css +196 -2
- package/bin/tutti.js +147 -147
- package/package.json +48 -48
- package/tools/wx_reader.py +261 -261
- package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +0 -88
- package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +0 -126
- 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 +0 -210
- 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/output/_createbook_body.json +0 -1
- package/app/output/_probe_fq_click.py +0 -50
- package/app/output/_probe_fq_form.py +0 -42
- package/app/output/_probe_fq_modal.py +0 -55
- package/app/output/_probe_fq_points.py +0 -71
- package/app/output/_probe_fq_submit.py +0 -34
- package/app/output/_probe_fq_watch.py +0 -62
package/app/main.py
CHANGED
|
@@ -143,6 +143,7 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
143
143
|
return cached
|
|
144
144
|
raw_len = self.headers.get("Content-Length")
|
|
145
145
|
if not raw_len:
|
|
146
|
+
self._raw_body = b""
|
|
146
147
|
body = {}
|
|
147
148
|
else:
|
|
148
149
|
try:
|
|
@@ -158,6 +159,7 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
158
159
|
body = {}
|
|
159
160
|
else:
|
|
160
161
|
data = self.rfile.read(n)
|
|
162
|
+
self._raw_body = data
|
|
161
163
|
try:
|
|
162
164
|
text = data.decode("utf-8")
|
|
163
165
|
except UnicodeDecodeError:
|
|
@@ -171,6 +173,12 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
171
173
|
self._parsed_body = body
|
|
172
174
|
return body
|
|
173
175
|
|
|
176
|
+
def _raw_request_body(self):
|
|
177
|
+
"""Return the exact bytes used for webhook signature verification."""
|
|
178
|
+
if not hasattr(self, "_raw_body"):
|
|
179
|
+
self._body()
|
|
180
|
+
return getattr(self, "_raw_body", b"")
|
|
181
|
+
|
|
174
182
|
# ------------------------------------------------------------ 远程访问
|
|
175
183
|
def _forwarded_ip(self):
|
|
176
184
|
"""经代理进来的真实客户端 IP:X-Forwarded-For 首跳,缺省 CF-Connecting-IP。"""
|
|
@@ -300,6 +308,13 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
300
308
|
qs = parse_qs(urlparse(self.path).query)
|
|
301
309
|
fid = (qs.get("id") or [""])[0]
|
|
302
310
|
return self._json(200, {"versions": flows.flow_versions(fid)})
|
|
311
|
+
m = re.match(r"^/api/flows/([^/]+)/graph$", path)
|
|
312
|
+
if m:
|
|
313
|
+
from core import flow_graph
|
|
314
|
+
try:
|
|
315
|
+
return self._json(200, {"graph": flow_graph.build(m.group(1))})
|
|
316
|
+
except ValueError as exc:
|
|
317
|
+
return self._json(404, {"error": str(exc)})
|
|
303
318
|
if path == "/api/skills":
|
|
304
319
|
from core import skills
|
|
305
320
|
v = skills.view()
|
|
@@ -339,8 +354,18 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
339
354
|
return self._json(400, {"error": str(exc)})
|
|
340
355
|
return self._json(200, {"query": (q.get("q") or [""])[0][:500],
|
|
341
356
|
"results": results})
|
|
357
|
+
if path == "/api/knowledge/pipeline":
|
|
358
|
+
from core import knowledge_pipeline
|
|
359
|
+
q = parse_qs(urlparse(self.path).query)
|
|
360
|
+
return self._json(200, knowledge_pipeline.quality_report(
|
|
361
|
+
(q.get("ingest_id") or [""])[0]))
|
|
362
|
+
if path == "/api/policy":
|
|
363
|
+
from core import policy
|
|
364
|
+
q = parse_qs(urlparse(self.path).query)
|
|
365
|
+
return self._json(200, policy.normalize_sandbox(
|
|
366
|
+
{}, (q.get("workdir") or [settings.default_workdir()])[0]))
|
|
342
367
|
if path == "/api/settings":
|
|
343
|
-
return self._json(200, dict(settings.
|
|
368
|
+
return self._json(200, dict(settings.public_view(), **jobs.workers_info()))
|
|
344
369
|
if path == "/api/settings-v2":
|
|
345
370
|
# schema 化设置全貌(secret 已脱敏;前端调参卡直读)
|
|
346
371
|
from core import settings_schema as ss2
|
|
@@ -554,6 +579,20 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
554
579
|
from core import dispatch_log, tracing
|
|
555
580
|
return self._json(200, {"trace": tracing.build_trace(
|
|
556
581
|
run, dispatch_log.replay(run_id=m.group(1), limit=200))})
|
|
582
|
+
m = re.match(r"^/api/runs/([^/]+)/otel$", path)
|
|
583
|
+
if m:
|
|
584
|
+
run = store.get_run(m.group(1))
|
|
585
|
+
if not run:
|
|
586
|
+
return self._json(404, {"error": "not found"})
|
|
587
|
+
from core import dispatch_log, tracing
|
|
588
|
+
trace = tracing.build_trace(run, dispatch_log.replay(run_id=m.group(1), limit=200))
|
|
589
|
+
return self._json(200, tracing.to_otel(trace))
|
|
590
|
+
m = re.match(r"^/api/runs/([^/]+)/checkpoints$", path)
|
|
591
|
+
if m:
|
|
592
|
+
if not store.get_run(m.group(1)):
|
|
593
|
+
return self._json(404, {"error": "not found"})
|
|
594
|
+
from core import checkpoints
|
|
595
|
+
return self._json(200, {"checkpoint": checkpoints.replay_preview(m.group(1))})
|
|
557
596
|
m = re.match(r"^/api/runs/([^/]+)/preview$", path)
|
|
558
597
|
if m:
|
|
559
598
|
# 网页成品预览:入口 HTML + 同目录代码文件(前端渲染「预览」页签)
|
|
@@ -947,7 +986,8 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
947
986
|
return self._json(200, dict(ok=True, **info))
|
|
948
987
|
if path == "/api/evalbench/cancel":
|
|
949
988
|
from core import evalbench
|
|
950
|
-
|
|
989
|
+
body = self._body() or {}
|
|
990
|
+
return self._json(200, evalbench.cancel(force=bool(body.get("force"))))
|
|
951
991
|
if path == "/api/evalbench/sample-op":
|
|
952
992
|
# 自定义样题增删:{op:add|delete, sample:{...}|sid}
|
|
953
993
|
from core import evalbench
|
|
@@ -972,6 +1012,32 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
972
1012
|
if err:
|
|
973
1013
|
return self._json(400, {"error": err})
|
|
974
1014
|
return self._json(200, dict(ok=True, **res))
|
|
1015
|
+
if path == "/api/eval-matrix/normalize":
|
|
1016
|
+
from core import eval_matrix
|
|
1017
|
+
try:
|
|
1018
|
+
return self._json(200, {"manifest": eval_matrix.normalize_manifest(self._body())})
|
|
1019
|
+
except ValueError as exc:
|
|
1020
|
+
return self._json(400, {"error": str(exc)})
|
|
1021
|
+
if path == "/api/eval-matrix/evaluate":
|
|
1022
|
+
from core import eval_matrix
|
|
1023
|
+
body = self._body() or {}
|
|
1024
|
+
try:
|
|
1025
|
+
report = eval_matrix.evaluate(body.get("manifest"),
|
|
1026
|
+
body.get("results"), body.get("baseline"))
|
|
1027
|
+
except ValueError as exc:
|
|
1028
|
+
return self._json(400, {"error": str(exc)})
|
|
1029
|
+
return self._json(200, {"report": report})
|
|
1030
|
+
if path == "/api/knowledge/pipeline":
|
|
1031
|
+
from core import knowledge_pipeline
|
|
1032
|
+
body = self._body() or {}
|
|
1033
|
+
try:
|
|
1034
|
+
report = knowledge_pipeline.ingest(
|
|
1035
|
+
body.get("source"), body.get("text"), body.get("metadata"),
|
|
1036
|
+
chunk_size=body.get("chunk_size", 1200),
|
|
1037
|
+
overlap=body.get("overlap", 120))
|
|
1038
|
+
except ValueError as exc:
|
|
1039
|
+
return self._json(400, {"error": str(exc)})
|
|
1040
|
+
return self._json(200, {"ok": True, "report": report})
|
|
975
1041
|
if path == "/api/hooks/run":
|
|
976
1042
|
return self._api_hook_run()
|
|
977
1043
|
if path == "/api/health/op":
|
|
@@ -1500,9 +1566,9 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
1500
1566
|
if path == "/api/settings":
|
|
1501
1567
|
view, err = settings.save(self._body())
|
|
1502
1568
|
if err:
|
|
1503
|
-
return self._json(400, {"error": err, "settings": view})
|
|
1569
|
+
return self._json(400, {"error": err, "settings": settings.public_view(view)})
|
|
1504
1570
|
n = jobs.configure(view["max_concurrent_jobs"])
|
|
1505
|
-
return self._json(200, {"ok": True, "settings": view, "workers": n})
|
|
1571
|
+
return self._json(200, {"ok": True, "settings": settings.public_view(view), "workers": n})
|
|
1506
1572
|
m = re.match(r"^/api/settings-v2/([a-z_-]+)$", path)
|
|
1507
1573
|
if m:
|
|
1508
1574
|
# schema 化设置写入口:{ops:[{op:"set",path,value}], expected_revision?}
|
|
@@ -2143,11 +2209,14 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
2143
2209
|
force = bool(body.get("force"))
|
|
2144
2210
|
force_confirmed = bool(body.get("force_confirmed"))
|
|
2145
2211
|
force_reason = str(body.get("force_reason") or "").strip()
|
|
2212
|
+
only = body.get("chapters") # 批量选择发布:章号数组(可省=全部待发)
|
|
2213
|
+
kwargs = {"only": only if isinstance(only, list) else None}
|
|
2214
|
+
if force or force_confirmed or force_reason:
|
|
2215
|
+
kwargs.update(force=force, force_confirmed=force_confirmed,
|
|
2216
|
+
force_reason=force_reason)
|
|
2146
2217
|
ok, err = pub_auto.publish_pending_async(
|
|
2147
2218
|
task_id, platform, auto_submit=bool(body.get("auto_submit")),
|
|
2148
|
-
**
|
|
2149
|
-
"force_reason": force_reason}
|
|
2150
|
-
if force or force_confirmed or force_reason else {}))
|
|
2219
|
+
**kwargs)
|
|
2151
2220
|
if not ok:
|
|
2152
2221
|
payload = {"error": err or "操作失败"}
|
|
2153
2222
|
if str(err or "").startswith("质量门禁拦截:"):
|
|
@@ -3024,10 +3093,13 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
3024
3093
|
POST /api/hooks/run,头 X-CodeBee-Token;体 {goal 必填, type, workdir,
|
|
3025
3094
|
context, title}。令牌取设置 hooks_token(data/settings.json,UI/文件均可
|
|
3026
3095
|
配置):已配置则必须精确匹配;未配置仅放行本机回环。任务链与 UI 完全相同。"""
|
|
3096
|
+
from core import webhooks
|
|
3027
3097
|
try:
|
|
3028
|
-
|
|
3098
|
+
cfg = settings.load()
|
|
3099
|
+
tok = str(cfg.get("hooks_token") or "")
|
|
3100
|
+
signing_secret = str(cfg.get("hooks_signing_secret") or "")
|
|
3029
3101
|
except Exception:
|
|
3030
|
-
tok = ""
|
|
3102
|
+
tok, signing_secret = "", ""
|
|
3031
3103
|
given = (self.headers.get("X-CodeBee-Token") or "").strip()
|
|
3032
3104
|
if tok:
|
|
3033
3105
|
if given != tok:
|
|
@@ -3037,6 +3109,18 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
3037
3109
|
if host not in ("127.0.0.1", "::1", "::ffff:127.0.0.1"):
|
|
3038
3110
|
return self._json(403, {
|
|
3039
3111
|
"error": "未配置 hooks.token,仅允许本机触发;请先在设置中配置令牌"})
|
|
3112
|
+
# Optional HMAC envelope for GitHub/GitLab-style integrations. A
|
|
3113
|
+
# configured secret makes the signature mandatory; token auth remains
|
|
3114
|
+
# useful for old local scripts when no secret is configured.
|
|
3115
|
+
if signing_secret:
|
|
3116
|
+
timestamp = self.headers.get("X-CodeBee-Timestamp") or ""
|
|
3117
|
+
signature = (self.headers.get("X-CodeBee-Signature") or
|
|
3118
|
+
self.headers.get("X-Hub-Signature-256") or "")
|
|
3119
|
+
signed = (webhooks.verify(signing_secret, self._raw_request_body(),
|
|
3120
|
+
signature, timestamp) if timestamp else
|
|
3121
|
+
webhooks.verify_raw(signing_secret, self._raw_request_body(), signature))
|
|
3122
|
+
if not signed:
|
|
3123
|
+
return self._json(401, {"error": "Webhook 签名无效或已过期"})
|
|
3040
3124
|
raw = self._body()
|
|
3041
3125
|
payload = {}
|
|
3042
3126
|
for k, cap in (("goal", 4000), ("context", 4000), ("type", 40),
|
|
@@ -3050,7 +3134,18 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
3050
3134
|
payload["type"] = "direct"
|
|
3051
3135
|
if not payload.get("title"):
|
|
3052
3136
|
payload["title"] = payload["goal"][:30]
|
|
3137
|
+
delivery_id = (self.headers.get("X-CodeBee-Delivery") or
|
|
3138
|
+
self.headers.get("X-Delivery-Id") or "").strip()
|
|
3139
|
+
if delivery_id:
|
|
3140
|
+
claimed, info = webhooks.claim_delivery(delivery_id)
|
|
3141
|
+
if not claimed:
|
|
3142
|
+
return self._json(200, {"ok": True, "deduplicated": True,
|
|
3143
|
+
"delivery_id": info.get("delivery_id")})
|
|
3053
3144
|
status, resp = self._create_and_start(payload)
|
|
3145
|
+
if delivery_id and status != 200:
|
|
3146
|
+
webhooks.release_delivery(delivery_id)
|
|
3147
|
+
if delivery_id:
|
|
3148
|
+
resp = dict(resp, delivery_id=delivery_id)
|
|
3054
3149
|
return self._json(status, resp)
|
|
3055
3150
|
|
|
3056
3151
|
def _enqueue_run(self, run_id, task_id, job):
|