fix server boot
Browse files- .agent/clone-body.json +1 -0
- .agent/cookies.txt +4 -0
- .agent/local-server.log +18 -0
- .agent/memory/session.json +2 -2
- .agent/start-body.json +1 -0
- .gitignore +2 -1
- dist/lib/docker/manager.js +51 -7
- dist/lib/env-config.js +19 -7
- dist/lib/hf/storage.js +9 -4
- dist/lib/idx/idx-engine.js +26 -21
- dist/server.js +4 -1
- lib/actions/emulator.ts +2 -1
- lib/docker/manager.ts +55 -7
- lib/env-config.ts +17 -5
- lib/hf/storage.ts +10 -4
- lib/idx/idx-engine.ts +28 -19
- local.db +0 -0
- package-lock.json +40 -3
- package.json +3 -1
- server.ts +5 -1
- workspaces/dev-user-id/local-repro +1 -0
.agent/clone-body.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"repoUrl":"https://github.com/octocat/Hello-World.git","projectName":"local-repro"}
|
.agent/cookies.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Netscape HTTP Cookie File
|
| 2 |
+
# https://curl.se/docs/http-cookies.html
|
| 3 |
+
# This file was generated by libcurl! Edit at your own risk.
|
| 4 |
+
|
.agent/local-server.log
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[BOOT] Starting Next.js preparation...
|
| 2 |
+
----------------------------------------------------
|
| 3 |
+
[READY] CodeVerse 1.0.0-stable
|
| 4 |
+
[READY] Interface: 0.0.0.0:7860
|
| 5 |
+
----------------------------------------------------
|
| 6 |
+
[BOOT] Next.js payload ready.
|
| 7 |
+
[BOOT] Environment validated. Synchronizing database...
|
| 8 |
+
[SYSTEM] Dev Seeding: 'Developer Guest' user ready.
|
| 9 |
+
[BOOT] Database synchronized.
|
| 10 |
+
[BOOT] Probing filesystem segment: d:\Code\codeverse\workspaces for existing sessions...
|
| 11 |
+
[Auto-Sleep] Cron initialized. Running every 30 minutes.
|
| 12 |
+
[BOOT] Global state stabilized. Application is fully operational.
|
| 13 |
+
⨯ SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
|
| 14 |
+
at JSON.parse (<anonymous>)
|
| 15 |
+
⨯ SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
|
| 16 |
+
at JSON.parse (<anonymous>)
|
| 17 |
+
(node:7268) [DEP0190] DeprecationWarning: Passing args to a child process with shell option true can lead to security vulnerabilities, as the arguments are not escaped, only concatenated.
|
| 18 |
+
(Use `node --trace-deprecation ...` to show where the warning was created)
|
.agent/memory/session.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
{
|
| 2 |
"version": "1.0.0",
|
| 3 |
-
"session_id": "
|
| 4 |
-
"started_at": "2026-04-
|
| 5 |
"workspace": "D:\\Code\\codeverse",
|
| 6 |
"active_task_id": null,
|
| 7 |
"active_agent": null,
|
|
|
|
| 1 |
{
|
| 2 |
"version": "1.0.0",
|
| 3 |
+
"session_id": "9c33f220",
|
| 4 |
+
"started_at": "2026-04-09T19:43:54.970676+05:30",
|
| 5 |
"workspace": "D:\\Code\\codeverse",
|
| 6 |
"active_task_id": null,
|
| 7 |
"active_agent": null,
|
.agent/start-body.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"action":"start","id":"74b4d115-7324-4cb5-bda8-6ad9027d86a4","withAndroidEmulator":false}
|
.gitignore
CHANGED
|
@@ -23,7 +23,8 @@
|
|
| 23 |
# misc
|
| 24 |
.DS_Store
|
| 25 |
*.pem
|
| 26 |
-
|
|
|
|
| 27 |
# debug
|
| 28 |
npm-debug.log*
|
| 29 |
yarn-debug.log*
|
|
|
|
| 23 |
# misc
|
| 24 |
.DS_Store
|
| 25 |
*.pem
|
| 26 |
+
*.npm-cache
|
| 27 |
+
*.codeverse-runtime
|
| 28 |
# debug
|
| 29 |
npm-debug.log*
|
| 30 |
yarn-debug.log*
|
dist/lib/docker/manager.js
CHANGED
|
@@ -22,6 +22,7 @@ const events_1 = require("events");
|
|
| 22 |
const idx_engine_1 = require("../idx/idx-engine");
|
| 23 |
const storage_1 = require("../hf/storage");
|
| 24 |
const isolation_1 = require("../fs/isolation");
|
|
|
|
| 25 |
/**
|
| 26 |
* Registry for native workspace processes (IDE instances running outside Docker)
|
| 27 |
* Map<workspaceId, { pid: number; port: number; process: WorkspaceProcess }>
|
|
@@ -40,7 +41,7 @@ exports.pendingProvisioning = new Map();
|
|
| 40 |
const SHORT_WORKSPACE_ID_LENGTH = 8;
|
| 41 |
const RUNTIME_ROOT_DIR_NAME = '.codeverse-runtime';
|
| 42 |
function getWorkspaceRootPath() {
|
| 43 |
-
return
|
| 44 |
}
|
| 45 |
function getRuntimeRootPath() {
|
| 46 |
return path_1.default.join(/*turbopackIgnore: true*/ getWorkspaceRootPath(), RUNTIME_ROOT_DIR_NAME);
|
|
@@ -73,6 +74,7 @@ async function resolveWorkspaceRuntimePaths(config) {
|
|
| 73 |
runtimeWorkspacePath: path_1.default.join(/*turbopackIgnore: true*/ runtimeRootPath, shortWorkspaceId),
|
| 74 |
userDataPath: path_1.default.join(/*turbopackIgnore: true*/ runtimeRootPath, `${shortWorkspaceId}-userdata`),
|
| 75 |
metadataPath: path_1.default.join(/*turbopackIgnore: true*/ runtimeRootPath, `${shortWorkspaceId}.id`),
|
|
|
|
| 76 |
};
|
| 77 |
}
|
| 78 |
function ensureRuntimeWorkspacePath(paths, log) {
|
|
@@ -81,6 +83,7 @@ function ensureRuntimeWorkspacePath(paths, log) {
|
|
| 81 |
}
|
| 82 |
fs_1.default.mkdirSync(paths.runtimeRootPath, { recursive: true });
|
| 83 |
fs_1.default.mkdirSync(paths.userDataPath, { recursive: true });
|
|
|
|
| 84 |
if (fs_1.default.existsSync(paths.runtimeWorkspacePath)) {
|
| 85 |
try {
|
| 86 |
const existingTargetPath = fs_1.default.realpathSync(paths.runtimeWorkspacePath);
|
|
@@ -144,6 +147,22 @@ function findAvailablePort() {
|
|
| 144 |
}
|
| 145 |
return port;
|
| 146 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
/**
|
| 148 |
* Checks if Docker is available in the current environment.
|
| 149 |
*/
|
|
@@ -255,8 +274,9 @@ async function performProvisioning(config) {
|
|
| 255 |
fs_1.default.writeFileSync(flagPath, new Date().toISOString());
|
| 256 |
}
|
| 257 |
// 5. Spawn code-server
|
| 258 |
-
const
|
| 259 |
-
const
|
|
|
|
| 260 |
const baseArgs = [
|
| 261 |
'--auth', 'none',
|
| 262 |
'--bind-addr', `127.0.0.1:${port}`,
|
|
@@ -265,16 +285,26 @@ async function performProvisioning(config) {
|
|
| 265 |
'--disable-update-check',
|
| 266 |
workspacePath
|
| 267 |
];
|
| 268 |
-
const spawnEnv = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
delete spawnEnv.PORT;
|
| 270 |
delete spawnEnv.SERVER_PORT;
|
| 271 |
const child = (0, child_process_1.spawn)(shellCommand, [...args, ...baseArgs], {
|
| 272 |
env: spawnEnv,
|
| 273 |
cwd: workspacePath,
|
| 274 |
-
shell:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
});
|
| 276 |
-
log(`Spawning VS Code Orchestrator (PID: ${child.pid})...`);
|
| 277 |
-
child.on('error', (err) => log(`[FATAL] IDE binary failure: ${err.message}`));
|
| 278 |
child.stdout.on('data', (data) => {
|
| 279 |
const out = data.toString().trim();
|
| 280 |
if (out.includes('listening on'))
|
|
@@ -288,6 +318,10 @@ async function performProvisioning(config) {
|
|
| 288 |
log(`[IDE:ERR] ${err}`);
|
| 289 |
});
|
| 290 |
child.on('close', (code, signal) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 291 |
log(`[IDE:EXIT] IDE process died with code ${code} (Signal: ${signal})`);
|
| 292 |
exports.nativeProcesses.delete(config.id);
|
| 293 |
});
|
|
@@ -296,6 +330,16 @@ async function performProvisioning(config) {
|
|
| 296 |
// 7. Handshake Loop
|
| 297 |
let attempts = 0;
|
| 298 |
while (attempts < 60) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
try {
|
| 300 |
const res = await fetch(`http://127.0.0.1:${port}`);
|
| 301 |
if (res.ok) {
|
|
|
|
| 22 |
const idx_engine_1 = require("../idx/idx-engine");
|
| 23 |
const storage_1 = require("../hf/storage");
|
| 24 |
const isolation_1 = require("../fs/isolation");
|
| 25 |
+
const env_config_1 = require("../env-config");
|
| 26 |
/**
|
| 27 |
* Registry for native workspace processes (IDE instances running outside Docker)
|
| 28 |
* Map<workspaceId, { pid: number; port: number; process: WorkspaceProcess }>
|
|
|
|
| 41 |
const SHORT_WORKSPACE_ID_LENGTH = 8;
|
| 42 |
const RUNTIME_ROOT_DIR_NAME = '.codeverse-runtime';
|
| 43 |
function getWorkspaceRootPath() {
|
| 44 |
+
return env_config_1.ENV_CONFIG.WORKSPACE_ROOT;
|
| 45 |
}
|
| 46 |
function getRuntimeRootPath() {
|
| 47 |
return path_1.default.join(/*turbopackIgnore: true*/ getWorkspaceRootPath(), RUNTIME_ROOT_DIR_NAME);
|
|
|
|
| 74 |
runtimeWorkspacePath: path_1.default.join(/*turbopackIgnore: true*/ runtimeRootPath, shortWorkspaceId),
|
| 75 |
userDataPath: path_1.default.join(/*turbopackIgnore: true*/ runtimeRootPath, `${shortWorkspaceId}-userdata`),
|
| 76 |
metadataPath: path_1.default.join(/*turbopackIgnore: true*/ runtimeRootPath, `${shortWorkspaceId}.id`),
|
| 77 |
+
npmCachePath: path_1.default.join(/*turbopackIgnore: true*/ runtimeRootPath, 'npm-cache'),
|
| 78 |
};
|
| 79 |
}
|
| 80 |
function ensureRuntimeWorkspacePath(paths, log) {
|
|
|
|
| 83 |
}
|
| 84 |
fs_1.default.mkdirSync(paths.runtimeRootPath, { recursive: true });
|
| 85 |
fs_1.default.mkdirSync(paths.userDataPath, { recursive: true });
|
| 86 |
+
fs_1.default.mkdirSync(paths.npmCachePath, { recursive: true });
|
| 87 |
if (fs_1.default.existsSync(paths.runtimeWorkspacePath)) {
|
| 88 |
try {
|
| 89 |
const existingTargetPath = fs_1.default.realpathSync(paths.runtimeWorkspacePath);
|
|
|
|
| 147 |
}
|
| 148 |
return port;
|
| 149 |
}
|
| 150 |
+
function resolveCodeServerLaunch() {
|
| 151 |
+
const overrideBinary = process.env.CODE_SERVER_BIN;
|
| 152 |
+
if (overrideBinary) {
|
| 153 |
+
return { command: overrideBinary, args: [], label: overrideBinary };
|
| 154 |
+
}
|
| 155 |
+
if (process.platform === 'win32') {
|
| 156 |
+
try {
|
| 157 |
+
(0, child_process_1.execSync)('where code-server.cmd', { stdio: 'ignore' });
|
| 158 |
+
return { command: 'code-server.cmd', args: [], label: 'code-server' };
|
| 159 |
+
}
|
| 160 |
+
catch (_a) {
|
| 161 |
+
return { command: 'npx.cmd', args: ['--yes', 'code-server'], label: 'npx code-server' };
|
| 162 |
+
}
|
| 163 |
+
}
|
| 164 |
+
return { command: 'code-server', args: [], label: 'code-server' };
|
| 165 |
+
}
|
| 166 |
/**
|
| 167 |
* Checks if Docker is available in the current environment.
|
| 168 |
*/
|
|
|
|
| 274 |
fs_1.default.writeFileSync(flagPath, new Date().toISOString());
|
| 275 |
}
|
| 276 |
// 5. Spawn code-server
|
| 277 |
+
const codeServerLaunch = resolveCodeServerLaunch();
|
| 278 |
+
const shellCommand = codeServerLaunch.command;
|
| 279 |
+
const args = codeServerLaunch.args;
|
| 280 |
const baseArgs = [
|
| 281 |
'--auth', 'none',
|
| 282 |
'--bind-addr', `127.0.0.1:${port}`,
|
|
|
|
| 285 |
'--disable-update-check',
|
| 286 |
workspacePath
|
| 287 |
];
|
| 288 |
+
const spawnEnv = {
|
| 289 |
+
...process.env,
|
| 290 |
+
HOME: workspacePath,
|
| 291 |
+
npm_config_cache: runtimePaths.npmCachePath,
|
| 292 |
+
npm_config_update_notifier: 'false',
|
| 293 |
+
};
|
| 294 |
delete spawnEnv.PORT;
|
| 295 |
delete spawnEnv.SERVER_PORT;
|
| 296 |
const child = (0, child_process_1.spawn)(shellCommand, [...args, ...baseArgs], {
|
| 297 |
env: spawnEnv,
|
| 298 |
cwd: workspacePath,
|
| 299 |
+
shell: false
|
| 300 |
+
});
|
| 301 |
+
log(`Spawning VS Code Orchestrator via ${codeServerLaunch.label} (PID: ${child.pid})...`);
|
| 302 |
+
let childExited = false;
|
| 303 |
+
let childFailureReason = null;
|
| 304 |
+
child.on('error', (err) => {
|
| 305 |
+
childFailureReason = `IDE_BINARY_FAILURE: ${err.message}`;
|
| 306 |
+
log(`[FATAL] IDE binary failure: ${err.message}`);
|
| 307 |
});
|
|
|
|
|
|
|
| 308 |
child.stdout.on('data', (data) => {
|
| 309 |
const out = data.toString().trim();
|
| 310 |
if (out.includes('listening on'))
|
|
|
|
| 318 |
log(`[IDE:ERR] ${err}`);
|
| 319 |
});
|
| 320 |
child.on('close', (code, signal) => {
|
| 321 |
+
childExited = true;
|
| 322 |
+
if (code !== 0 || signal) {
|
| 323 |
+
childFailureReason = childFailureReason !== null && childFailureReason !== void 0 ? childFailureReason : `IDE_PROCESS_EXIT_${code !== null && code !== void 0 ? code : 'unknown'}${signal ? `_${signal}` : ''}`;
|
| 324 |
+
}
|
| 325 |
log(`[IDE:EXIT] IDE process died with code ${code} (Signal: ${signal})`);
|
| 326 |
exports.nativeProcesses.delete(config.id);
|
| 327 |
});
|
|
|
|
| 330 |
// 7. Handshake Loop
|
| 331 |
let attempts = 0;
|
| 332 |
while (attempts < 60) {
|
| 333 |
+
if (childExited) {
|
| 334 |
+
const rawFailureMessage = childFailureReason !== null && childFailureReason !== void 0 ? childFailureReason : 'IDE_PROCESS_EXITED_BEFORE_HANDSHAKE';
|
| 335 |
+
const failureMessage = shellCommand === 'npx'
|
| 336 |
+
? `${rawFailureMessage}. code-server could not be bootstrapped via npx. Install code-server globally or set CODE_SERVER_BIN.`
|
| 337 |
+
: rawFailureMessage;
|
| 338 |
+
log(`[FATAL] IDE bootstrap aborted before handshake: ${failureMessage}`);
|
| 339 |
+
const errResult = { success: false, error: failureMessage };
|
| 340 |
+
exports.provisioningBus.emit(`error:${config.id}`, errResult);
|
| 341 |
+
return errResult;
|
| 342 |
+
}
|
| 343 |
try {
|
| 344 |
const res = await fetch(`http://127.0.0.1:${port}`);
|
| 345 |
if (res.ok) {
|
dist/lib/env-config.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
| 1 |
"use strict";
|
|
|
|
|
|
|
|
|
|
| 2 |
Object.defineProperty(exports, "__esModule", { value: true });
|
| 3 |
exports.ENV_CONFIG = void 0;
|
| 4 |
exports.validateEnvironment = validateEnvironment;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
/**
|
| 6 |
* CodeVerse Environment Configuration & Requirements Manifest.
|
| 7 |
* Centralizing all system variables for production-grade reliability.
|
|
@@ -10,7 +19,7 @@ exports.ENV_CONFIG = {
|
|
| 10 |
// 1. Storage & Persistence
|
| 11 |
HF_TOKEN: process.env.HF_TOKEN || process.env.hfToken || process.env.HF_SPACE || process.env.HuggingFaceToken,
|
| 12 |
HF_DATASET_ID: process.env.HF_DATASET_ID || process.env.hfDataset || process.env.HF_DATASET,
|
| 13 |
-
WORKSPACE_ROOT: process.env.WORKSPACE_ROOT ||
|
| 14 |
// 2. Build Acceleration
|
| 15 |
CACHIX_CACHE_NAME: process.env.CACHIX_CACHE_NAME || 'code-nix',
|
| 16 |
CACHIX_AUTH_TOKEN: process.env.CACHIX_AUTH_TOKEN,
|
|
@@ -20,8 +29,8 @@ exports.ENV_CONFIG = {
|
|
| 20 |
APP_BASE_URL: process.env.NEXTAUTH_URL || 'http://localhost:7860',
|
| 21 |
IS_SBC: !!process.env.SPACE_ID,
|
| 22 |
// 4. Database & Auth
|
| 23 |
-
AUTH_SECRET: process.env.AUTH_SECRET || process.env.authSecret,
|
| 24 |
-
TURSO_URL: process.env.TURSO_URL || process.env.turso_url || process.env.database_url || process.env.TURSO_DATABASE_URL || process.env.DB_URL || process.env.turso_database_url,
|
| 25 |
TURSO_AUTH_TOKEN: process.env.TURSO_AUTH_TOKEN || process.env.turso_auth_token || process.env.DB_TOKEN,
|
| 26 |
TMPDIR: '/tmp',
|
| 27 |
HF_HOME: '/tmp/.cache/huggingface',
|
|
@@ -31,14 +40,17 @@ exports.ENV_CONFIG = {
|
|
| 31 |
*/
|
| 32 |
function validateEnvironment() {
|
| 33 |
const missing = [];
|
| 34 |
-
if (!exports.ENV_CONFIG.HF_TOKEN)
|
| 35 |
-
missing.push('HF_TOKEN (Missing Persistence Link)');
|
| 36 |
-
if (!exports.ENV_CONFIG.HF_DATASET_ID)
|
| 37 |
-
missing.push('HF_DATASET_ID (Missing Data Segment)');
|
| 38 |
if (!exports.ENV_CONFIG.AUTH_SECRET)
|
| 39 |
missing.push('AUTH_SECRET (Security Risk)');
|
| 40 |
if (!exports.ENV_CONFIG.TURSO_URL)
|
| 41 |
missing.push('TURSO_URL (Database Missing)');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
// Strategic Dataset Validation
|
| 43 |
if (exports.ENV_CONFIG.HF_DATASET_ID && !exports.ENV_CONFIG.HF_DATASET_ID.includes('/')) {
|
| 44 |
return { valid: false, missing: ['HF_DATASET_ID_FORMAT_ERROR: Must be "username/dataset"'] };
|
|
|
|
| 1 |
"use strict";
|
| 2 |
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
| 3 |
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
| 4 |
+
};
|
| 5 |
Object.defineProperty(exports, "__esModule", { value: true });
|
| 6 |
exports.ENV_CONFIG = void 0;
|
| 7 |
exports.validateEnvironment = validateEnvironment;
|
| 8 |
+
const path_1 = __importDefault(require("path"));
|
| 9 |
+
function getDefaultWorkspaceRoot() {
|
| 10 |
+
return process.platform === "win32"
|
| 11 |
+
? path_1.default.join(process.cwd(), "workspaces")
|
| 12 |
+
: "/home/node/w";
|
| 13 |
+
}
|
| 14 |
/**
|
| 15 |
* CodeVerse Environment Configuration & Requirements Manifest.
|
| 16 |
* Centralizing all system variables for production-grade reliability.
|
|
|
|
| 19 |
// 1. Storage & Persistence
|
| 20 |
HF_TOKEN: process.env.HF_TOKEN || process.env.hfToken || process.env.HF_SPACE || process.env.HuggingFaceToken,
|
| 21 |
HF_DATASET_ID: process.env.HF_DATASET_ID || process.env.hfDataset || process.env.HF_DATASET,
|
| 22 |
+
WORKSPACE_ROOT: process.env.WORKSPACE_ROOT || getDefaultWorkspaceRoot(),
|
| 23 |
// 2. Build Acceleration
|
| 24 |
CACHIX_CACHE_NAME: process.env.CACHIX_CACHE_NAME || 'code-nix',
|
| 25 |
CACHIX_AUTH_TOKEN: process.env.CACHIX_AUTH_TOKEN,
|
|
|
|
| 29 |
APP_BASE_URL: process.env.NEXTAUTH_URL || 'http://localhost:7860',
|
| 30 |
IS_SBC: !!process.env.SPACE_ID,
|
| 31 |
// 4. Database & Auth
|
| 32 |
+
AUTH_SECRET: process.env.AUTH_SECRET || process.env.NEXTAUTH_SECRET || process.env.authSecret,
|
| 33 |
+
TURSO_URL: process.env.TURSO_URL || process.env.turso_url || process.env.DATABASE_URL || process.env.database_url || process.env.TURSO_DATABASE_URL || process.env.DB_URL || process.env.turso_database_url,
|
| 34 |
TURSO_AUTH_TOKEN: process.env.TURSO_AUTH_TOKEN || process.env.turso_auth_token || process.env.DB_TOKEN,
|
| 35 |
TMPDIR: '/tmp',
|
| 36 |
HF_HOME: '/tmp/.cache/huggingface',
|
|
|
|
| 40 |
*/
|
| 41 |
function validateEnvironment() {
|
| 42 |
const missing = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
if (!exports.ENV_CONFIG.AUTH_SECRET)
|
| 44 |
missing.push('AUTH_SECRET (Security Risk)');
|
| 45 |
if (!exports.ENV_CONFIG.TURSO_URL)
|
| 46 |
missing.push('TURSO_URL (Database Missing)');
|
| 47 |
+
// HF persistence is required in deployed Spaces, not for local production testing.
|
| 48 |
+
if (exports.ENV_CONFIG.SPACE_ID) {
|
| 49 |
+
if (!exports.ENV_CONFIG.HF_TOKEN)
|
| 50 |
+
missing.push('HF_TOKEN (Missing Persistence Link)');
|
| 51 |
+
if (!exports.ENV_CONFIG.HF_DATASET_ID)
|
| 52 |
+
missing.push('HF_DATASET_ID (Missing Data Segment)');
|
| 53 |
+
}
|
| 54 |
// Strategic Dataset Validation
|
| 55 |
if (exports.ENV_CONFIG.HF_DATASET_ID && !exports.ENV_CONFIG.HF_DATASET_ID.includes('/')) {
|
| 56 |
return { valid: false, missing: ['HF_DATASET_ID_FORMAT_ERROR: Must be "username/dataset"'] };
|
dist/lib/hf/storage.js
CHANGED
|
@@ -7,10 +7,14 @@ exports.HFStorage = void 0;
|
|
| 7 |
const child_process_1 = require("child_process");
|
| 8 |
const path_1 = __importDefault(require("path"));
|
| 9 |
const fs_1 = __importDefault(require("fs"));
|
|
|
|
| 10 |
/**
|
| 11 |
* Hugging Face Storage Utility for 2026 CodeVerse Persistence.
|
| 12 |
*/
|
| 13 |
class HFStorage {
|
|
|
|
|
|
|
|
|
|
| 14 |
/**
|
| 15 |
* Internal helper for asynchronous execution with logging.
|
| 16 |
*/
|
|
@@ -40,13 +44,14 @@ class HFStorage {
|
|
| 40 |
else
|
| 41 |
reject(new Error(`Command failed with code ${code}: ${command}`));
|
| 42 |
});
|
|
|
|
| 43 |
});
|
| 44 |
}
|
| 45 |
/**
|
| 46 |
* Synchronizes the environment FROM the Hugging Face Dataset (Pull).
|
| 47 |
*/
|
| 48 |
static async syncFromDataset(onLog) {
|
| 49 |
-
if (!this.
|
| 50 |
onLog === null || onLog === void 0 ? void 0 : onLog(`[HF:STORAGE] Persistence layer inactive. Missing credentials.`);
|
| 51 |
return;
|
| 52 |
}
|
|
@@ -77,7 +82,7 @@ class HFStorage {
|
|
| 77 |
* Synchronizes the environment TO the Hugging Face Dataset (Push).
|
| 78 |
*/
|
| 79 |
static async syncToDataset(onLog) {
|
| 80 |
-
if (!this.
|
| 81 |
return;
|
| 82 |
try {
|
| 83 |
onLog === null || onLog === void 0 ? void 0 : onLog(`[HF:STORAGE] Saving persistent profile to '${this.HF_DATASET_ID}'...`);
|
|
@@ -99,7 +104,7 @@ class HFStorage {
|
|
| 99 |
}
|
| 100 |
}
|
| 101 |
static startAutoSave(intervalMs = 300000) {
|
| 102 |
-
if (this.autoSaveStarted)
|
| 103 |
return;
|
| 104 |
this.autoSaveStarted = true;
|
| 105 |
console.log(`[HF:STORAGE] Persistence heartbeat initialized (Interval: ${intervalMs}ms)`);
|
|
@@ -112,7 +117,7 @@ exports.HFStorage = HFStorage;
|
|
| 112 |
HFStorage.HF_TOKEN = process.env.HF_TOKEN;
|
| 113 |
HFStorage.HF_DATASET_ID = process.env.HF_DATASET_ID;
|
| 114 |
HFStorage.PROFILE_PATH = path_1.default.join(process.env.HOME || '/home/node', '.nix-profile');
|
| 115 |
-
HFStorage.WORKSPACE_ROOT =
|
| 116 |
/**
|
| 117 |
* Starts the periodic persistence interval (Singleton Heartbeat).
|
| 118 |
*/
|
|
|
|
| 7 |
const child_process_1 = require("child_process");
|
| 8 |
const path_1 = __importDefault(require("path"));
|
| 9 |
const fs_1 = __importDefault(require("fs"));
|
| 10 |
+
const env_config_1 = require("../env-config");
|
| 11 |
/**
|
| 12 |
* Hugging Face Storage Utility for 2026 CodeVerse Persistence.
|
| 13 |
*/
|
| 14 |
class HFStorage {
|
| 15 |
+
static get isPersistenceRuntimeEnabled() {
|
| 16 |
+
return Boolean(env_config_1.ENV_CONFIG.SPACE_ID && this.HF_TOKEN && this.HF_DATASET_ID && process.platform !== 'win32');
|
| 17 |
+
}
|
| 18 |
/**
|
| 19 |
* Internal helper for asynchronous execution with logging.
|
| 20 |
*/
|
|
|
|
| 44 |
else
|
| 45 |
reject(new Error(`Command failed with code ${code}: ${command}`));
|
| 46 |
});
|
| 47 |
+
child.on('error', (error) => reject(error));
|
| 48 |
});
|
| 49 |
}
|
| 50 |
/**
|
| 51 |
* Synchronizes the environment FROM the Hugging Face Dataset (Pull).
|
| 52 |
*/
|
| 53 |
static async syncFromDataset(onLog) {
|
| 54 |
+
if (!this.isPersistenceRuntimeEnabled) {
|
| 55 |
onLog === null || onLog === void 0 ? void 0 : onLog(`[HF:STORAGE] Persistence layer inactive. Missing credentials.`);
|
| 56 |
return;
|
| 57 |
}
|
|
|
|
| 82 |
* Synchronizes the environment TO the Hugging Face Dataset (Push).
|
| 83 |
*/
|
| 84 |
static async syncToDataset(onLog) {
|
| 85 |
+
if (!this.isPersistenceRuntimeEnabled)
|
| 86 |
return;
|
| 87 |
try {
|
| 88 |
onLog === null || onLog === void 0 ? void 0 : onLog(`[HF:STORAGE] Saving persistent profile to '${this.HF_DATASET_ID}'...`);
|
|
|
|
| 104 |
}
|
| 105 |
}
|
| 106 |
static startAutoSave(intervalMs = 300000) {
|
| 107 |
+
if (this.autoSaveStarted || !this.isPersistenceRuntimeEnabled)
|
| 108 |
return;
|
| 109 |
this.autoSaveStarted = true;
|
| 110 |
console.log(`[HF:STORAGE] Persistence heartbeat initialized (Interval: ${intervalMs}ms)`);
|
|
|
|
| 117 |
HFStorage.HF_TOKEN = process.env.HF_TOKEN;
|
| 118 |
HFStorage.HF_DATASET_ID = process.env.HF_DATASET_ID;
|
| 119 |
HFStorage.PROFILE_PATH = path_1.default.join(process.env.HOME || '/home/node', '.nix-profile');
|
| 120 |
+
HFStorage.WORKSPACE_ROOT = env_config_1.ENV_CONFIG.WORKSPACE_ROOT;
|
| 121 |
/**
|
| 122 |
* Starts the periodic persistence interval (Singleton Heartbeat).
|
| 123 |
*/
|
dist/lib/idx/idx-engine.js
CHANGED
|
@@ -12,6 +12,15 @@ const child_process_1 = require("child_process");
|
|
| 12 |
* Refactored for 2026 Asynchronous Execution to prevent Event Loop blocking.
|
| 13 |
*/
|
| 14 |
class IdxEngine {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
/**
|
| 16 |
* Returns a robust baseline configuration for workspaces without a dev.nix.
|
| 17 |
*/
|
|
@@ -19,7 +28,7 @@ class IdxEngine {
|
|
| 19 |
return {
|
| 20 |
packages: ['pkgs.nodejs', 'pkgs.go', 'pkgs.python3', 'pkgs.docker', 'pkgs.python3Packages.huggingface-hub'],
|
| 21 |
onCreate: 'npm install',
|
| 22 |
-
onStart: 'sleep 5 && npm run dev'
|
| 23 |
};
|
| 24 |
}
|
| 25 |
/**
|
|
@@ -60,25 +69,14 @@ class IdxEngine {
|
|
| 60 |
const log = (msg) => { if (onLog)
|
| 61 |
onLog(`[IDX:NIX] ${msg}`); };
|
| 62 |
log(`Syncing system packages: ${config.packages.join(', ')}...`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
// CACHIX ACCELERATION: Robust check for binary existence to prevent ENOENT crash
|
| 64 |
const cachixName = process.env.CACHIX_CACHE_NAME || 'code-nix';
|
| 65 |
-
|
| 66 |
-
try {
|
| 67 |
-
await new Promise((resolve) => {
|
| 68 |
-
const check = (0, child_process_1.spawn)('command', ['-v', 'cachix'], { shell: true });
|
| 69 |
-
check.on('close', (code) => {
|
| 70 |
-
hasCachix = (code === 0);
|
| 71 |
-
resolve();
|
| 72 |
-
});
|
| 73 |
-
check.on('error', () => {
|
| 74 |
-
hasCachix = false;
|
| 75 |
-
resolve();
|
| 76 |
-
});
|
| 77 |
-
});
|
| 78 |
-
}
|
| 79 |
-
catch (_a) {
|
| 80 |
-
hasCachix = false;
|
| 81 |
-
}
|
| 82 |
if (hasCachix) {
|
| 83 |
const cachixToken = process.env.CACHIX_AUTH_TOKEN;
|
| 84 |
if (cachixToken) {
|
|
@@ -99,7 +97,7 @@ class IdxEngine {
|
|
| 99 |
child.on('close', (code) => code === 0 ? resolve() : reject(new Error(`Cachix failed with code ${code}`)));
|
| 100 |
});
|
| 101 |
}
|
| 102 |
-
catch (
|
| 103 |
log(`[WARN] Cachix setup bypassed. Falling back to default binary cache.`);
|
| 104 |
}
|
| 105 |
}
|
|
@@ -120,7 +118,7 @@ class IdxEngine {
|
|
| 120 |
return;
|
| 121 |
}
|
| 122 |
}
|
| 123 |
-
catch (
|
| 124 |
log(`[WARN] Manifest corruption detected. Forcing re-sync.`);
|
| 125 |
}
|
| 126 |
}
|
|
@@ -155,6 +153,7 @@ class IdxEngine {
|
|
| 155 |
});
|
| 156 |
child.stdout.on('data', (data) => log(data.toString().trim()));
|
| 157 |
child.stderr.on('data', (data) => log(`[INFO] ${data.toString().trim()}`));
|
|
|
|
| 158 |
child.on('close', (code) => {
|
| 159 |
if (code === 0) {
|
| 160 |
fs_1.default.writeFileSync(manifestPath, JSON.stringify({ packages: config.packages, timestamp: new Date().toISOString() }));
|
|
@@ -183,12 +182,18 @@ class IdxEngine {
|
|
| 183 |
const spawnEnv = { ...process.env, HOME: workspacePath };
|
| 184 |
delete spawnEnv.PORT;
|
| 185 |
delete spawnEnv.SERVER_PORT;
|
| 186 |
-
const
|
|
|
|
|
|
|
| 187 |
cwd: workspacePath,
|
| 188 |
env: spawnEnv
|
| 189 |
});
|
| 190 |
child.stdout.on('data', (data) => log(data.toString().trim()));
|
| 191 |
child.stderr.on('data', (data) => log(`[WARN] ${data.toString().trim()}`));
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
child.on('close', (code) => {
|
| 193 |
if (code === 0) {
|
| 194 |
log(`Hook ${hookName} completed successfully.`);
|
|
|
|
| 12 |
* Refactored for 2026 Asynchronous Execution to prevent Event Loop blocking.
|
| 13 |
*/
|
| 14 |
class IdxEngine {
|
| 15 |
+
static async hasCommand(command) {
|
| 16 |
+
const probeCommand = process.platform === 'win32' ? 'where' : 'command';
|
| 17 |
+
const probeArgs = process.platform === 'win32' ? [command] : ['-v', command];
|
| 18 |
+
return await new Promise((resolve) => {
|
| 19 |
+
const child = (0, child_process_1.spawn)(probeCommand, probeArgs, { shell: process.platform !== 'win32' });
|
| 20 |
+
child.on('close', (code) => resolve(code === 0));
|
| 21 |
+
child.on('error', () => resolve(false));
|
| 22 |
+
});
|
| 23 |
+
}
|
| 24 |
/**
|
| 25 |
* Returns a robust baseline configuration for workspaces without a dev.nix.
|
| 26 |
*/
|
|
|
|
| 28 |
return {
|
| 29 |
packages: ['pkgs.nodejs', 'pkgs.go', 'pkgs.python3', 'pkgs.docker', 'pkgs.python3Packages.huggingface-hub'],
|
| 30 |
onCreate: 'npm install',
|
| 31 |
+
onStart: process.platform === 'win32' ? 'Start-Sleep -Seconds 5; npm run dev' : 'sleep 5 && npm run dev'
|
| 32 |
};
|
| 33 |
}
|
| 34 |
/**
|
|
|
|
| 69 |
const log = (msg) => { if (onLog)
|
| 70 |
onLog(`[IDX:NIX] ${msg}`); };
|
| 71 |
log(`Syncing system packages: ${config.packages.join(', ')}...`);
|
| 72 |
+
const hasNix = await this.hasCommand('nix');
|
| 73 |
+
if (!hasNix) {
|
| 74 |
+
log(`Nix is unavailable on this host. Skipping declarative package sync.`);
|
| 75 |
+
return;
|
| 76 |
+
}
|
| 77 |
// CACHIX ACCELERATION: Robust check for binary existence to prevent ENOENT crash
|
| 78 |
const cachixName = process.env.CACHIX_CACHE_NAME || 'code-nix';
|
| 79 |
+
const hasCachix = await this.hasCommand('cachix');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
if (hasCachix) {
|
| 81 |
const cachixToken = process.env.CACHIX_AUTH_TOKEN;
|
| 82 |
if (cachixToken) {
|
|
|
|
| 97 |
child.on('close', (code) => code === 0 ? resolve() : reject(new Error(`Cachix failed with code ${code}`)));
|
| 98 |
});
|
| 99 |
}
|
| 100 |
+
catch (_a) {
|
| 101 |
log(`[WARN] Cachix setup bypassed. Falling back to default binary cache.`);
|
| 102 |
}
|
| 103 |
}
|
|
|
|
| 118 |
return;
|
| 119 |
}
|
| 120 |
}
|
| 121 |
+
catch (_b) {
|
| 122 |
log(`[WARN] Manifest corruption detected. Forcing re-sync.`);
|
| 123 |
}
|
| 124 |
}
|
|
|
|
| 153 |
});
|
| 154 |
child.stdout.on('data', (data) => log(data.toString().trim()));
|
| 155 |
child.stderr.on('data', (data) => log(`[INFO] ${data.toString().trim()}`));
|
| 156 |
+
child.on('error', (error) => reject(error));
|
| 157 |
child.on('close', (code) => {
|
| 158 |
if (code === 0) {
|
| 159 |
fs_1.default.writeFileSync(manifestPath, JSON.stringify({ packages: config.packages, timestamp: new Date().toISOString() }));
|
|
|
|
| 182 |
const spawnEnv = { ...process.env, HOME: workspacePath };
|
| 183 |
delete spawnEnv.PORT;
|
| 184 |
delete spawnEnv.SERVER_PORT;
|
| 185 |
+
const shellCommand = process.platform === 'win32' ? 'powershell.exe' : '/bin/bash';
|
| 186 |
+
const shellArgs = process.platform === 'win32' ? ['-NoProfile', '-Command', script] : ['-c', script];
|
| 187 |
+
const child = (0, child_process_1.spawn)(shellCommand, shellArgs, {
|
| 188 |
cwd: workspacePath,
|
| 189 |
env: spawnEnv
|
| 190 |
});
|
| 191 |
child.stdout.on('data', (data) => log(data.toString().trim()));
|
| 192 |
child.stderr.on('data', (data) => log(`[WARN] ${data.toString().trim()}`));
|
| 193 |
+
child.on('error', (error) => {
|
| 194 |
+
log(`[ERROR] ${error.message}`);
|
| 195 |
+
reject(error);
|
| 196 |
+
});
|
| 197 |
child.on('close', (code) => {
|
| 198 |
if (code === 0) {
|
| 199 |
log(`Hook ${hookName} completed successfully.`);
|
dist/server.js
CHANGED
|
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
| 36 |
return (mod && mod.__esModule) ? mod : { "default": mod };
|
| 37 |
};
|
| 38 |
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
| 39 |
/**
|
| 40 |
* 🛰️ GLOBAL STABILIZATION (April 2026): Catch unhandled errors that cause HF Space restarts.
|
| 41 |
*/
|
|
@@ -294,7 +295,9 @@ server.listen(PORT, HOST, () => {
|
|
| 294 |
console.log("[BOOT] Database synchronized.");
|
| 295 |
// Reconnect and Warmup
|
| 296 |
(0, manager_1.reconnectRunningWorkspaces)().catch(() => { });
|
| 297 |
-
|
|
|
|
|
|
|
| 298 |
// Crons and Persistence
|
| 299 |
storage_1.HFStorage.startAutoSave(constants_1.INFRA_CONFIG.PERSISTENCE_INTERVAL_MS * 5);
|
| 300 |
(0, auto_sleep_1.startAutoSleepCron)();
|
|
|
|
| 36 |
return (mod && mod.__esModule) ? mod : { "default": mod };
|
| 37 |
};
|
| 38 |
Object.defineProperty(exports, "__esModule", { value: true });
|
| 39 |
+
require("dotenv/config");
|
| 40 |
/**
|
| 41 |
* 🛰️ GLOBAL STABILIZATION (April 2026): Catch unhandled errors that cause HF Space restarts.
|
| 42 |
*/
|
|
|
|
| 295 |
console.log("[BOOT] Database synchronized.");
|
| 296 |
// Reconnect and Warmup
|
| 297 |
(0, manager_1.reconnectRunningWorkspaces)().catch(() => { });
|
| 298 |
+
if (process.env.ENABLE_BASELINE_PREWARM === 'true') {
|
| 299 |
+
(0, manager_1.prewarmWorkspace)({ id: 'baseline-warmup', userId: 'system', projectName: 'CodeVerse-Internal' }).catch(() => { });
|
| 300 |
+
}
|
| 301 |
// Crons and Persistence
|
| 302 |
storage_1.HFStorage.startAutoSave(constants_1.INFRA_CONFIG.PERSISTENCE_INTERVAL_MS * 5);
|
| 303 |
(0, auto_sleep_1.startAutoSleepCron)();
|
lib/actions/emulator.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
"use server";
|
| 2 |
|
| 3 |
import Docker from 'dockerode';
|
|
|
|
| 4 |
|
| 5 |
const docker = new Docker({
|
| 6 |
socketPath: process.platform === 'win32' ? '//./pipe/docker_engine' : '/var/run/docker.sock'
|
|
@@ -38,7 +39,7 @@ export async function checkDeviceAvailability(platform: string, workspaceId: str
|
|
| 38 |
const path = await import('path');
|
| 39 |
|
| 40 |
const userId = "default-user";
|
| 41 |
-
const workspaceRoot =
|
| 42 |
const dataPath = path.join(/*turbopackIgnore: true*/ workspaceRoot, userId, workspaceId);
|
| 43 |
|
| 44 |
try {
|
|
|
|
| 1 |
"use server";
|
| 2 |
|
| 3 |
import Docker from 'dockerode';
|
| 4 |
+
import { ENV_CONFIG } from '@/lib/env-config';
|
| 5 |
|
| 6 |
const docker = new Docker({
|
| 7 |
socketPath: process.platform === 'win32' ? '//./pipe/docker_engine' : '/var/run/docker.sock'
|
|
|
|
| 39 |
const path = await import('path');
|
| 40 |
|
| 41 |
const userId = "default-user";
|
| 42 |
+
const workspaceRoot = ENV_CONFIG.WORKSPACE_ROOT || path.join(/*turbopackIgnore: true*/ '/home/node/w');
|
| 43 |
const dataPath = path.join(/*turbopackIgnore: true*/ workspaceRoot, userId, workspaceId);
|
| 44 |
|
| 45 |
try {
|
lib/docker/manager.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { EventEmitter } from 'events';
|
|
| 6 |
import { IdxEngine } from '../idx/idx-engine';
|
| 7 |
import { HFStorage } from '../hf/storage';
|
| 8 |
import { resolveSafeProjectPath } from '../fs/isolation';
|
|
|
|
| 9 |
|
| 10 |
/**
|
| 11 |
* Lighter process interface for reconnected sessions that don't have a full Node.js ChildProcess object.
|
|
@@ -42,13 +43,14 @@ interface WorkspaceRuntimePaths {
|
|
| 42 |
runtimeWorkspacePath: string;
|
| 43 |
userDataPath: string;
|
| 44 |
metadataPath: string;
|
|
|
|
| 45 |
}
|
| 46 |
|
| 47 |
const SHORT_WORKSPACE_ID_LENGTH = 8;
|
| 48 |
const RUNTIME_ROOT_DIR_NAME = '.codeverse-runtime';
|
| 49 |
|
| 50 |
function getWorkspaceRootPath(): string {
|
| 51 |
-
return
|
| 52 |
}
|
| 53 |
|
| 54 |
function getRuntimeRootPath(): string {
|
|
@@ -89,6 +91,7 @@ async function resolveWorkspaceRuntimePaths(config: Pick<WorkspaceConfig, 'id' |
|
|
| 89 |
runtimeWorkspacePath: path.join(/*turbopackIgnore: true*/ runtimeRootPath, shortWorkspaceId),
|
| 90 |
userDataPath: path.join(/*turbopackIgnore: true*/ runtimeRootPath, `${shortWorkspaceId}-userdata`),
|
| 91 |
metadataPath: path.join(/*turbopackIgnore: true*/ runtimeRootPath, `${shortWorkspaceId}.id`),
|
|
|
|
| 92 |
};
|
| 93 |
}
|
| 94 |
|
|
@@ -99,6 +102,7 @@ function ensureRuntimeWorkspacePath(paths: WorkspaceRuntimePaths, log?: (msg: st
|
|
| 99 |
|
| 100 |
fs.mkdirSync(paths.runtimeRootPath, { recursive: true });
|
| 101 |
fs.mkdirSync(paths.userDataPath, { recursive: true });
|
|
|
|
| 102 |
|
| 103 |
if (fs.existsSync(paths.runtimeWorkspacePath)) {
|
| 104 |
try {
|
|
@@ -166,6 +170,24 @@ function findAvailablePort(): number {
|
|
| 166 |
return port;
|
| 167 |
}
|
| 168 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
/**
|
| 170 |
* Checks if Docker is available in the current environment.
|
| 171 |
*/
|
|
@@ -312,8 +334,9 @@ async function performProvisioning(config: WorkspaceConfig): Promise<WorkspaceOp
|
|
| 312 |
}
|
| 313 |
|
| 314 |
// 5. Spawn code-server
|
| 315 |
-
const
|
| 316 |
-
const
|
|
|
|
| 317 |
|
| 318 |
const baseArgs = [
|
| 319 |
'--auth', 'none',
|
|
@@ -324,19 +347,29 @@ async function performProvisioning(config: WorkspaceConfig): Promise<WorkspaceOp
|
|
| 324 |
workspacePath
|
| 325 |
];
|
| 326 |
|
| 327 |
-
const spawnEnv: NodeJS.ProcessEnv = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
delete spawnEnv.PORT;
|
| 329 |
delete spawnEnv.SERVER_PORT;
|
| 330 |
|
| 331 |
const child = spawn(shellCommand, [...args, ...baseArgs], {
|
| 332 |
env: spawnEnv,
|
| 333 |
cwd: workspacePath,
|
| 334 |
-
shell:
|
| 335 |
});
|
| 336 |
|
| 337 |
-
log(`Spawning VS Code Orchestrator (PID: ${child.pid})...`);
|
|
|
|
|
|
|
| 338 |
|
| 339 |
-
child.on('error', (err) =>
|
|
|
|
|
|
|
|
|
|
| 340 |
child.stdout.on('data', (data) => {
|
| 341 |
const out = data.toString().trim();
|
| 342 |
if (out.includes('listening on')) log(`[IDX:UP] ${out}`);
|
|
@@ -349,6 +382,10 @@ async function performProvisioning(config: WorkspaceConfig): Promise<WorkspaceOp
|
|
| 349 |
});
|
| 350 |
|
| 351 |
child.on('close', (code, signal) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
log(`[IDE:EXIT] IDE process died with code ${code} (Signal: ${signal})`);
|
| 353 |
nativeProcesses.delete(config.id);
|
| 354 |
});
|
|
@@ -359,6 +396,17 @@ async function performProvisioning(config: WorkspaceConfig): Promise<WorkspaceOp
|
|
| 359 |
// 7. Handshake Loop
|
| 360 |
let attempts = 0;
|
| 361 |
while (attempts < 60) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
try {
|
| 363 |
const res = await fetch(`http://127.0.0.1:${port}`);
|
| 364 |
if (res.ok) {
|
|
|
|
| 6 |
import { IdxEngine } from '../idx/idx-engine';
|
| 7 |
import { HFStorage } from '../hf/storage';
|
| 8 |
import { resolveSafeProjectPath } from '../fs/isolation';
|
| 9 |
+
import { ENV_CONFIG } from '../env-config';
|
| 10 |
|
| 11 |
/**
|
| 12 |
* Lighter process interface for reconnected sessions that don't have a full Node.js ChildProcess object.
|
|
|
|
| 43 |
runtimeWorkspacePath: string;
|
| 44 |
userDataPath: string;
|
| 45 |
metadataPath: string;
|
| 46 |
+
npmCachePath: string;
|
| 47 |
}
|
| 48 |
|
| 49 |
const SHORT_WORKSPACE_ID_LENGTH = 8;
|
| 50 |
const RUNTIME_ROOT_DIR_NAME = '.codeverse-runtime';
|
| 51 |
|
| 52 |
function getWorkspaceRootPath(): string {
|
| 53 |
+
return ENV_CONFIG.WORKSPACE_ROOT;
|
| 54 |
}
|
| 55 |
|
| 56 |
function getRuntimeRootPath(): string {
|
|
|
|
| 91 |
runtimeWorkspacePath: path.join(/*turbopackIgnore: true*/ runtimeRootPath, shortWorkspaceId),
|
| 92 |
userDataPath: path.join(/*turbopackIgnore: true*/ runtimeRootPath, `${shortWorkspaceId}-userdata`),
|
| 93 |
metadataPath: path.join(/*turbopackIgnore: true*/ runtimeRootPath, `${shortWorkspaceId}.id`),
|
| 94 |
+
npmCachePath: path.join(/*turbopackIgnore: true*/ runtimeRootPath, 'npm-cache'),
|
| 95 |
};
|
| 96 |
}
|
| 97 |
|
|
|
|
| 102 |
|
| 103 |
fs.mkdirSync(paths.runtimeRootPath, { recursive: true });
|
| 104 |
fs.mkdirSync(paths.userDataPath, { recursive: true });
|
| 105 |
+
fs.mkdirSync(paths.npmCachePath, { recursive: true });
|
| 106 |
|
| 107 |
if (fs.existsSync(paths.runtimeWorkspacePath)) {
|
| 108 |
try {
|
|
|
|
| 170 |
return port;
|
| 171 |
}
|
| 172 |
|
| 173 |
+
function resolveCodeServerLaunch(): { command: string; args: string[]; label: string } {
|
| 174 |
+
const overrideBinary = process.env.CODE_SERVER_BIN;
|
| 175 |
+
if (overrideBinary) {
|
| 176 |
+
return { command: overrideBinary, args: [], label: overrideBinary };
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
if (process.platform === 'win32') {
|
| 180 |
+
try {
|
| 181 |
+
execSync('where code-server.cmd', { stdio: 'ignore' });
|
| 182 |
+
return { command: 'code-server.cmd', args: [], label: 'code-server' };
|
| 183 |
+
} catch {
|
| 184 |
+
return { command: 'npx.cmd', args: ['--yes', 'code-server'], label: 'npx code-server' };
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
return { command: 'code-server', args: [], label: 'code-server' };
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
/**
|
| 192 |
* Checks if Docker is available in the current environment.
|
| 193 |
*/
|
|
|
|
| 334 |
}
|
| 335 |
|
| 336 |
// 5. Spawn code-server
|
| 337 |
+
const codeServerLaunch = resolveCodeServerLaunch();
|
| 338 |
+
const shellCommand = codeServerLaunch.command;
|
| 339 |
+
const args = codeServerLaunch.args;
|
| 340 |
|
| 341 |
const baseArgs = [
|
| 342 |
'--auth', 'none',
|
|
|
|
| 347 |
workspacePath
|
| 348 |
];
|
| 349 |
|
| 350 |
+
const spawnEnv: NodeJS.ProcessEnv = {
|
| 351 |
+
...process.env,
|
| 352 |
+
HOME: workspacePath,
|
| 353 |
+
npm_config_cache: runtimePaths.npmCachePath,
|
| 354 |
+
npm_config_update_notifier: 'false',
|
| 355 |
+
};
|
| 356 |
delete spawnEnv.PORT;
|
| 357 |
delete spawnEnv.SERVER_PORT;
|
| 358 |
|
| 359 |
const child = spawn(shellCommand, [...args, ...baseArgs], {
|
| 360 |
env: spawnEnv,
|
| 361 |
cwd: workspacePath,
|
| 362 |
+
shell: false
|
| 363 |
});
|
| 364 |
|
| 365 |
+
log(`Spawning VS Code Orchestrator via ${codeServerLaunch.label} (PID: ${child.pid})...`);
|
| 366 |
+
let childExited = false;
|
| 367 |
+
let childFailureReason: string | null = null;
|
| 368 |
|
| 369 |
+
child.on('error', (err) => {
|
| 370 |
+
childFailureReason = `IDE_BINARY_FAILURE: ${err.message}`;
|
| 371 |
+
log(`[FATAL] IDE binary failure: ${err.message}`);
|
| 372 |
+
});
|
| 373 |
child.stdout.on('data', (data) => {
|
| 374 |
const out = data.toString().trim();
|
| 375 |
if (out.includes('listening on')) log(`[IDX:UP] ${out}`);
|
|
|
|
| 382 |
});
|
| 383 |
|
| 384 |
child.on('close', (code, signal) => {
|
| 385 |
+
childExited = true;
|
| 386 |
+
if (code !== 0 || signal) {
|
| 387 |
+
childFailureReason = childFailureReason ?? `IDE_PROCESS_EXIT_${code ?? 'unknown'}${signal ? `_${signal}` : ''}`;
|
| 388 |
+
}
|
| 389 |
log(`[IDE:EXIT] IDE process died with code ${code} (Signal: ${signal})`);
|
| 390 |
nativeProcesses.delete(config.id);
|
| 391 |
});
|
|
|
|
| 396 |
// 7. Handshake Loop
|
| 397 |
let attempts = 0;
|
| 398 |
while (attempts < 60) {
|
| 399 |
+
if (childExited) {
|
| 400 |
+
const rawFailureMessage = childFailureReason ?? 'IDE_PROCESS_EXITED_BEFORE_HANDSHAKE';
|
| 401 |
+
const failureMessage = shellCommand === 'npx'
|
| 402 |
+
? `${rawFailureMessage}. code-server could not be bootstrapped via npx. Install code-server globally or set CODE_SERVER_BIN.`
|
| 403 |
+
: rawFailureMessage;
|
| 404 |
+
log(`[FATAL] IDE bootstrap aborted before handshake: ${failureMessage}`);
|
| 405 |
+
const errResult = { success: false, error: failureMessage };
|
| 406 |
+
provisioningBus.emit(`error:${config.id}`, errResult);
|
| 407 |
+
return errResult;
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
try {
|
| 411 |
const res = await fetch(`http://127.0.0.1:${port}`);
|
| 412 |
if (res.ok) {
|
lib/env-config.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
/**
|
| 2 |
* CodeVerse Environment Configuration & Requirements Manifest.
|
| 3 |
* Centralizing all system variables for production-grade reliability.
|
|
@@ -6,7 +14,7 @@ export const ENV_CONFIG = {
|
|
| 6 |
// 1. Storage & Persistence
|
| 7 |
HF_TOKEN: process.env.HF_TOKEN || process.env.hfToken || process.env.HF_SPACE || process.env.HuggingFaceToken,
|
| 8 |
HF_DATASET_ID: process.env.HF_DATASET_ID || process.env.hfDataset || process.env.HF_DATASET,
|
| 9 |
-
WORKSPACE_ROOT: process.env.WORKSPACE_ROOT ||
|
| 10 |
|
| 11 |
// 2. Build Acceleration
|
| 12 |
CACHIX_CACHE_NAME: process.env.CACHIX_CACHE_NAME || 'code-nix',
|
|
@@ -19,8 +27,8 @@ export const ENV_CONFIG = {
|
|
| 19 |
IS_SBC: !!process.env.SPACE_ID,
|
| 20 |
|
| 21 |
// 4. Database & Auth
|
| 22 |
-
AUTH_SECRET: process.env.AUTH_SECRET || process.env.authSecret,
|
| 23 |
-
TURSO_URL: process.env.TURSO_URL || process.env.turso_url || process.env.database_url || process.env.TURSO_DATABASE_URL || process.env.DB_URL || process.env.turso_database_url,
|
| 24 |
TURSO_AUTH_TOKEN: process.env.TURSO_AUTH_TOKEN || process.env.turso_auth_token || process.env.DB_TOKEN,
|
| 25 |
TMPDIR: '/tmp',
|
| 26 |
HF_HOME: '/tmp/.cache/huggingface',
|
|
@@ -31,11 +39,15 @@ export const ENV_CONFIG = {
|
|
| 31 |
*/
|
| 32 |
export function validateEnvironment() {
|
| 33 |
const missing: string[] = [];
|
| 34 |
-
if (!ENV_CONFIG.HF_TOKEN) missing.push('HF_TOKEN (Missing Persistence Link)');
|
| 35 |
-
if (!ENV_CONFIG.HF_DATASET_ID) missing.push('HF_DATASET_ID (Missing Data Segment)');
|
| 36 |
if (!ENV_CONFIG.AUTH_SECRET) missing.push('AUTH_SECRET (Security Risk)');
|
| 37 |
if (!ENV_CONFIG.TURSO_URL) missing.push('TURSO_URL (Database Missing)');
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
// Strategic Dataset Validation
|
| 40 |
if (ENV_CONFIG.HF_DATASET_ID && !ENV_CONFIG.HF_DATASET_ID.includes('/')) {
|
| 41 |
return { valid: false, missing: ['HF_DATASET_ID_FORMAT_ERROR: Must be "username/dataset"'] };
|
|
|
|
| 1 |
+
import path from "path";
|
| 2 |
+
|
| 3 |
+
function getDefaultWorkspaceRoot(): string {
|
| 4 |
+
return process.platform === "win32"
|
| 5 |
+
? path.join(process.cwd(), "workspaces")
|
| 6 |
+
: "/home/node/w";
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
/**
|
| 10 |
* CodeVerse Environment Configuration & Requirements Manifest.
|
| 11 |
* Centralizing all system variables for production-grade reliability.
|
|
|
|
| 14 |
// 1. Storage & Persistence
|
| 15 |
HF_TOKEN: process.env.HF_TOKEN || process.env.hfToken || process.env.HF_SPACE || process.env.HuggingFaceToken,
|
| 16 |
HF_DATASET_ID: process.env.HF_DATASET_ID || process.env.hfDataset || process.env.HF_DATASET,
|
| 17 |
+
WORKSPACE_ROOT: process.env.WORKSPACE_ROOT || getDefaultWorkspaceRoot(),
|
| 18 |
|
| 19 |
// 2. Build Acceleration
|
| 20 |
CACHIX_CACHE_NAME: process.env.CACHIX_CACHE_NAME || 'code-nix',
|
|
|
|
| 27 |
IS_SBC: !!process.env.SPACE_ID,
|
| 28 |
|
| 29 |
// 4. Database & Auth
|
| 30 |
+
AUTH_SECRET: process.env.AUTH_SECRET || process.env.NEXTAUTH_SECRET || process.env.authSecret,
|
| 31 |
+
TURSO_URL: process.env.TURSO_URL || process.env.turso_url || process.env.DATABASE_URL || process.env.database_url || process.env.TURSO_DATABASE_URL || process.env.DB_URL || process.env.turso_database_url,
|
| 32 |
TURSO_AUTH_TOKEN: process.env.TURSO_AUTH_TOKEN || process.env.turso_auth_token || process.env.DB_TOKEN,
|
| 33 |
TMPDIR: '/tmp',
|
| 34 |
HF_HOME: '/tmp/.cache/huggingface',
|
|
|
|
| 39 |
*/
|
| 40 |
export function validateEnvironment() {
|
| 41 |
const missing: string[] = [];
|
|
|
|
|
|
|
| 42 |
if (!ENV_CONFIG.AUTH_SECRET) missing.push('AUTH_SECRET (Security Risk)');
|
| 43 |
if (!ENV_CONFIG.TURSO_URL) missing.push('TURSO_URL (Database Missing)');
|
| 44 |
|
| 45 |
+
// HF persistence is required in deployed Spaces, not for local production testing.
|
| 46 |
+
if (ENV_CONFIG.SPACE_ID) {
|
| 47 |
+
if (!ENV_CONFIG.HF_TOKEN) missing.push('HF_TOKEN (Missing Persistence Link)');
|
| 48 |
+
if (!ENV_CONFIG.HF_DATASET_ID) missing.push('HF_DATASET_ID (Missing Data Segment)');
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
// Strategic Dataset Validation
|
| 52 |
if (ENV_CONFIG.HF_DATASET_ID && !ENV_CONFIG.HF_DATASET_ID.includes('/')) {
|
| 53 |
return { valid: false, missing: ['HF_DATASET_ID_FORMAT_ERROR: Must be "username/dataset"'] };
|
lib/hf/storage.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
import { spawn } from 'child_process';
|
| 2 |
import path from 'path';
|
| 3 |
import fs from 'fs';
|
|
|
|
| 4 |
|
| 5 |
/**
|
| 6 |
* Hugging Face Storage Utility for 2026 CodeVerse Persistence.
|
|
@@ -9,7 +10,11 @@ export class HFStorage {
|
|
| 9 |
private static readonly HF_TOKEN = process.env.HF_TOKEN;
|
| 10 |
private static readonly HF_DATASET_ID = process.env.HF_DATASET_ID;
|
| 11 |
private static readonly PROFILE_PATH = path.join(process.env.HOME || '/home/node', '.nix-profile');
|
| 12 |
-
private static readonly WORKSPACE_ROOT =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
/**
|
| 15 |
* Internal helper for asynchronous execution with logging.
|
|
@@ -40,6 +45,7 @@ export class HFStorage {
|
|
| 40 |
if (code === 0) resolve();
|
| 41 |
else reject(new Error(`Command failed with code ${code}: ${command}`));
|
| 42 |
});
|
|
|
|
| 43 |
});
|
| 44 |
}
|
| 45 |
|
|
@@ -47,7 +53,7 @@ export class HFStorage {
|
|
| 47 |
* Synchronizes the environment FROM the Hugging Face Dataset (Pull).
|
| 48 |
*/
|
| 49 |
static async syncFromDataset(onLog?: (msg: string) => void): Promise<void> {
|
| 50 |
-
if (!this.
|
| 51 |
onLog?.(`[HF:STORAGE] Persistence layer inactive. Missing credentials.`);
|
| 52 |
return;
|
| 53 |
}
|
|
@@ -80,7 +86,7 @@ export class HFStorage {
|
|
| 80 |
* Synchronizes the environment TO the Hugging Face Dataset (Push).
|
| 81 |
*/
|
| 82 |
static async syncToDataset(onLog?: (msg: string) => void): Promise<void> {
|
| 83 |
-
if (!this.
|
| 84 |
|
| 85 |
try {
|
| 86 |
onLog?.(`[HF:STORAGE] Saving persistent profile to '${this.HF_DATASET_ID}'...`);
|
|
@@ -107,7 +113,7 @@ export class HFStorage {
|
|
| 107 |
*/
|
| 108 |
private static autoSaveStarted = false;
|
| 109 |
static startAutoSave(intervalMs: number = 300000) {
|
| 110 |
-
if (this.autoSaveStarted) return;
|
| 111 |
this.autoSaveStarted = true;
|
| 112 |
|
| 113 |
console.log(`[HF:STORAGE] Persistence heartbeat initialized (Interval: ${intervalMs}ms)`);
|
|
|
|
| 1 |
import { spawn } from 'child_process';
|
| 2 |
import path from 'path';
|
| 3 |
import fs from 'fs';
|
| 4 |
+
import { ENV_CONFIG } from '../env-config';
|
| 5 |
|
| 6 |
/**
|
| 7 |
* Hugging Face Storage Utility for 2026 CodeVerse Persistence.
|
|
|
|
| 10 |
private static readonly HF_TOKEN = process.env.HF_TOKEN;
|
| 11 |
private static readonly HF_DATASET_ID = process.env.HF_DATASET_ID;
|
| 12 |
private static readonly PROFILE_PATH = path.join(process.env.HOME || '/home/node', '.nix-profile');
|
| 13 |
+
private static readonly WORKSPACE_ROOT = ENV_CONFIG.WORKSPACE_ROOT;
|
| 14 |
+
|
| 15 |
+
private static get isPersistenceRuntimeEnabled(): boolean {
|
| 16 |
+
return Boolean(ENV_CONFIG.SPACE_ID && this.HF_TOKEN && this.HF_DATASET_ID && process.platform !== 'win32');
|
| 17 |
+
}
|
| 18 |
|
| 19 |
/**
|
| 20 |
* Internal helper for asynchronous execution with logging.
|
|
|
|
| 45 |
if (code === 0) resolve();
|
| 46 |
else reject(new Error(`Command failed with code ${code}: ${command}`));
|
| 47 |
});
|
| 48 |
+
child.on('error', (error) => reject(error));
|
| 49 |
});
|
| 50 |
}
|
| 51 |
|
|
|
|
| 53 |
* Synchronizes the environment FROM the Hugging Face Dataset (Pull).
|
| 54 |
*/
|
| 55 |
static async syncFromDataset(onLog?: (msg: string) => void): Promise<void> {
|
| 56 |
+
if (!this.isPersistenceRuntimeEnabled) {
|
| 57 |
onLog?.(`[HF:STORAGE] Persistence layer inactive. Missing credentials.`);
|
| 58 |
return;
|
| 59 |
}
|
|
|
|
| 86 |
* Synchronizes the environment TO the Hugging Face Dataset (Push).
|
| 87 |
*/
|
| 88 |
static async syncToDataset(onLog?: (msg: string) => void): Promise<void> {
|
| 89 |
+
if (!this.isPersistenceRuntimeEnabled) return;
|
| 90 |
|
| 91 |
try {
|
| 92 |
onLog?.(`[HF:STORAGE] Saving persistent profile to '${this.HF_DATASET_ID}'...`);
|
|
|
|
| 113 |
*/
|
| 114 |
private static autoSaveStarted = false;
|
| 115 |
static startAutoSave(intervalMs: number = 300000) {
|
| 116 |
+
if (this.autoSaveStarted || !this.isPersistenceRuntimeEnabled) return;
|
| 117 |
this.autoSaveStarted = true;
|
| 118 |
|
| 119 |
console.log(`[HF:STORAGE] Persistence heartbeat initialized (Interval: ${intervalMs}ms)`);
|
lib/idx/idx-engine.ts
CHANGED
|
@@ -16,6 +16,17 @@ export interface IdxConfig {
|
|
| 16 |
* Refactored for 2026 Asynchronous Execution to prevent Event Loop blocking.
|
| 17 |
*/
|
| 18 |
export class IdxEngine {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
/**
|
| 20 |
* Returns a robust baseline configuration for workspaces without a dev.nix.
|
| 21 |
*/
|
|
@@ -23,7 +34,7 @@ export class IdxEngine {
|
|
| 23 |
return {
|
| 24 |
packages: ['pkgs.nodejs', 'pkgs.go', 'pkgs.python3', 'pkgs.docker', 'pkgs.python3Packages.huggingface-hub'],
|
| 25 |
onCreate: 'npm install',
|
| 26 |
-
onStart: 'sleep 5 && npm run dev'
|
| 27 |
};
|
| 28 |
}
|
| 29 |
|
|
@@ -68,25 +79,15 @@ export class IdxEngine {
|
|
| 68 |
const log = (msg: string) => { if (onLog) onLog(`[IDX:NIX] ${msg}`); };
|
| 69 |
log(`Syncing system packages: ${config.packages.join(', ')}...`);
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
// CACHIX ACCELERATION: Robust check for binary existence to prevent ENOENT crash
|
| 72 |
const cachixName = process.env.CACHIX_CACHE_NAME || 'code-nix';
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
try {
|
| 76 |
-
await new Promise<void>((resolve) => {
|
| 77 |
-
const check = spawn('command', ['-v', 'cachix'], { shell: true });
|
| 78 |
-
check.on('close', (code) => {
|
| 79 |
-
hasCachix = (code === 0);
|
| 80 |
-
resolve();
|
| 81 |
-
});
|
| 82 |
-
check.on('error', () => {
|
| 83 |
-
hasCachix = false;
|
| 84 |
-
resolve();
|
| 85 |
-
});
|
| 86 |
-
});
|
| 87 |
-
} catch {
|
| 88 |
-
hasCachix = false;
|
| 89 |
-
}
|
| 90 |
|
| 91 |
if (hasCachix) {
|
| 92 |
const cachixToken = process.env.CACHIX_AUTH_TOKEN;
|
|
@@ -170,6 +171,7 @@ export class IdxEngine {
|
|
| 170 |
|
| 171 |
child.stdout.on('data', (data) => log(data.toString().trim()));
|
| 172 |
child.stderr.on('data', (data) => log(`[INFO] ${data.toString().trim()}`));
|
|
|
|
| 173 |
|
| 174 |
child.on('close', (code) => {
|
| 175 |
if (code === 0) {
|
|
@@ -200,13 +202,20 @@ export class IdxEngine {
|
|
| 200 |
delete spawnEnv.PORT;
|
| 201 |
delete spawnEnv.SERVER_PORT;
|
| 202 |
|
| 203 |
-
const
|
|
|
|
|
|
|
|
|
|
| 204 |
cwd: workspacePath,
|
| 205 |
env: spawnEnv
|
| 206 |
});
|
| 207 |
|
| 208 |
child.stdout.on('data', (data) => log(data.toString().trim()));
|
| 209 |
child.stderr.on('data', (data) => log(`[WARN] ${data.toString().trim()}`));
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
|
| 211 |
child.on('close', (code) => {
|
| 212 |
if (code === 0) {
|
|
|
|
| 16 |
* Refactored for 2026 Asynchronous Execution to prevent Event Loop blocking.
|
| 17 |
*/
|
| 18 |
export class IdxEngine {
|
| 19 |
+
private static async hasCommand(command: string): Promise<boolean> {
|
| 20 |
+
const probeCommand = process.platform === 'win32' ? 'where' : 'command';
|
| 21 |
+
const probeArgs = process.platform === 'win32' ? [command] : ['-v', command];
|
| 22 |
+
|
| 23 |
+
return await new Promise<boolean>((resolve) => {
|
| 24 |
+
const child = spawn(probeCommand, probeArgs, { shell: process.platform !== 'win32' });
|
| 25 |
+
child.on('close', (code) => resolve(code === 0));
|
| 26 |
+
child.on('error', () => resolve(false));
|
| 27 |
+
});
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
/**
|
| 31 |
* Returns a robust baseline configuration for workspaces without a dev.nix.
|
| 32 |
*/
|
|
|
|
| 34 |
return {
|
| 35 |
packages: ['pkgs.nodejs', 'pkgs.go', 'pkgs.python3', 'pkgs.docker', 'pkgs.python3Packages.huggingface-hub'],
|
| 36 |
onCreate: 'npm install',
|
| 37 |
+
onStart: process.platform === 'win32' ? 'Start-Sleep -Seconds 5; npm run dev' : 'sleep 5 && npm run dev'
|
| 38 |
};
|
| 39 |
}
|
| 40 |
|
|
|
|
| 79 |
const log = (msg: string) => { if (onLog) onLog(`[IDX:NIX] ${msg}`); };
|
| 80 |
log(`Syncing system packages: ${config.packages.join(', ')}...`);
|
| 81 |
|
| 82 |
+
const hasNix = await this.hasCommand('nix');
|
| 83 |
+
if (!hasNix) {
|
| 84 |
+
log(`Nix is unavailable on this host. Skipping declarative package sync.`);
|
| 85 |
+
return;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
// CACHIX ACCELERATION: Robust check for binary existence to prevent ENOENT crash
|
| 89 |
const cachixName = process.env.CACHIX_CACHE_NAME || 'code-nix';
|
| 90 |
+
const hasCachix = await this.hasCommand('cachix');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
if (hasCachix) {
|
| 93 |
const cachixToken = process.env.CACHIX_AUTH_TOKEN;
|
|
|
|
| 171 |
|
| 172 |
child.stdout.on('data', (data) => log(data.toString().trim()));
|
| 173 |
child.stderr.on('data', (data) => log(`[INFO] ${data.toString().trim()}`));
|
| 174 |
+
child.on('error', (error) => reject(error));
|
| 175 |
|
| 176 |
child.on('close', (code) => {
|
| 177 |
if (code === 0) {
|
|
|
|
| 202 |
delete spawnEnv.PORT;
|
| 203 |
delete spawnEnv.SERVER_PORT;
|
| 204 |
|
| 205 |
+
const shellCommand = process.platform === 'win32' ? 'powershell.exe' : '/bin/bash';
|
| 206 |
+
const shellArgs = process.platform === 'win32' ? ['-NoProfile', '-Command', script] : ['-c', script];
|
| 207 |
+
|
| 208 |
+
const child = spawn(shellCommand, shellArgs, {
|
| 209 |
cwd: workspacePath,
|
| 210 |
env: spawnEnv
|
| 211 |
});
|
| 212 |
|
| 213 |
child.stdout.on('data', (data) => log(data.toString().trim()));
|
| 214 |
child.stderr.on('data', (data) => log(`[WARN] ${data.toString().trim()}`));
|
| 215 |
+
child.on('error', (error) => {
|
| 216 |
+
log(`[ERROR] ${error.message}`);
|
| 217 |
+
reject(error);
|
| 218 |
+
});
|
| 219 |
|
| 220 |
child.on('close', (code) => {
|
| 221 |
if (code === 0) {
|
local.db
ADDED
|
File without changes
|
package-lock.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
| 16 |
"@libsql/client": "^0.17.2",
|
| 17 |
"@modelcontextprotocol/sdk": "^1.29.0",
|
| 18 |
"@monaco-editor/react": "^4.7.0",
|
| 19 |
-
"@radix-ui/react-dialog": "
|
| 20 |
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
| 21 |
"@radix-ui/react-progress": "^1.1.8",
|
| 22 |
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
@@ -27,11 +27,13 @@
|
|
| 27 |
"@types/dockerode": "^4.0.1",
|
| 28 |
"@xterm/addon-fit": "^0.11.0",
|
| 29 |
"@xterm/xterm": "^6.0.0",
|
| 30 |
-
"ai": "
|
| 31 |
"clsx": "^2.1.1",
|
| 32 |
"cmdk": "^1.1.1",
|
|
|
|
| 33 |
"date-fns": "^4.1.0",
|
| 34 |
"dockerode": "^4.0.10",
|
|
|
|
| 35 |
"framer-motion": "^12.38.0",
|
| 36 |
"http-proxy": "^1.18.1",
|
| 37 |
"lib0": "^0.2.117",
|
|
@@ -59,7 +61,7 @@
|
|
| 59 |
"ws": "^8.19.0",
|
| 60 |
"y-protocols": "^1.0.7",
|
| 61 |
"yjs": "^13.6.29",
|
| 62 |
-
"zod": "
|
| 63 |
"zustand": "^5.0.12"
|
| 64 |
},
|
| 65 |
"devDependencies": {
|
|
@@ -950,6 +952,12 @@
|
|
| 950 |
"tslib": "^2.4.0"
|
| 951 |
}
|
| 952 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 953 |
"node_modules/@eslint-community/eslint-utils": {
|
| 954 |
"version": "4.9.1",
|
| 955 |
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
|
|
@@ -5560,6 +5568,23 @@
|
|
| 5560 |
"node": ">=10.0.0"
|
| 5561 |
}
|
| 5562 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5563 |
"node_modules/cross-fetch": {
|
| 5564 |
"version": "4.1.0",
|
| 5565 |
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz",
|
|
@@ -6032,6 +6057,18 @@
|
|
| 6032 |
"@types/trusted-types": "^2.0.7"
|
| 6033 |
}
|
| 6034 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6035 |
"node_modules/dunder-proto": {
|
| 6036 |
"version": "1.0.1",
|
| 6037 |
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
|
|
|
| 16 |
"@libsql/client": "^0.17.2",
|
| 17 |
"@modelcontextprotocol/sdk": "^1.29.0",
|
| 18 |
"@monaco-editor/react": "^4.7.0",
|
| 19 |
+
"@radix-ui/react-dialog": "1.1.15",
|
| 20 |
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
| 21 |
"@radix-ui/react-progress": "^1.1.8",
|
| 22 |
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
|
|
| 27 |
"@types/dockerode": "^4.0.1",
|
| 28 |
"@xterm/addon-fit": "^0.11.0",
|
| 29 |
"@xterm/xterm": "^6.0.0",
|
| 30 |
+
"ai": "6.0.146",
|
| 31 |
"clsx": "^2.1.1",
|
| 32 |
"cmdk": "^1.1.1",
|
| 33 |
+
"cross-env": "^10.1.0",
|
| 34 |
"date-fns": "^4.1.0",
|
| 35 |
"dockerode": "^4.0.10",
|
| 36 |
+
"dotenv": "^17.2.3",
|
| 37 |
"framer-motion": "^12.38.0",
|
| 38 |
"http-proxy": "^1.18.1",
|
| 39 |
"lib0": "^0.2.117",
|
|
|
|
| 61 |
"ws": "^8.19.0",
|
| 62 |
"y-protocols": "^1.0.7",
|
| 63 |
"yjs": "^13.6.29",
|
| 64 |
+
"zod": "4.3.6",
|
| 65 |
"zustand": "^5.0.12"
|
| 66 |
},
|
| 67 |
"devDependencies": {
|
|
|
|
| 952 |
"tslib": "^2.4.0"
|
| 953 |
}
|
| 954 |
},
|
| 955 |
+
"node_modules/@epic-web/invariant": {
|
| 956 |
+
"version": "1.0.0",
|
| 957 |
+
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
|
| 958 |
+
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
|
| 959 |
+
"license": "MIT"
|
| 960 |
+
},
|
| 961 |
"node_modules/@eslint-community/eslint-utils": {
|
| 962 |
"version": "4.9.1",
|
| 963 |
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
|
|
|
|
| 5568 |
"node": ">=10.0.0"
|
| 5569 |
}
|
| 5570 |
},
|
| 5571 |
+
"node_modules/cross-env": {
|
| 5572 |
+
"version": "10.1.0",
|
| 5573 |
+
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
|
| 5574 |
+
"integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
|
| 5575 |
+
"license": "MIT",
|
| 5576 |
+
"dependencies": {
|
| 5577 |
+
"@epic-web/invariant": "^1.0.0",
|
| 5578 |
+
"cross-spawn": "^7.0.6"
|
| 5579 |
+
},
|
| 5580 |
+
"bin": {
|
| 5581 |
+
"cross-env": "dist/bin/cross-env.js",
|
| 5582 |
+
"cross-env-shell": "dist/bin/cross-env-shell.js"
|
| 5583 |
+
},
|
| 5584 |
+
"engines": {
|
| 5585 |
+
"node": ">=20"
|
| 5586 |
+
}
|
| 5587 |
+
},
|
| 5588 |
"node_modules/cross-fetch": {
|
| 5589 |
"version": "4.1.0",
|
| 5590 |
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz",
|
|
|
|
| 6057 |
"@types/trusted-types": "^2.0.7"
|
| 6058 |
}
|
| 6059 |
},
|
| 6060 |
+
"node_modules/dotenv": {
|
| 6061 |
+
"version": "17.4.1",
|
| 6062 |
+
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz",
|
| 6063 |
+
"integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==",
|
| 6064 |
+
"license": "BSD-2-Clause",
|
| 6065 |
+
"engines": {
|
| 6066 |
+
"node": ">=12"
|
| 6067 |
+
},
|
| 6068 |
+
"funding": {
|
| 6069 |
+
"url": "https://dotenvx.com"
|
| 6070 |
+
}
|
| 6071 |
+
},
|
| 6072 |
"node_modules/dunder-proto": {
|
| 6073 |
"version": "1.0.1",
|
| 6074 |
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
| 6 |
"dev": "next dev",
|
| 7 |
"prebuild": "npx rimraf dist",
|
| 8 |
"build": "next build && tsc server.ts --esModuleInterop --skipLibCheck --target es2018 --module commonjs --moduleResolution node --downlevelIteration --outDir ./dist",
|
| 9 |
-
"start": "node dist/server.js",
|
| 10 |
"lint": "eslint . --ignore-pattern dist/ --ignore-pattern .next/"
|
| 11 |
},
|
| 12 |
"dependencies": {
|
|
@@ -31,9 +31,11 @@
|
|
| 31 |
"@xterm/xterm": "^6.0.0",
|
| 32 |
"ai": "6.0.146",
|
| 33 |
"clsx": "^2.1.1",
|
|
|
|
| 34 |
"cmdk": "^1.1.1",
|
| 35 |
"date-fns": "^4.1.0",
|
| 36 |
"dockerode": "^4.0.10",
|
|
|
|
| 37 |
"framer-motion": "^12.38.0",
|
| 38 |
"http-proxy": "^1.18.1",
|
| 39 |
"lib0": "^0.2.117",
|
|
|
|
| 6 |
"dev": "next dev",
|
| 7 |
"prebuild": "npx rimraf dist",
|
| 8 |
"build": "next build && tsc server.ts --esModuleInterop --skipLibCheck --target es2018 --module commonjs --moduleResolution node --downlevelIteration --outDir ./dist",
|
| 9 |
+
"start": "cross-env NODE_ENV=production node dist/server.js",
|
| 10 |
"lint": "eslint . --ignore-pattern dist/ --ignore-pattern .next/"
|
| 11 |
},
|
| 12 |
"dependencies": {
|
|
|
|
| 31 |
"@xterm/xterm": "^6.0.0",
|
| 32 |
"ai": "6.0.146",
|
| 33 |
"clsx": "^2.1.1",
|
| 34 |
+
"cross-env": "^10.1.0",
|
| 35 |
"cmdk": "^1.1.1",
|
| 36 |
"date-fns": "^4.1.0",
|
| 37 |
"dockerode": "^4.0.10",
|
| 38 |
+
"dotenv": "^17.2.3",
|
| 39 |
"framer-motion": "^12.38.0",
|
| 40 |
"http-proxy": "^1.18.1",
|
| 41 |
"lib0": "^0.2.117",
|
server.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
|
|
|
|
|
| 1 |
/**
|
| 2 |
* 🛰️ GLOBAL STABILIZATION (April 2026): Catch unhandled errors that cause HF Space restarts.
|
| 3 |
*/
|
|
@@ -289,7 +291,9 @@ server.listen(PORT, HOST, () => {
|
|
| 289 |
|
| 290 |
// Reconnect and Warmup
|
| 291 |
reconnectRunningWorkspaces().catch(() => {});
|
| 292 |
-
|
|
|
|
|
|
|
| 293 |
|
| 294 |
// Crons and Persistence
|
| 295 |
HFStorage.startAutoSave(INFRA_CONFIG.PERSISTENCE_INTERVAL_MS * 5);
|
|
|
|
| 1 |
+
import "dotenv/config";
|
| 2 |
+
|
| 3 |
/**
|
| 4 |
* 🛰️ GLOBAL STABILIZATION (April 2026): Catch unhandled errors that cause HF Space restarts.
|
| 5 |
*/
|
|
|
|
| 291 |
|
| 292 |
// Reconnect and Warmup
|
| 293 |
reconnectRunningWorkspaces().catch(() => {});
|
| 294 |
+
if (process.env.ENABLE_BASELINE_PREWARM === 'true') {
|
| 295 |
+
prewarmWorkspace({ id: 'baseline-warmup', userId: 'system', projectName: 'CodeVerse-Internal' }).catch(() => {});
|
| 296 |
+
}
|
| 297 |
|
| 298 |
// Crons and Persistence
|
| 299 |
HFStorage.startAutoSave(INFRA_CONFIG.PERSISTENCE_INTERVAL_MS * 5);
|
workspaces/dev-user-id/local-repro
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Subproject commit 7fd1a60b01f91b314f59955a4e4d4e80d8edf11d
|