improve handshake
Browse files- .agent/memory/session.json +2 -2
- lib/docker/manager.ts +17 -11
- lib/fs/isolation.ts +4 -4
- lib/idx/idx-engine.ts +1 -1
- next.config.ts +0 -1
.agent/memory/session.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
{
|
| 2 |
"version": "1.0.0",
|
| 3 |
-
"session_id": "
|
| 4 |
-
"started_at": "2026-04-06T22:
|
| 5 |
"workspace": "D:\\Code\\codeverse",
|
| 6 |
"active_task_id": null,
|
| 7 |
"active_agent": null,
|
|
|
|
| 1 |
{
|
| 2 |
"version": "1.0.0",
|
| 3 |
+
"session_id": "625a7b0d",
|
| 4 |
+
"started_at": "2026-04-06T22:23:02.815736+05:30",
|
| 5 |
"workspace": "D:\\Code\\codeverse",
|
| 6 |
"active_task_id": null,
|
| 7 |
"active_agent": null,
|
lib/docker/manager.ts
CHANGED
|
@@ -131,7 +131,7 @@ export async function prewarmWorkspace(config: WorkspaceConfig): Promise<void> {
|
|
| 131 |
const workspacePath = path.join(/*turbopackIgnore: true*/ workspaceRoot, config.id);
|
| 132 |
|
| 133 |
if (!fs.existsSync(workspacePath)) {
|
| 134 |
-
fs.mkdirSync(workspacePath, { recursive: true });
|
| 135 |
}
|
| 136 |
|
| 137 |
const idxConfig = IdxEngine.getIdxConfig(workspacePath);
|
|
@@ -164,7 +164,7 @@ async function performProvisioning(config: WorkspaceConfig): Promise<WorkspaceOp
|
|
| 164 |
const userDataPath = path.join(/*turbopackIgnore: true*/ workspacePath, '.vscode-server');
|
| 165 |
|
| 166 |
if (!fs.existsSync(workspacePath)) {
|
| 167 |
-
fs.mkdirSync(workspacePath, { recursive: true });
|
| 168 |
log(`Allocated isolated filesystem segment: ${config.id.slice(0, 8)}`);
|
| 169 |
}
|
| 170 |
|
|
@@ -174,7 +174,7 @@ async function performProvisioning(config: WorkspaceConfig): Promise<WorkspaceOp
|
|
| 174 |
|
| 175 |
await IdxEngine.syncNixEnvironment(workspacePath, idxConfig, (msg) => log(msg));
|
| 176 |
|
| 177 |
-
const flagPath = path.join(workspacePath, '.idx-created');
|
| 178 |
if (!fs.existsSync(flagPath)) {
|
| 179 |
if (idxConfig.onCreate) {
|
| 180 |
log(`Executing onCreate lifecycle hook...`);
|
|
@@ -183,10 +183,9 @@ async function performProvisioning(config: WorkspaceConfig): Promise<WorkspaceOp
|
|
| 183 |
fs.writeFileSync(flagPath, new Date().toISOString());
|
| 184 |
}
|
| 185 |
|
| 186 |
-
if (idxConfig.
|
| 187 |
-
log(`Executing
|
| 188 |
-
|
| 189 |
-
await IdxEngine.runHook(workspacePath, 'onStart', idxConfig.onStart, (msg) => log(msg), true);
|
| 190 |
}
|
| 191 |
|
| 192 |
// 4. Identify Target Port
|
|
@@ -224,11 +223,18 @@ async function performProvisioning(config: WorkspaceConfig): Promise<WorkspaceOp
|
|
| 224 |
|
| 225 |
// 7. Handshake Loop
|
| 226 |
let attempts = 0;
|
| 227 |
-
while (attempts <
|
| 228 |
try {
|
| 229 |
const res = await fetch(`http://127.0.0.1:${port}`);
|
| 230 |
if (res.ok) {
|
| 231 |
log(`Handshake verified. Studio Engine Online.`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
const finalResult = {
|
| 233 |
success: true,
|
| 234 |
containerId: `native-${config.id}`,
|
|
@@ -239,9 +245,9 @@ async function performProvisioning(config: WorkspaceConfig): Promise<WorkspaceOp
|
|
| 239 |
return finalResult;
|
| 240 |
}
|
| 241 |
} catch {
|
| 242 |
-
if (attempts %
|
| 243 |
-
if (attempts ===
|
| 244 |
-
if (attempts ===
|
| 245 |
await delay(1000);
|
| 246 |
attempts++;
|
| 247 |
}
|
|
|
|
| 131 |
const workspacePath = path.join(/*turbopackIgnore: true*/ workspaceRoot, config.id);
|
| 132 |
|
| 133 |
if (!fs.existsSync(workspacePath)) {
|
| 134 |
+
fs.mkdirSync(/*turbopackIgnore: true*/ workspacePath, { recursive: true });
|
| 135 |
}
|
| 136 |
|
| 137 |
const idxConfig = IdxEngine.getIdxConfig(workspacePath);
|
|
|
|
| 164 |
const userDataPath = path.join(/*turbopackIgnore: true*/ workspacePath, '.vscode-server');
|
| 165 |
|
| 166 |
if (!fs.existsSync(workspacePath)) {
|
| 167 |
+
fs.mkdirSync(/*turbopackIgnore: true*/ workspacePath, { recursive: true });
|
| 168 |
log(`Allocated isolated filesystem segment: ${config.id.slice(0, 8)}`);
|
| 169 |
}
|
| 170 |
|
|
|
|
| 174 |
|
| 175 |
await IdxEngine.syncNixEnvironment(workspacePath, idxConfig, (msg) => log(msg));
|
| 176 |
|
| 177 |
+
const flagPath = path.join(/*turbopackIgnore: true*/ workspacePath, '.idx-created');
|
| 178 |
if (!fs.existsSync(flagPath)) {
|
| 179 |
if (idxConfig.onCreate) {
|
| 180 |
log(`Executing onCreate lifecycle hook...`);
|
|
|
|
| 183 |
fs.writeFileSync(flagPath, new Date().toISOString());
|
| 184 |
}
|
| 185 |
|
| 186 |
+
if (idxConfig.onCreate) {
|
| 187 |
+
log(`Executing onCreate lifecycle hook...`);
|
| 188 |
+
await IdxEngine.runHook(workspacePath, 'onCreate', idxConfig.onCreate, (msg) => log(msg));
|
|
|
|
| 189 |
}
|
| 190 |
|
| 191 |
// 4. Identify Target Port
|
|
|
|
| 223 |
|
| 224 |
// 7. Handshake Loop
|
| 225 |
let attempts = 0;
|
| 226 |
+
while (attempts < 60) {
|
| 227 |
try {
|
| 228 |
const res = await fetch(`http://127.0.0.1:${port}`);
|
| 229 |
if (res.ok) {
|
| 230 |
log(`Handshake verified. Studio Engine Online.`);
|
| 231 |
+
|
| 232 |
+
// 🟢 PRIORITY SHIFT: Start hooks ONLY AFTER the IDE is confirmed ready
|
| 233 |
+
if (idxConfig.onStart) {
|
| 234 |
+
log(`Executing background onStart lifecycle hooks...`);
|
| 235 |
+
IdxEngine.runHook(workspacePath, 'onStart', idxConfig.onStart, (msg) => log(msg), true);
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
const finalResult = {
|
| 239 |
success: true,
|
| 240 |
containerId: `native-${config.id}`,
|
|
|
|
| 245 |
return finalResult;
|
| 246 |
}
|
| 247 |
} catch {
|
| 248 |
+
if (attempts % 5 === 0) log(`[INFO] Scanning for IDE heartbeat... (Attempt ${attempts}/60)`);
|
| 249 |
+
if (attempts === 15) log(`[INFO] Nix evaluation in progress. Cold boot detected.`);
|
| 250 |
+
if (attempts === 45) log(`[WARN] Handshake threshold approaching. IDE core high load.`);
|
| 251 |
await delay(1000);
|
| 252 |
attempts++;
|
| 253 |
}
|
lib/fs/isolation.ts
CHANGED
|
@@ -32,7 +32,7 @@ const WORKSPACE_BASE = resolveWorkspaceBase();
|
|
| 32 |
* e.g., /path/to/codeverse/workspaces/{userId}
|
| 33 |
*/
|
| 34 |
export async function getUserWorkspaceRoot(userId: string): Promise<string> {
|
| 35 |
-
const userRoot = path.join(WORKSPACE_BASE, userId);
|
| 36 |
try {
|
| 37 |
await fs.mkdir(userRoot, { recursive: true });
|
| 38 |
} catch (e: unknown) {
|
|
@@ -50,10 +50,10 @@ export async function getUserWorkspaceRoot(userId: string): Promise<string> {
|
|
| 50 |
*/
|
| 51 |
export async function resolveSafeProjectPath(userId: string, projectName: string, subPath: string = ""): Promise<string> {
|
| 52 |
const userRoot = await getUserWorkspaceRoot(userId);
|
| 53 |
-
const projectRoot = path.resolve(userRoot, projectName.replace(/[^a-zA-Z0-9-_]/g, "-").slice(0, 60));
|
| 54 |
|
| 55 |
// Normalize and resolve the absolute path
|
| 56 |
-
const targetPath = path.resolve(projectRoot, subPath);
|
| 57 |
|
| 58 |
// Security Check: Ensure the resolved path is still within the project root
|
| 59 |
if (!targetPath.startsWith(projectRoot)) {
|
|
@@ -70,7 +70,7 @@ export async function resolveSafePath(userId: string, subPath: string): Promise<
|
|
| 70 |
const userRoot = await getUserWorkspaceRoot(userId);
|
| 71 |
|
| 72 |
// Normalize and resolve the absolute path
|
| 73 |
-
const targetPath = path.resolve(userRoot, subPath);
|
| 74 |
|
| 75 |
// Security Check: Ensure the resolved path is still within the user's root
|
| 76 |
if (!targetPath.startsWith(userRoot)) {
|
|
|
|
| 32 |
* e.g., /path/to/codeverse/workspaces/{userId}
|
| 33 |
*/
|
| 34 |
export async function getUserWorkspaceRoot(userId: string): Promise<string> {
|
| 35 |
+
const userRoot = path.join(/*turbopackIgnore: true*/ WORKSPACE_BASE, userId);
|
| 36 |
try {
|
| 37 |
await fs.mkdir(userRoot, { recursive: true });
|
| 38 |
} catch (e: unknown) {
|
|
|
|
| 50 |
*/
|
| 51 |
export async function resolveSafeProjectPath(userId: string, projectName: string, subPath: string = ""): Promise<string> {
|
| 52 |
const userRoot = await getUserWorkspaceRoot(userId);
|
| 53 |
+
const projectRoot = path.resolve(/*turbopackIgnore: true*/ userRoot, projectName.replace(/[^a-zA-Z0-9-_]/g, "-").slice(0, 60));
|
| 54 |
|
| 55 |
// Normalize and resolve the absolute path
|
| 56 |
+
const targetPath = path.resolve(/*turbopackIgnore: true*/ projectRoot, subPath);
|
| 57 |
|
| 58 |
// Security Check: Ensure the resolved path is still within the project root
|
| 59 |
if (!targetPath.startsWith(projectRoot)) {
|
|
|
|
| 70 |
const userRoot = await getUserWorkspaceRoot(userId);
|
| 71 |
|
| 72 |
// Normalize and resolve the absolute path
|
| 73 |
+
const targetPath = path.resolve(/*turbopackIgnore: true*/ userRoot, subPath);
|
| 74 |
|
| 75 |
// Security Check: Ensure the resolved path is still within the user's root
|
| 76 |
if (!targetPath.startsWith(userRoot)) {
|
lib/idx/idx-engine.ts
CHANGED
|
@@ -31,7 +31,7 @@ export class IdxEngine {
|
|
| 31 |
* Detects and parses the .idx/dev.nix file in the workspace root.
|
| 32 |
*/
|
| 33 |
static getIdxConfig(workspacePath: string): IdxConfig {
|
| 34 |
-
const configPath = path.join(workspacePath, '.idx', 'dev.nix');
|
| 35 |
if (!fs.existsSync(configPath)) return this.getDefaultConfig();
|
| 36 |
|
| 37 |
try {
|
|
|
|
| 31 |
* Detects and parses the .idx/dev.nix file in the workspace root.
|
| 32 |
*/
|
| 33 |
static getIdxConfig(workspacePath: string): IdxConfig {
|
| 34 |
+
const configPath = path.join(/*turbopackIgnore: true*/ workspacePath, '.idx', 'dev.nix');
|
| 35 |
if (!fs.existsSync(configPath)) return this.getDefaultConfig();
|
| 36 |
|
| 37 |
try {
|
next.config.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
import type { NextConfig } from "next";
|
| 2 |
|
| 3 |
const nextConfig: NextConfig = {
|
| 4 |
-
output: "standalone",
|
| 5 |
enablePrerenderSourceMaps: true,
|
| 6 |
serverExternalPackages: ["dockerode", "ssh2", "tar-fs", "node-pty"],
|
| 7 |
};
|
|
|
|
| 1 |
import type { NextConfig } from "next";
|
| 2 |
|
| 3 |
const nextConfig: NextConfig = {
|
|
|
|
| 4 |
enablePrerenderSourceMaps: true,
|
| 5 |
serverExternalPackages: ["dockerode", "ssh2", "tar-fs", "node-pty"],
|
| 6 |
};
|