codebee 0.1.0 → 0.1.1
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/README.md +413 -392
- package/app/core/catalog.py +32 -11
- package/app/core/gitmod.py +953 -949
- package/app/core/manager.py +1462 -1415
- package/app/core/modelhub.py +10 -4
- package/app/core/paths.py +12 -6
- package/app/ui/app.js +22 -11
- package/app/ui/i18n.js +1711 -1708
- package/app/ui/style.css +1 -1
- package/bin/tutti.js +35 -9
- package/package.json +1 -1
package/app/core/modelhub.py
CHANGED
|
@@ -1329,8 +1329,9 @@ def _src_ccswitch():
|
|
|
1329
1329
|
except Exception:
|
|
1330
1330
|
pass
|
|
1331
1331
|
if skipped:
|
|
1332
|
-
out["note"] = "跳过
|
|
1332
|
+
out["note"] = "跳过 {0} 条(缺地址或格式不识别):{1}".format(
|
|
1333
1333
|
len(skipped), "、".join(sorted(set(skipped))[:4]))
|
|
1334
|
+
out["note_args"] = [len(skipped), "、".join(sorted(set(skipped))[:4])]
|
|
1334
1335
|
return out
|
|
1335
1336
|
|
|
1336
1337
|
|
|
@@ -1414,7 +1415,9 @@ def _src_zcode():
|
|
|
1414
1415
|
else:
|
|
1415
1416
|
bad.append(str(p.get("name") or pid))
|
|
1416
1417
|
if bad:
|
|
1417
|
-
out["note"] = "跳过
|
|
1418
|
+
out["note"] = "跳过 {0} 个(缺合法 baseURL):{1}".format(
|
|
1419
|
+
len(bad), "、".join(bad[:4]))
|
|
1420
|
+
out["note_args"] = [len(bad), "、".join(bad[:4])]
|
|
1418
1421
|
return out
|
|
1419
1422
|
|
|
1420
1423
|
|
|
@@ -1697,6 +1700,7 @@ def _collect(fn):
|
|
|
1697
1700
|
res.setdefault("providers", [])
|
|
1698
1701
|
res.setdefault("pricing", {})
|
|
1699
1702
|
res.setdefault("note", "")
|
|
1703
|
+
res.setdefault("note_args", [])
|
|
1700
1704
|
res.setdefault("error", "")
|
|
1701
1705
|
res.setdefault("found", False)
|
|
1702
1706
|
return res
|
|
@@ -1709,11 +1713,12 @@ def sources():
|
|
|
1709
1713
|
ps = [p for p in paths() if p]
|
|
1710
1714
|
found = any(os.path.isfile(p) for p in ps)
|
|
1711
1715
|
row = {"id": sid, "name": name, "desc": desc, "paths": ps,
|
|
1712
|
-
"found": found, "count": 0, "note": "", "error": ""}
|
|
1716
|
+
"found": found, "count": 0, "note": "", "note_args": [], "error": ""}
|
|
1713
1717
|
if found:
|
|
1714
1718
|
res = _collect(fn)
|
|
1715
1719
|
row["count"] = len(res["providers"])
|
|
1716
1720
|
row["note"] = res["note"]
|
|
1721
|
+
row["note_args"] = res.get("note_args", [])
|
|
1717
1722
|
row["error"] = res["error"]
|
|
1718
1723
|
rows.append(row)
|
|
1719
1724
|
return rows
|
|
@@ -1754,7 +1759,8 @@ def import_sources(ids=None):
|
|
|
1754
1759
|
out["sources"].append({
|
|
1755
1760
|
"id": sid, "name": name, "found": res["found"],
|
|
1756
1761
|
"added": added, "updated": updated, "duplicate": dup,
|
|
1757
|
-
"count": added + updated, "note": res["note"],
|
|
1762
|
+
"count": added + updated, "note": res["note"],
|
|
1763
|
+
"note_args": res.get("note_args", []), "error": res["error"]})
|
|
1758
1764
|
if out["added"] or out["updated"] or out["pricing"]:
|
|
1759
1765
|
_save(data)
|
|
1760
1766
|
out["imported"] = out["added"] + out["updated"]
|
package/app/core/paths.py
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
数据目录选择顺序:
|
|
5
5
|
1. TUTTI_DATA 环境变量(测试/多实例时整体指到别处,必须在任何模块使用前设置);
|
|
6
6
|
2. 仓库内 data/ 且已有内容——开发仓库与老安装平滑沿用,升级不动数据;
|
|
7
|
-
3. 用户目录:Windows %APPDATA%\\
|
|
7
|
+
3. 用户目录:Windows %APPDATA%\\CodeBee,macOS/Linux ~/.codebee。
|
|
8
|
+
老安装的 %APPDATA%\\Tutti / ~/.tutti 若存在则自动沿用(零迁移),
|
|
9
|
+
新安装一律落 CodeBee 目录。
|
|
8
10
|
npm/pip 全局安装的包目录会在 `npm update -g` 时被整体替换,
|
|
9
11
|
数据绝不能落在包内,故全新安装一律走用户目录。
|
|
10
12
|
"""
|
|
@@ -23,16 +25,20 @@ _DATA_SENTINELS = ("catalog.json", "models.json", "orchestration.json", "tasks",
|
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
def _user_data_dir(platform=None, environ=None, home=None) -> Path:
|
|
26
|
-
"""全局安装场景的用户数据目录(不创建,仅计算)。home 供测试注入。
|
|
28
|
+
"""全局安装场景的用户数据目录(不创建,仅计算)。home 供测试注入。
|
|
29
|
+
新装落 CodeBee;老 Tutti 目录存在则沿用(改名零迁移)。"""
|
|
27
30
|
platform = platform or sys.platform
|
|
28
31
|
env = os.environ if environ is None else environ
|
|
29
32
|
home = Path(home) if home else Path.home()
|
|
30
33
|
if platform == "win32":
|
|
31
34
|
appdata = env.get("APPDATA")
|
|
32
|
-
if appdata
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
base = Path(appdata) if appdata else home / "AppData" / "Roaming"
|
|
36
|
+
if (base / "Tutti").exists():
|
|
37
|
+
return base / "Tutti"
|
|
38
|
+
return base / "CodeBee"
|
|
39
|
+
if (home / ".tutti").exists():
|
|
40
|
+
return home / ".tutti"
|
|
41
|
+
return home / ".codebee"
|
|
36
42
|
|
|
37
43
|
|
|
38
44
|
def default_data_dir(repo=None, environ=None) -> Path:
|
package/app/ui/app.js
CHANGED
|
@@ -843,11 +843,20 @@ function renderBindings() {
|
|
|
843
843
|
const protoWarn = bound && !bindable.some((p) => p.id === bound.id)
|
|
844
844
|
? '<p class="hint warn">' + t("该供应商协议为 ") + esc(protoLabel(bound)) +
|
|
845
845
|
t(",当前没有可注入的 CLI,编排时会回落为 CLI 默认配置。") + '</p>' : "";
|
|
846
|
+
// 供应商停用但链上勾选了它家模型:链条目在解析时也会被整条跳过,
|
|
847
|
+
// 链空了照样回落本机默认——这是 2026-09-16 配额事故的隐藏形态,单说
|
|
848
|
+
// 「供应商下拉」警告不够,链本身的死活也要点名。
|
|
849
|
+
const chainDead = bindChain(b).some((c2) => c2.p && (() => {
|
|
850
|
+
const pv = provs.find((p) => p.id === c2.p);
|
|
851
|
+
return !pv || pv.enabled === false;
|
|
852
|
+
})());
|
|
853
|
+
const chainWarn = (!bound || bound.enabled !== false) && chainDead
|
|
854
|
+
? '<p class="hint warn">' + t("模型链里有已停用/已删除的供应商:这些条目解析时会被跳过,链可能因此整体失效。") + '</p>' : "";
|
|
846
855
|
return '<div class="card"><div class="head"><span class="name">' + esc(c.name) + "</span>" +
|
|
847
856
|
'<span class="tag">' + esc(c.orch_kind) + "</span></div>" +
|
|
848
857
|
'<div class="field"><label>' + t("供应商") + '</label><select id="bindprov-' + esc(c.id) + '">' + opts + "</select></div>" +
|
|
849
858
|
'<p class="hint">' + t("绑定后编排调用会注入该供应商的 API key 与地址;不绑定则只按下方模型链传 -m 参数。") + '</p>' +
|
|
850
|
-
bindModelBox(c, b.provider_id) + offWarn + protoWarn +
|
|
859
|
+
bindModelBox(c, b.provider_id) + offWarn + protoWarn + chainWarn +
|
|
851
860
|
'<div class="ops"><label class="toggle"><input type="checkbox" id="binddiff-' + esc(c.id) + '"' +
|
|
852
861
|
(b.difficulty_routing ? " checked" : "") + '>' + t(" 按难度自动选模型(简单/困难)") + '</label>' +
|
|
853
862
|
'<button class="ghost small" onclick="saveBinding(\'' + esc(c.id) + '\')">' + t("保存") + '</button></div></div>';
|
|
@@ -1593,7 +1602,7 @@ async function openImportDialog() {
|
|
|
1593
1602
|
'<div class="src-name">' + esc(s.name) + status + "</div>" +
|
|
1594
1603
|
'<div class="src-desc">' + esc(t(s.desc)) + "</div>" +
|
|
1595
1604
|
'<div class="src-path">' + esc((s.paths || []).join(" · ")) + "</div>" +
|
|
1596
|
-
(s.note ? '<div class="src-note">' + esc(t(s.note)) + "</div>" : "") +
|
|
1605
|
+
(s.note ? '<div class="src-note">' + esc(t.apply(null, [s.note].concat(s.note_args || []))) + "</div>" : "") +
|
|
1597
1606
|
(s.error ? '<div class="src-note bad">' + esc(s.error) + "</div>" : "") +
|
|
1598
1607
|
"</div></label>";
|
|
1599
1608
|
}).join("");
|
|
@@ -1646,7 +1655,7 @@ async function doImport() {
|
|
|
1646
1655
|
if (s.error) return "<li>" + esc(s.name) + t(":") + '<span class="bad">' + esc(s.error) + "</span></li>";
|
|
1647
1656
|
let txt = t("新增 ") + s.added + t(",更新 ") + s.updated;
|
|
1648
1657
|
if (s.duplicate) txt += t(",跳过重复 ") + s.duplicate;
|
|
1649
|
-
const extra = s.note ? t("(") + esc(t(s.note)) + t(")") : "";
|
|
1658
|
+
const extra = s.note ? t("(") + esc(t.apply(null, [s.note].concat(s.note_args || []))) + t(")") : "";
|
|
1650
1659
|
return "<li>" + esc(s.name) + t(":") + txt + extra + "</li>";
|
|
1651
1660
|
}).join("");
|
|
1652
1661
|
$("import-result").innerHTML =
|
|
@@ -1862,7 +1871,7 @@ function onGoalPaste(e) {
|
|
|
1862
1871
|
|
|
1863
1872
|
/* ---------------------------------------------------------- 代码版本(Git) */
|
|
1864
1873
|
/* 工作目录指向 git 仓库时给出分支/标签/提交选择;创建任务后执行前由服务端
|
|
1865
|
-
* 从所选版本检出任务分支
|
|
1874
|
+
* 从所选版本检出任务分支 codebee/<task-id>(脏工作区会显式失败,不静默降级)。 */
|
|
1866
1875
|
let _gitProbeTimer = null;
|
|
1867
1876
|
|
|
1868
1877
|
function queueGitProbe() {
|
|
@@ -1911,7 +1920,7 @@ function renderGitHint() {
|
|
|
1911
1920
|
const base = t("当前分支 %1 @ %2").replace("%1", info.branch).replace("%2", (info.head || "").slice(0, 8));
|
|
1912
1921
|
const dirty = info.dirty ? t(";工作区有 %1 处未提交改动").replace("%1", info.dirty_count) : "";
|
|
1913
1922
|
const act = kind
|
|
1914
|
-
? t(";运行时将从「%1」检出任务分支
|
|
1923
|
+
? t(";运行时将从「%1」检出任务分支 codebee/<任务ID>").replace("%1", rev)
|
|
1915
1924
|
: "";
|
|
1916
1925
|
$("git-hint").innerHTML = esc(base + dirty + act);
|
|
1917
1926
|
}
|
|
@@ -3239,7 +3248,7 @@ async function renderRunDetail() {
|
|
|
3239
3248
|
});
|
|
3240
3249
|
}
|
|
3241
3250
|
|
|
3242
|
-
/* 代码版本隔离面板:run 检出任务分支
|
|
3251
|
+
/* 代码版本隔离面板:run 检出任务分支 codebee/<id> 后,产物提交在该分支上、
|
|
3243
3252
|
* 用户工作区已切回原分支。分支是「每任务一条」,裁决(合并/丢弃)在任务级生效。
|
|
3244
3253
|
* 数据源两路:任务级 side(详情页自拉,运行中实时 numstat、结束后最新 run 快照,
|
|
3245
3254
|
* 续跑/重试的新 run 没带 git 字段也能从任务带出分支)优先;side 不可用
|
|
@@ -3561,7 +3570,7 @@ function _renderGitSnapshot(run, task) {
|
|
|
3561
3570
|
'<div class="git-head"><svg class="ico" aria-hidden="true"><use href="#i-git-branch"></use></svg>' +
|
|
3562
3571
|
'<span class="sec-title">' + t("代码版本隔离") + '</span>' +
|
|
3563
3572
|
'<span class="chip failed">' + t("检出失败") + "</span>" +
|
|
3564
|
-
'<code class="git-branch">
|
|
3573
|
+
'<code class="git-branch">codebee/' + esc(tid || t("(无主运行)")) + t("(") + esc(t("未创建")) + t(")") + "</code></div>" +
|
|
3565
3574
|
(lastErr ? '<div class="hint warn">' + esc(lastErr) + "</div>" : "") +
|
|
3566
3575
|
'<div class="hint">' + esc(t("处理后点「重试任务」,运行会重新检出任务分支。")) + "</div>";
|
|
3567
3576
|
rdTabsSync();
|
|
@@ -3711,7 +3720,7 @@ function renderGitWbMain(d, task) {
|
|
|
3711
3720
|
if (iso.rev && !iso.state) {
|
|
3712
3721
|
html += '<div class="hint">' + esc(t("已配置代码版本隔离,但任务分支尚未创建(检出失败见运行错误)。")) + "</div>";
|
|
3713
3722
|
} else if (iso.state === "isolated") {
|
|
3714
|
-
html += '<div class="git-meta"><span>' + esc(t("任务分支")) + " <b>
|
|
3723
|
+
html += '<div class="git-meta"><span>' + esc(t("任务分支")) + " <b>codebee/" + esc(tid) + "</b></span>" +
|
|
3715
3724
|
'<span class="chip isolated">' + t("待裁决") + "</span></div>";
|
|
3716
3725
|
if (!ro) {
|
|
3717
3726
|
html += '<div class="git-actions">' +
|
|
@@ -3723,7 +3732,7 @@ function renderGitWbMain(d, task) {
|
|
|
3723
3732
|
}
|
|
3724
3733
|
} else if (iso.state) {
|
|
3725
3734
|
const chip = GIT_STATE_CHIP[iso.state] || ["muted", "—"];
|
|
3726
|
-
html += '<div class="git-meta"><span>' + esc(t("任务分支")) + " <b>
|
|
3735
|
+
html += '<div class="git-meta"><span>' + esc(t("任务分支")) + " <b>codebee/" + esc(tid) + "</b></span>" +
|
|
3727
3736
|
'<span class="chip ' + chip[0] + '">' + t(chip[1]) + "</span></div>";
|
|
3728
3737
|
}
|
|
3729
3738
|
box.classList.remove("hidden");
|
|
@@ -3974,7 +3983,7 @@ function drawInspector() {
|
|
|
3974
3983
|
const lastErr = ((d.run || {}).error || "");
|
|
3975
3984
|
if (revChosen) {
|
|
3976
3985
|
chipEl.className = "chip failed"; chipEl.textContent = t("检出失败"); chipEl.classList.remove("hidden");
|
|
3977
|
-
main.innerHTML = '<div class="insp-branch"><code>
|
|
3986
|
+
main.innerHTML = '<div class="insp-branch"><code>codebee/' + esc(d.task.id || "") +
|
|
3978
3987
|
t("(") + esc(t("未创建")) + t(")") + '</code></div>' +
|
|
3979
3988
|
(lastErr ? '<div class="hint warn">' + esc(lastErr) + "</div>" : "") +
|
|
3980
3989
|
'<span class="insp-hint">' + esc(t("处理后点「重试任务」,运行会重新检出任务分支。")) + "</span>";
|
|
@@ -5498,7 +5507,9 @@ function bindModelBox(c, provId) {
|
|
|
5498
5507
|
'" title="' + t("移除") + '" onclick="bindRemove(\'' + esc(c.id) + '\', this)">×</button>' +
|
|
5499
5508
|
"</span>";
|
|
5500
5509
|
}).join("")
|
|
5501
|
-
: '<span class="hint">' + t("未设置") + (provId
|
|
5510
|
+
: '<span class="hint">' + t("未设置") + (provId
|
|
5511
|
+
? t("(按供应商/难度自动解析——供应商协议不匹配或被停用时解析为空,回落 CLI 本机默认)")
|
|
5512
|
+
: t("(用 CLI 默认模型——不会注入任何供应商凭据)")) + "</span>";
|
|
5502
5513
|
return '<div class="field"><label>' + t("运行时模型链(跨厂商,最多 ") + MAX_ORCH_MODELS + t(" 条)") + "</label>" +
|
|
5503
5514
|
'<div class="orch-row">' + chips +
|
|
5504
5515
|
'<button class="ghost small" onclick="bindToggle(\'' + esc(c.id) + '\')">' +
|