codebee 0.1.64 → 0.1.66

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 (110) hide show
  1. package/CHANGELOG.md +520 -507
  2. package/LICENSE +21 -21
  3. package/README.md +494 -487
  4. package/app/core/aiflavor.py +197 -197
  5. package/app/core/artifacts.py +148 -148
  6. package/app/core/attachments.py +532 -532
  7. package/app/core/automation.py +758 -758
  8. package/app/core/backup.py +470 -470
  9. package/app/core/beekeeper.py +183 -183
  10. package/app/core/board.py +209 -209
  11. package/app/core/bookmeta.py +272 -217
  12. package/app/core/bookmeta_catalog.py +180 -180
  13. package/app/core/branching.py +137 -137
  14. package/app/core/builtin_agent.py +1234 -1191
  15. package/app/core/capability.py +144 -144
  16. package/app/core/catalog.py +23 -24
  17. package/app/core/chaptersafe.py +32 -32
  18. package/app/core/cleanup.py +303 -303
  19. package/app/core/compaction.py +201 -186
  20. package/app/core/covergen.py +356 -356
  21. package/app/core/diagnostics.py +114 -114
  22. package/app/core/dispatch.py +173 -173
  23. package/app/core/dispatch_log.py +113 -113
  24. package/app/core/env_scrub.py +85 -85
  25. package/app/core/error_codes.py +220 -65
  26. package/app/core/errorlog.py +137 -137
  27. package/app/core/evaluation.py +174 -0
  28. package/app/core/failure_notify.py +290 -0
  29. package/app/core/flows.py +355 -355
  30. package/app/core/goal_service.py +167 -167
  31. package/app/core/health.py +374 -342
  32. package/app/core/history.py +38 -38
  33. package/app/core/hooks.py +162 -156
  34. package/app/core/jobs.py +1034 -1022
  35. package/app/core/knowledge.py +6 -2
  36. package/app/core/manager.py +2115 -1996
  37. package/app/core/market.py +362 -362
  38. package/app/core/market_remote.py +66 -66
  39. package/app/core/mocks.py +64 -64
  40. package/app/core/modelhub.py +3517 -3332
  41. package/app/core/notify.py +106 -106
  42. package/app/core/paihang.py +109 -109
  43. package/app/core/paths.py +2 -2
  44. package/app/core/pipeline.py +4614 -4483
  45. package/app/core/planner.py +33 -31
  46. package/app/core/portguard.py +111 -111
  47. package/app/core/preview.py +293 -293
  48. package/app/core/publish/__init__.py +7 -7
  49. package/app/core/publish/auto.py +367 -367
  50. package/app/core/publish/browser.py +533 -533
  51. package/app/core/publish/fanqie.py +128 -128
  52. package/app/core/publish/flow.py +432 -406
  53. package/app/core/publish/flows-fanqie-calibrated.json +17 -9
  54. package/app/core/publish/flows-qimao-calibrated.json +6 -2
  55. package/app/core/publish/ledger.py +198 -198
  56. package/app/core/publish/manager.py +623 -590
  57. package/app/core/publish/qimao.py +141 -141
  58. package/app/core/publish/ws.py +139 -139
  59. package/app/core/redact.py +38 -38
  60. package/app/core/repeat_guard.py +129 -124
  61. package/app/core/router.py +96 -22
  62. package/app/core/runner.py +2050 -2019
  63. package/app/core/selfupdate.py +384 -384
  64. package/app/core/session_log.py +161 -161
  65. package/app/core/sessions.py +312 -312
  66. package/app/core/settings.py +149 -149
  67. package/app/core/settings_schema.py +262 -262
  68. package/app/core/share_page.py +114 -114
  69. package/app/core/skill_scan.py +114 -106
  70. package/app/core/skillpacks/fanqie-novel.md +80 -80
  71. package/app/core/skillpacks/market/character-bible.md +66 -66
  72. package/app/core/skillpacks/market/code-risk-checklist.md +58 -58
  73. package/app/core/skillpacks/market/git-workflow.md +57 -57
  74. package/app/core/skillpacks/market/release-notes.md +72 -72
  75. package/app/core/skillpacks/market/weekly-report.md +71 -71
  76. package/app/core/skillpacks/market/worldview-consistency.md +70 -70
  77. package/app/core/skills.py +221 -72
  78. package/app/core/step_runner.py +132 -132
  79. package/app/core/store.py +23 -0
  80. package/app/core/task_compile.py +199 -199
  81. package/app/core/telemetry.py +292 -292
  82. package/app/core/tlsctx.py +64 -64
  83. package/app/core/token_meter.py +147 -147
  84. package/app/core/upstream.py +41 -0
  85. package/app/core/usage.py +114 -3
  86. package/app/core/volumes.py +355 -355
  87. package/app/core/wxdigest.py +858 -858
  88. package/app/core/zentao.py +2447 -2447
  89. package/app/main.py +2838 -2832
  90. package/app/pick_dialog.py +110 -110
  91. package/app/ui/app.js +13994 -13977
  92. package/app/ui/board.css +20 -41
  93. package/app/ui/board.html +162 -162
  94. package/app/ui/board.js +305 -305
  95. package/app/ui/i18n.js +2716 -2711
  96. package/app/ui/icons/bee.svg +79 -79
  97. package/app/ui/index.html +1 -1
  98. package/app/ui/manifest.json +16 -16
  99. package/app/ui/qrcode.js +2297 -2297
  100. package/app/ui/style.css +6107 -6125
  101. package/bin/tutti.js +147 -147
  102. package/package.json +46 -41
  103. package/tools/wx_reader.py +261 -261
  104. package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +0 -88
  105. package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +0 -126
  106. package/app/.playwright-cli/page-2026-09-23T08-29-44-203Z.png +0 -0
  107. package/app/.playwright-cli/page-2026-09-23T08-35-42-046Z.yml +0 -210
  108. package/app/.playwright-cli/page-2026-09-23T08-36-52-858Z.png +0 -0
  109. package/app/.playwright-cli/page-2026-09-23T08-37-33-938Z.png +0 -0
  110. package/app/.playwright-cli/run-detail-after.png +0 -0
@@ -1,149 +1,149 @@
1
- # -*- coding: utf-8 -*-
2
- """运行设置(data/settings.json):并发 worker 数 + 默认保存路径。"""
3
- from __future__ import annotations
4
-
5
- import json
6
- import threading
7
- from pathlib import Path
8
-
9
- from . import paths
10
-
11
- _LOCK = threading.RLock()
12
- _FILE = paths.DATA_DIR / "settings.json"
13
-
14
- # default_workdir 为空表示未自定义,用 builtin_workdir() 回落;
15
- # telemetry_errors:匿名错误回传开关(默认开;关掉后版本 ping/错误上传/诊断包遥测部分全部停发,
16
- # 「导出诊断包」是用户手动操作不受此开关限制)
17
- # publish_daily_cap / publish_fail_streak:自动发布护栏——每任务每平台每日
18
- # 成功发章上限、平台连续失败几次后暂停自动发布(publish/auto.py 读取)
19
- # pet_enabled / pet_mode:桌面蜜蜂(app/pet.py)开关与显示模式。关闭后看护线程
20
- # 不再拉起、在岗蜜蜂轮询到 false 自行退出;mode=always 常驻,tasks_only 仅任务
21
- # 运行时出现(空闲 90s 隐身)。
22
- # cleanup_enabled / cleanup_retention_days:每日垃圾清理(core/cleanup.py)——
23
- # 运行过程日志/发布截图/bak 残留等超期自动清理;retention 为保留天数。
24
- DEFAULTS = {"max_concurrent_jobs": 12, "default_workdir": "", "hooks_token": "",
25
- "telemetry_errors": True, "publish_daily_cap": 10,
26
- "publish_fail_streak": 3, "notify_webhook": "", "notify_base_url": "",
27
- "pet_enabled": True, "pet_mode": "always", "pet_skin": "plush",
28
- "cleanup_enabled": True, "cleanup_retention_days": 14,
29
- # claude_config_sync:打开/运行前防线是否直写 ~/.claude/settings.json。
30
- # 关闭后交互 claude 配置归用户手动管理(cc-switch 等),编排步骤经
31
- # runner 一次性 --settings 注入绑定链,路由不受影响(manager 读取)
32
- "claude_config_sync": True}
33
- # 桌宠形象白名单(与 app/pet.py 的 SKINS 对齐;这里不 import pet 模块,避免
34
- # core 反向依赖 app 根目录脚本)
35
- PET_SKINS = ("plush", "robot")
36
- # 并发保护上限 24:编排任务有空位即直接启动,满载回滚 queued 排队自动补跑,
37
- # 不秒判失败;同任务单飞守卫在 jobs 层。该上限保护的是本机资源(CLI 子进程
38
- # 数),与各 API 厂商的限流无关;direct 对话走 jobs 轻量池不占此额度。
39
- MIN_WORKERS, MAX_WORKERS = 1, 24
40
-
41
-
42
- def builtin_workdir():
43
- """内置默认保存路径:<data 目录同级>/workspace。"""
44
- return str(paths.DATA_DIR.parent / "workspace")
45
-
46
-
47
- def default_workdir():
48
- """当前生效的默认保存路径(自定义优先,回落内置值)。"""
49
- raw = str(load().get("default_workdir") or "").strip()
50
- return raw if raw else builtin_workdir()
51
-
52
-
53
- def _norm_workdir(v):
54
- """展开 ~ 并转绝对路径;空值合法(回落内置)。返回 (标准化串, 错误)。"""
55
- v = str(v or "").strip()
56
- if not v:
57
- return "", None
58
- if v.startswith("~"):
59
- v = str(Path(v).expanduser())
60
- p = Path(v)
61
- if not p.is_absolute():
62
- return "", "默认保存路径必须是绝对路径"
63
- return str(p), None
64
-
65
-
66
- def load():
67
- with _LOCK:
68
- try:
69
- data = json.loads(_FILE.read_text(encoding="utf-8"))
70
- except Exception:
71
- data = {}
72
- out = dict(DEFAULTS)
73
- if isinstance(data, dict):
74
- out.update({k: v for k, v in data.items() if k in DEFAULTS})
75
- try:
76
- out["max_concurrent_jobs"] = max(MIN_WORKERS, min(MAX_WORKERS, int(out["max_concurrent_jobs"])))
77
- except Exception:
78
- out["max_concurrent_jobs"] = DEFAULTS["max_concurrent_jobs"]
79
- wd, _ = _norm_workdir(out.get("default_workdir"))
80
- out["default_workdir"] = wd
81
- out["default_workdir_effective"] = wd if wd else builtin_workdir()
82
- return out
83
-
84
-
85
- def save(patch):
86
- """合并保存。返回 (view, 错误)。"""
87
- if not isinstance(patch, dict):
88
- return load(), "payload 必须是对象"
89
- with _LOCK:
90
- cur = load()
91
- if "max_concurrent_jobs" in patch:
92
- try:
93
- cur["max_concurrent_jobs"] = int(patch["max_concurrent_jobs"])
94
- except Exception:
95
- return cur, "max_concurrent_jobs 必须是整数"
96
- if not MIN_WORKERS <= cur["max_concurrent_jobs"] <= MAX_WORKERS:
97
- return cur, "max_concurrent_jobs 取值 %d-%d" % (MIN_WORKERS, MAX_WORKERS)
98
- if "default_workdir" in patch:
99
- wd, err = _norm_workdir(patch.get("default_workdir"))
100
- if err:
101
- return cur, err
102
- cur["default_workdir"] = wd
103
- if "hooks_token" in patch:
104
- cur["hooks_token"] = str(patch.get("hooks_token") or "").strip()[:128]
105
- if "telemetry_errors" in patch:
106
- cur["telemetry_errors"] = bool(patch.get("telemetry_errors"))
107
- if "publish_daily_cap" in patch:
108
- try:
109
- cur["publish_daily_cap"] = max(1, min(50, int(patch.get("publish_daily_cap"))))
110
- except (TypeError, ValueError):
111
- return cur, "publish_daily_cap 必须是 1-50 的整数"
112
- if "publish_fail_streak" in patch:
113
- try:
114
- cur["publish_fail_streak"] = max(1, min(10, int(patch.get("publish_fail_streak"))))
115
- except (TypeError, ValueError):
116
- return cur, "publish_fail_streak 必须是 1-10 的整数"
117
- if "notify_webhook" in patch:
118
- cur["notify_webhook"] = str(patch.get("notify_webhook") or "").strip()[:300]
119
- if "notify_base_url" in patch:
120
- cur["notify_base_url"] = str(patch.get("notify_base_url") or "").strip()[:200]
121
- if "pet_enabled" in patch:
122
- cur["pet_enabled"] = bool(patch.get("pet_enabled"))
123
- if "pet_mode" in patch:
124
- pm = str(patch.get("pet_mode") or "").strip()
125
- if pm not in ("always", "tasks_only"):
126
- return cur, "pet_mode 只能是 always 或 tasks_only"
127
- cur["pet_mode"] = pm
128
- if "pet_skin" in patch:
129
- ps = str(patch.get("pet_skin") or "").strip()
130
- if ps not in PET_SKINS:
131
- return cur, "pet_skin 只能是 %s 之一" % "/".join(PET_SKINS)
132
- cur["pet_skin"] = ps
133
- if "cleanup_enabled" in patch:
134
- cur["cleanup_enabled"] = bool(patch.get("cleanup_enabled"))
135
- if "claude_config_sync" in patch:
136
- cur["claude_config_sync"] = bool(patch.get("claude_config_sync"))
137
- if "cleanup_retention_days" in patch:
138
- try:
139
- cur["cleanup_retention_days"] = int(patch.get("cleanup_retention_days"))
140
- except (TypeError, ValueError):
141
- return cur, "cleanup_retention_days 必须是整数"
142
- if not 1 <= cur["cleanup_retention_days"] <= 365:
143
- return cur, "cleanup_retention_days 取值 1-365"
144
- _FILE.parent.mkdir(parents=True, exist_ok=True)
145
- tmp = _FILE.with_suffix(".tmp")
146
- tmp.write_text(json.dumps(cur, ensure_ascii=False, indent=2), encoding="utf-8")
147
- tmp.replace(_FILE)
148
- cur["default_workdir_effective"] = cur["default_workdir"] if cur["default_workdir"] else builtin_workdir()
149
- return cur, None
1
+ # -*- coding: utf-8 -*-
2
+ """运行设置(data/settings.json):并发 worker 数 + 默认保存路径。"""
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import threading
7
+ from pathlib import Path
8
+
9
+ from . import paths
10
+
11
+ _LOCK = threading.RLock()
12
+ _FILE = paths.DATA_DIR / "settings.json"
13
+
14
+ # default_workdir 为空表示未自定义,用 builtin_workdir() 回落;
15
+ # telemetry_errors:匿名错误回传开关(默认开;关掉后版本 ping/错误上传/诊断包遥测部分全部停发,
16
+ # 「导出诊断包」是用户手动操作不受此开关限制)
17
+ # publish_daily_cap / publish_fail_streak:自动发布护栏——每任务每平台每日
18
+ # 成功发章上限、平台连续失败几次后暂停自动发布(publish/auto.py 读取)
19
+ # pet_enabled / pet_mode:桌面蜜蜂(app/pet.py)开关与显示模式。关闭后看护线程
20
+ # 不再拉起、在岗蜜蜂轮询到 false 自行退出;mode=always 常驻,tasks_only 仅任务
21
+ # 运行时出现(空闲 90s 隐身)。
22
+ # cleanup_enabled / cleanup_retention_days:每日垃圾清理(core/cleanup.py)——
23
+ # 运行过程日志/发布截图/bak 残留等超期自动清理;retention 为保留天数。
24
+ DEFAULTS = {"max_concurrent_jobs": 12, "default_workdir": "", "hooks_token": "",
25
+ "telemetry_errors": True, "publish_daily_cap": 10,
26
+ "publish_fail_streak": 3, "notify_webhook": "", "notify_base_url": "",
27
+ "pet_enabled": True, "pet_mode": "always", "pet_skin": "plush",
28
+ "cleanup_enabled": True, "cleanup_retention_days": 14,
29
+ # claude_config_sync:打开/运行前防线是否直写 ~/.claude/settings.json。
30
+ # 关闭后交互 claude 配置归用户手动管理(cc-switch 等),编排步骤经
31
+ # runner 一次性 --settings 注入绑定链,路由不受影响(manager 读取)
32
+ "claude_config_sync": True}
33
+ # 桌宠形象白名单(与 app/pet.py 的 SKINS 对齐;这里不 import pet 模块,避免
34
+ # core 反向依赖 app 根目录脚本)
35
+ PET_SKINS = ("plush", "robot")
36
+ # 并发保护上限 24:编排任务有空位即直接启动,满载回滚 queued 排队自动补跑,
37
+ # 不秒判失败;同任务单飞守卫在 jobs 层。该上限保护的是本机资源(CLI 子进程
38
+ # 数),与各 API 厂商的限流无关;direct 对话走 jobs 轻量池不占此额度。
39
+ MIN_WORKERS, MAX_WORKERS = 1, 24
40
+
41
+
42
+ def builtin_workdir():
43
+ """内置默认保存路径:<data 目录同级>/workspace。"""
44
+ return str(paths.DATA_DIR.parent / "workspace")
45
+
46
+
47
+ def default_workdir():
48
+ """当前生效的默认保存路径(自定义优先,回落内置值)。"""
49
+ raw = str(load().get("default_workdir") or "").strip()
50
+ return raw if raw else builtin_workdir()
51
+
52
+
53
+ def _norm_workdir(v):
54
+ """展开 ~ 并转绝对路径;空值合法(回落内置)。返回 (标准化串, 错误)。"""
55
+ v = str(v or "").strip()
56
+ if not v:
57
+ return "", None
58
+ if v.startswith("~"):
59
+ v = str(Path(v).expanduser())
60
+ p = Path(v)
61
+ if not p.is_absolute():
62
+ return "", "默认保存路径必须是绝对路径"
63
+ return str(p), None
64
+
65
+
66
+ def load():
67
+ with _LOCK:
68
+ try:
69
+ data = json.loads(_FILE.read_text(encoding="utf-8"))
70
+ except Exception:
71
+ data = {}
72
+ out = dict(DEFAULTS)
73
+ if isinstance(data, dict):
74
+ out.update({k: v for k, v in data.items() if k in DEFAULTS})
75
+ try:
76
+ out["max_concurrent_jobs"] = max(MIN_WORKERS, min(MAX_WORKERS, int(out["max_concurrent_jobs"])))
77
+ except Exception:
78
+ out["max_concurrent_jobs"] = DEFAULTS["max_concurrent_jobs"]
79
+ wd, _ = _norm_workdir(out.get("default_workdir"))
80
+ out["default_workdir"] = wd
81
+ out["default_workdir_effective"] = wd if wd else builtin_workdir()
82
+ return out
83
+
84
+
85
+ def save(patch):
86
+ """合并保存。返回 (view, 错误)。"""
87
+ if not isinstance(patch, dict):
88
+ return load(), "payload 必须是对象"
89
+ with _LOCK:
90
+ cur = load()
91
+ if "max_concurrent_jobs" in patch:
92
+ try:
93
+ cur["max_concurrent_jobs"] = int(patch["max_concurrent_jobs"])
94
+ except Exception:
95
+ return cur, "max_concurrent_jobs 必须是整数"
96
+ if not MIN_WORKERS <= cur["max_concurrent_jobs"] <= MAX_WORKERS:
97
+ return cur, "max_concurrent_jobs 取值 %d-%d" % (MIN_WORKERS, MAX_WORKERS)
98
+ if "default_workdir" in patch:
99
+ wd, err = _norm_workdir(patch.get("default_workdir"))
100
+ if err:
101
+ return cur, err
102
+ cur["default_workdir"] = wd
103
+ if "hooks_token" in patch:
104
+ cur["hooks_token"] = str(patch.get("hooks_token") or "").strip()[:128]
105
+ if "telemetry_errors" in patch:
106
+ cur["telemetry_errors"] = bool(patch.get("telemetry_errors"))
107
+ if "publish_daily_cap" in patch:
108
+ try:
109
+ cur["publish_daily_cap"] = max(1, min(50, int(patch.get("publish_daily_cap"))))
110
+ except (TypeError, ValueError):
111
+ return cur, "publish_daily_cap 必须是 1-50 的整数"
112
+ if "publish_fail_streak" in patch:
113
+ try:
114
+ cur["publish_fail_streak"] = max(1, min(10, int(patch.get("publish_fail_streak"))))
115
+ except (TypeError, ValueError):
116
+ return cur, "publish_fail_streak 必须是 1-10 的整数"
117
+ if "notify_webhook" in patch:
118
+ cur["notify_webhook"] = str(patch.get("notify_webhook") or "").strip()[:300]
119
+ if "notify_base_url" in patch:
120
+ cur["notify_base_url"] = str(patch.get("notify_base_url") or "").strip()[:200]
121
+ if "pet_enabled" in patch:
122
+ cur["pet_enabled"] = bool(patch.get("pet_enabled"))
123
+ if "pet_mode" in patch:
124
+ pm = str(patch.get("pet_mode") or "").strip()
125
+ if pm not in ("always", "tasks_only"):
126
+ return cur, "pet_mode 只能是 always 或 tasks_only"
127
+ cur["pet_mode"] = pm
128
+ if "pet_skin" in patch:
129
+ ps = str(patch.get("pet_skin") or "").strip()
130
+ if ps not in PET_SKINS:
131
+ return cur, "pet_skin 只能是 %s 之一" % "/".join(PET_SKINS)
132
+ cur["pet_skin"] = ps
133
+ if "cleanup_enabled" in patch:
134
+ cur["cleanup_enabled"] = bool(patch.get("cleanup_enabled"))
135
+ if "claude_config_sync" in patch:
136
+ cur["claude_config_sync"] = bool(patch.get("claude_config_sync"))
137
+ if "cleanup_retention_days" in patch:
138
+ try:
139
+ cur["cleanup_retention_days"] = int(patch.get("cleanup_retention_days"))
140
+ except (TypeError, ValueError):
141
+ return cur, "cleanup_retention_days 必须是整数"
142
+ if not 1 <= cur["cleanup_retention_days"] <= 365:
143
+ return cur, "cleanup_retention_days 取值 1-365"
144
+ _FILE.parent.mkdir(parents=True, exist_ok=True)
145
+ tmp = _FILE.with_suffix(".tmp")
146
+ tmp.write_text(json.dumps(cur, ensure_ascii=False, indent=2), encoding="utf-8")
147
+ tmp.replace(_FILE)
148
+ cur["default_workdir_effective"] = cur["default_workdir"] if cur["default_workdir"] else builtin_workdir()
149
+ return cur, None