codebee 0.1.95 → 0.1.97
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 +733 -720
- package/LICENSE +21 -21
- package/README.md +516 -509
- package/app/core/acceptance.py +100 -100
- package/app/core/agent_context.py +118 -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 +246 -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 +2055 -1680
- package/app/core/capability.py +144 -144
- package/app/core/chaptersafe.py +32 -32
- package/app/core/checkpoints.py +648 -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/mcp_client.py +200 -29
- 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 +319 -59
- package/app/core/planner.py +17 -11
- package/app/core/policy.py +84 -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/project_memory.py +139 -0
- package/app/core/prompt_guard.py +78 -78
- package/app/core/publish/__init__.py +7 -7
- package/app/core/publish/auto.py +106 -22
- package/app/core/publish/browser.py +694 -655
- package/app/core/publish/chaprows.py +138 -0
- package/app/core/publish/fanqie.py +212 -165
- package/app/core/publish/flow.py +832 -640
- package/app/core/publish/flows-fanqie-calibrated.json +361 -336
- package/app/core/publish/ledger.py +51 -2
- 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 +2239 -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 +33 -5
- package/app/core/settings_schema.py +282 -271
- 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 +24 -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/tool_outputs.py +107 -0
- 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 +865 -858
- package/app/core/zentao.py +2692 -2692
- package/app/main.py +179 -10
- package/app/mcp_server.py +244 -244
- package/app/pick_dialog.py +110 -110
- package/app/ui/app.js +563 -21
- 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/assets/workbench-reference.png +0 -0
- package/app/ui/board.css +451 -666
- package/app/ui/board.html +165 -162
- package/app/ui/board.js +509 -305
- package/app/ui/hive3d.js +242 -0
- package/app/ui/i18n.js +125 -9
- package/app/ui/icons/bee.svg +79 -79
- package/app/ui/index.html +60 -3
- package/app/ui/manifest.json +16 -16
- package/app/ui/qrcode.js +2297 -2297
- package/app/ui/style.css +189 -9
- package/bin/tutti.js +147 -147
- package/package.json +49 -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/ui/style.css
CHANGED
|
@@ -344,6 +344,52 @@ html[data-skin="hermes"][data-theme="light"] {
|
|
|
344
344
|
--uc-ca: #b0763f;
|
|
345
345
|
--uc-out: #3d8f5f;
|
|
346
346
|
}
|
|
347
|
+
/* 简白(Codex 式极简):日间=纯白画布 + 浅灰分区 + 发丝分隔 + 克制蓝,素面无装饰;
|
|
348
|
+
* 夜间=近黑画布 + 深灰分区同构。装饰性渐变/网格在下方统一压掉(与 contrast 同款规则)。 */
|
|
349
|
+
html[data-skin="paper"] {
|
|
350
|
+
--bg: #0f0f0e;
|
|
351
|
+
--sidebar: #161615;
|
|
352
|
+
--panel: #1d1d1c;
|
|
353
|
+
--panel2: #272726;
|
|
354
|
+
--border: rgba(255, 255, 255, .10);
|
|
355
|
+
--border-strong: rgba(255, 255, 255, .22);
|
|
356
|
+
--text: #ecebe8;
|
|
357
|
+
--muted: #9a998f;
|
|
358
|
+
--accent: #6ea8fe;
|
|
359
|
+
--accent2: #94c1ff;
|
|
360
|
+
--ok: #3fbf8a;
|
|
361
|
+
--warn: #e2b34a;
|
|
362
|
+
--bad: #ef6a6a;
|
|
363
|
+
--chip-queued-bg: #272726;
|
|
364
|
+
--log-bg: #0b0b0a;
|
|
365
|
+
--log-text: #c9c8c2;
|
|
366
|
+
--shadow: 0 10px 30px rgba(0, 0, 0, .5);
|
|
367
|
+
--uc-in: #6ea8fe;
|
|
368
|
+
--uc-ca: #2fc6b1;
|
|
369
|
+
--uc-out: #e2b34a;
|
|
370
|
+
}
|
|
371
|
+
html[data-skin="paper"][data-theme="light"] {
|
|
372
|
+
--bg: #ffffff;
|
|
373
|
+
--sidebar: #f7f7f5;
|
|
374
|
+
--panel: #ffffff;
|
|
375
|
+
--panel2: #f5f5f3;
|
|
376
|
+
--border: #e8e8e4;
|
|
377
|
+
--border-strong: #d7d7d2;
|
|
378
|
+
--text: #21201e;
|
|
379
|
+
--muted: #87867f;
|
|
380
|
+
--accent: #2563eb;
|
|
381
|
+
--accent2: #1d4ed8;
|
|
382
|
+
--ok: #1a7f37;
|
|
383
|
+
--warn: #9a6700;
|
|
384
|
+
--bad: #cf222e;
|
|
385
|
+
--chip-queued-bg: #f5f5f3;
|
|
386
|
+
--log-bg: #f6f7f8;
|
|
387
|
+
--log-text: #3c3f43;
|
|
388
|
+
--shadow: 0 10px 30px rgba(0, 0, 0, .06);
|
|
389
|
+
--uc-in: #2563eb;
|
|
390
|
+
--uc-ca: #0d9488;
|
|
391
|
+
--uc-out: #d97706;
|
|
392
|
+
}
|
|
347
393
|
* { box-sizing: border-box; }
|
|
348
394
|
html, body { height: 100%; }
|
|
349
395
|
body {
|
|
@@ -369,8 +415,6 @@ body.side-collapsed #app { grid-template-columns: 0 1fr; }
|
|
|
369
415
|
background: var(--sidebar); border-right: 1px solid var(--border);
|
|
370
416
|
padding: 14px 12px 12px; min-width: 0; overflow: hidden;
|
|
371
417
|
transition: opacity .15s;
|
|
372
|
-
} padding: 14px 12px 12px; min-width: 0; overflow: hidden;
|
|
373
|
-
transition: opacity .15s;
|
|
374
418
|
position: relative;
|
|
375
419
|
}
|
|
376
420
|
body.side-collapsed #sidebar { border-right: none; padding-left: 0; padding-right: 0; opacity: 0; pointer-events: none; }
|
|
@@ -1235,9 +1279,8 @@ body.inspector-open #inspector { display: flex; }
|
|
|
1235
1279
|
/* 手机抽屉遮罩:抽屉展开时盖住内容区,点击关闭抽屉 */
|
|
1236
1280
|
#drawer-mask { display: none; }
|
|
1237
1281
|
@media (max-width: 900px) {
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
body.side-collapsed #app { grid-template-columns: 1fr; }
|
|
1282
|
+
/* 保留 42px 导航轨列:纯 1fr 单列会让 .side-rail(grid 首子)独占一行变顶部横条 */
|
|
1283
|
+
#app { grid-template-columns: 42px minmax(0, 1fr); }
|
|
1241
1284
|
#side-resizer { display: none; }
|
|
1242
1285
|
#sidebar {
|
|
1243
1286
|
position: fixed; z-index: 60; top: 0; left: 0; bottom: 0;
|
|
@@ -3142,7 +3185,10 @@ body {
|
|
|
3142
3185
|
var(--bg);
|
|
3143
3186
|
background-attachment: fixed;
|
|
3144
3187
|
}
|
|
3145
|
-
html[data-skin="contrast"] body
|
|
3188
|
+
html[data-skin="contrast"] body,
|
|
3189
|
+
html[data-skin="paper"] body { background-image: none; }
|
|
3190
|
+
/* 简白连角落的网格纹理一起收掉:纯白画布上任何底纹都是脏 */
|
|
3191
|
+
html[data-skin="paper"] body::before { content: none; }
|
|
3146
3192
|
|
|
3147
3193
|
/* 2) 品牌字标:主色字 + 底部发丝分隔线(仍是纯文字品牌,无任何图片) */
|
|
3148
3194
|
.brand-name { color: var(--accent); letter-spacing: .02em; }
|
|
@@ -3480,7 +3526,9 @@ button.primary:hover { filter: brightness(.96); }
|
|
|
3480
3526
|
|
|
3481
3527
|
@media (max-width: 860px) {
|
|
3482
3528
|
#app { grid-template-columns: 42px minmax(0, 1fr); }
|
|
3483
|
-
|
|
3529
|
+
/* z-index 必须压过 #drawer-mask(55):低于遮罩时抽屉整层被半透明遮罩盖住,
|
|
3530
|
+
* 抽屉里点什么都是点到遮罩(=关抽屉),主区也同时被盖——手机端「无法操作」实案 */
|
|
3531
|
+
#sidebar { position: fixed; z-index: 60; left: 42px; top: 0; bottom: 0; width: 228px; transform: translateX(0); box-shadow: 16px 0 34px rgba(30,40,50,.12); }
|
|
3484
3532
|
body.side-collapsed #app { grid-template-columns: 42px minmax(0, 1fr); }
|
|
3485
3533
|
body.side-collapsed #sidebar { transform: translateX(-110%); opacity: 0; }
|
|
3486
3534
|
main { padding: 20px 16px 58px; }
|
|
@@ -3833,6 +3881,21 @@ body.inspector-open #run-detail #rd-log.rd-logdrawer {
|
|
|
3833
3881
|
#run-detail .rd-pane[data-pane="bookmeta"] #rd-bookmeta {
|
|
3834
3882
|
padding-top: 4px;
|
|
3835
3883
|
}
|
|
3884
|
+
.rd-checkpoints, .rd-project-memory {
|
|
3885
|
+
margin: 8px 0 14px; padding: 12px 14px; border: 1px solid var(--border);
|
|
3886
|
+
border-radius: 12px; background: color-mix(in srgb, var(--panel) 72%, transparent);
|
|
3887
|
+
}
|
|
3888
|
+
.rd-checkpoints-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
|
|
3889
|
+
.rd-checkpoints-head strong { margin-right: auto; }
|
|
3890
|
+
.rd-checkpoint-row, .rd-memory-row { padding: 9px 0; border-top: 1px solid var(--border); }
|
|
3891
|
+
.rd-checkpoint-row > strong { margin-right: 8px; overflow-wrap: anywhere; }
|
|
3892
|
+
.rd-checkpoint-row pre, .rd-memory-row pre {
|
|
3893
|
+
max-height: 240px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere;
|
|
3894
|
+
margin: 8px 0 0; padding: 9px; border-radius: 8px; background: var(--bg);
|
|
3895
|
+
color: var(--text); font: 12px/1.5 ui-monospace, Consolas, monospace;
|
|
3896
|
+
}
|
|
3897
|
+
.rd-memory-row > div:first-child { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
3898
|
+
.rd-memory-actions { display: flex; gap: 6px; margin-top: 8px; }
|
|
3836
3899
|
#run-detail .rd-pane[data-pane="git"] #rd-git,
|
|
3837
3900
|
#run-detail .rd-pane[data-pane="bible"] #rd-bible,
|
|
3838
3901
|
#run-detail .rd-pane[data-pane="bookmeta"] #rd-bookmeta {
|
|
@@ -5109,6 +5172,9 @@ main.chat-fill #run-detail.chat-mode > .rd-main { overflow: hidden; }
|
|
|
5109
5172
|
max-height: none;
|
|
5110
5173
|
margin-bottom: 12px;
|
|
5111
5174
|
}
|
|
5175
|
+
/* 浮卡让位 padding 只属于 ≥980 的桌面浮卡布局:窄屏对话态吃满主区,
|
|
5176
|
+
32px 左右留白在 390 视口里白吃 64px(对话列只剩 284px)实案 */
|
|
5177
|
+
main.chat-fill { padding: 12px 14px; }
|
|
5112
5178
|
#rd-rail > .detail-head > .rd-actions #btn-delete {
|
|
5113
5179
|
grid-column: auto;
|
|
5114
5180
|
justify-self: stretch;
|
|
@@ -6583,7 +6649,8 @@ button.primary { background:var(--accent); box-shadow:0 3px 10px color-mix(in sr
|
|
|
6583
6649
|
button.primary:hover { filter:brightness(.96); }
|
|
6584
6650
|
@media (max-width:860px) {
|
|
6585
6651
|
#app, body.side-collapsed #app { grid-template-columns:42px minmax(0,1fr); }
|
|
6586
|
-
|
|
6652
|
+
/* z-index 必须压过 #drawer-mask(55):低于遮罩时抽屉整层被盖、手机端无法操作(同 3528 层注释) */
|
|
6653
|
+
#sidebar { position:fixed; z-index:60; left:42px; top:0; bottom:0; width:228px; transform:translateX(0); box-shadow:16px 0 34px rgba(30,40,50,.12); }
|
|
6587
6654
|
body.side-collapsed #sidebar { transform:translateX(-110%); opacity:0; }
|
|
6588
6655
|
main { padding:20px 16px 58px; }
|
|
6589
6656
|
.topbar { padding:9px 14px; }
|
|
@@ -6605,7 +6672,7 @@ button.primary:hover { filter:brightness(.96); }
|
|
|
6605
6672
|
inherit ocean/forest/amber/violet/contrast choices instead of imposing a new
|
|
6606
6673
|
brand color. */
|
|
6607
6674
|
html[data-skin="ocean"][data-theme="light"] {
|
|
6608
|
-
--bg: #
|
|
6675
|
+
--bg: #ffffff; --sidebar: #eff5fd; --panel: #fff; --panel2: #f0f6fd;
|
|
6609
6676
|
--border: #d7e3f4; --border-strong: #b9cde8; --text: #0c1a2c; --muted: #526983;
|
|
6610
6677
|
--accent: #1f6fd0; --accent2: #5b4fe0; --ok: #0d9a72; --warn: #a9761a;
|
|
6611
6678
|
--bad: #d23c58; --chip-queued-bg: #f0f6fd; --log-bg: #f4f8fe; --log-text: #33475f;
|
|
@@ -7096,3 +7163,116 @@ main.browser-page-active #page-settings > #sub-browser .browser-shell {
|
|
|
7096
7163
|
font-size: 12.5px; line-height: 1.65; white-space: pre-wrap; word-break: break-word;
|
|
7097
7164
|
max-height: 180px; overflow: auto; color: var(--text);
|
|
7098
7165
|
}
|
|
7166
|
+
|
|
7167
|
+
/* 关于页「交流与支持」二维码卡(追加尾防并行重排):白底垫卡保证暗色皮肤下
|
|
7168
|
+
* 黑白二维码仍清晰,两卡并排窄屏自动换行。 */
|
|
7169
|
+
.qr-row { display: flex; flex-wrap: wrap; gap: 14px; margin: 8px 0 4px; }
|
|
7170
|
+
.qr-card {
|
|
7171
|
+
margin: 0; background: #fff; border: 1px solid var(--border); border-radius: 10px;
|
|
7172
|
+
padding: 8px 8px 4px; text-align: center;
|
|
7173
|
+
}
|
|
7174
|
+
.qr-card img { display: block; width: 132px; height: auto; border-radius: 6px; }
|
|
7175
|
+
.qr-card .hint { margin: 4px 0 2px; color: var(--muted); max-width: 140px; }
|
|
7176
|
+
|
|
7177
|
+
/* Reference office: artwork and live screens always share one aspect ratio. */
|
|
7178
|
+
#rd-hive .sec-title { margin-bottom: 10px; }
|
|
7179
|
+
.hive-scene-tools { display: flex; align-items: center; gap: 7px; min-height: 38px; margin-bottom: 8px; color: var(--muted); font-size: 11px; }
|
|
7180
|
+
.hive-view-switch { display: inline-flex; gap: 2px; padding: 3px; border: 1px solid var(--border); border-radius: 9px; background: var(--panel); }
|
|
7181
|
+
.hive-view-btn, .hive-scene-btn {
|
|
7182
|
+
min-height: 28px; padding: 4px 10px; border: 1px solid transparent; border-radius: 6px;
|
|
7183
|
+
background: transparent; color: var(--muted); font: inherit; cursor: pointer;
|
|
7184
|
+
}
|
|
7185
|
+
.hive-view-btn:hover, .hive-scene-btn:hover { color: var(--text); background: color-mix(in srgb, var(--accent) 8%, transparent); }
|
|
7186
|
+
.hive-view-btn.active { border-color: color-mix(in srgb, var(--accent) 32%, var(--border)); background: color-mix(in srgb, var(--accent) 12%, var(--panel)); color: var(--accent); }
|
|
7187
|
+
.hive-scene-hint { margin-left: 8px; }
|
|
7188
|
+
.hive-scene-spacer { flex: 1; }
|
|
7189
|
+
.hive-scene-btn { min-width: 31px; border-color: var(--border); background: var(--panel); font-size: 13px; }
|
|
7190
|
+
.hive-scene-btn:disabled { opacity: .4; cursor: default; }
|
|
7191
|
+
.hive-scene-reset { font-size: 11px; }
|
|
7192
|
+
.hive-scene-pager { display: flex; align-items: center; gap: 5px; }
|
|
7193
|
+
.hive-scene-pager[hidden] { display: none; }
|
|
7194
|
+
.hive-scene-page-label { white-space: nowrap; font-family: var(--mono); }
|
|
7195
|
+
.hive-viewport {
|
|
7196
|
+
position: relative; width: 100%; aspect-ratio: 2848 / 1600; overflow: hidden;
|
|
7197
|
+
container-type: inline-size; border: 1px solid var(--border); border-radius: 12px;
|
|
7198
|
+
background: #d4e7f7; cursor: grab; touch-action: pan-y;
|
|
7199
|
+
}
|
|
7200
|
+
.hive-viewport.is-dragging { cursor: grabbing; }
|
|
7201
|
+
.hive-reference-layer { position: absolute; inset: 0; transform-origin: center; }
|
|
7202
|
+
.hive-reference-layer[hidden] { display: none; }
|
|
7203
|
+
.hive-reference { position: absolute; inset: 0; display: block; width: 100%; height: 100%; user-select: none; pointer-events: none; }
|
|
7204
|
+
.hive-gl { display: none; }
|
|
7205
|
+
.hive-gl-overlay { position: absolute; inset: 0; pointer-events: none; }
|
|
7206
|
+
#rd-hive-viewport.hive-mode-3d #rd-hive-cells { display: none; }
|
|
7207
|
+
#rd-hive-viewport.hive-mode-2d { height: auto; aspect-ratio: auto; overflow: visible; border: 0; border-radius: 0; background: transparent; cursor: auto; }
|
|
7208
|
+
#rd-hive-viewport.hive-mode-2d .hive-reference-layer { display: none; }
|
|
7209
|
+
.hg-badge {
|
|
7210
|
+
position: absolute; transform: translate(-50%, -50%); width: 10.8%; height: 7.6%;
|
|
7211
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .4cqw;
|
|
7212
|
+
padding: .3cqw; border: 0; border-radius: 1.4cqw; pointer-events: auto; cursor: pointer;
|
|
7213
|
+
color: #101c29; background: rgba(255,255,255,.98); box-shadow: 0 .35cqw .9cqw rgba(14,55,88,.22);
|
|
7214
|
+
text-align: center; font-family: "Microsoft YaHei", sans-serif;
|
|
7215
|
+
}
|
|
7216
|
+
.hg-badge .hg-name { font-size: 1.25cqw; font-weight: 800; line-height: 1.15; white-space: nowrap; }
|
|
7217
|
+
.hg-badge .hg-meta { font-size: .58cqw; color: #536d7d; line-height: 1; white-space: nowrap; }
|
|
7218
|
+
.hg-badge:disabled { cursor: default; opacity: 1; }
|
|
7219
|
+
.hg-badge:hover:not(:disabled), .hg-badge:focus-visible { outline: .12cqw solid #71eaf9; outline-offset: .2cqw; }
|
|
7220
|
+
.hg-badge.hg-active { box-shadow: 0 0 1.1cqw rgba(0, 236, 255, .65); }
|
|
7221
|
+
.hg-monitor {
|
|
7222
|
+
position: absolute; display: grid; grid-template-columns: 1fr auto; grid-template-rows: auto auto 1fr;
|
|
7223
|
+
padding: .12cqw .3cqw; gap: .08cqw .18cqw; overflow: hidden; border: 0; border-radius: 0;
|
|
7224
|
+
pointer-events: auto; cursor: pointer; text-align: left; color: #d8f9ff; background: #06244a;
|
|
7225
|
+
font-family: var(--mono); font-size: .57cqw; line-height: 1.15;
|
|
7226
|
+
}
|
|
7227
|
+
.hg-monitor:disabled { opacity: 1; cursor: default; }
|
|
7228
|
+
.hg-monitor:hover:not(:disabled), .hg-monitor:focus-visible { outline: .12cqw solid #91f6ff; outline-offset: .1cqw; z-index: 1; }
|
|
7229
|
+
.hg-monitor-role { grid-column: 1 / -1; font-size: .69cqw; line-height: 1.16; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
7230
|
+
.hg-monitor-status { color: #94deee; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
7231
|
+
.hg-monitor-time { grid-column: 2; grid-row: 2; color: #9dd8e4; white-space: nowrap; }
|
|
7232
|
+
.hg-monitor-tail { grid-column: 1 / -1; color: #b3e7f2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
7233
|
+
.hg-monitor.st-running { box-shadow: inset 0 -.1cqw #52edf5; }
|
|
7234
|
+
.hg-monitor.st-running .hg-monitor-status { color: #5cf5fc; }
|
|
7235
|
+
.hg-monitor.st-done .hg-monitor-status { color: #99f3bf; }
|
|
7236
|
+
.hg-monitor.st-failed .hg-monitor-status, .hg-monitor.st-timeout .hg-monitor-status { color: #ffa2a2; }
|
|
7237
|
+
.hg-monitor.st-idle { color: #9abccf; }
|
|
7238
|
+
.hive-scene-tools.hive-2d .hive-scene-hint, .hive-scene-tools.hive-2d .hive-scene-pager { display: none; }
|
|
7239
|
+
.hg-chip, .hg-tip { display: none; }
|
|
7240
|
+
@media (prefers-reduced-motion: reduce) {
|
|
7241
|
+
.hive-reference-layer { transition: none; }
|
|
7242
|
+
}
|
|
7243
|
+
|
|
7244
|
+
.hive-detail-dialog {
|
|
7245
|
+
width: min(800px, calc(100vw - 32px)); max-height: 84vh; padding: 0; overflow: hidden;
|
|
7246
|
+
border: 1px solid var(--border); border-radius: 14px; color: var(--text); background: var(--panel);
|
|
7247
|
+
box-shadow: 0 24px 90px #0005;
|
|
7248
|
+
}
|
|
7249
|
+
.hive-detail-dialog::backdrop { background: #04182c66; backdrop-filter: blur(3px); }
|
|
7250
|
+
.hive-detail-head { display: flex; align-items: center; gap: 12px; padding: 18px 22px; border-bottom: 1px solid var(--border); }
|
|
7251
|
+
.hive-detail-head h3 { margin: 0; flex: 1; overflow-wrap: anywhere; font-size: 17px; }
|
|
7252
|
+
.hive-detail-close { min-width: 32px; min-height: 32px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel2); color: var(--text); cursor: pointer; }
|
|
7253
|
+
.hive-detail-meta { display: flex; flex-wrap: wrap; gap: 8px 16px; padding: 12px 22px; font-size: 12px; color: var(--muted); }
|
|
7254
|
+
.hive-detail-status { color: var(--accent); font-weight: 600; }
|
|
7255
|
+
.hive-detail-log { margin: 0; padding: 18px 22px; min-height: 160px; max-height: 56vh; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--log-text); background: var(--log-bg); font: 12px/1.7 var(--mono); }
|
|
7256
|
+
.hive-detail-error { margin: 0; padding: 8px 22px; color: var(--bad); font-size: 12px; }
|
|
7257
|
+
.hive-detail-error:empty { display: none; }
|
|
7258
|
+
@media (max-width: 720px) {
|
|
7259
|
+
.hive-scene-tools { flex-wrap: wrap; }
|
|
7260
|
+
.hive-scene-hint { order: 2; width: 100%; margin: 0 0 3px; }
|
|
7261
|
+
.hive-scene-spacer { flex: 1 1 0; }
|
|
7262
|
+
.hive-detail-head, .hive-detail-meta, .hive-detail-log { padding-inline: 14px; }
|
|
7263
|
+
}
|
|
7264
|
+
|
|
7265
|
+
/* ---- 发布台·批量选择(pb-pend):待发章号 chips + 全选/清空 + 发布所选/直发所选 ---- */
|
|
7266
|
+
.pb-pend { width: 100%; margin-top: 6px; padding: 6px 8px; border: 1px solid var(--line);
|
|
7267
|
+
border-radius: 8px; font-size: 12px; }
|
|
7268
|
+
.pb-pend-head { display: flex; gap: 10px; align-items: center; color: var(--muted); }
|
|
7269
|
+
.pb-pend-head a { color: var(--accent, #4a7dbe); text-decoration: none; }
|
|
7270
|
+
.pb-pend-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px;
|
|
7271
|
+
max-height: 96px; overflow: auto; }
|
|
7272
|
+
.pb-chip { display: inline-flex; align-items: center; gap: 3px; padding: 1px 6px;
|
|
7273
|
+
border: 1px solid var(--line); border-radius: 10px; cursor: pointer;
|
|
7274
|
+
color: var(--muted); user-select: none; }
|
|
7275
|
+
.pb-chip.on { border-color: var(--accent, #4a7dbe); color: var(--text); }
|
|
7276
|
+
.pb-chip input { margin: 0; }
|
|
7277
|
+
.pb-pend-btns { display: flex; gap: 8px; margin-top: 6px; }
|
|
7278
|
+
.pb-vol { font-size: 11px; color: var(--accent, #4a7dbe); margin-top: 5px; font-weight: 600; }
|
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,48 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "codebee",
|
|
3
|
-
"version": "0.1.
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "codebee",
|
|
3
|
+
"version": "0.1.97",
|
|
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
|
+
"postpublish": "node scripts/postpublish_tag.cjs"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/Vercel-By-WXP/CodeBee.git"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|