cicy-desktop 2.1.165 → 2.1.166

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.
@@ -4,11 +4,11 @@
4
4
  // (Apache-2.0, no licensing) INSIDE a WSL2 Ubuntu distro and drive it with
5
5
  // deterministic Linux commands — no UAC click-through, no whale-icon wait, no
6
6
  // leftover-staging / PATH issues. WSL2 forwards localhost, so a container
7
- // published on :8008 in Ubuntu is reachable at 127.0.0.1:8008 on Windows.
7
+ // published on :8009 in Ubuntu is reachable at 127.0.0.1:8009 on Windows.
8
8
  //
9
9
  // Flow: ensure WSL2 → ensure Ubuntu distro → apt install docker.io → start
10
10
  // dockerd → docker load (image tarball from ~/Downloads via /mnt/c) → docker run
11
- // → health-probe :8008 from Windows. Every step checks-then-acts and is
11
+ // → health-probe :8009 from Windows. Every step checks-then-acts and is
12
12
  // idempotent, so 重试 resumes.
13
13
 
14
14
  const { execFile, execFileSync, spawn } = require("child_process");
@@ -411,7 +411,7 @@ function shareMountArg() {
411
411
  } catch (e) { log.warn(`[wsl-docker] Share mount setup failed: ${e.message}`); return ""; }
412
412
  }
413
413
 
414
- async function runContainer({ port = 8008, container = "cicy-code-docker", volume = "cicy-team-8008", env = {} } = {}) {
414
+ async function runContainer({ port = 8009, container = "cicy-code-docker", volume = "cicy-team-8009", env = {} } = {}) {
415
415
  // 每次容器"启动"(含已在跑被 adopt)都确保桌面快捷方式存在 —— 不存在就建,坏了就修。
416
416
  if (await probeHealth(port)) { ensureDesktopShortcut(volume, port).catch(() => {}); return { adopted: true }; }
417
417
  // Replace any stale same-named container.
@@ -423,7 +423,7 @@ async function runContainer({ port = 8008, container = "cicy-code-docker", volum
423
423
  // --dns: WSL2's auto resolv.conf points the distro at the host NAT gateway
424
424
  // (172.x.x.1), which docker's default DNS forwarding does NOT reach from inside a
425
425
  // bridge container → every lookup is EAI_AGAIN and cicy-code's startup `npm i`
426
- // crash-loops the container (:8008 never comes up). Pin public resolvers: Aliyun
426
+ // crash-loops the container (:8009 never comes up). Pin public resolvers: Aliyun
427
427
  // 223.5.5.5 (CN-fast) first, Google 8.8.8.8 as the overseas fallback.
428
428
  const cmd = `docker run -d --name ${container} --restart unless-stopped --dns 223.5.5.5 --dns 8.8.8.8 -p 127.0.0.1:${port}:8008 -e CICY_PUBLIC=1 -v ${volume}:/home/cicy ${shareMountArg()} ${envArgs} ${IMAGE}`;
429
429
  await wslRun(cmd, { timeout: 60000 });
@@ -432,11 +432,11 @@ async function runContainer({ port = 8008, container = "cicy-code-docker", volum
432
432
  }
433
433
 
434
434
  // Read the container's OWN api_token (its volume-persisted global.json). This is
435
- // the ONLY correct credential for :8008 — the host's 8008 token is different and
436
- // 8008 rejects it. Retries because right after start the entrypoint may not have
435
+ // the ONLY correct credential for :8009 — the host's 8008 token is different and
436
+ // 8009 rejects it. Retries because right after start the entrypoint may not have
437
437
  // written global.json yet; returns "" only if it truly can't be read (callers
438
438
  // must then NOT open with a wrong/host token — that strands the user at login).
439
- async function readContainerToken(port = 8008, container = "cicy-code-docker", volume = "cicy-team-8008") {
439
+ async function readContainerToken(port = 8009, container = "cicy-code-docker", volume = "cicy-team-8009") {
440
440
  for (let attempt = 1; attempt <= 5; attempt++) {
441
441
  // 1) Fast + reliable: read the volume-backed global.json straight from the
442
442
  // distro fs. `docker exec` into a just-loaded/busy container is slow and
@@ -459,7 +459,7 @@ async function readContainerToken(port = 8008, container = "cicy-code-docker", v
459
459
  }
460
460
 
461
461
  // Register a Windows logon task that starts dockerd in our distro on every
462
- // logon — old inbox WSL ignores wsl.conf [boot], so without this :8008 is dead
462
+ // logon — old inbox WSL ignores wsl.conf [boot], so without this :8009 is dead
463
463
  // after a Windows reboot until the user clicks 启动. The container's
464
464
  // --restart unless-stopped then brings cicy-code back automatically. Idempotent
465
465
  // (start-dockerd.sh is `pgrep dockerd || dockerd`); /f overwrites a stale task.
@@ -477,7 +477,7 @@ function ensureAutostart() {
477
477
  }
478
478
 
479
479
  // Drop a desktop shortcut (folder icon) to the container's /home/cicy — i.e. the
480
- // cicy-team volume on the distro — so the user can browse :8008's files from
480
+ // cicy-team volume on the distro — so the user can browse :8009's files from
481
481
  // Windows Explorer. \\wsl$\<distro>\… is the UNC view of the WSL filesystem.
482
482
  // Idempotent: CreateShortcut overwrites. Best-effort (errors swallowed).
483
483
  // PowerShell single-quoted literal. PowerShell does NOT treat backslash as an
@@ -487,7 +487,7 @@ function ensureAutostart() {
487
487
  // backslashes) which Explorer can't open. That was the broken "WSL 快捷方式".
488
488
  function psSingle(s) { return "'" + String(s).replace(/'/g, "''") + "'"; }
489
489
 
490
- function ensureDesktopShortcut(volume = "cicy-team-8008", port = 8008) {
490
+ function ensureDesktopShortcut(volume = "cicy-team-8009", port = 8009) {
491
491
  if (process.platform !== "win32") return Promise.resolve();
492
492
  return new Promise((res) => {
493
493
  // 快捷方式名带 port —— 多个 docker(不同端口)各自一个桌面文件夹快捷方式。
@@ -511,7 +511,7 @@ function ensureDesktopShortcut(volume = "cicy-team-8008", port = 8008) {
511
511
  }
512
512
 
513
513
  // Composite status for the card.
514
- async function status(port = 8008) {
514
+ async function status(port = 8009) {
515
515
  // `unknown` = WSL didn't answer a probe (stuck/booting). The homepage uses it
516
516
  // to show 「检测中/WSL 无响应·重试」instead of falsely showing 「下载安装」.
517
517
  const miss = await docker.wslMissing(); // true | false | null(unknown)
@@ -542,7 +542,7 @@ async function bootstrap(opts = {}) {
542
542
  return _bootstrapInFlight;
543
543
  }
544
544
 
545
- async function _bootstrap({ onProgress, port = 8008, container = "cicy-code-docker", volume = "cicy-team", env = {} } = {}) {
545
+ async function _bootstrap({ onProgress, port = 8009, container = "cicy-code-docker", volume = "cicy-team", env = {} } = {}) {
546
546
  const emit = (ev) => { try { onProgress && onProgress(ev); } catch {} };
547
547
 
548
548
  // Structured, PERSISTED trace of the whole run (electron-log → main.log) so a
@@ -650,7 +650,7 @@ async function _bootstrap({ onProgress, port = 8008, container = "cicy-code-dock
650
650
  // Restart ONLY cicy-code via supervisor — cron / sshd / user daemons keep
651
651
  // running (that's the whole point of the supervisor layout). Falls back to a
652
652
  // full container restart on the pre-supervisor image.
653
- async function restart({ container = "cicy-code-docker", port = 8008, volume = "cicy-team-8008" } = {}) {
653
+ async function restart({ container = "cicy-code-docker", port = 8009, volume = "cicy-team-8009" } = {}) {
654
654
  await startEngine();
655
655
  try {
656
656
  await wslRun(`docker exec ${container} supervisorctl -c /etc/supervisor/supervisord.conf restart cicy-code`, { timeout: 30000 });
@@ -666,22 +666,19 @@ async function restart({ container = "cicy-code-docker", port = 8008, volume = "
666
666
  // which installs the latest version side-by-side, repoints the symlink, and
667
667
  // `supervisorctl restart cicy-code` — no container recreate, daemons untouched.
668
668
  // Streamed to the drawer so the user sees the npm pull + restart.
669
- async function update({ onProgress, container = "cicy-code-docker", port = 8008 } = {}) {
669
+ async function update({ onProgress, container = "cicy-code-docker", port = 8009 } = {}) {
670
670
  const emit = (ev) => { try { onProgress && onProgress(ev); } catch {} };
671
671
  await startEngine();
672
- emit({ phase: "image", status: "running", message: "更新 cicy-code(npm 拉取最新版)…" });
673
- // 容器里 cicy-code 是 npm 全局装的(无 update 脚本)。npm 装最新版(CN npmmirror)+ 重启容器。
672
+ emit({ phase: "image", status: "running", message: "更新 cicy-code(拉取最新版)…" });
674
673
  try {
675
- await wslRunStream(`docker exec ${container} bash -lc "npm install -g cicy-code@latest --registry https://registry.npmmirror.com"`,
674
+ await wslRunStream(`docker exec ${container} bash -lc "command -v cicy-code-update.sh >/dev/null && cicy-code-update.sh || /usr/local/bin/cicy-code-update.sh"`,
676
675
  { emit, phase: "image", timeout: 300000 });
677
- emit({ phase: "container", status: "running", message: "重启容器以应用新版本…" });
678
- await wslRun(`docker restart ${container}`, { timeout: 60000 });
679
676
  } catch (e) {
680
- emit({ phase: "done", status: "error", message: `更新失败:${e.message}(试试「升级」重装)` });
677
+ emit({ phase: "done", status: "error", message: `更新失败:${e.message}(此镜像可能不支持,试试「升级」重装)` });
681
678
  return { ok: false, reason: "update_failed" };
682
679
  }
683
680
  const healthy = await docker.waitUntil(() => probeHealth(port), { totalMs: 120000, everyMs: 3000 });
684
- emit({ phase: "done", status: healthy ? "done" : "error", message: healthy ? "cicy-code 已更新到最新 🎉" : "更新了但 :8008 还没响应——稍等或点重试" });
681
+ emit({ phase: "done", status: healthy ? "done" : "error", message: healthy ? "cicy-code 已更新到最新 🎉" : "更新了但 :8009 还没响应——稍等或点重试" });
685
682
  return { ok: healthy };
686
683
  }
687
684
  async function stop({ container = "cicy-code-docker" } = {}) {
@@ -691,7 +688,7 @@ async function stop({ container = "cicy-code-docker" } = {}) {
691
688
  // docker restart 整个容器(stop+start 同一个容器)—— 区别于 restart()(supervisorctl
692
689
  // 重启容器内的 cicy-code 进程)和重建(rm+run)。容器重启后 entrypoint 重跑,会重读
693
690
  // volume global.json,所以若先把新 key 写进 volume,这个就能让 cicy-code 用上新 key。
694
- async function dockerRestart({ container = "cicy-code-docker-8008" } = {}) {
691
+ async function dockerRestart({ container = "cicy-code-docker-8009" } = {}) {
695
692
  await wslRun(`docker restart ${container}`, { timeout: 45000 });
696
693
  return true;
697
694
  }
@@ -699,7 +696,7 @@ async function dockerRestart({ container = "cicy-code-docker-8008" } = {}) {
699
696
  // 重建容器:docker rm -f 旧容器 + docker run 新容器(用新 env,如新的 docker team 网关
700
697
  // key)。**保留 volume**(数据/api_token/deviceId 不丢),只是换掉容器本身 + env。
701
698
  // 破坏性(短暂中断 + 换 key)→ 调用方要 confirm。
702
- async function recreate({ port = 8008, container = "cicy-code-docker-8008", volume = "cicy-team-8008", env = {} } = {}) {
699
+ async function recreate({ port = 8009, container = "cicy-code-docker-8009", volume = "cicy-team-8009", env = {} } = {}) {
703
700
  // 强删占用该端口的**任何**容器(含老名字 cicy-code-docker)+ 目标容器 —— 否则
704
701
  // runContainer 开头的 probeHealth 看到旧容器还健康会 adopt 它、不重建,key 就换不了。
705
702
  try { await wslRun(`docker ps -aq --filter publish=${port} | xargs -r docker rm -f 2>/dev/null; docker rm -f ${container} 2>/dev/null; true`, { timeout: 30000 }); } catch {}
@@ -721,7 +718,7 @@ function unregisterDistro() {
721
718
  // downloader, which copes with the flaky CN DNS that bare curl can't) is the
722
719
  // only reliable CN update path. This RESETS the distro: the cicy-team volume is
723
720
  // re-created and the instance re-seeds (new token) on next boot.
724
- async function upgrade({ onProgress, port = 8008, container = "cicy-code-docker", volume = "cicy-team", env = {} } = {}) {
721
+ async function upgrade({ onProgress, port = 8009, container = "cicy-code-docker", volume = "cicy-team", env = {} } = {}) {
725
722
  const emit = (ev) => { try { onProgress && onProgress(ev); } catch {} };
726
723
  emit({ phase: "install-docker", status: "running", message: "升级 = 拉取最新运行环境并重装(会重置容器数据)…" });
727
724
  try { await stop({ container }); } catch {}
@@ -730,16 +727,7 @@ async function upgrade({ onProgress, port = 8008, container = "cicy-code-docker"
730
727
  return await _bootstrap({ onProgress, port, container, volume, env });
731
728
  }
732
729
 
733
- // 容器 env 里有没有网关 LLM key —— daemon 判断是否需要带 key 重建(同 colima-docker)。
734
- async function hasGatewayKey(container = "cicy-code-docker") {
735
- try {
736
- const r = await wslRun(`docker exec ${container} printenv CICY_AI_GATEWAY_LLM_API_KEY`, { timeout: 8000 });
737
- const s = (r && r.stdout != null) ? r.stdout : (r || "");
738
- return /sk-/.test(String(s));
739
- } catch { return false; }
740
- }
741
-
742
730
  module.exports = {
743
731
  bootstrap, status, restart, stop, dockerRestart, recreate, update, upgrade, runContainer, readContainerToken,
744
- distroInstalled, dockerInstalled, dockerEngineUp, imagePresent, probeHealth, wslRun, hasGatewayKey,
732
+ distroInstalled, dockerInstalled, dockerEngineUp, imagePresent, probeHealth, wslRun,
745
733
  };
@@ -839,7 +839,7 @@ body {
839
839
  .bcard__menu-item.is-accent:hover { background: var(--accent-soft); color: #c7dbff; }
840
840
  .bcard__menu-item.is-danger { color: #f7a3a3; }
841
841
  .bcard__menu-item.is-danger:hover { background: rgba(239,68,68,.16); color: #fff; }
842
- /* Plain divider line separating the Docker-container ops (主人: 加个分隔线就好). */
842
+ /* Plain divider line in the DockerCard ⋯ menu (cicy-code ops / Docker 容器 ops 下). */
843
843
  .bcard__menu-sep { height: 1px; margin: 5px 10px; background: rgba(255,255,255,.08); }
844
844
  /* Portaled to document.body to escape .bcard's overflow:hidden (which clipped the
845
845
  dropdown). Inline top/left position it under the kebab; here just a high
@@ -745,7 +745,7 @@ export default function App() {
745
745
 
746
746
  // Logged in: unified tabs + cards grid on the left, full-height webview
747
747
  // drawer on the right.
748
- // The Docker-版 cicy-code on :8008 has its own dedicated <DockerCard> (right of
748
+ // The Docker-版 cicy-code on :8009 has its own dedicated <DockerCard> (right of
749
749
  // the local card), so pull it out of the generic node list — else it'd ALSO
750
750
  // render as a 自定义 card (the bootstrap registers it as a team for the
751
751
  // token-injected 打开/刷新 flow).
@@ -833,37 +833,11 @@ export default function App() {
833
833
  )}
834
834
 
835
835
  <div className="app__grid">
836
- {/* The docker team (:8008) is rendered ONCE — as the dedicated <DockerCard>
837
- below, NOT also as a LocalTeamCard here. Since :8008 is now BOTH the local
838
- sidecar port AND the docker app port, it matched localList + dockerTeam and
839
- rendered twice (the duplicate card). Exclude it from this map. */}
840
- {showLocal && localList.filter((t) => !isDockerApp(t.base_url)).map((t) => (
836
+ {showLocal && localList.map((t) => (
841
837
  <LocalTeamCard key={"local:" + t.id} team={t} onOpen={() => openLocalTeam(t.id)} onRename={renameLocalTeam} onRefresh={fetchLocalTeams} />
842
838
  ))}
843
- {/* 占位卡 (主人: "本地团队没有占位"): a fresh install starts the sidecar
844
- and main auto-registers 本地团队 once :8008 answers — until that
845
- lands, hold its spot so the 本地 tab is never blank. The slow
846
- localTeams poll swaps this for the real card automatically. */}
847
- {showLocal && localList.length === 0 && (
848
- <div data-id="LocalTeamPlaceholder" className="bcard bcard--local">
849
- <div className="bcard__accent" />
850
- <div className="bcard__top">
851
- <div className="bcard__pill">
852
- <span className="bcard__dot" data-tone="warn" />
853
- <LaptopIcon />
854
- </div>
855
- </div>
856
- <div className="bcard__body">
857
- <h3 className="bcard__name">本地团队</h3>
858
- <div className="bcard__host">http://127.0.0.1:8008</div>
859
- <div className="bcard__meta" />
860
- </div>
861
- <button type="button" className="bcard__cta" disabled>
862
- <Spinner />
863
- <span>{localTeamsFetched ? "正在启动,就绪后自动加入…" : "检测中…"}</span>
864
- </button>
865
- </div>
866
- )}
839
+ {/* 主人: native :8008 退役 —— 不再有"本地团队 正在启动"占位卡(native 已删,
840
+ :8008 永远不会起来,占位会一直转)。cicy-code 用下面的 Docker 卡(:8009)。 */}
867
841
  {showLocal && (
868
842
  <DockerCard
869
843
  dockerTeam={dockerTeam}
@@ -873,7 +847,7 @@ export default function App() {
873
847
  // (主人: 必须拿到 token 才能打开,否则被卡在登录页).
874
848
  try {
875
849
  const r = await window.cicy?.docker?.appOpen?.();
876
- if (!r?.ok) toast.show({ id: "docker-open", status: "error", ttl: 6000, message: tr("docker.openNoToken", "服务还没就绪,稍等几秒再点「打开」(或用卡片菜单「重启」)") });
850
+ if (!r?.ok) window.alert("拿不到容器 token,无法打开 :8009。请确认服务已就绪(或用卡片菜单「重启」)后再试。");
877
851
  } catch (e) { console.warn("[DockerCard] open", e); }
878
852
  }}
879
853
  cloudTitle={dockerCloudTeam?.title}
@@ -1695,7 +1669,7 @@ function DockerInstallDrawerHost() {
1695
1669
  </span>
1696
1670
  <div>
1697
1671
  <div className="drawer__h">{tr("docker.setupTitle", "安装 Docker cicy-code")}</div>
1698
- <div className="drawer__sub">127.0.0.1:8008</div>
1672
+ <div className="drawer__sub">127.0.0.1:8009</div>
1699
1673
  </div>
1700
1674
  </div>
1701
1675
  <div className="drawer__headbtns">
@@ -1774,7 +1748,7 @@ function DockerInstallDrawerHost() {
1774
1748
  }
1775
1749
 
1776
1750
  // Docker-版 cicy-code card (Windows only): a SECOND cicy-code instance running
1777
- // in Docker on :8008, alongside the native local daemon (:8008). If Docker
1751
+ // in Docker on :8009, alongside the native local daemon (:8008). If Docker
1778
1752
  // Desktop is missing, the install flow downloads its installer to the user's
1779
1753
  // Desktop and runs it (主人指令), streaming progress through the drawer above.
1780
1754
  function DockerCard({ dockerTeam, cloudTitle, onOpen, onRename, onRefresh }) {
@@ -1782,21 +1756,12 @@ function DockerCard({ dockerTeam, cloudTitle, onOpen, onRename, onRefresh }) {
1782
1756
  const [busy, setBusy] = useState(""); // "" | bootstrap | restart | stop | upgrade | probe
1783
1757
  const [menuOpen, setMenuOpen] = useState(false);
1784
1758
  const [confirmRecreate, setConfirmRecreate] = useState(false); // 重建容器 in-app 确认弹窗(不用 native confirm)
1785
- // 容器里 cicy-code 的版本(卡片底部显示,主人: team bottom 以前有版本)。docker-only 下
1786
- // :8008 就是容器,running 时从 sidecar.versions() 的 running(= :8008 /api/health 版本)拿。
1787
- const [codeVer, setCodeVer] = useState(null);
1788
- useEffect(() => {
1789
- if (!status?.running) { setCodeVer(null); return; }
1790
- let dead = false;
1791
- window.cicy?.sidecar?.versions?.().then((v) => { if (!dead) setCodeVer((v && v.running) || null); }).catch(() => {});
1792
- return () => { dead = true; };
1793
- }, [status?.running]);
1794
1759
  // Inline rename (mirrors LocalTeamCard): double-click the title to edit.
1795
1760
  const [editing, setEditing] = useState(false);
1796
1761
  const [draft, setDraft] = useState("");
1797
1762
  // 标题 = 云端 team 的 title(和其它团队同一套;refreshCloudTeams 周期刷新 → 自动跟随
1798
- // 云端改名)。还没建好云端 team 时回退 "本地团队"(docker-only:这就是本地团队卡)
1799
- const displayName = cloudTitle || "本地团队";
1763
+ // 云端改名)。还没建好云端 team 时回退 "Docker 团队"。
1764
+ const displayName = cloudTitle || "Docker 团队";
1800
1765
  const startEdit = (e) => { e?.stopPropagation?.(); setDraft(displayName); setEditing(true); };
1801
1766
  const commitName = async () => {
1802
1767
  setEditing(false);
@@ -1930,7 +1895,7 @@ function DockerCard({ dockerTeam, cloudTitle, onOpen, onRename, onRefresh }) {
1930
1895
  if (platform !== "win32" && platform !== "darwin") return null;
1931
1896
 
1932
1897
  // Distinct states (主人: 状态分清楚):
1933
- // running — :8008 container healthy → 打开
1898
+ // running — :8009 container healthy → 打开
1934
1899
  // dockerRunning — engine up, no container → 启动 (build/start container)
1935
1900
  // installed — Docker on disk but engine down → 启动 Docker
1936
1901
  // else — not installed → 下载安装
@@ -1972,10 +1937,10 @@ function DockerCard({ dockerTeam, cloudTitle, onOpen, onRename, onRefresh }) {
1972
1937
  const onCta = async () => {
1973
1938
  if (isBusy) return;
1974
1939
  if (running) {
1975
- // 主人令:打开很慢 → 先探这个 :8008 tab 开过没。开过(openedWc 里有它的
1940
+ // 主人令:打开很慢 → 先探这个 :8009 tab 开过没。开过(openedWc 里有它的
1976
1941
  // webContentsId)就**直接 active 秒切**,不再拿 token / 注册 team(那是慢的根)。
1977
1942
  try {
1978
- const r = await window.cicy?.tabs?.activateIfOpen?.("http://127.0.0.1:8008");
1943
+ const r = await window.cicy?.tabs?.activateIfOpen?.("http://127.0.0.1:8009");
1979
1944
  if (r?.active) return;
1980
1945
  } catch {}
1981
1946
  // 没开过 → 显示 loading(打开中…),走慢路径:拿容器 token 再开 tab。
@@ -2030,7 +1995,7 @@ function DockerCard({ dockerTeam, cloudTitle, onOpen, onRename, onRefresh }) {
2030
1995
  ref={menuRef}
2031
1996
  style={{ position: "fixed", top: menuPos.top, left: menuPos.left, width: MENU_W }}
2032
1997
  onClick={(e) => e.stopPropagation()}>
2033
- {/* cicy-code 操作:更新 / 重启 cicy-code / 停止(都是容器内的 cicy-code,不是 Docker 容器) */}
1998
+ {/* cicy-code 操作(容器内的 cicy-code:更新 / 重启 / 停止)*/}
2034
1999
  <button type="button" data-id="DockerCard-update" className="bcard__menu-item is-accent" onClick={runUpdate}>
2035
2000
  {tr("docker.update", "更新 cicy-code")}
2036
2001
  </button>
@@ -2042,16 +2007,16 @@ function DockerCard({ dockerTeam, cloudTitle, onOpen, onRename, onRefresh }) {
2042
2007
  onClick={() => runOp("stop", () => window.cicy.docker.appStop(), tr("docker.stopped", "已停止 cicy-code"))}>
2043
2008
  {tr("docker.stop", "停止 cicy-code")}
2044
2009
  </button>
2045
- {/* 常用:刷新窗口 / 帐单 / 地址 */}
2010
+ {/* 常用 */}
2046
2011
  <button type="button" data-id="DockerCard-reload" className="bcard__menu-item"
2047
- onClick={(e) => { e.stopPropagation(); setMenuOpen(false); window.cicy?.tabs?.reloadIfOpen?.("http://127.0.0.1:8008", displayName); }}>
2012
+ onClick={(e) => { e.stopPropagation(); setMenuOpen(false); window.cicy?.tabs?.reloadIfOpen?.("http://127.0.0.1:8009", "Docker 团队"); }}>
2048
2013
  {tr("docker.reloadWindow", "刷新窗口")}
2049
2014
  </button>
2050
2015
  <button type="button" data-id="DockerCard-billing" className="bcard__menu-item"
2051
2016
  onClick={() => { setMenuOpen(false); openCloudPage(dockerTeam?.cloud_team_id ? `?team=${encodeURIComponent(dockerTeam.cloud_team_id)}` : "?view=usage"); }}>
2052
2017
  {tr("docker.billing", "帐单")}
2053
2018
  </button>
2054
- {/* 分隔线:下面是真正操作整个 Docker 容器的(重启 Docker / 重建 Docker) */}
2019
+ {/* 分隔线:下面是操作整个 Docker 容器的(重启 Docker / 重建 Docker)*/}
2055
2020
  <div className="bcard__menu-sep" data-id="DockerCard-menu-sep" role="separator" aria-hidden />
2056
2021
  <button type="button" data-id="DockerCard-docker-restart" className="bcard__menu-item"
2057
2022
  onClick={() => runOp("restart", () => window.cicy.docker.appDockerRestart(), tr("docker.dockerRestarted", "已重启 Docker 容器"))}>
@@ -2068,7 +2033,7 @@ function DockerCard({ dockerTeam, cloudTitle, onOpen, onRename, onRefresh }) {
2068
2033
  )}
2069
2034
  </div>
2070
2035
  <div className="bcard__body">
2071
- {/* 8008 现在有独立云端 team(cloud_team_id 是它自己的,不再和 8008 串),所以
2036
+ {/* 8009 现在有独立云端 team(cloud_team_id 是它自己的,不再和 8008 串),所以
2072
2037
  标题可改名:本地节点名 + 云端 PATCH 双写(onRename 在父组件处理)。 */}
2073
2038
  <div style={{ height: 28, display: "flex", alignItems: "center" }}>
2074
2039
  {editing ? (
@@ -2092,10 +2057,7 @@ function DockerCard({ dockerTeam, cloudTitle, onOpen, onRename, onRefresh }) {
2092
2057
  </h3>
2093
2058
  )}
2094
2059
  </div>
2095
- <div className="bcard__meta">
2096
- <span className="bcard__chip">Docker</span>
2097
- {codeVer && <span className="bcard__ver" data-id="DockerCard-ver" style={{ marginLeft: 8, fontSize: 11, opacity: 0.6 }}>v{codeVer}</span>}
2098
- </div>
2060
+ <div className="bcard__meta"><span className="bcard__chip">Docker</span></div>
2099
2061
  </div>
2100
2062
  <button
2101
2063
  type="button"
@@ -2546,13 +2508,13 @@ function isLocalSidecar(baseUrl) {
2546
2508
  } catch { return false; }
2547
2509
  }
2548
2510
 
2549
- // The Docker-版 cicy-code instance — localhost:8008. Owned by <DockerCard>, so
2511
+ // The Docker-版 cicy-code instance — localhost:8009. Owned by <DockerCard>, so
2550
2512
  // it's filtered out of the generic node lists.
2551
2513
  function isDockerApp(baseUrl) {
2552
2514
  try {
2553
2515
  const p = new URL(baseUrl);
2554
2516
  const local = p.hostname === "127.0.0.1" || p.hostname === "localhost" || p.hostname === "::1";
2555
- return local && p.port === "8008";
2517
+ return local && p.port === "8009";
2556
2518
  } catch { return false; }
2557
2519
  }
2558
2520