bingocode 1.1.171 → 1.1.172
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/entrypoints/tray-only.ts +89 -7
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import path from 'path';
|
|
|
9
9
|
import fs from 'fs';
|
|
10
10
|
import os from 'os';
|
|
11
11
|
import http from 'http';
|
|
12
|
+
import { execSync } from 'child_process';
|
|
12
13
|
import { fileURLToPath } from 'url';
|
|
13
14
|
|
|
14
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -20,6 +21,65 @@ const HOST = process.env.BINGO_SERVER_HOST || '127.0.0.1';
|
|
|
20
21
|
|
|
21
22
|
let serverHandle: any = null;
|
|
22
23
|
|
|
24
|
+
// ── Autostart helpers (Windows Startup folder, fallback to registry) ────────
|
|
25
|
+
const STARTUP_DIR = path.join(os.homedir(), 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
|
|
26
|
+
const STARTUP_CMD = path.join(STARTUP_DIR, 'Bingo.cmd');
|
|
27
|
+
const REG_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';
|
|
28
|
+
const REG_NAME = 'Bingo';
|
|
29
|
+
|
|
30
|
+
function isAutostartEnabled(): boolean {
|
|
31
|
+
if (process.platform !== 'win32') return false;
|
|
32
|
+
if (fs.existsSync(STARTUP_CMD)) return true;
|
|
33
|
+
try {
|
|
34
|
+
execSync(`reg query "${REG_KEY}" /v ${REG_NAME}`, { stdio: 'ignore' });
|
|
35
|
+
return true;
|
|
36
|
+
} catch { return false; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function findBunExe(): string {
|
|
40
|
+
const appData = process.env.APPDATA || '';
|
|
41
|
+
const home = os.homedir();
|
|
42
|
+
const candidates = [
|
|
43
|
+
path.join(appData, 'npm', 'node_modules', 'bun', 'bin', 'bun.exe'),
|
|
44
|
+
path.join(home, '.bun', 'bin', 'bun.exe'),
|
|
45
|
+
];
|
|
46
|
+
// also search PATH
|
|
47
|
+
for (const dir of (process.env.PATH || '').split(';')) {
|
|
48
|
+
candidates.push(path.join(dir.trim(), 'bun.exe'));
|
|
49
|
+
}
|
|
50
|
+
return candidates.find(p => { try { return fs.existsSync(p); } catch { return false; } }) || 'bun';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function enableAutostart(): void {
|
|
54
|
+
if (process.platform !== 'win32') return;
|
|
55
|
+
const bunExe = findBunExe();
|
|
56
|
+
const preload = path.join(ROOT_DIR, 'preload.ts');
|
|
57
|
+
const trayEntry = path.join(ROOT_DIR, 'src', 'entrypoints', 'tray-only.ts');
|
|
58
|
+
// Startup .cmd: launch tray daemon only (no CLI window)
|
|
59
|
+
try {
|
|
60
|
+
const cmd = [
|
|
61
|
+
'@echo off',
|
|
62
|
+
`start "" /B "${bunExe}" "--preload=${preload}" "${trayEntry}"`,
|
|
63
|
+
'',
|
|
64
|
+
].join('\r\n');
|
|
65
|
+
fs.writeFileSync(STARTUP_CMD, cmd, { encoding: 'utf8' });
|
|
66
|
+
return;
|
|
67
|
+
} catch {}
|
|
68
|
+
// fallback: registry
|
|
69
|
+
try {
|
|
70
|
+
const val = `"${bunExe}" "--preload=${preload}" "${trayEntry}"`;
|
|
71
|
+
execSync(`reg add "${REG_KEY}" /v ${REG_NAME} /t REG_SZ /d "${val}" /f`);
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error('[tray] Failed to enable autostart:', e);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function disableAutostart(): void {
|
|
78
|
+
if (process.platform !== 'win32') return;
|
|
79
|
+
try { fs.unlinkSync(STARTUP_CMD); } catch {}
|
|
80
|
+
try { execSync(`reg delete "${REG_KEY}" /v ${REG_NAME} /f`, { stdio: 'ignore' }); } catch {}
|
|
81
|
+
}
|
|
82
|
+
|
|
23
83
|
// ── Daemon PID lock ──────────────────────────────────────────────────────
|
|
24
84
|
const DAEMON_LOCK_FILE = path.join(os.homedir(), '.claude-cli', 'runtime', 'daemon.lock');
|
|
25
85
|
try { fs.mkdirSync(path.dirname(DAEMON_LOCK_FILE), { recursive: true }); } catch {}
|
|
@@ -37,6 +97,13 @@ const iconB64 = fs.existsSync(_iconFile)
|
|
|
37
97
|
: _iconFallback;
|
|
38
98
|
|
|
39
99
|
try {
|
|
100
|
+
const autostartItem = {
|
|
101
|
+
title: (isAutostartEnabled() ? '✓ ' : '') + 'Run on ststem startup',
|
|
102
|
+
tooltip: 'Toggle auto-start Bingo on Windows login',
|
|
103
|
+
checked: false,
|
|
104
|
+
enabled: true,
|
|
105
|
+
};
|
|
106
|
+
|
|
40
107
|
const systray = new SysTray({
|
|
41
108
|
menu: {
|
|
42
109
|
icon: iconB64,
|
|
@@ -49,6 +116,7 @@ try {
|
|
|
49
116
|
checked: false,
|
|
50
117
|
enabled: false,
|
|
51
118
|
},
|
|
119
|
+
autostartItem,
|
|
52
120
|
{
|
|
53
121
|
title: 'Exit Bingo',
|
|
54
122
|
tooltip: 'Stop all bingo services',
|
|
@@ -62,18 +130,32 @@ try {
|
|
|
62
130
|
});
|
|
63
131
|
|
|
64
132
|
systray.onClick((action: any) => {
|
|
65
|
-
|
|
66
|
-
|
|
133
|
+
if (action.item?.title?.includes('Start on Login')) {
|
|
134
|
+
if (isAutostartEnabled()) {
|
|
135
|
+
disableAutostart();
|
|
136
|
+
autostartItem.title = 'Start on Login';
|
|
137
|
+
} else {
|
|
138
|
+
enableAutostart();
|
|
139
|
+
autostartItem.title = '✓ Start on Login';
|
|
140
|
+
}
|
|
141
|
+
systray.sendAction({ type: 'update-item', item: autostartItem });
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (action.item?.title === 'Exit Bingo') {
|
|
67
145
|
console.log('[tray] Exiting via tray menu');
|
|
146
|
+
// try graceful server shutdown, then force exit regardless
|
|
68
147
|
const req = http.request(
|
|
69
148
|
`http://${HOST}:${PORT}/exit`,
|
|
70
|
-
{ method: 'POST', timeout:
|
|
71
|
-
() => {
|
|
149
|
+
{ method: 'POST', timeout: 3000 },
|
|
150
|
+
() => {
|
|
151
|
+
// server responded (any status) → kill tray + exit
|
|
152
|
+
try { systray.kill(false); } catch {}
|
|
153
|
+
process.exit(0);
|
|
154
|
+
},
|
|
72
155
|
);
|
|
73
156
|
req.on('error', () => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
} catch {}
|
|
157
|
+
// server unreachable → still exit
|
|
158
|
+
try { systray.kill(false); } catch {}
|
|
77
159
|
process.exit(0);
|
|
78
160
|
});
|
|
79
161
|
req.end();
|