cicy-desktop 2.1.65 → 2.1.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/backends/homepage-preload.js +7 -0
- package/src/backends/homepage-react/assets/{index-BV0z4aaf.css → index-ByYzSssW.css} +1 -1
- package/src/backends/homepage-react/assets/index-DHvg4jSd.js +49 -0
- package/src/backends/homepage-react/index.html +2 -2
- package/src/backends/sidecar-ipc.js +8 -4
- package/src/sidecar/cicy-code.js +15 -4
- package/src/sidecar/native.js +26 -5
- package/workers/render/src/App.css +18 -0
- package/workers/render/src/App.jsx +31 -4
- package/src/backends/homepage-react/assets/index-C0vFqx44.js +0 -49
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>CiCy Desktop</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-DHvg4jSd.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="./assets/index-ByYzSssW.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
@@ -116,16 +116,20 @@ function register({ sidecarLogPath } = {}) {
|
|
|
116
116
|
// Update: stop + spawn cicy-code@latest (or reload the Docker image on
|
|
117
117
|
// win32). The npx re-resolve / image pull can take a while on a cold cache,
|
|
118
118
|
// so allow a longer window for :8008 to come back.
|
|
119
|
-
ipcMain.handle("sidecar:update", async () => {
|
|
119
|
+
ipcMain.handle("sidecar:update", async (e) => {
|
|
120
|
+
// Stream phase/progress events to the homepage so the user SEES the
|
|
121
|
+
// update working (download %, swap, restart) instead of a frozen label.
|
|
122
|
+
const emit = (ev) => { try { e.sender.send("sidecar:op-progress", { op: "update", ...ev }); } catch {} };
|
|
120
123
|
try {
|
|
121
|
-
const child = await sidecar.update({ logPath: sidecarLogPath });
|
|
124
|
+
const child = await sidecar.update({ logPath: sidecarLogPath, port: PORT, emit });
|
|
122
125
|
for (let i = 0; i < 240; i++) {
|
|
123
126
|
if (await sidecar.probeExisting(PORT)) return { ok: true, pid: child?.pid || null };
|
|
124
127
|
await new Promise((r) => setTimeout(r, 250));
|
|
125
128
|
}
|
|
126
129
|
return { ok: true, pid: child?.pid || null, warning: "updated but did not bind :8008 within 60s" };
|
|
127
|
-
} catch (
|
|
128
|
-
|
|
130
|
+
} catch (err) {
|
|
131
|
+
emit({ phase: "done", status: "error", message: `更新失败:${err.message}` });
|
|
132
|
+
return { ok: false, error: err.message };
|
|
129
133
|
}
|
|
130
134
|
});
|
|
131
135
|
}
|
package/src/sidecar/cicy-code.js
CHANGED
|
@@ -219,17 +219,28 @@ async function restart({ logPath, port = DEFAULT_PORT } = {}) {
|
|
|
219
219
|
// win32 → reload the Docker image (from R2) and re-run the container.
|
|
220
220
|
// else → clear the npx cache + spawn `cicy-code@latest` so npx re-resolves
|
|
221
221
|
// against the registry (npmmirror for CN) and pulls a newer build.
|
|
222
|
-
async function update({ logPath, port = DEFAULT_PORT } = {}) {
|
|
223
|
-
|
|
222
|
+
async function update({ logPath, port = DEFAULT_PORT, emit } = {}) {
|
|
223
|
+
const e = emit || (() => {});
|
|
224
224
|
if (process.platform === "win32") {
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
// NATIVE route: safe-swap update with real download progress.
|
|
226
|
+
if (process.env.CICY_WIN_NATIVE === "1") {
|
|
227
|
+
return require("./native").update({ port, logPath, emit });
|
|
228
|
+
}
|
|
229
|
+
// Transitional Docker route. loadImage streams 下载镜像 % via emit.
|
|
230
|
+
await stop({ port });
|
|
231
|
+
try { await require("./docker").loadImage({ emit }); } catch (err) {
|
|
232
|
+
console.warn(`[cicy-code-sidecar] docker image reload failed: ${err.message}`);
|
|
233
|
+
e({ phase: "download", status: "error", message: `镜像更新失败:${err.message}` });
|
|
227
234
|
}
|
|
228
235
|
await new Promise(r => setTimeout(r, 300));
|
|
236
|
+
e({ phase: "swap", status: "running", message: "重启容器…" });
|
|
229
237
|
return start({ logPath, port, force: true });
|
|
230
238
|
}
|
|
239
|
+
e({ phase: "download", status: "running", message: "获取最新版 cicy-code…" });
|
|
240
|
+
await stop({ port });
|
|
231
241
|
clearNpxCache();
|
|
232
242
|
await new Promise(r => setTimeout(r, 300));
|
|
243
|
+
e({ phase: "swap", status: "running", message: "启动新版本…" });
|
|
233
244
|
return start({ logPath, port, force: true, version: "latest" });
|
|
234
245
|
}
|
|
235
246
|
|
package/src/sidecar/native.js
CHANGED
|
@@ -167,12 +167,33 @@ async function restart({ port = 8008, logPath = null } = {}) {
|
|
|
167
167
|
return start({ port, logPath });
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
// Update
|
|
171
|
-
//
|
|
170
|
+
// Update — SAFE swap order: acquire the new build FIRST (progress streamed),
|
|
171
|
+
// only then stop the running instance and switch. A dead network therefore
|
|
172
|
+
// fails the update loudly but never kills a working install. Dev route
|
|
173
|
+
// downloads to .new and renames; npm route re-resolves @latest in place
|
|
174
|
+
// (npm's own staging is already atomic).
|
|
172
175
|
async function update({ port = 8008, logPath = null, emit } = {}) {
|
|
173
|
-
|
|
174
|
-
if (devExeUrl()) {
|
|
175
|
-
|
|
176
|
+
const e = emit || (() => {});
|
|
177
|
+
if (devExeUrl()) {
|
|
178
|
+
const tmp = DEV_EXE_PATH + ".new";
|
|
179
|
+
try { fs.unlinkSync(tmp); } catch {}
|
|
180
|
+
e({ phase: "download", status: "running", message: "下载新版 cicy-code.exe…", progress: 0 });
|
|
181
|
+
await docker.ensureDownloaded(devExeUrl(), tmp, null, { emit, phase: "download", label: "下载新版" });
|
|
182
|
+
if (!fs.existsSync(tmp)) throw new Error("下载未完成,保留当前版本");
|
|
183
|
+
e({ phase: "swap", status: "running", message: "停止旧版本…" });
|
|
184
|
+
await stop({ port });
|
|
185
|
+
try { fs.unlinkSync(DEV_EXE_PATH + ".bak"); } catch {}
|
|
186
|
+
try { fs.renameSync(DEV_EXE_PATH, DEV_EXE_PATH + ".bak"); } catch {}
|
|
187
|
+
fs.renameSync(tmp, DEV_EXE_PATH);
|
|
188
|
+
} else {
|
|
189
|
+
e({ phase: "download", status: "running", message: "npm 获取最新版…" });
|
|
190
|
+
await ensureExe({ emit, version: "latest" }); // service untouched until this succeeds
|
|
191
|
+
e({ phase: "swap", status: "running", message: "重启服务…" });
|
|
192
|
+
await stop({ port });
|
|
193
|
+
}
|
|
194
|
+
const r = await start({ port, logPath, emit });
|
|
195
|
+
e({ phase: "done", status: r ? "done" : "error", message: r ? "已更新并重启" : "更新后启动失败" });
|
|
196
|
+
return r;
|
|
176
197
|
}
|
|
177
198
|
|
|
178
199
|
async function checkStatus({ port = 8008 } = {}) {
|
|
@@ -733,3 +733,21 @@ body {
|
|
|
733
733
|
font-size: 11.5px; color: #9ca3af;
|
|
734
734
|
word-break: break-word;
|
|
735
735
|
}
|
|
736
|
+
|
|
737
|
+
/* live op progress on the local team card (更新 download %/phase stream) */
|
|
738
|
+
.bcard__prog {
|
|
739
|
+
display: flex; flex-direction: column; gap: 4px;
|
|
740
|
+
margin-top: 6px; font-size: 12px; line-height: 1.35;
|
|
741
|
+
color: var(--text-dim, #9da7b3);
|
|
742
|
+
}
|
|
743
|
+
.bcard__prog[data-status="error"] .bcard__progmsg { color: #f87171; }
|
|
744
|
+
.bcard__prog[data-status="done"] .bcard__progmsg { color: #4ade80; }
|
|
745
|
+
.bcard__progbar {
|
|
746
|
+
display: block; height: 4px; border-radius: 2px;
|
|
747
|
+
background: rgba(125, 135, 150, 0.25); overflow: hidden;
|
|
748
|
+
}
|
|
749
|
+
.bcard__progbar > span {
|
|
750
|
+
display: block; height: 100%; border-radius: 2px;
|
|
751
|
+
background: var(--accent, #3b82f6);
|
|
752
|
+
transition: width 0.25s ease;
|
|
753
|
+
}
|
|
@@ -543,6 +543,7 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
|
|
|
543
543
|
const running = team.status === "running";
|
|
544
544
|
const [busy, setBusy] = useState(""); // "" | start | restart | update | stop
|
|
545
545
|
const [opMsg, setOpMsg] = useState("");
|
|
546
|
+
const [opProg, setOpProg] = useState(null); // live {message, progress?, status} during 更新
|
|
546
547
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
547
548
|
const [latest, setLatest] = useState(null); // newest cicy-code on the registry
|
|
548
549
|
const [checking, setChecking] = useState(false);
|
|
@@ -610,7 +611,15 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
|
|
|
610
611
|
const runOp = async (kind, fn, doneText) => {
|
|
611
612
|
setMenuOpen(false);
|
|
612
613
|
if (busy) return;
|
|
613
|
-
setBusy(kind); setOpMsg("");
|
|
614
|
+
setBusy(kind); setOpMsg(""); setOpProg(null);
|
|
615
|
+
// 更新 streams real phase/percent events from the main process — surface
|
|
616
|
+
// them live on the card so the user SEES the download/swap happening.
|
|
617
|
+
let unsub = null;
|
|
618
|
+
if (kind === "update" && window.cicy?.sidecar?.onOpProgress) {
|
|
619
|
+
unsub = window.cicy.sidecar.onOpProgress((ev) => {
|
|
620
|
+
if (ev?.op === "update") setOpProg(ev);
|
|
621
|
+
});
|
|
622
|
+
}
|
|
614
623
|
try {
|
|
615
624
|
const r = await fn();
|
|
616
625
|
setOpMsg(r?.ok
|
|
@@ -619,8 +628,10 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
|
|
|
619
628
|
} catch (err) {
|
|
620
629
|
setOpMsg(tr("sidecar.failed", "操作失败") + `: ${err?.message || err}`);
|
|
621
630
|
} finally {
|
|
622
|
-
|
|
631
|
+
try { unsub && unsub(); } catch {}
|
|
632
|
+
setBusy(""); setOpProg(null);
|
|
623
633
|
onRefresh?.(); // re-probe so the status dot/chip catches up
|
|
634
|
+
setTimeout(() => setOpMsg(""), 5000); // result line is transient
|
|
624
635
|
}
|
|
625
636
|
};
|
|
626
637
|
const BUSY_LABEL = { start: "启动中…", restart: "重启中…", update: "更新中…", stop: "停止中…" };
|
|
@@ -771,6 +782,22 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
|
|
|
771
782
|
<span className="bcard__ver" data-id="LocalTeamCard-version">v{team.version}</span>
|
|
772
783
|
)}
|
|
773
784
|
</div>
|
|
785
|
+
{busy && (
|
|
786
|
+
<div className="bcard__prog" data-id="LocalTeamCard-progress" data-status={opProg?.status || "running"}>
|
|
787
|
+
<span className="bcard__progmsg">
|
|
788
|
+
{opProg?.message || BUSY_LABEL[busy] || `${busy}…`}
|
|
789
|
+
{Number.isFinite(opProg?.progress) ? ` ${opProg.progress}%` : ""}
|
|
790
|
+
</span>
|
|
791
|
+
{Number.isFinite(opProg?.progress) && (
|
|
792
|
+
<span className="bcard__progbar"><span style={{ width: `${Math.min(100, opProg.progress)}%` }} /></span>
|
|
793
|
+
)}
|
|
794
|
+
</div>
|
|
795
|
+
)}
|
|
796
|
+
{!busy && opMsg && (
|
|
797
|
+
<div className="bcard__prog" data-id="LocalTeamCard-progress" data-status={/失败|error/i.test(opMsg) ? "error" : "done"}>
|
|
798
|
+
<span className="bcard__progmsg">{opMsg}</span>
|
|
799
|
+
</div>
|
|
800
|
+
)}
|
|
774
801
|
</div>
|
|
775
802
|
<button
|
|
776
803
|
type="button"
|
|
@@ -779,8 +806,8 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
|
|
|
779
806
|
disabled={!!busy || !team.base_url}
|
|
780
807
|
onClick={handleOpen}
|
|
781
808
|
>
|
|
782
|
-
{busy
|
|
783
|
-
<span>{openLabel}</span>
|
|
809
|
+
{busy && busy !== "stop" ? <Spinner /> : <ArrowIcon />}
|
|
810
|
+
<span>{busy ? (BUSY_LABEL[busy] || openLabel) : openLabel}</span>
|
|
784
811
|
</button>
|
|
785
812
|
</div>
|
|
786
813
|
);
|