@aliwey/bmo 2.1.2 → 2.1.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/bin/bmo.js CHANGED
@@ -58,6 +58,34 @@ async function isPortOpen(port, host = '127.0.0.1') {
58
58
  });
59
59
  }
60
60
 
61
+ function killProcessByPort(port) {
62
+ try {
63
+ if (os.platform() === 'win32') {
64
+ const output = execSync(`netstat -ano`, { encoding: 'utf8' });
65
+ const lines = output.split('\n');
66
+ for (const line of lines) {
67
+ if (line.includes(`:${port}`) && line.includes('LISTENING')) {
68
+ const parts = line.trim().split(/\s+/);
69
+ const pid = parts[parts.length - 1];
70
+ if (pid && pid !== '0') {
71
+ try {
72
+ execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' });
73
+ } catch (e) {}
74
+ }
75
+ }
76
+ }
77
+ } else {
78
+ try {
79
+ execSync(`fuser -k ${port}/tcp`, { stdio: 'ignore' });
80
+ } catch {
81
+ try {
82
+ execSync(`kill -9 $(lsof -t -i:${port})`, { stdio: 'ignore' });
83
+ } catch {}
84
+ }
85
+ }
86
+ } catch (e) {}
87
+ }
88
+
61
89
  /** Resolve the embedded or system Python executable */
62
90
  function getPython() {
63
91
  const embeddedWin = path.join(BMO_HOME, 'python', 'python.exe');
@@ -152,12 +180,17 @@ Data lives in: ${BMO_HOME_DISPLAY}
152
180
  // ── bmo --update [version] ────────────────────────────────────────────────
153
181
  if (cmd === '--update' || cmd === '-update' || cmd === 'update') {
154
182
  const ver = args[0] ? `@${args[0]}` : '@latest';
155
- console.log(`Stopping any running BMO background processes to release file locks...`);
183
+ console.log(`Stopping any running BMO processes and freeing ports...`);
184
+
185
+ // Kill processes on ports 3456 and 4097
186
+ killProcessByPort(3456);
187
+ killProcessByPort(4097);
188
+
156
189
  try {
157
190
  if (os.platform() === 'win32') {
158
- execSync(`powershell -Command "Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*main.py*' -or $_.CommandLine -like '*cli.py*' -or $_.CommandLine -like '*bmo*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }"`, { stdio: 'ignore' });
191
+ execSync(`powershell -Command "Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*main.py*' -or $_.CommandLine -like '*cli.py*' -or $_.CommandLine -like '*bmo*' -or $_.CommandLine -like '*cloudflared*' -or $_.CommandLine -like '*server.js*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }"`, { stdio: 'ignore' });
159
192
  } else {
160
- execSync(`pkill -f "main.py|cli.py|bmo" || true`, { stdio: 'ignore' });
193
+ execSync(`pkill -f "main.py|cli.py|bmo|cloudflared|server.js" || true`, { stdio: 'ignore' });
161
194
  }
162
195
  } catch (e) {
163
196
  // Ignore failures
@@ -166,7 +199,8 @@ Data lives in: ${BMO_HOME_DISPLAY}
166
199
  if (os.platform() === 'win32') {
167
200
  console.log('Launching updater in a new window to release folder/file locks...');
168
201
 
169
- const cmdStr = `timeout /t 2 /nobreak >nul && echo [BMO Updater] Upgrading to @aliwey/bmo${ver}... && npm install -g @aliwey/bmo${ver} && echo [OK] BMO updated successfully! Press any key to close. && pause`;
202
+ const psKillCmd = `powershell -Command \\"Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*main.py*' -or $_.CommandLine -like '*cli.py*' -or $_.CommandLine -like '*bmo*' -or $_.CommandLine -like '*cloudflared*' -or $_.CommandLine -like '*server.js*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }\\"`;
203
+ const cmdStr = `timeout /t 2 /nobreak >nul && ${psKillCmd} && echo [BMO Updater] Upgrading to @aliwey/bmo${ver}... && npm install -g @aliwey/bmo${ver} && echo [OK] BMO updated successfully! Press any key to close. && pause`;
170
204
 
171
205
  spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/c', cmdStr], {
172
206
  detached: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliwey/bmo",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "description": "BMO — AI coding assistant with Telegram, CLI & Web sync. One command, all frontends.",
5
5
  "keywords": ["ai", "coding-assistant", "telegram-bot", "cli", "opencode", "bfp"],
6
6
  "homepage": "https://github.com/aliwey/bmo",
@@ -41,6 +41,34 @@ function getCloudflaredExe() {
41
41
  process.exit(1);
42
42
  }
43
43
 
44
+ function killProcessByPort(port) {
45
+ try {
46
+ if (os.platform() === 'win32') {
47
+ const output = execSync(`netstat -ano`, { encoding: 'utf8' });
48
+ const lines = output.split('\n');
49
+ for (const line of lines) {
50
+ if (line.includes(`:${port}`) && line.includes('LISTENING')) {
51
+ const parts = line.trim().split(/\s+/);
52
+ const pid = parts[parts.length - 1];
53
+ if (pid && pid !== '0') {
54
+ try {
55
+ execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' });
56
+ } catch (e) {}
57
+ }
58
+ }
59
+ }
60
+ } else {
61
+ try {
62
+ execSync(`fuser -k ${port}/tcp`, { stdio: 'ignore' });
63
+ } catch {
64
+ try {
65
+ execSync(`kill -9 $(lsof -t -i:${port})`, { stdio: 'ignore' });
66
+ } catch {}
67
+ }
68
+ }
69
+ } catch (e) {}
70
+ }
71
+
44
72
  function loadTasks() {
45
73
  try { return JSON.parse(fs.readFileSync(TASKS_FILE, 'utf8')); } catch { return {}; }
46
74
  }
@@ -74,6 +102,10 @@ function waitForTunnelUrl(proc) {
74
102
  console.log(` Local: http://127.0.0.1:${WEBCHAT_PORT}`);
75
103
  console.log(` Public: ${existing}\n`);
76
104
  process.exit(0);
105
+ } else {
106
+ console.log(`[Info] Port ${WEBCHAT_PORT} is in use by an unrecognized process. Freeing port...`);
107
+ killProcessByPort(WEBCHAT_PORT);
108
+ await sleep(1000); // Wait for the port to release
77
109
  }
78
110
  }
79
111