bingocode 1.1.170 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bingocode",
3
- "version": "1.1.170",
3
+ "version": "1.1.172",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "claude": "bin/claude-win.cjs",
@@ -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 {}
@@ -28,10 +88,22 @@ process.on('exit', () => {
28
88
  try { fs.unlinkSync(DAEMON_LOCK_FILE); } catch {}
29
89
  });
30
90
 
31
- // Tray icon: hexagon black-gold with "B" initial (16x16 RGBA PNG, pre-encoded)
32
- const iconB64 = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAS0lEQVR4nGNgGLRAQUHhPzKmSDPRhiAr/n/AAAXjNQjdJnTN2AxBMQibRnyGEW0AMk2WAfR3AbJB+FxFVGyQFI1US0j4DCFJM10BADtB9/MMO0W9AAAAAElFTkSuQmCC';
91
+ // Tray icon: Windows=ICO, others=PNG
92
+ const _iconExt = process.platform === 'win32' ? 'ico' : 'png';
93
+ const _iconFile = path.join(ROOT_DIR, 'assets', `tray-icon.${_iconExt}`);
94
+ const _iconFallback = 'AAABAAEAEBAAAAEAIACEAAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAEtJREFUeJxjYBi0QEFB4T8ypkgz0YYgK/5/wAAF4zUI3SZ0zdgMQTEIm0Z8hhFtADJNlgH0dwGyQfhcRVRskBSNVEtI+AwhSTNdAQA7QffzDDtFvQAAAABJRU5ErkJggg==';
95
+ const iconB64 = fs.existsSync(_iconFile)
96
+ ? fs.readFileSync(_iconFile).toString('base64')
97
+ : _iconFallback;
33
98
 
34
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
+
35
107
  const systray = new SysTray({
36
108
  menu: {
37
109
  icon: iconB64,
@@ -44,6 +116,7 @@ try {
44
116
  checked: false,
45
117
  enabled: false,
46
118
  },
119
+ autostartItem,
47
120
  {
48
121
  title: 'Exit Bingo',
49
122
  tooltip: 'Stop all bingo services',
@@ -57,18 +130,31 @@ try {
57
130
  });
58
131
 
59
132
  systray.onClick((action: any) => {
60
- if (action.seq_id === 1) {
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') {
61
145
  console.log('[tray] Exiting via tray menu');
146
+ // try graceful server shutdown, then force exit regardless
62
147
  const req = http.request(
63
148
  `http://${HOST}:${PORT}/exit`,
64
- { method: 'POST', timeout: 5000 },
65
- (res) => {
66
- res.resume();
149
+ { method: 'POST', timeout: 3000 },
150
+ () => {
151
+ // server responded (any status) → kill tray + exit
67
152
  try { systray.kill(false); } catch {}
68
153
  process.exit(0);
69
154
  },
70
155
  );
71
156
  req.on('error', () => {
157
+ // server unreachable → still exit
72
158
  try { systray.kill(false); } catch {}
73
159
  process.exit(0);
74
160
  });