codebee 0.1.71 → 0.1.73

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 (118) hide show
  1. package/CHANGELOG.md +601 -589
  2. package/LICENSE +21 -21
  3. package/README.md +508 -509
  4. package/app/core/acceptance.py +100 -100
  5. package/app/core/aiflavor.py +197 -197
  6. package/app/core/artifacts.py +148 -148
  7. package/app/core/assets.py +110 -110
  8. package/app/core/attachments.py +623 -623
  9. package/app/core/audit.py +30 -30
  10. package/app/core/backup.py +470 -470
  11. package/app/core/beekeeper.py +183 -183
  12. package/app/core/benchstore.py +95 -95
  13. package/app/core/board.py +209 -209
  14. package/app/core/bookmeta.py +935 -935
  15. package/app/core/bookmeta_catalog.py +180 -180
  16. package/app/core/branching.py +137 -137
  17. package/app/core/builtin_agent.py +1680 -1673
  18. package/app/core/capability.py +144 -144
  19. package/app/core/chaptersafe.py +32 -32
  20. package/app/core/cleanup.py +303 -303
  21. package/app/core/compaction.py +201 -201
  22. package/app/core/covergen.py +356 -356
  23. package/app/core/defectretro.py +43 -43
  24. package/app/core/diagnostics.py +114 -114
  25. package/app/core/dispatch.py +190 -190
  26. package/app/core/dispatch_log.py +128 -128
  27. package/app/core/env_scrub.py +85 -85
  28. package/app/core/errorlog.py +137 -137
  29. package/app/core/evalbench.py +836 -720
  30. package/app/core/evaluation.py +174 -174
  31. package/app/core/failure_notify.py +290 -290
  32. package/app/core/flows.py +8 -8
  33. package/app/core/goal_service.py +167 -167
  34. package/app/core/health.py +374 -374
  35. package/app/core/history.py +38 -38
  36. package/app/core/hooks.py +162 -162
  37. package/app/core/jobs.py +25 -1
  38. package/app/core/manager.py +2208 -2143
  39. package/app/core/market.py +90 -4
  40. package/app/core/market_remote.py +16 -0
  41. package/app/core/mocks.py +64 -64
  42. package/app/core/modelhub.py +3 -2
  43. package/app/core/notify.py +201 -201
  44. package/app/core/novel_quality.py +189 -189
  45. package/app/core/operations.py +228 -228
  46. package/app/core/paihang.py +145 -138
  47. package/app/core/pipeline.py +102 -30
  48. package/app/core/planner.py +2 -0
  49. package/app/core/plugins.py +34 -0
  50. package/app/core/portguard.py +111 -111
  51. package/app/core/preview.py +293 -293
  52. package/app/core/prompt_guard.py +78 -0
  53. package/app/core/publish/__init__.py +7 -7
  54. package/app/core/publish/auto.py +396 -367
  55. package/app/core/publish/browser.py +655 -655
  56. package/app/core/publish/fanqie.py +165 -165
  57. package/app/core/publish/flow.py +640 -589
  58. package/app/core/publish/flows-fanqie-calibrated.json +336 -336
  59. package/app/core/publish/ledger.py +307 -229
  60. package/app/core/publish/manager.py +1118 -824
  61. package/app/core/publish/qimao.py +176 -146
  62. package/app/core/publish/ws.py +139 -139
  63. package/app/core/quality_gate.py +194 -0
  64. package/app/core/redact.py +38 -38
  65. package/app/core/repeat_guard.py +129 -129
  66. package/app/core/router.py +398 -398
  67. package/app/core/runner.py +2142 -2142
  68. package/app/core/selfupdate.py +384 -384
  69. package/app/core/session_log.py +161 -161
  70. package/app/core/sessions.py +312 -312
  71. package/app/core/settings_schema.py +270 -270
  72. package/app/core/share_page.py +114 -114
  73. package/app/core/skill_scan.py +139 -122
  74. package/app/core/skillpacks/fanqie-novel.md +98 -80
  75. package/app/core/skillpacks/market/character-bible.md +66 -66
  76. package/app/core/skillpacks/market/code-risk-checklist.md +58 -58
  77. package/app/core/skillpacks/market/git-workflow.md +57 -57
  78. package/app/core/skillpacks/market/release-notes.md +72 -72
  79. package/app/core/skillpacks/market/weekly-report.md +71 -71
  80. package/app/core/skillpacks/market/worldview-consistency.md +70 -70
  81. package/app/core/skillpacks/qimao-signing.md +70 -59
  82. package/app/core/skills.py +58 -8
  83. package/app/core/step_runner.py +132 -132
  84. package/app/core/store.py +51 -0
  85. package/app/core/task_compile.py +199 -199
  86. package/app/core/telemetry.py +292 -292
  87. package/app/core/tlsctx.py +64 -64
  88. package/app/core/token_meter.py +147 -147
  89. package/app/core/tracing.py +144 -144
  90. package/app/core/upstream.py +41 -41
  91. package/app/core/usage.py +1035 -1035
  92. package/app/core/volumes.py +389 -389
  93. package/app/core/wxdigest.py +858 -858
  94. package/app/core/zentao.py +2692 -2614
  95. package/app/main.py +110 -39
  96. package/app/mcp_server.py +175 -175
  97. package/app/pick_dialog.py +110 -110
  98. package/app/ui/app.js +509 -114
  99. package/app/ui/artdiff.js +74 -74
  100. package/app/ui/board.html +162 -162
  101. package/app/ui/board.js +305 -305
  102. package/app/ui/i18n.js +41 -2
  103. package/app/ui/icons/bee.svg +79 -79
  104. package/app/ui/index.html +8 -3
  105. package/app/ui/manifest.json +16 -16
  106. package/app/ui/qrcode.js +2297 -2297
  107. package/app/ui/style.css +74 -26
  108. package/bin/tutti.js +147 -147
  109. package/package.json +48 -47
  110. package/tools/wx_reader.py +261 -261
  111. package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +0 -88
  112. package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +0 -126
  113. package/app/.playwright-cli/page-2026-09-23T08-29-44-203Z.png +0 -0
  114. package/app/.playwright-cli/page-2026-09-23T08-35-42-046Z.yml +0 -210
  115. package/app/.playwright-cli/page-2026-09-23T08-36-52-858Z.png +0 -0
  116. package/app/.playwright-cli/page-2026-09-23T08-37-33-938Z.png +0 -0
  117. package/app/.playwright-cli/run-detail-after.png +0 -0
  118. package/app//344/275/234/345/223/201/344/277/241/346/201/257-/344/270/203/347/214/253.md +0 -18
package/app/ui/style.css CHANGED
@@ -356,6 +356,9 @@ body {
356
356
  *::-webkit-scrollbar-track { background: transparent; }
357
357
 
358
358
  /* ---------------- 布局骨架 ---------------- */
359
+ /* ---------------- 布局骨架 ---------------- */
360
+ /* ⚠️ 此层列宽已被后发皮肤层(「46px rail + 216px project column」)整体覆盖,
361
+ 侧栏宽度真源在那一层的 --side-w;此处保留原始骨架注释性记录 */
359
362
  #app { display: grid; grid-template-columns: 264px 1fr; height: 100vh; }
360
363
  body.side-collapsed #app { grid-template-columns: 0 1fr; }
361
364
  #mainwrap { display: flex; flex-direction: column; min-width: 0; height: 100vh; }
@@ -366,8 +369,24 @@ body.side-collapsed #app { grid-template-columns: 0 1fr; }
366
369
  background: var(--sidebar); border-right: 1px solid var(--border);
367
370
  padding: 14px 12px 12px; min-width: 0; overflow: hidden;
368
371
  transition: opacity .15s;
372
+ } padding: 14px 12px 12px; min-width: 0; overflow: hidden;
373
+ transition: opacity .15s;
374
+ position: relative;
369
375
  }
370
376
  body.side-collapsed #sidebar { border-right: none; padding-left: 0; padding-right: 0; opacity: 0; pointer-events: none; }
377
+ body.side-collapsed #sidebar { border-right: none; padding-left: 0; padding-right: 0; opacity: 0; pointer-events: none; }
378
+ /* 右缘拖拽手柄:贴右缘 7px 热区,hover/拖动中亮竖条;手柄属 sidebar 内,不受内容滚动影响 */
379
+ #side-resizer {
380
+ position: absolute; top: 0; bottom: 0; right: 0; width: 7px;
381
+ cursor: col-resize; z-index: 20; touch-action: none;
382
+ }
383
+ #side-resizer::after {
384
+ content: ""; position: absolute; top: 0; bottom: 0; right: 0; width: 3px;
385
+ background: transparent; transition: background .15s;
386
+ }
387
+ #side-resizer:hover::after, body.side-resizing #side-resizer::after { background: var(--accent); opacity: .5; }
388
+ body.side-resizing { cursor: col-resize; user-select: none; }
389
+ body.side-resizing iframe { pointer-events: none; }
371
390
  /* 品牌:方形字母标磁贴 + 字标,单行位于侧栏最顶上 */
372
391
  .brand { display: flex; align-items: center; gap: 10px; font-size: 15.5px; font-weight: 700; padding: 6px 8px 12px; white-space: nowrap; overflow: hidden; }
373
392
  /* 品牌:Codex 式低调字标——纯正文色小字,无磁贴无渐变(2026-09-15 参考 Codex 桌面端定版) */
@@ -683,12 +702,15 @@ main { flex: 1; overflow-y: auto; padding: 28px 32px 96px; }
683
702
  * 统一无框化:内容直接落在背景上,靠 panel-head 分隔线分节;
684
703
  * 内部小卡片(.mgmt-panel/.opanel/.cards 卡片等)不受影响,仍保留边框 */
685
704
  main .panel.wide { background: transparent; border: none; box-shadow: none; }
705
+ /* space-between 会把标题后的 ? 帮助钮当成独立一项推去行尾/行中——
706
+ * 改成起排 + .actions 自推右侧:标题旁的元素(? 问号、计数徽标)贴文字走 */
686
707
  .panel-head {
687
- display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap;
708
+ display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
688
709
  margin: 0 0 16px; padding-bottom: 12px; border-bottom: 1px solid var(--border);
689
710
  }
690
711
  .panel-head h2 { margin: 0; }
691
- .panel-head .actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
712
+ .panel-head .actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-left: auto; }
713
+ .panel-head > .spacer { flex: 1; }
692
714
  h2 { font-size: 15.5px; margin: 0 0 12px; font-weight: 700; }
693
715
  h3 { font-size: 13px; margin: 18px 0 8px; color: var(--muted); }
694
716
  /* 面板内分节标题:小号大写字距 + 贯穿分隔线 */
@@ -868,23 +890,24 @@ button.small { padding: 4px 10px; font-size: 12px; }
868
890
  .route b { color: var(--text); }
869
891
 
870
892
  .steps { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
893
+ /* 弹性行而非网格:行内子元素数量可变(st-model/st-prov 徽章有无),网格定死
894
+ * 7 列会把 chip+日志按钮挤到隐含第二行,「运行中」掉进 30px 窄列一字一行竖排 */
871
895
  .step {
872
- display: grid; grid-template-columns: 30px 92px 118px minmax(0, 1fr) auto auto auto;
873
- align-items: center; gap: 12px;
896
+ display: flex; align-items: center; gap: 12px;
874
897
  background: var(--panel2); border: 1px solid transparent; border-radius: var(--r-md);
875
898
  padding: 10px 14px; cursor: pointer; font-size: 13px;
876
899
  transition: border-color .15s;
877
900
  }
878
901
  .step:hover { border-color: var(--border-strong); }
879
902
  .step .n {
880
- width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center;
903
+ flex: none; width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center;
881
904
  background: var(--panel); border: 1px solid var(--border);
882
905
  font-family: var(--mono); font-size: 11px; color: var(--muted);
883
906
  }
884
- .step .role { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
885
- .step .who { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
886
- .step .sum { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
887
- .step .dur { color: var(--muted); font-size: 12px; font-family: var(--mono); }
907
+ .step .role { flex: 0 0 92px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
908
+ .step .who { flex: 0 0 118px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
909
+ .step .sum { flex: 1 1 auto; min-width: 0; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
910
+ .step .dur { flex: none; color: var(--muted); font-size: 12px; font-family: var(--mono); }
888
911
  .step-log-btn {
889
912
  border: 1px solid var(--border); border-radius: 999px; background: transparent;
890
913
  color: var(--muted); padding: 3px 8px; font-size: 11px; line-height: 1.2;
@@ -1214,6 +1237,8 @@ body.inspector-open #inspector { display: flex; }
1214
1237
  @media (max-width: 900px) {
1215
1238
  #app { grid-template-columns: 1fr; }
1216
1239
  body.side-collapsed #app { grid-template-columns: 1fr; }
1240
+ body.side-collapsed #app { grid-template-columns: 1fr; }
1241
+ #side-resizer { display: none; }
1217
1242
  #sidebar {
1218
1243
  position: fixed; z-index: 60; top: 0; left: 0; bottom: 0;
1219
1244
  width: min(300px, 84vw);
@@ -2768,6 +2793,9 @@ details.sf-dir > summary i, a.sf-file i { font-style: normal; color: var(--muted
2768
2793
  .bm-head .ico { color: var(--accent); width: 16px; height: 16px; }
2769
2794
  .bm-head .hint { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
2770
2795
  .bm-warn { color: var(--warn); font-size: 12px; flex: none; }
2796
+ .bm-inherit { flex: none; font-size: 12px; color: var(--muted);
2797
+ border: 1px solid var(--border-strong); border-radius: 999px; padding: 1px 9px;
2798
+ max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2771
2799
  .bm-guide { display: flex; gap: 12px; align-items: flex-start; padding: 14px 18px; margin: 12px 0 16px;
2772
2800
  border: 1px dashed var(--border-strong); border-radius: var(--r-md); background: none; }
2773
2801
  .bm-guide .ico { flex: none; margin-top: 2px; width: 18px; height: 18px; color: var(--accent); }
@@ -4641,8 +4669,8 @@ main:has(> #page-settings > #sub-runs:not(.hidden)) { padding-bottom: 28px; }
4641
4669
  }
4642
4670
  #sub-tasks #att-chips:empty { display: none; }
4643
4671
  #sub-tasks .att-chip.att-image {
4644
- position: relative; width: 88px; height: 88px; max-width: none; padding: 0;
4645
- overflow: visible; border-radius: 12px; background: var(--panel2);
4672
+ position: relative; width: 56px; height: 56px; max-width: none; padding: 0;
4673
+ overflow: visible; border-radius: 10px; background: var(--panel2);
4646
4674
  border-color: var(--border); box-shadow: 0 2px 10px color-mix(in srgb, #000 10%, transparent);
4647
4675
  }
4648
4676
  #sub-tasks .att-chip.att-image img {
@@ -4654,13 +4682,13 @@ main:has(> #page-settings > #sub-runs:not(.hidden)) { padding-bottom: 28px; }
4654
4682
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
4655
4683
  }
4656
4684
  #sub-tasks .att-chip.att-image .ax {
4657
- position: absolute; z-index: 2; top: -6px; right: -6px;
4685
+ position: absolute; z-index: 2; top: -5px; right: -5px;
4658
4686
  display: inline-flex; align-items: center; justify-content: center;
4659
- width: 20px; height: 20px; padding: 0; border: 1px solid var(--panel);
4687
+ width: 18px; height: 18px; padding: 0; border: 1px solid var(--panel);
4660
4688
  border-radius: 50%; background: var(--text); color: var(--panel);
4661
4689
  box-shadow: 0 2px 6px color-mix(in srgb, #000 18%, transparent);
4662
4690
  }
4663
- #sub-tasks .att-chip.att-image .ax .ico { width: 11px; height: 11px; }
4691
+ #sub-tasks .att-chip.att-image .ax .ico { width: 10px; height: 10px; }
4664
4692
  #sub-tasks .att-chip.att-image .ax:hover,
4665
4693
  #sub-tasks .att-chip.att-image .ax:focus-visible { background: var(--bad); color: #fff; outline: none; }
4666
4694
  #sub-tasks .cmp-box > .att-chips + textarea { padding-top: 8px; }
@@ -4670,7 +4698,7 @@ main:has(> #page-settings > #sub-runs:not(.hidden)) { padding-bottom: 28px; }
4670
4698
  left: 8px; right: 8px; width: auto; top: calc(100% + 2px);
4671
4699
  }
4672
4700
  #run-detail .rd-meta-popover #rd-overview { grid-template-columns: minmax(0, 1fr) auto; }
4673
- #sub-tasks .att-chip.att-image { width: 82px; height: 82px; }
4701
+ #sub-tasks .att-chip.att-image { width: 56px; height: 56px; }
4674
4702
  }
4675
4703
 
4676
4704
  /* ================= 详情页侧边栏重构 =================
@@ -5034,9 +5062,15 @@ main.chat-fill #run-detail.chat-mode > .rd-main { overflow: hidden; }
5034
5062
  background: var(--panel); /* 浮卡实底,不透出底下时间线 */
5035
5063
  box-shadow: 0 18px 44px rgba(6, 18, 35, .16), 0 2px 10px rgba(6, 18, 35, .08);
5036
5064
  }
5065
+ /* 对话柱向左加宽(用户 2026-09-29):给浮卡让位的空白不再两侧对称预留,
5066
+ 只在右缘留一份(版心右距 = 卡宽 + 2×间隙)——柱体在「版心左缘↔浮卡」
5067
+ 之间自动居中,吃满原先空旷的左半边;宽帽 960→1120 */
5068
+ #run-detail.chat-mode .rd-pane[data-pane="chat"] {
5069
+ padding-right: calc(var(--rd-card-w) + 2 * var(--rd-card-gap));
5070
+ }
5037
5071
  #run-detail.chat-mode .rd-chat-flow,
5038
5072
  #run-detail.chat-mode .rd-chat-bar {
5039
- max-width: min(960px, calc(100% - 2 * (var(--rd-card-w) + var(--rd-card-gap))));
5073
+ max-width: min(1120px, 100%);
5040
5074
  }
5041
5075
  /* 对话模式里按需打开的其它分区(步骤/蜂巢…):右侧整体给浮卡让位 */
5042
5076
  #run-detail.chat-mode .rd-pane:not([data-pane="chat"]) {
@@ -5963,23 +5997,24 @@ button.request-busy > * { opacity: .18; }
5963
5997
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
5964
5998
  #mk-grid.mk-grouped .sec-title { margin: 16px 0 8px; }
5965
5999
 
5966
- /* 首页 composer 图片附件缩略图:与任务页同款 88×88 卡(此前样式只写在
6000
+ /* 首页 composer 图片附件缩略图:与任务页同款 56×56 紧凑缩略卡(此前样式只写在
5967
6001
  * #sub-tasks 作用域,首页 img 按原始尺寸渲染直接压住输入文字——2026-09-21
5968
- * 用户实测「有遮挡」)。删除钮右上角悬浮。 */
5969
- .att-chip.att-image { position: relative; width: 88px; height: 88px; max-width: none;
5970
- padding: 0; overflow: visible; border-radius: 12px; background: var(--panel2);
6002
+ * 用户实测「有遮挡」;2026-09-29 88×88 仍嫌太大挡视野,收紧到 56)。
6003
+ * 删除钮右上角悬浮。 */
6004
+ .att-chip.att-image { position: relative; width: 56px; height: 56px; max-width: none;
6005
+ padding: 0; overflow: visible; border-radius: 10px; background: var(--panel2);
5971
6006
  border-color: var(--border); box-shadow: 0 2px 10px color-mix(in srgb, #000 10%, transparent); }
5972
6007
  .att-chip.att-image img { display: block; width: 100%; height: 100%; object-fit: cover;
5973
6008
  border-radius: inherit; background: var(--panel2); }
5974
6009
  .att-chip.att-image .att-image-name { position: absolute; width: 1px; height: 1px;
5975
6010
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0);
5976
6011
  white-space: nowrap; border: 0; }
5977
- .att-chip.att-image .ax { position: absolute; z-index: 2; top: -6px; right: -6px;
5978
- display: inline-flex; align-items: center; justify-content: center; width: 20px;
5979
- height: 20px; padding: 0; border: 1px solid var(--panel); border-radius: 50%;
6012
+ .att-chip.att-image .ax { position: absolute; z-index: 2; top: -5px; right: -5px;
6013
+ display: inline-flex; align-items: center; justify-content: center; width: 18px;
6014
+ height: 18px; padding: 0; border: 1px solid var(--panel); border-radius: 50%;
5980
6015
  background: var(--panel); color: var(--muted); }
5981
6016
  .att-chip.att-image .ax:hover { background: var(--bad); color: #fff; }
5982
- .att-chip.att-image .ax .ico { width: 11px; height: 11px; }
6017
+ .att-chip.att-image .ax .ico { width: 10px; height: 10px; }
5983
6018
 
5984
6019
  /* 对话气泡图片附件缩略图(2026-09-21 用户反馈:图片只显示文件名要点开才见):
5985
6020
  * 96×96 圆角卡,点击仍走 att-open 弹大图 */
@@ -6623,7 +6658,9 @@ main { font-size: 14px; }
6623
6658
  #f-goal::placeholder { color: color-mix(in srgb, var(--muted) 78%, #fff); }
6624
6659
 
6625
6660
  /* Match the reference proportions: 46px rail + 216px project column. */
6626
- #app { grid-template-columns: 46px 216px minmax(0, 1fr); }
6661
+ #app { grid-template-columns: 46px 216px minmax(0, 1fr); }/* Match the reference proportions: 46px rail + 216px project column.
6662
+ 侧栏列宽走 --side-w(#side-resizer 拖动实时改,localStorage 记忆)。 */
6663
+ #app { grid-template-columns: 46px var(--side-w, 216px) minmax(0, 1fr); }
6627
6664
  body.side-collapsed #app { grid-template-columns: 46px 0 minmax(0, 1fr); }
6628
6665
  #sidebar { padding: 13px 0 10px; }
6629
6666
  .brand { width: 100%; padding: 4px 12px 12px; justify-content: flex-start; text-align: left; }
@@ -6687,7 +6724,7 @@ body.side-collapsed #app { grid-template-columns: 46px 0 minmax(0, 1fr); }
6687
6724
  Keep the rail and project column in the grid when the right detail panel opens;
6688
6725
  the older three-column rule put the inspector on a new row and squeezed the main area. */
6689
6726
  body.inspector-open #app {
6690
- grid-template-columns: 46px 216px minmax(0, 1fr) minmax(280px, 340px);
6727
+ grid-template-columns: 46px 216px minmax(0, 1fr) minmax(280px, 340px); grid-template-columns: 46px var(--side-w, 216px) minmax(0, 1fr) minmax(280px, 340px);
6691
6728
  }
6692
6729
  body.side-collapsed.inspector-open #app {
6693
6730
  grid-template-columns: 46px 0 minmax(0, 1fr) minmax(280px, 340px);
@@ -7010,3 +7047,14 @@ main.browser-page-active #page-settings > #sub-browser .browser-shell {
7010
7047
  .browser-home-workflows { grid-template-columns: 1fr; }
7011
7048
  .browser-home-action { min-height: 72px; }
7012
7049
  }
7050
+
7051
+ /* 步骤行 flex 化后的防压缩补充(追加于文件尾防并行重排):徽章/按钮不许被
7052
+ * 挤压收缩,摘要列吸收余量——运行中行(无摘要/耗时)状态 chip 与 CLI 日志
7053
+ * 按钮才能稳定贴右单行,与完成行同构 */
7054
+ .step .st-model, .step .st-prov, .step .step-log-btn { flex: none; }
7055
+
7056
+ /* 供应商/模型状态过滤 chips(追加于文件尾防并行重排):默认只看启用,
7057
+ * 停用的点「已停用/全部」才现身;复用 .cat-chip 药丸,窄侧栏收紧行距 */
7058
+ .prov-status { margin: 2px 0 8px; }
7059
+ .prov-status .cat-chip { padding: 3px 9px; font-size: 12px; }
7060
+ .pm-status { margin: 10px 0 2px; }
package/bin/tutti.js CHANGED
@@ -1,147 +1,147 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
- /*
4
- * CodeBee 启动垫片:在本机找 Python 3.8+,以子进程方式运行包内 app/main.py。
5
- * npm 只负责分发与版本管理,不携带解释器——所以运行时才探测 python,
6
- * 找不到就报错并给出安装指引(不写 postinstall,避免在别人机器上制造环境事故)。
7
- *
8
- * 安全边界:
9
- * - 子进程可执行文件全部是本文件内的字面量(py / python / python3)——包括
10
- * TUTTI_PYTHON 覆盖时:它只允许在这三个字面量里选一个,绝不拼接任意路径;
11
- * - 参数数组全由字面量常量拼成,不启用 shell;
12
- * - 探测只传 `--version`(不向解释器传递任何代码);Windows 商店的 python.exe
13
- * 假占位会非零退出,被 catch 后自然跳过;
14
- * - 用户参数逐个过白名单校验(仅字母数字与 _ - . : = / @ % 空格),
15
- * 含 shell 元字符/控制字符的参数直接拒绝。
16
- */
17
- const { execFileSync, spawn } = require('child_process');
18
- const path = require('path');
19
-
20
- const MIN_MAJOR = 3;
21
- const MIN_MINOR = 8;
22
- const MAIN = path.join(__dirname, '..', 'app', 'main.py');
23
- const SAFE_ARG = /^[A-Za-z0-9_\-.:=/@% ]+$/;
24
-
25
- // 解析 `--version` 输出:形如 "Python 3.12.4",返回 {ver, major, minor} 或 null。
26
- // 纯字符串切分,不用正则匹配执行路径。
27
- function parseVersion(text) {
28
- const at = String(text).indexOf('Python ');
29
- if (at === -1) return null;
30
- const parts = String(text).slice(at + 7).trim().split(' ')[0].split('.');
31
- const major = Number(parts[0]);
32
- const minor = Number(parts[1]);
33
- if (!Number.isFinite(major) || !Number.isFinite(minor)) return null;
34
- return { ver: major + '.' + minor, major: major, minor: minor };
35
- }
36
-
37
- function probePy() {
38
- try {
39
- return parseVersion(execFileSync('py', ['-3', '--version'],
40
- { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }));
41
- } catch (e) { return null; }
42
- }
43
- function probePython() {
44
- try {
45
- return parseVersion(execFileSync('python', ['--version'],
46
- { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }));
47
- } catch (e) { return null; }
48
- }
49
- function probePython3() {
50
- try {
51
- return parseVersion(execFileSync('python3', ['--version'],
52
- { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }));
53
- } catch (e) { return null; }
54
- }
55
-
56
- // 探测顺序:win32 优先 py 启动器(跨版本选真 3.x),其余按 python3 → python。
57
- function resolvePython() {
58
- const order = process.platform === 'win32'
59
- ? [['py', probePy], ['python', probePython], ['python3', probePython3]]
60
- : [['python3', probePython3], ['python', probePython]];
61
- for (const [name, probe] of order) {
62
- const hit = probe();
63
- if (!hit) continue;
64
- if (hit.major > MIN_MAJOR || (hit.major === MIN_MAJOR && hit.minor >= MIN_MINOR)) {
65
- return { name: name };
66
- }
67
- return { tooOld: true, name: name, ver: hit.ver };
68
- }
69
- return null;
70
- }
71
-
72
- function guide() {
73
- const url = { win32: 'https://www.python.org/downloads/windows/',
74
- darwin: 'https://www.python.org/downloads/macos/',
75
- linux: 'https://docs.python.org/3/using/unix.html' }[process.platform]
76
- || 'https://www.python.org/downloads/';
77
- return 'CodeBee 需要本机安装 Python 3.8+(npm 包不含解释器)。\n'
78
- + ' · 安装:' + url + '\n'
79
- + ' · Windows 安装时勾选 “Add python.exe to PATH”(自带 py 启动器)\n'
80
- + '装好后重新运行:codebee';
81
- }
82
-
83
- const py = resolvePython();
84
- if (!py) {
85
- console.error('[CodeBee] 未找到可用的 Python。\n' + guide());
86
- process.exit(1);
87
- }
88
- if (py.tooOld) {
89
- console.error('[CodeBee] 找到 ' + py.name + '(' + py.ver + '),但版本过低。\n' + guide());
90
- process.exit(1);
91
- }
92
-
93
- const userArgs = [];
94
- for (const a of process.argv.slice(2)) {
95
- if (typeof a === 'string' && SAFE_ARG.test(a)) {
96
- userArgs.push(a);
97
- } else {
98
- console.error('[CodeBee] 参数含非法字符,已拒绝:' + String(a).slice(0, 80));
99
- process.exit(2);
100
- }
101
- }
102
-
103
- // CLI 转发契约:codebee 之后的用户参数(已过白名单)原样作为 main.py 的 argv。
104
- // 控制台日志即见:GBK 控制台下强制 UTF-8 输出(否则中文 print 触发 UnicodeEncodeError 静默卡死)。
105
- const childEnv = Object.assign({}, process.env, { PYTHONIOENCODING: 'utf-8', PYTHONUTF8: '1', PYTHONUNBUFFERED: '1' });
106
- // TUTTI_PYTHON 覆盖解释器:只允许在三个字面量里选(py/python/python3,.exe
107
- // 后缀可选、大小写不限)——环境变量永远只做「选择题」,不做「填空题」;
108
- // 机器上有多个解释器且探测选错时,用它切换。
109
- let exe = py.name;
110
- const ovRaw = String(process.env.TUTTI_PYTHON || '').trim().toLowerCase();
111
- const ov = ovRaw.endsWith('.exe') ? ovRaw.slice(0, -4) : ovRaw;
112
- if (ov === 'python') {
113
- exe = 'python';
114
- } else if (ov === 'python3') {
115
- exe = 'python3';
116
- } else if (ov === 'py') {
117
- exe = 'py';
118
- }
119
-
120
- let child;
121
- if (exe === 'py') {
122
- child = spawn('py', ['-3', MAIN, ...userArgs], { stdio: 'inherit', env: childEnv });
123
- } else if (exe === 'python') {
124
- child = spawn('python', [MAIN, ...userArgs], { stdio: 'inherit', env: childEnv });
125
- } else {
126
- child = spawn('python3', [MAIN, ...userArgs], { stdio: 'inherit', env: childEnv });
127
- }
128
- console.log('[CodeBee] 正在启动(Python: ' + exe + ')… 浏览器将自动打开 http://127.0.0.1:8765;'
129
- + '本窗口保持开着就是服务,Ctrl+C 退出。');
130
-
131
- for (const sig of ['SIGINT', 'SIGTERM']) {
132
- process.on(sig, () => { try { child.kill(sig); } catch (e) { /* 已退出 */ } });
133
- }
134
- const t0 = Date.now();
135
- child.on('exit', (code, signal) => {
136
- if (!signal && code != null && code !== 0 && Date.now() - t0 < 4000) {
137
- // 秒退:多半是 py 启动器指到了坏解释器(如损坏的 Anaconda)。给排障出口。
138
- console.error('[CodeBee] 启动失败:Python 进程 ' + (code ? '退出码 ' + code : '异常终止') + '。'
139
- + '\n · 运行 python --version 确认可用且为 3.8+'
140
- + '\n · 若装有多个 Python,可设环境变量 TUTTI_PYTHON 切换(cmd:set TUTTI_PYTHON=python)');
141
- }
142
- process.exit(signal ? 1 : (code == null ? 0 : code));
143
- });
144
- child.on('error', (e) => {
145
- console.error('[CodeBee] 无法启动 Python:' + e.message);
146
- process.exit(1);
147
- });
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /*
4
+ * CodeBee 启动垫片:在本机找 Python 3.8+,以子进程方式运行包内 app/main.py。
5
+ * npm 只负责分发与版本管理,不携带解释器——所以运行时才探测 python,
6
+ * 找不到就报错并给出安装指引(不写 postinstall,避免在别人机器上制造环境事故)。
7
+ *
8
+ * 安全边界:
9
+ * - 子进程可执行文件全部是本文件内的字面量(py / python / python3)——包括
10
+ * TUTTI_PYTHON 覆盖时:它只允许在这三个字面量里选一个,绝不拼接任意路径;
11
+ * - 参数数组全由字面量常量拼成,不启用 shell;
12
+ * - 探测只传 `--version`(不向解释器传递任何代码);Windows 商店的 python.exe
13
+ * 假占位会非零退出,被 catch 后自然跳过;
14
+ * - 用户参数逐个过白名单校验(仅字母数字与 _ - . : = / @ % 空格),
15
+ * 含 shell 元字符/控制字符的参数直接拒绝。
16
+ */
17
+ const { execFileSync, spawn } = require('child_process');
18
+ const path = require('path');
19
+
20
+ const MIN_MAJOR = 3;
21
+ const MIN_MINOR = 8;
22
+ const MAIN = path.join(__dirname, '..', 'app', 'main.py');
23
+ const SAFE_ARG = /^[A-Za-z0-9_\-.:=/@% ]+$/;
24
+
25
+ // 解析 `--version` 输出:形如 "Python 3.12.4",返回 {ver, major, minor} 或 null。
26
+ // 纯字符串切分,不用正则匹配执行路径。
27
+ function parseVersion(text) {
28
+ const at = String(text).indexOf('Python ');
29
+ if (at === -1) return null;
30
+ const parts = String(text).slice(at + 7).trim().split(' ')[0].split('.');
31
+ const major = Number(parts[0]);
32
+ const minor = Number(parts[1]);
33
+ if (!Number.isFinite(major) || !Number.isFinite(minor)) return null;
34
+ return { ver: major + '.' + minor, major: major, minor: minor };
35
+ }
36
+
37
+ function probePy() {
38
+ try {
39
+ return parseVersion(execFileSync('py', ['-3', '--version'],
40
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }));
41
+ } catch (e) { return null; }
42
+ }
43
+ function probePython() {
44
+ try {
45
+ return parseVersion(execFileSync('python', ['--version'],
46
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }));
47
+ } catch (e) { return null; }
48
+ }
49
+ function probePython3() {
50
+ try {
51
+ return parseVersion(execFileSync('python3', ['--version'],
52
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }));
53
+ } catch (e) { return null; }
54
+ }
55
+
56
+ // 探测顺序:win32 优先 py 启动器(跨版本选真 3.x),其余按 python3 → python。
57
+ function resolvePython() {
58
+ const order = process.platform === 'win32'
59
+ ? [['py', probePy], ['python', probePython], ['python3', probePython3]]
60
+ : [['python3', probePython3], ['python', probePython]];
61
+ for (const [name, probe] of order) {
62
+ const hit = probe();
63
+ if (!hit) continue;
64
+ if (hit.major > MIN_MAJOR || (hit.major === MIN_MAJOR && hit.minor >= MIN_MINOR)) {
65
+ return { name: name };
66
+ }
67
+ return { tooOld: true, name: name, ver: hit.ver };
68
+ }
69
+ return null;
70
+ }
71
+
72
+ function guide() {
73
+ const url = { win32: 'https://www.python.org/downloads/windows/',
74
+ darwin: 'https://www.python.org/downloads/macos/',
75
+ linux: 'https://docs.python.org/3/using/unix.html' }[process.platform]
76
+ || 'https://www.python.org/downloads/';
77
+ return 'CodeBee 需要本机安装 Python 3.8+(npm 包不含解释器)。\n'
78
+ + ' · 安装:' + url + '\n'
79
+ + ' · Windows 安装时勾选 “Add python.exe to PATH”(自带 py 启动器)\n'
80
+ + '装好后重新运行:codebee';
81
+ }
82
+
83
+ const py = resolvePython();
84
+ if (!py) {
85
+ console.error('[CodeBee] 未找到可用的 Python。\n' + guide());
86
+ process.exit(1);
87
+ }
88
+ if (py.tooOld) {
89
+ console.error('[CodeBee] 找到 ' + py.name + '(' + py.ver + '),但版本过低。\n' + guide());
90
+ process.exit(1);
91
+ }
92
+
93
+ const userArgs = [];
94
+ for (const a of process.argv.slice(2)) {
95
+ if (typeof a === 'string' && SAFE_ARG.test(a)) {
96
+ userArgs.push(a);
97
+ } else {
98
+ console.error('[CodeBee] 参数含非法字符,已拒绝:' + String(a).slice(0, 80));
99
+ process.exit(2);
100
+ }
101
+ }
102
+
103
+ // CLI 转发契约:codebee 之后的用户参数(已过白名单)原样作为 main.py 的 argv。
104
+ // 控制台日志即见:GBK 控制台下强制 UTF-8 输出(否则中文 print 触发 UnicodeEncodeError 静默卡死)。
105
+ const childEnv = Object.assign({}, process.env, { PYTHONIOENCODING: 'utf-8', PYTHONUTF8: '1', PYTHONUNBUFFERED: '1' });
106
+ // TUTTI_PYTHON 覆盖解释器:只允许在三个字面量里选(py/python/python3,.exe
107
+ // 后缀可选、大小写不限)——环境变量永远只做「选择题」,不做「填空题」;
108
+ // 机器上有多个解释器且探测选错时,用它切换。
109
+ let exe = py.name;
110
+ const ovRaw = String(process.env.TUTTI_PYTHON || '').trim().toLowerCase();
111
+ const ov = ovRaw.endsWith('.exe') ? ovRaw.slice(0, -4) : ovRaw;
112
+ if (ov === 'python') {
113
+ exe = 'python';
114
+ } else if (ov === 'python3') {
115
+ exe = 'python3';
116
+ } else if (ov === 'py') {
117
+ exe = 'py';
118
+ }
119
+
120
+ let child;
121
+ if (exe === 'py') {
122
+ child = spawn('py', ['-3', MAIN, ...userArgs], { stdio: 'inherit', env: childEnv });
123
+ } else if (exe === 'python') {
124
+ child = spawn('python', [MAIN, ...userArgs], { stdio: 'inherit', env: childEnv });
125
+ } else {
126
+ child = spawn('python3', [MAIN, ...userArgs], { stdio: 'inherit', env: childEnv });
127
+ }
128
+ console.log('[CodeBee] 正在启动(Python: ' + exe + ')… 浏览器将自动打开 http://127.0.0.1:8765;'
129
+ + '本窗口保持开着就是服务,Ctrl+C 退出。');
130
+
131
+ for (const sig of ['SIGINT', 'SIGTERM']) {
132
+ process.on(sig, () => { try { child.kill(sig); } catch (e) { /* 已退出 */ } });
133
+ }
134
+ const t0 = Date.now();
135
+ child.on('exit', (code, signal) => {
136
+ if (!signal && code != null && code !== 0 && Date.now() - t0 < 4000) {
137
+ // 秒退:多半是 py 启动器指到了坏解释器(如损坏的 Anaconda)。给排障出口。
138
+ console.error('[CodeBee] 启动失败:Python 进程 ' + (code ? '退出码 ' + code : '异常终止') + '。'
139
+ + '\n · 运行 python --version 确认可用且为 3.8+'
140
+ + '\n · 若装有多个 Python,可设环境变量 TUTTI_PYTHON 切换(cmd:set TUTTI_PYTHON=python)');
141
+ }
142
+ process.exit(signal ? 1 : (code == null ? 0 : code));
143
+ });
144
+ child.on('error', (e) => {
145
+ console.error('[CodeBee] 无法启动 Python:' + e.message);
146
+ process.exit(1);
147
+ });
package/package.json CHANGED
@@ -1,47 +1,48 @@
1
- {
2
- "name": "codebee",
3
- "version": "0.1.71",
4
- "description": "CodeBee · 多智能体编排台 — 统一编排本机 AI 编码 CLI(Codex / Claude Code / QwenCode / OpenCode / Aider…):目标→拆解→路由→执行→验证→跨厂商评审→修复→报告。纯 Python 标准库实现,本地 Web UI。",
5
- "license": "MIT",
6
- "bin": {
7
- "codebee": "bin/tutti.js"
8
- },
9
- "files": [
10
- "app",
11
- "!app/tests",
12
- "bin",
13
- "tools/wx_reader.py",
14
- "CHANGELOG.md",
15
- "README.md",
16
- "LICENSE"
17
- ],
18
- "engines": {
19
- "node": ">=14"
20
- },
21
- "os": [
22
- "win32",
23
- "darwin",
24
- "linux"
25
- ],
26
- "keywords": [
27
- "agent",
28
- "orchestration",
29
- "codex",
30
- "claude",
31
- "llm",
32
- "cli",
33
- "multi-agent"
34
- ],
35
- "scripts": {
36
- "release:gate": "python scripts/release_gate.py",
37
- "release": "python scripts/release_gate.py --publish",
38
- "prepublishOnly": "python scripts/release_gate.py"
39
- },
40
- "repository": {
41
- "type": "git",
42
- "url": "git+https://github.com/Vercel-By-WXP/CodeBee.git"
43
- },
44
- "publishConfig": {
45
- "access": "public"
46
- }
47
- }
1
+ {
2
+ "name": "codebee",
3
+ "version": "0.1.73",
4
+ "description": "CodeBee · 多智能体编排台 — 统一编排本机 AI 编码 CLI(Codex / Claude Code / QwenCode / OpenCode / Aider…):目标→拆解→路由→执行→验证→跨厂商评审→修复→报告。纯 Python 标准库实现,本地 Web UI。",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "codebee": "bin/tutti.js"
8
+ },
9
+ "files": [
10
+ "app",
11
+ "!app/tests",
12
+ "bin",
13
+ "tools/wx_reader.py",
14
+ "CHANGELOG.md",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
18
+ "engines": {
19
+ "node": ">=14"
20
+ },
21
+ "os": [
22
+ "win32",
23
+ "darwin",
24
+ "linux"
25
+ ],
26
+ "keywords": [
27
+ "agent",
28
+ "orchestration",
29
+ "codex",
30
+ "claude",
31
+ "llm",
32
+ "cli",
33
+ "multi-agent"
34
+ ],
35
+ "scripts": {
36
+ "release:gate": "python scripts/release_gate.py",
37
+ "release": "python scripts/release_gate.py --publish",
38
+ "prepublishOnly": "python scripts/release_gate.py",
39
+ "release:clean": "python scripts/prune_release_debris.py"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/Vercel-By-WXP/CodeBee.git"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ }
48
+ }