@bakapiano/ccsm 0.15.2 → 0.15.3
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/server.js +94 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bakapiano/ccsm",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.3",
|
|
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",
|
package/server.js
CHANGED
|
@@ -1145,18 +1145,108 @@ function findAppModeBrowser() {
|
|
|
1145
1145
|
return null;
|
|
1146
1146
|
}
|
|
1147
1147
|
|
|
1148
|
-
//
|
|
1149
|
-
//
|
|
1150
|
-
//
|
|
1151
|
-
//
|
|
1148
|
+
// Look for a Chrome/Edge PWA that the user already installed locally
|
|
1149
|
+
// pointing at the ccsm frontend. When found, we launch it via
|
|
1150
|
+
// `chrome.exe --profile-directory=... --app-id=<id>` — same as the
|
|
1151
|
+
// shortcut Start Menu creates at install time. That path opens the
|
|
1152
|
+
// PWA fully chromeless (respects manifest display:standalone + WCO).
|
|
1153
|
+
// Without this we'd fall back to `--app=<URL> --user-data-dir=<ours>`
|
|
1154
|
+
// which uses an isolated profile that doesn't see the install, so
|
|
1155
|
+
// Chrome shows a minimal-ui address bar.
|
|
1156
|
+
function findInstalledCcsmPwa() {
|
|
1157
|
+
if (process.platform !== 'win32') return null;
|
|
1158
|
+
const appData = process.env.APPDATA;
|
|
1159
|
+
if (!appData) return null;
|
|
1160
|
+
const fs = require('node:fs');
|
|
1161
|
+
const startMenu = path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs');
|
|
1162
|
+
const dirs = [
|
|
1163
|
+
path.join(startMenu, 'Chrome Apps'),
|
|
1164
|
+
path.join(startMenu, 'Edge Apps'),
|
|
1165
|
+
];
|
|
1166
|
+
const candidates = [];
|
|
1167
|
+
for (const dir of dirs) {
|
|
1168
|
+
let names;
|
|
1169
|
+
try { names = fs.readdirSync(dir); } catch { continue; }
|
|
1170
|
+
for (const name of names) {
|
|
1171
|
+
if (!name.toLowerCase().endsWith('.lnk')) continue;
|
|
1172
|
+
// Filter by filename — Chrome names PWA shortcuts after the
|
|
1173
|
+
// manifest's short_name/name. CCSM matches our manifest.
|
|
1174
|
+
if (!/ccsm/i.test(name)) continue;
|
|
1175
|
+
const full = path.join(dir, name);
|
|
1176
|
+
try {
|
|
1177
|
+
candidates.push({ name, path: full, mtime: fs.statSync(full).mtimeMs });
|
|
1178
|
+
} catch {}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
if (candidates.length === 0) return null;
|
|
1182
|
+
// Newest install wins (covers the case where the user re-installed
|
|
1183
|
+
// and accumulated CCSM, CCSM (1), etc.).
|
|
1184
|
+
candidates.sort((a, b) => b.mtime - a.mtime);
|
|
1185
|
+
// Resolve via WScript.Shell COM. Single PowerShell call enumerates
|
|
1186
|
+
// every candidate; we stop at the first one whose target looks like
|
|
1187
|
+
// a Chrome/Edge binary and whose args carry an --app-id.
|
|
1188
|
+
const { spawnSync } = require('node:child_process');
|
|
1189
|
+
const psPaths = candidates
|
|
1190
|
+
.map((c) => `'${c.path.replace(/'/g, "''")}'`).join(',');
|
|
1191
|
+
const script = `
|
|
1192
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
1193
|
+
$wsh = New-Object -ComObject WScript.Shell
|
|
1194
|
+
foreach ($p in @(${psPaths})) {
|
|
1195
|
+
$sc = $wsh.CreateShortcut($p)
|
|
1196
|
+
Write-Output ($sc.TargetPath + '|' + $sc.Arguments)
|
|
1197
|
+
}`;
|
|
1198
|
+
const r = spawnSync('powershell.exe',
|
|
1199
|
+
['-NoProfile', '-NonInteractive', '-Command', script],
|
|
1200
|
+
{ encoding: 'utf8', windowsHide: true });
|
|
1201
|
+
if (r.status !== 0 || !r.stdout) return null;
|
|
1202
|
+
for (const line of r.stdout.split(/\r?\n/)) {
|
|
1203
|
+
if (!line.trim()) continue;
|
|
1204
|
+
const sep = line.indexOf('|');
|
|
1205
|
+
if (sep < 0) continue;
|
|
1206
|
+
const target = line.slice(0, sep).trim();
|
|
1207
|
+
const args = line.slice(sep + 1).trim();
|
|
1208
|
+
if (!/chrome(_proxy)?\.exe$|msedge(_proxy)?\.exe$/i.test(target)) continue;
|
|
1209
|
+
const appId = (args.match(/--app-id=(\S+)/) || [])[1];
|
|
1210
|
+
if (!appId) continue;
|
|
1211
|
+
const profile = (args.match(/--profile-directory=(\S+)/) || [])[1] || 'Default';
|
|
1212
|
+
return { browserPath: target, appId, profile };
|
|
1213
|
+
}
|
|
1214
|
+
return null;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// Auto-open the frontend in a browser when ccsm boots. Strategy:
|
|
1218
|
+
// 1. If the user already installed the CCSM PWA, launch THAT (fully
|
|
1219
|
+
// chromeless via --app-id, uses user's main browser profile).
|
|
1220
|
+
// 2. Otherwise try a generic --app= window in an isolated profile —
|
|
1221
|
+
// this shows a thin minimal-ui address bar but at least it's
|
|
1222
|
+
// a dedicated window.
|
|
1223
|
+
// 3. Fall back to the OS default browser as a regular tab.
|
|
1224
|
+
// On non-Windows we skip — the bundled launcher isn't ported yet.
|
|
1152
1225
|
function openInBrowser(url) {
|
|
1153
1226
|
if (process.platform !== 'win32') return { kind: 'none', child: null };
|
|
1154
1227
|
const { spawn } = require('node:child_process');
|
|
1155
1228
|
const fs = require('node:fs');
|
|
1229
|
+
|
|
1230
|
+
const installed = findInstalledCcsmPwa();
|
|
1231
|
+
if (installed) {
|
|
1232
|
+
console.log(`[ccsm] launching installed PWA · app-id=${installed.appId} profile=${installed.profile}`);
|
|
1233
|
+
const child = spawn(
|
|
1234
|
+
installed.browserPath,
|
|
1235
|
+
[
|
|
1236
|
+
`--profile-directory=${installed.profile}`,
|
|
1237
|
+
`--app-id=${installed.appId}`,
|
|
1238
|
+
],
|
|
1239
|
+
{ detached: true, stdio: 'ignore' }
|
|
1240
|
+
);
|
|
1241
|
+
child.unref();
|
|
1242
|
+
return { kind: 'pwa', child };
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1156
1245
|
const exe = findAppModeBrowser();
|
|
1157
1246
|
if (exe) {
|
|
1158
1247
|
const profileDir = path.join(DATA_DIR, 'browser-profile');
|
|
1159
1248
|
fs.mkdirSync(profileDir, { recursive: true });
|
|
1249
|
+
console.log(`[ccsm] no installed PWA found · falling back to --app= window`);
|
|
1160
1250
|
const child = spawn(
|
|
1161
1251
|
exe,
|
|
1162
1252
|
[
|