@bakapiano/ccsm 0.15.0 → 0.15.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/lib/config.js +13 -19
- package/package.json +1 -1
- package/public/js/pages/AboutPage.js +9 -1
- package/public/js/pages/ConfigurePage.js +9 -1
- package/scripts/restart-helper.js +6 -1
- package/scripts/upgrade-helper.js +6 -1
- package/server.js +7 -3
package/lib/config.js
CHANGED
|
@@ -130,26 +130,20 @@ function mergeWithDefaults(partial) {
|
|
|
130
130
|
if (existing) {
|
|
131
131
|
existing.builtin = true;
|
|
132
132
|
// Backfill defaults from the built-in template for any field the
|
|
133
|
-
// user's saved copy is missing
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
|
|
133
|
+
// user's saved copy is missing OR has as an empty array. Empty
|
|
134
|
+
// arrays matter because users upgrading from a pre-0.15 config
|
|
135
|
+
// never wrote `newSessionIdArgs` (didn't exist), AND a partial
|
|
136
|
+
// 0.14→0.15 dev iteration shipped codex with `[]`. Treat both
|
|
137
|
+
// the same: a builtin with no template means "use the canonical
|
|
138
|
+
// one ccsm now knows about", since these fields are the
|
|
139
|
+
// integration contract with the upstream CLI — not user knobs.
|
|
140
|
+
const needsBackfill = (v) => v == null || (Array.isArray(v) && v.length === 0);
|
|
141
|
+
if (needsBackfill(existing.resumeIdArgs)) existing.resumeIdArgs = def.resumeIdArgs;
|
|
142
|
+
if (needsBackfill(existing.newSessionIdArgs)) existing.newSessionIdArgs = def.newSessionIdArgs;
|
|
143
|
+
// Drop the v0.x `resumeArgs` fallback — every builtin now has a
|
|
144
|
+
// pre-assigned UUID (claude/copilot via flag, codex via seed) so
|
|
145
|
+
// resumeIdArgs always applies on resume; the field is dead weight.
|
|
141
146
|
delete existing.resumeArgs;
|
|
142
|
-
// Special-case codex: an unreleased earlier iteration of this
|
|
143
|
-
// schema shipped `newSessionIdArgs: []` for codex. The seeded-file
|
|
144
|
-
// trick (lib/codexSeed) now lets us pre-assign, so backfill the
|
|
145
|
-
// ['resume','<id>'] template over an empty array too.
|
|
146
|
-
if (existing.id === 'codex'
|
|
147
|
-
&& Array.isArray(existing.newSessionIdArgs)
|
|
148
|
-
&& existing.newSessionIdArgs.length === 0
|
|
149
|
-
&& Array.isArray(def.newSessionIdArgs)
|
|
150
|
-
&& def.newSessionIdArgs.length > 0) {
|
|
151
|
-
existing.newSessionIdArgs = def.newSessionIdArgs;
|
|
152
|
-
}
|
|
153
147
|
if (!existing.type) existing.type = def.type;
|
|
154
148
|
} else {
|
|
155
149
|
out.clis.unshift({ ...def });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bakapiano/ccsm",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Claude Code Session Manager — Windows web UI to manage many concurrent claude sessions: live list, snapshot/restore, focus existing window, new session in an isolated workspace with repo clones",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "server.js",
|
|
@@ -65,8 +65,16 @@ function UpgradeCard() {
|
|
|
65
65
|
if (!info?.updateAvailable) return;
|
|
66
66
|
setUpgrading(true);
|
|
67
67
|
try {
|
|
68
|
-
await api('POST', '/api/upgrade', { target: 'latest' });
|
|
68
|
+
const r = await api('POST', '/api/upgrade', { target: 'latest' });
|
|
69
69
|
setToast(`upgrading to v${info.latest} · backend will restart`);
|
|
70
|
+
if (r?.closeFrontend) {
|
|
71
|
+
// Backend will respawn with a fresh browser window — close this
|
|
72
|
+
// one so the user isn't stuck on the OfflineBanner during the
|
|
73
|
+
// upgrade window. window.close() only works when the window was
|
|
74
|
+
// script-opened (Edge --app=, our spawned browser); regular tabs
|
|
75
|
+
// ignore it silently, which is fine (OfflineBanner takes over).
|
|
76
|
+
setTimeout(() => { try { window.close(); } catch {} }, 400);
|
|
77
|
+
}
|
|
70
78
|
} catch (e) {
|
|
71
79
|
setUpgrading(false);
|
|
72
80
|
setToast(e.message, 'error');
|
|
@@ -431,8 +431,16 @@ function RestartButton() {
|
|
|
431
431
|
if (!ok) return;
|
|
432
432
|
setBusy(true);
|
|
433
433
|
try {
|
|
434
|
-
await restartBackend();
|
|
434
|
+
const r = await restartBackend();
|
|
435
435
|
setToast('restarting backend…');
|
|
436
|
+
if (r?.closeFrontend) {
|
|
437
|
+
// Backend respawn will pop a fresh browser window — close this
|
|
438
|
+
// one so the user isn't stuck on the OfflineBanner during the
|
|
439
|
+
// ~3s downtime. window.close() only fires in script-opened
|
|
440
|
+
// windows (Edge --app=); regular tabs ignore it and stay open,
|
|
441
|
+
// which is the right behavior for them.
|
|
442
|
+
setTimeout(() => { try { window.close(); } catch {} }, 400);
|
|
443
|
+
}
|
|
436
444
|
} catch (e) {
|
|
437
445
|
setBusy(false);
|
|
438
446
|
setToast(e.message, 'error');
|
|
@@ -62,7 +62,12 @@ function pidAlive(pid) {
|
|
|
62
62
|
|
|
63
63
|
const isWin = process.platform === 'win32';
|
|
64
64
|
const ccsmCmd = isWin ? 'ccsm.cmd' : 'ccsm';
|
|
65
|
-
|
|
65
|
+
// Inherit env but DROP CCSM_NO_BROWSER so the respawned server pops a
|
|
66
|
+
// fresh browser window — the frontend that triggered the restart
|
|
67
|
+
// called window.close() in parallel, and the new window takes its
|
|
68
|
+
// place without the OfflineBanner gap.
|
|
69
|
+
const childEnv = { ...process.env };
|
|
70
|
+
delete childEnv.CCSM_NO_BROWSER;
|
|
66
71
|
let exe, exeArgs;
|
|
67
72
|
if (isWin) {
|
|
68
73
|
exe = process.env.ComSpec || 'cmd.exe';
|
|
@@ -126,7 +126,12 @@ function pidAlive(pid) {
|
|
|
126
126
|
const ccsmCmd = installPrefix
|
|
127
127
|
? (isWin ? path.join(installPrefix, 'ccsm.cmd') : path.join(installPrefix, 'bin', 'ccsm'))
|
|
128
128
|
: (isWin ? 'ccsm.cmd' : 'ccsm');
|
|
129
|
-
|
|
129
|
+
// Drop CCSM_NO_BROWSER so the post-upgrade server pops a fresh window
|
|
130
|
+
// — the frontend that triggered the upgrade window.close()'d in
|
|
131
|
+
// parallel, and the new window takes its place. Skips the
|
|
132
|
+
// OfflineBanner gap that used to bridge the upgrade.
|
|
133
|
+
const childEnv = { ...process.env };
|
|
134
|
+
delete childEnv.CCSM_NO_BROWSER;
|
|
130
135
|
let exe, exeArgs;
|
|
131
136
|
if (isWin) {
|
|
132
137
|
exe = process.env.ComSpec || 'cmd.exe';
|
package/server.js
CHANGED
|
@@ -926,7 +926,7 @@ app.post('/api/restart', asyncH(async (_req, res) => {
|
|
|
926
926
|
restartInFlight = true;
|
|
927
927
|
|
|
928
928
|
if (process.env.CCSM_DEV === '1') {
|
|
929
|
-
res.json({ ok: true, started: true, mode: 'dev' });
|
|
929
|
+
res.json({ ok: true, started: true, mode: 'dev', closeFrontend: false });
|
|
930
930
|
setImmediate(() => gracefulShutdown('restart (dev)'));
|
|
931
931
|
return;
|
|
932
932
|
}
|
|
@@ -941,7 +941,11 @@ app.post('/api/restart', asyncH(async (_req, res) => {
|
|
|
941
941
|
return res.status(500).json({ error: `helper copy failed: ${e.message}` });
|
|
942
942
|
}
|
|
943
943
|
const args = [helperTmp, String(currentPort), String(process.pid)];
|
|
944
|
-
|
|
944
|
+
// closeFrontend asks the calling tab to window.close() itself — the
|
|
945
|
+
// helper will respawn ccsm WITHOUT CCSM_NO_BROWSER, so a fresh window
|
|
946
|
+
// pops up once the new backend is listening. Net effect: the user
|
|
947
|
+
// never sees the OfflineBanner during a restart.
|
|
948
|
+
res.json({ ok: true, started: true, helper: helperTmp, closeFrontend: true });
|
|
945
949
|
|
|
946
950
|
setImmediate(() => {
|
|
947
951
|
const { spawn } = require('node:child_process');
|
|
@@ -1081,7 +1085,7 @@ app.post('/api/upgrade', asyncH(async (req, res) => {
|
|
|
1081
1085
|
}
|
|
1082
1086
|
const args = [helperTmp, target, String(currentPort), String(process.pid), installPrefix, respawn];
|
|
1083
1087
|
|
|
1084
|
-
res.json({ ok: true, started: true, target, helper: helperTmp });
|
|
1088
|
+
res.json({ ok: true, started: true, target, helper: helperTmp, closeFrontend: true });
|
|
1085
1089
|
|
|
1086
1090
|
// Flush response, then spawn helper detached and gracefulShutdown so
|
|
1087
1091
|
// the helper's npm install isn't fighting our open file handles.
|