fluxy-bot 0.5.23 → 0.5.24
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
package/supervisor/backend.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn, type ChildProcess } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { PKG_DIR } from '../shared/paths.js';
|
|
4
5
|
import { log } from '../shared/logger.js';
|
|
@@ -10,6 +11,8 @@ let intentionallyStopped = false;
|
|
|
10
11
|
const MAX_RESTARTS = 3;
|
|
11
12
|
const STABLE_THRESHOLD = 30_000; // 30s — if backend ran this long, it wasn't a crash loop
|
|
12
13
|
|
|
14
|
+
const LOG_FILE = path.join(PKG_DIR, 'workspace', '.backend.log');
|
|
15
|
+
|
|
13
16
|
export function getBackendPort(basePort: number): number {
|
|
14
17
|
return basePort + 4;
|
|
15
18
|
}
|
|
@@ -19,6 +22,9 @@ export function spawnBackend(port: number): ChildProcess {
|
|
|
19
22
|
lastSpawnTime = Date.now();
|
|
20
23
|
intentionallyStopped = false;
|
|
21
24
|
|
|
25
|
+
// Clear log file on each restart — only keeps current run
|
|
26
|
+
try { fs.writeFileSync(LOG_FILE, ''); } catch {}
|
|
27
|
+
|
|
22
28
|
child = spawn(process.execPath, ['--import', 'tsx/esm', backendPath], {
|
|
23
29
|
cwd: path.join(PKG_DIR, 'workspace'),
|
|
24
30
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -27,10 +33,12 @@ export function spawnBackend(port: number): ChildProcess {
|
|
|
27
33
|
|
|
28
34
|
child.stdout?.on('data', (d) => {
|
|
29
35
|
process.stdout.write(d);
|
|
36
|
+
try { fs.appendFileSync(LOG_FILE, d); } catch {}
|
|
30
37
|
});
|
|
31
38
|
|
|
32
39
|
child.stderr?.on('data', (d) => {
|
|
33
40
|
process.stderr.write(d);
|
|
41
|
+
try { fs.appendFileSync(LOG_FILE, d); } catch {}
|
|
34
42
|
});
|
|
35
43
|
|
|
36
44
|
child.on('exit', (code) => {
|
|
@@ -56,10 +64,26 @@ export function spawnBackend(port: number): ChildProcess {
|
|
|
56
64
|
return child;
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
/** Stop the backend and wait for the process to fully exit before resolving.
|
|
68
|
+
* This prevents port collisions when restarting (old process must release the port first). */
|
|
69
|
+
export function stopBackend(): Promise<void> {
|
|
70
|
+
return new Promise((resolve) => {
|
|
71
|
+
if (!child || child.exitCode !== null) {
|
|
72
|
+
child = null;
|
|
73
|
+
resolve();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
intentionallyStopped = true;
|
|
77
|
+
const dying = child;
|
|
78
|
+
child = null;
|
|
79
|
+
dying.once('exit', () => resolve());
|
|
80
|
+
dying.kill();
|
|
81
|
+
// Safety: force kill after 3s if SIGTERM doesn't work
|
|
82
|
+
setTimeout(() => {
|
|
83
|
+
try { dying.kill('SIGKILL'); } catch {}
|
|
84
|
+
resolve();
|
|
85
|
+
}, 3000);
|
|
86
|
+
});
|
|
63
87
|
}
|
|
64
88
|
|
|
65
89
|
export function isBackendAlive(): boolean {
|
package/supervisor/index.ts
CHANGED
|
@@ -507,8 +507,7 @@ export async function startSupervisor() {
|
|
|
507
507
|
console.log('[supervisor] Agent turn ended — restarting backend');
|
|
508
508
|
pendingBackendRestart = false;
|
|
509
509
|
resetBackendRestarts();
|
|
510
|
-
stopBackend();
|
|
511
|
-
spawnBackend(backendPort);
|
|
510
|
+
stopBackend().then(() => spawnBackend(backendPort));
|
|
512
511
|
}
|
|
513
512
|
return; // don't forward bot:done to client
|
|
514
513
|
}
|
|
@@ -640,9 +639,9 @@ export async function startSupervisor() {
|
|
|
640
639
|
startScheduler({
|
|
641
640
|
broadcastFluxy,
|
|
642
641
|
workerApi,
|
|
643
|
-
restartBackend: () => {
|
|
642
|
+
restartBackend: async () => {
|
|
644
643
|
resetBackendRestarts();
|
|
645
|
-
stopBackend();
|
|
644
|
+
await stopBackend();
|
|
646
645
|
spawnBackend(backendPort);
|
|
647
646
|
},
|
|
648
647
|
getModel: () => loadConfig().ai.model,
|
|
@@ -662,10 +661,10 @@ export async function startSupervisor() {
|
|
|
662
661
|
return;
|
|
663
662
|
}
|
|
664
663
|
if (backendRestartTimer) clearTimeout(backendRestartTimer);
|
|
665
|
-
backendRestartTimer = setTimeout(() => {
|
|
664
|
+
backendRestartTimer = setTimeout(async () => {
|
|
666
665
|
log.info(`[watcher] ${reason} — restarting backend...`);
|
|
667
666
|
resetBackendRestarts();
|
|
668
|
-
stopBackend();
|
|
667
|
+
await stopBackend();
|
|
669
668
|
spawnBackend(backendPort);
|
|
670
669
|
}, 1000);
|
|
671
670
|
}
|
|
@@ -799,7 +798,7 @@ export async function startSupervisor() {
|
|
|
799
798
|
delete latestConfig.tunnelUrl;
|
|
800
799
|
saveConfig(latestConfig);
|
|
801
800
|
stopWorker();
|
|
802
|
-
stopBackend();
|
|
801
|
+
await stopBackend();
|
|
803
802
|
stopTunnel();
|
|
804
803
|
console.log('[supervisor] Stopping Vite dev servers...');
|
|
805
804
|
await stopViteDevServers();
|
|
@@ -230,7 +230,7 @@ The supervisor manages the backend process. You don't need to manage it yourself
|
|
|
230
230
|
|
|
231
231
|
**During your turn:** The backend does NOT restart mid-turn. All your edits are batched — the backend restarts once when you're done. This means if you're writing multi-file changes, everything applies atomically.
|
|
232
232
|
|
|
233
|
-
**If the backend crashes:** It auto-restarts up to 3 times. If it keeps crashing,
|
|
233
|
+
**If the backend crashes:** It auto-restarts up to 3 times. If it keeps crashing, read `.backend.log` to see the error output, then fix the code. The log file is cleared on each restart so it only contains the current/last run — no need to truncate it yourself.
|
|
234
234
|
|
|
235
235
|
**NEVER do these:**
|
|
236
236
|
- Never `kill` processes or run `pkill`/`killall` — you don't manage the supervisor or its children
|