Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codbash-app",
"version": "7.5.1",
"version": "7.5.2",
"description": "Dashboard + CLI for AI coding agents — Claude Code, Codex, Cursor, OpenCode, Kiro. View, search, resume, convert, sync sessions.",
"bin": {
"codbash": "./bin/cli.js",
Expand Down
10 changes: 10 additions & 0 deletions src/changelog.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
'use strict';

const CHANGELOG = [
{
version: '7.5.2',
date: '2026-07-19',
title: 'Reliable self-update restart',
changes: [
'Fix: "Update Now" no longer leaves the server dead after installing the new version — the restart now spawns a fully-detached process instead of an unreliable process.on("exit") handler',
'Self-update restarter reclaims the same port/host and skips reopening the browser (the existing tab reloads itself)',
'npm install timeout during self-update raised from 60s to 120s for slower connections',
],
},
{
version: '7.5.1',
date: '2026-05-26',
Expand Down
30 changes: 20 additions & 10 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -795,17 +795,27 @@ function startServer(host, port, openBrowser = true) {
json(res, { ok: true, message: 'Updating... Page will reload.' });
// Run update in background after response is sent
setTimeout(() => {
const { execSync } = require('child_process');
const { execSync, spawn } = require('child_process');
try {
execSync('npm i -g codbash-app@latest', { stdio: 'inherit', timeout: 60000 });
log('UPDATE', 'Updated. Restarting...');
// Restart the process
process.on('exit', () => {
require('child_process').spawn(process.argv[0], process.argv.slice(1), {
detached: true, stdio: 'inherit'
}).unref();
});
process.exit(0);
execSync('npm i -g codbash-app@latest', { stdio: 'inherit', timeout: 120000 });
log('UPDATE', 'Update installed. Restarting server...');
// Spawn a FULLY-detached restarter BEFORE exiting. Spawning inside a
// `process.on('exit')` handler is unreliable — the event loop is already
// draining, so the child frequently never survives and the server ends
// up dead (nothing reclaims the port). Instead: detach + unref + ignore
// stdio so the child outlives us, become its own process-group leader
// (so the restarter's `lsof … | kill -9` on the port can't take it down),
// then exit cleanly. The restarter loads the freshly-installed code from
// the same argv[1] path that npm just overwrote.
const child = spawn(
process.argv[0],
[process.argv[1], 'restart', `--port=${port}`, `--host=${host}`, '--no-browser'],
{ detached: true, stdio: 'ignore' }
);
child.unref();
// Give the OS a tick to fully spawn the detached child before we exit
// and release the port for the restarter to grab.
setTimeout(() => process.exit(0), 250);
} catch (e) {
log('ERROR', `Update failed: ${e.message}`);
}
Expand Down
Loading