fix
Browse files- lib/docker/manager.ts +32 -27
- server.ts +3 -3
lib/docker/manager.ts
CHANGED
|
@@ -334,9 +334,10 @@ export async function reconnectRunningWorkspaces() {
|
|
| 334 |
const lines = output.split('\n');
|
| 335 |
|
| 336 |
for (const line of lines) {
|
| 337 |
-
// Looking for: ... --bind-addr 127.0.0.1:8548 ...
|
| 338 |
const bindMatch = line.match(/--bind-addr 127\.0\.0\.1:(\d+)/);
|
| 339 |
-
|
|
|
|
| 340 |
|
| 341 |
if (bindMatch && pathMatch) {
|
| 342 |
const port = parseInt(bindMatch[1]);
|
|
@@ -344,33 +345,37 @@ export async function reconnectRunningWorkspaces() {
|
|
| 344 |
const fullPath = path.join(workspaceRoot, shortId);
|
| 345 |
const idFile = path.join(fullPath, '.codeverse-id');
|
| 346 |
|
|
|
|
| 347 |
if (fs.existsSync(idFile)) {
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
|
|
|
|
|
|
|
|
|
| 370 |
}
|
| 371 |
-
}
|
| 372 |
-
}
|
| 373 |
-
}
|
| 374 |
}
|
| 375 |
}
|
| 376 |
}
|
|
|
|
| 334 |
const lines = output.split('\n');
|
| 335 |
|
| 336 |
for (const line of lines) {
|
| 337 |
+
// Looking for: ... --bind-addr 127.0.0.1:8548 ... w/44c7597c
|
| 338 |
const bindMatch = line.match(/--bind-addr 127\.0\.0\.1:(\d+)/);
|
| 339 |
+
// Flexible path match: look for the ID prefix after 'w/' or 'workspaces/' or just the root
|
| 340 |
+
const pathMatch = line.match(/[ /](?:w|workspaces)\/([a-zA-Z0-9]{8})/);
|
| 341 |
|
| 342 |
if (bindMatch && pathMatch) {
|
| 343 |
const port = parseInt(bindMatch[1]);
|
|
|
|
| 345 |
const fullPath = path.join(workspaceRoot, shortId);
|
| 346 |
const idFile = path.join(fullPath, '.codeverse-id');
|
| 347 |
|
| 348 |
+
let foundFullId = "";
|
| 349 |
if (fs.existsSync(idFile)) {
|
| 350 |
+
foundFullId = fs.readFileSync(idFile, 'utf-8').trim();
|
| 351 |
+
} else {
|
| 352 |
+
// Fallback: If no ID file, we use the shortId as the temporary key.
|
| 353 |
+
foundFullId = shortId;
|
| 354 |
+
console.warn(`[RECONNECT:WARN] No .codeverse-id for session ${shortId}. Using prefix mapping.`);
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
if (foundFullId && !nativeProcesses.has(foundFullId)) {
|
| 358 |
+
// Capture PID reliably from ps output (column 2)
|
| 359 |
+
const psParts = line.trim().split(/\s+/);
|
| 360 |
+
const pid = parseInt(psParts[1]);
|
| 361 |
+
|
| 362 |
+
console.log(`[RECONNECT] Identified active IDE ${foundFullId} (PID: ${pid}) on port ${port}. Restoration complete.`);
|
| 363 |
+
nativeProcesses.set(foundFullId, {
|
| 364 |
+
pid,
|
| 365 |
+
port,
|
| 366 |
+
process: {
|
| 367 |
+
pid,
|
| 368 |
+
kill: (_signal?: string | number) => {
|
| 369 |
+
try {
|
| 370 |
+
process.kill(pid, 'SIGKILL');
|
| 371 |
+
return true;
|
| 372 |
+
} catch {
|
| 373 |
+
try { execSync(`fuser -k ${port}/tcp`); } catch {}
|
| 374 |
+
return true;
|
| 375 |
}
|
| 376 |
+
}
|
| 377 |
+
} as WorkspaceProcess
|
| 378 |
+
});
|
| 379 |
}
|
| 380 |
}
|
| 381 |
}
|
server.ts
CHANGED
|
@@ -12,9 +12,9 @@ import * as pty from "node-pty";
|
|
| 12 |
import os from "os";
|
| 13 |
import { Duplex } from "stream";
|
| 14 |
import { startAutoSleepCron } from "./lib/jobs/auto-sleep";
|
| 15 |
-
import { getNativeWorkspacePort, getAndroidPort, isNativeWorkspaceRunning, prewarmWorkspace, reconnectRunningWorkspaces } from "./lib/docker/manager";
|
| 16 |
import { initDb } from "./lib/db/schema";
|
| 17 |
-
import {
|
| 18 |
import httpProxy from "http-proxy";
|
| 19 |
|
| 20 |
/**
|
|
@@ -176,9 +176,9 @@ app.prepare()
|
|
| 176 |
if (!req.url.startsWith("/")) req.url = "/" + req.url;
|
| 177 |
}
|
| 178 |
|
| 179 |
-
console.log(`[PROXY:IDE] Mapping ${pathname} -> 127.0.0.1:${port}${req.url}`);
|
| 180 |
return proxy.web(req, res, { target: `http://127.0.0.1:${port}`, changeOrigin: true });
|
| 181 |
} else if (!pathname?.startsWith("/api/")) {
|
|
|
|
| 182 |
// EXCLUSIVE FIX (April 2026): Prevent Next.js 404 fallthrough
|
| 183 |
// Only show for main workspace routes, let API routes pass to Next.js for provisioning.
|
| 184 |
res.writeHead(503, { 'Content-Type': 'text/html', 'Retry-After': '5' });
|
|
|
|
| 12 |
import os from "os";
|
| 13 |
import { Duplex } from "stream";
|
| 14 |
import { startAutoSleepCron } from "./lib/jobs/auto-sleep";
|
| 15 |
+
import { getNativeWorkspacePort, getAndroidPort, isNativeWorkspaceRunning, prewarmWorkspace, reconnectRunningWorkspaces, nativeProcesses } from "./lib/docker/manager";
|
| 16 |
import { initDb } from "./lib/db/schema";
|
| 17 |
+
import { validateEnvironment } from "./lib/env-config";
|
| 18 |
import httpProxy from "http-proxy";
|
| 19 |
|
| 20 |
/**
|
|
|
|
| 176 |
if (!req.url.startsWith("/")) req.url = "/" + req.url;
|
| 177 |
}
|
| 178 |
|
|
|
|
| 179 |
return proxy.web(req, res, { target: `http://127.0.0.1:${port}`, changeOrigin: true });
|
| 180 |
} else if (!pathname?.startsWith("/api/")) {
|
| 181 |
+
console.log(`[PROXY:DEBUG] Workspace ${id} not found in nativeProcesses. Available:`, Array.from(nativeProcesses.keys()));
|
| 182 |
// EXCLUSIVE FIX (April 2026): Prevent Next.js 404 fallthrough
|
| 183 |
// Only show for main workspace routes, let API routes pass to Next.js for provisioning.
|
| 184 |
res.writeHead(503, { 'Content-Type': 'text/html', 'Retry-After': '5' });
|