4runr-os 2.10.9 → 2.10.13
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/apps/gateway/dist/apps/gateway/src/db/docker-manager.d.ts +1 -0
- package/apps/gateway/dist/apps/gateway/src/db/docker-manager.d.ts.map +1 -1
- package/apps/gateway/dist/apps/gateway/src/db/docker-manager.js +22 -2
- package/apps/gateway/dist/apps/gateway/src/db/docker-manager.js.map +1 -1
- package/apps/gateway/dist/apps/gateway/src/db/init.d.ts.map +1 -1
- package/apps/gateway/dist/apps/gateway/src/db/init.js +8 -0
- package/apps/gateway/dist/apps/gateway/src/db/init.js.map +1 -1
- package/apps/gateway/package-lock.json +13 -13
- package/apps/gateway/src/db/docker-manager.ts +26 -3
- package/apps/gateway/src/db/init.ts +299 -289
- package/apps/gateway/src/health/index.ts +268 -268
- package/dist/boot-sequence.d.ts +24 -0
- package/dist/boot-sequence.d.ts.map +1 -0
- package/dist/boot-sequence.js +188 -0
- package/dist/boot-sequence.js.map +1 -0
- package/dist/index.js +52 -6
- package/dist/index.js.map +1 -1
- package/dist/tui-handlers.js +3 -3
- package/dist/tui-handlers.js.map +1 -1
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +71 -25
- package/dist/version-check.js.map +1 -1
- package/dist/watchdog.d.ts +13 -0
- package/dist/watchdog.d.ts.map +1 -0
- package/dist/watchdog.js +192 -0
- package/dist/watchdog.js.map +1 -0
- package/mk3-tui/src/ui/portal_monitoring.rs +259 -259
- package/package.json +2 -2
- package/prisma/schema.prisma +470 -470
- package/scripts/postinstall-gateway.js +63 -63
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boot sequence - ensures clean system state before launching 4r
|
|
3
|
+
* Handles stale processes, in-progress updates, and broken installs
|
|
4
|
+
*/
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
/**
|
|
10
|
+
* Kill any stale 4r/Gateway processes that might lock files
|
|
11
|
+
*/
|
|
12
|
+
export function killStaleProcesses() {
|
|
13
|
+
if (process.platform !== 'win32') {
|
|
14
|
+
// POSIX: killall is less aggressive than pkill -9
|
|
15
|
+
try {
|
|
16
|
+
execSync('pkill -f "4runr-os|mk3-tui" || true', { stdio: 'ignore' });
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// ignore
|
|
20
|
+
}
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// Windows: kill node.exe processes running from global 4runr-os or Gateway paths
|
|
24
|
+
try {
|
|
25
|
+
const output = execSync('wmic process where "name=\'node.exe\'" get ProcessId,CommandLine /format:csv', { encoding: 'utf-8', windowsHide: true });
|
|
26
|
+
const lines = output.split(/\r?\n/);
|
|
27
|
+
for (const line of lines) {
|
|
28
|
+
if (line.includes('4runr-os') || line.includes('gateway')) {
|
|
29
|
+
const match = line.match(/,(\d+)$/);
|
|
30
|
+
if (match) {
|
|
31
|
+
const pid = parseInt(match[1], 10);
|
|
32
|
+
if (pid > 0 && pid !== process.pid) {
|
|
33
|
+
try {
|
|
34
|
+
process.kill(pid, 'SIGTERM');
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// ignore
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Fallback: just kill all node.exe (aggressive but effective)
|
|
46
|
+
try {
|
|
47
|
+
execSync('taskkill /F /IM node.exe /FI "PID ne ' + process.pid + '"', {
|
|
48
|
+
stdio: 'ignore',
|
|
49
|
+
windowsHide: true,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// ignore
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Wait for any in-progress npm install -g 4runr-os to complete
|
|
59
|
+
*/
|
|
60
|
+
export async function waitForInProgressUpdate(maxWaitMs = 120000) {
|
|
61
|
+
const lockPath = path.join(os.tmpdir(), '4runr-os-gnpm-in-progress.lock');
|
|
62
|
+
const start = Date.now();
|
|
63
|
+
while (Date.now() - start < maxWaitMs) {
|
|
64
|
+
if (!fs.existsSync(lockPath)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
// Check if lock is stale (>15 min)
|
|
68
|
+
try {
|
|
69
|
+
const { mtimeMs } = fs.statSync(lockPath);
|
|
70
|
+
if (Date.now() - mtimeMs > 15 * 60 * 1000) {
|
|
71
|
+
fs.unlinkSync(lockPath);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
// Show progress every 5s
|
|
79
|
+
if ((Date.now() - start) % 5000 < 1000) {
|
|
80
|
+
process.stderr.write('.');
|
|
81
|
+
}
|
|
82
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check if Prisma client is properly generated
|
|
88
|
+
*/
|
|
89
|
+
function checkPrismaClient() {
|
|
90
|
+
try {
|
|
91
|
+
const globalRoot = execSync('npm root -g', { encoding: 'utf-8' }).trim();
|
|
92
|
+
const prismaPath = path.join(globalRoot, '4runr-os', 'apps', 'gateway', 'node_modules', '.prisma', 'client');
|
|
93
|
+
return fs.existsSync(prismaPath);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if Gateway bundle is intact
|
|
101
|
+
*/
|
|
102
|
+
function checkGatewayBundle() {
|
|
103
|
+
try {
|
|
104
|
+
const globalRoot = execSync('npm root -g', { encoding: 'utf-8' }).trim();
|
|
105
|
+
const gatewayDist = path.join(globalRoot, '4runr-os', 'apps', 'gateway', 'dist', 'index.js');
|
|
106
|
+
return fs.existsSync(gatewayDist);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Repair broken install by regenerating Prisma
|
|
114
|
+
*/
|
|
115
|
+
async function repairInstall() {
|
|
116
|
+
try {
|
|
117
|
+
const globalRoot = execSync('npm root -g', { encoding: 'utf-8' }).trim();
|
|
118
|
+
const gatewayDir = path.join(globalRoot, '4runr-os', 'apps', 'gateway');
|
|
119
|
+
if (!fs.existsSync(gatewayDir)) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
process.stderr.write('🔧 Repairing installation...\n');
|
|
123
|
+
// Run npm install + prisma generate in gateway
|
|
124
|
+
execSync('npm install --no-audit', {
|
|
125
|
+
cwd: gatewayDir,
|
|
126
|
+
stdio: 'pipe',
|
|
127
|
+
windowsHide: true,
|
|
128
|
+
});
|
|
129
|
+
execSync('npm run db:generate', {
|
|
130
|
+
cwd: gatewayDir,
|
|
131
|
+
stdio: 'pipe',
|
|
132
|
+
windowsHide: true,
|
|
133
|
+
});
|
|
134
|
+
process.stderr.write('✓ Repair complete\n');
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
process.stderr.write(`⚠️ Repair failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Perform full boot sequence health check
|
|
144
|
+
*/
|
|
145
|
+
export async function performBootSequence() {
|
|
146
|
+
const warnings = [];
|
|
147
|
+
// Step 1: Kill stale processes
|
|
148
|
+
try {
|
|
149
|
+
killStaleProcesses();
|
|
150
|
+
await new Promise(r => setTimeout(r, 500));
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
warnings.push(`Could not kill stale processes: ${err instanceof Error ? err.message : String(err)}`);
|
|
154
|
+
}
|
|
155
|
+
// Step 2: Wait for in-progress updates
|
|
156
|
+
const lockPath = path.join(os.tmpdir(), '4runr-os-gnpm-in-progress.lock');
|
|
157
|
+
if (fs.existsSync(lockPath)) {
|
|
158
|
+
process.stderr.write('⏳ Waiting for background update to complete');
|
|
159
|
+
const ok = await waitForInProgressUpdate(60000);
|
|
160
|
+
process.stderr.write('\n');
|
|
161
|
+
if (!ok) {
|
|
162
|
+
// Force clear stale lock
|
|
163
|
+
try {
|
|
164
|
+
fs.unlinkSync(lockPath);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// ignore
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Step 3: Health checks
|
|
172
|
+
const prismaOk = checkPrismaClient();
|
|
173
|
+
const gatewayOk = checkGatewayBundle();
|
|
174
|
+
if (!prismaOk || !gatewayOk) {
|
|
175
|
+
warnings.push('Installation health check failed');
|
|
176
|
+
// Attempt repair
|
|
177
|
+
const repaired = await repairInstall();
|
|
178
|
+
if (!repaired) {
|
|
179
|
+
return {
|
|
180
|
+
ok: false,
|
|
181
|
+
error: 'Installation is broken and auto-repair failed. Run: npm install -g 4runr-os@latest',
|
|
182
|
+
warnings,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return { ok: true, warnings: warnings.length > 0 ? warnings : undefined };
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=boot-sequence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot-sequence.js","sourceRoot":"","sources":["../src/boot-sequence.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2460,15 +2460,45 @@ async function startREPL() {
|
|
|
2460
2460
|
process.exit(0);
|
|
2461
2461
|
});
|
|
2462
2462
|
}
|
|
2463
|
-
//
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2463
|
+
// Graceful shutdown handlers
|
|
2464
|
+
let isShuttingDown = false;
|
|
2465
|
+
async function gracefulShutdown(signal) {
|
|
2466
|
+
if (isShuttingDown)
|
|
2467
|
+
return;
|
|
2468
|
+
isShuttingDown = true;
|
|
2469
|
+
process.stderr.write(`\n\n[4Runr] Received ${signal}, shutting down gracefully...\n`);
|
|
2470
|
+
// Stop watchdog and let it do cleanup
|
|
2471
|
+
try {
|
|
2472
|
+
const { stopWatchdog } = await import('./watchdog.js');
|
|
2473
|
+
stopWatchdog();
|
|
2474
|
+
}
|
|
2475
|
+
catch {
|
|
2476
|
+
// Fallback manual cleanup
|
|
2477
|
+
try {
|
|
2478
|
+
const { killRecordedLocalGateway } = await import('./local-gateway-pid.js');
|
|
2479
|
+
killRecordedLocalGateway();
|
|
2480
|
+
}
|
|
2481
|
+
catch {
|
|
2482
|
+
// ignore
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
process.stderr.write('[4Runr] Shutdown complete\n');
|
|
2467
2486
|
process.exit(0);
|
|
2487
|
+
}
|
|
2488
|
+
process.on('SIGINT', () => {
|
|
2489
|
+
gracefulShutdown('SIGINT');
|
|
2468
2490
|
});
|
|
2469
2491
|
process.on('SIGTERM', () => {
|
|
2470
|
-
|
|
2471
|
-
|
|
2492
|
+
gracefulShutdown('SIGTERM');
|
|
2493
|
+
});
|
|
2494
|
+
// Critical: cleanup on uncaught exceptions/unhandled rejections
|
|
2495
|
+
process.on('uncaughtException', async (err) => {
|
|
2496
|
+
process.stderr.write(`\n[4Runr] Uncaught exception: ${err.message}\n`);
|
|
2497
|
+
await gracefulShutdown('uncaughtException');
|
|
2498
|
+
});
|
|
2499
|
+
process.on('unhandledRejection', async (reason) => {
|
|
2500
|
+
process.stderr.write(`\n[4Runr] Unhandled rejection: ${reason}\n`);
|
|
2501
|
+
await gracefulShutdown('unhandledRejection');
|
|
2472
2502
|
});
|
|
2473
2503
|
// main() function removed - all routing now at top level
|
|
2474
2504
|
/**
|
|
@@ -2648,9 +2678,25 @@ else {
|
|
|
2648
2678
|
// Default mode - Launch terminal interface + WebSocket server
|
|
2649
2679
|
// Launch Rust binary with WebSocket server embedded
|
|
2650
2680
|
(async () => {
|
|
2681
|
+
// === BOOT SEQUENCE: Ensure clean system state ===
|
|
2682
|
+
const { performBootSequence } = await import('./boot-sequence.js');
|
|
2683
|
+
const bootResult = await performBootSequence();
|
|
2684
|
+
if (!bootResult.ok) {
|
|
2685
|
+
process.stderr.write(`\n❌ Boot check failed: ${bootResult.error}\n\n`);
|
|
2686
|
+
process.exit(1);
|
|
2687
|
+
}
|
|
2688
|
+
if (bootResult.warnings && bootResult.warnings.length > 0) {
|
|
2689
|
+
for (const w of bootResult.warnings) {
|
|
2690
|
+
process.stderr.write(`⚠️ ${w}\n`);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
// === START WATCHDOG: Monitors this process and cleans up Docker on ANY exit ===
|
|
2694
|
+
const { startWatchdog } = await import('./watchdog.js');
|
|
2695
|
+
startWatchdog(process.pid);
|
|
2651
2696
|
// Start WebSocket server for TUI communication
|
|
2652
2697
|
const { startTUIServer } = await import('./tui-server.js');
|
|
2653
2698
|
const { initializeTUIHandlers } = await import('./tui-handlers.js');
|
|
2699
|
+
process.stderr.write('[4Runr] WebSocket server listening on ws://localhost:' + tuiPort + '\n');
|
|
2654
2700
|
const tuiServer = await startTUIServer(tuiPort);
|
|
2655
2701
|
initializeTUIHandlers(tuiServer, client, localMode);
|
|
2656
2702
|
process.stderr.write(`[4Runr] TUI WebSocket server started on port ${tuiPort}\n`);
|