episoda 0.2.90 → 0.2.91
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.
|
@@ -2786,7 +2786,7 @@ var require_package = __commonJS({
|
|
|
2786
2786
|
"package.json"(exports2, module2) {
|
|
2787
2787
|
module2.exports = {
|
|
2788
2788
|
name: "episoda",
|
|
2789
|
-
version: "0.2.
|
|
2789
|
+
version: "0.2.91",
|
|
2790
2790
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2791
2791
|
main: "dist/index.js",
|
|
2792
2792
|
types: "dist/index.d.ts",
|
|
@@ -7379,6 +7379,130 @@ async function handleWorktreeList(workspaceSlug, projectSlug) {
|
|
|
7379
7379
|
return { success: false, worktrees: [], error: error.message };
|
|
7380
7380
|
}
|
|
7381
7381
|
}
|
|
7382
|
+
async function handleProjectEject(request2) {
|
|
7383
|
+
const { workspaceSlug, projectSlug, projectId } = request2;
|
|
7384
|
+
console.log(`[Worktree] EP1144: Ejecting project ${projectSlug} from workspace ${workspaceSlug}`);
|
|
7385
|
+
try {
|
|
7386
|
+
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
7387
|
+
const bareRepoPath = path14.join(projectPath, ".bare");
|
|
7388
|
+
if (!fs13.existsSync(projectPath)) {
|
|
7389
|
+
console.log(`[Worktree] EP1144: Project path not found, nothing to eject: ${projectPath}`);
|
|
7390
|
+
return { success: true };
|
|
7391
|
+
}
|
|
7392
|
+
if (!fs13.existsSync(bareRepoPath)) {
|
|
7393
|
+
console.log(`[Worktree] EP1144: Bare repo not found, nothing to eject: ${bareRepoPath}`);
|
|
7394
|
+
return { success: true };
|
|
7395
|
+
}
|
|
7396
|
+
const manager = new WorktreeManager(projectPath);
|
|
7397
|
+
const initialized2 = await manager.initialize();
|
|
7398
|
+
if (initialized2) {
|
|
7399
|
+
const worktrees = manager.listWorktrees();
|
|
7400
|
+
if (worktrees.length > 0) {
|
|
7401
|
+
return {
|
|
7402
|
+
success: false,
|
|
7403
|
+
error: `Cannot eject project with ${worktrees.length} active worktrees`
|
|
7404
|
+
};
|
|
7405
|
+
}
|
|
7406
|
+
}
|
|
7407
|
+
const artifactsPath = path14.join(projectPath, "artifacts");
|
|
7408
|
+
if (fs13.existsSync(artifactsPath)) {
|
|
7409
|
+
await persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug);
|
|
7410
|
+
}
|
|
7411
|
+
console.log(`[Worktree] EP1144: Removing project directory: ${projectPath}`);
|
|
7412
|
+
await fs13.promises.rm(projectPath, { recursive: true, force: true });
|
|
7413
|
+
console.log(`[Worktree] EP1144: Successfully ejected project ${projectSlug}`);
|
|
7414
|
+
return { success: true };
|
|
7415
|
+
} catch (error) {
|
|
7416
|
+
console.error(`[Worktree] EP1144: Eject failed: ${error.message}`);
|
|
7417
|
+
return {
|
|
7418
|
+
success: false,
|
|
7419
|
+
error: error.message
|
|
7420
|
+
};
|
|
7421
|
+
}
|
|
7422
|
+
}
|
|
7423
|
+
async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug) {
|
|
7424
|
+
const MAX_ARTIFACT_SIZE = 10 * 1024 * 1024;
|
|
7425
|
+
try {
|
|
7426
|
+
const files = await fs13.promises.readdir(artifactsPath);
|
|
7427
|
+
if (files.length === 0) {
|
|
7428
|
+
return;
|
|
7429
|
+
}
|
|
7430
|
+
console.log(`[Worktree] EP1144: Found ${files.length} artifacts to persist for ${projectSlug}`);
|
|
7431
|
+
const config = await getConfigForApi();
|
|
7432
|
+
const containerId = process.env.EPISODA_CONTAINER_ID;
|
|
7433
|
+
if (!config?.access_token || !containerId || !projectId) {
|
|
7434
|
+
console.warn(`[Worktree] EP1144: Cannot persist artifacts - missing auth, container ID, or project ID`);
|
|
7435
|
+
console.warn(`[Worktree] EP1144: ${files.length} artifacts will be lost for ${projectSlug}`);
|
|
7436
|
+
return;
|
|
7437
|
+
}
|
|
7438
|
+
const artifacts = [];
|
|
7439
|
+
for (const fileName of files) {
|
|
7440
|
+
const filePath = path14.join(artifactsPath, fileName);
|
|
7441
|
+
const stat = await fs13.promises.stat(filePath);
|
|
7442
|
+
if (stat.isDirectory()) {
|
|
7443
|
+
continue;
|
|
7444
|
+
}
|
|
7445
|
+
if (stat.size > MAX_ARTIFACT_SIZE) {
|
|
7446
|
+
console.warn(`[Worktree] EP1144: Skipping artifact ${fileName} - exceeds 10MB limit`);
|
|
7447
|
+
continue;
|
|
7448
|
+
}
|
|
7449
|
+
try {
|
|
7450
|
+
const content = await fs13.promises.readFile(filePath);
|
|
7451
|
+
const base64Content = content.toString("base64");
|
|
7452
|
+
const ext = path14.extname(fileName).toLowerCase();
|
|
7453
|
+
const mimeTypes = {
|
|
7454
|
+
".json": "application/json",
|
|
7455
|
+
".txt": "text/plain",
|
|
7456
|
+
".md": "text/markdown",
|
|
7457
|
+
".html": "text/html",
|
|
7458
|
+
".css": "text/css",
|
|
7459
|
+
".js": "application/javascript",
|
|
7460
|
+
".ts": "application/typescript",
|
|
7461
|
+
".png": "image/png",
|
|
7462
|
+
".jpg": "image/jpeg",
|
|
7463
|
+
".jpeg": "image/jpeg",
|
|
7464
|
+
".gif": "image/gif",
|
|
7465
|
+
".svg": "image/svg+xml",
|
|
7466
|
+
".pdf": "application/pdf"
|
|
7467
|
+
};
|
|
7468
|
+
artifacts.push({
|
|
7469
|
+
name: fileName,
|
|
7470
|
+
content: base64Content,
|
|
7471
|
+
mime_type: mimeTypes[ext] || "application/octet-stream"
|
|
7472
|
+
});
|
|
7473
|
+
} catch (readError) {
|
|
7474
|
+
console.warn(`[Worktree] EP1144: Failed to read artifact ${fileName}: ${readError.message}`);
|
|
7475
|
+
}
|
|
7476
|
+
}
|
|
7477
|
+
if (artifacts.length === 0) {
|
|
7478
|
+
console.log(`[Worktree] EP1144: No valid artifacts to persist`);
|
|
7479
|
+
return;
|
|
7480
|
+
}
|
|
7481
|
+
const apiUrl = config.api_url || "https://episoda.dev";
|
|
7482
|
+
const url = `${apiUrl}/api/cloud/containers/${containerId}/artifacts`;
|
|
7483
|
+
console.log(`[Worktree] EP1144: Uploading ${artifacts.length} artifacts to ${url}`);
|
|
7484
|
+
const response = await fetch(url, {
|
|
7485
|
+
method: "POST",
|
|
7486
|
+
headers: {
|
|
7487
|
+
"Authorization": `Bearer ${config.access_token}`,
|
|
7488
|
+
"Content-Type": "application/json"
|
|
7489
|
+
},
|
|
7490
|
+
body: JSON.stringify({
|
|
7491
|
+
project_id: projectId,
|
|
7492
|
+
artifacts
|
|
7493
|
+
})
|
|
7494
|
+
});
|
|
7495
|
+
if (!response.ok) {
|
|
7496
|
+
const text = await response.text();
|
|
7497
|
+
console.warn(`[Worktree] EP1144: Artifact upload failed: ${response.status} ${text}`);
|
|
7498
|
+
return;
|
|
7499
|
+
}
|
|
7500
|
+
const result = await response.json();
|
|
7501
|
+
console.log(`[Worktree] EP1144: Successfully persisted ${result.data?.uploaded || 0} artifacts for ${projectSlug}`);
|
|
7502
|
+
} catch (error) {
|
|
7503
|
+
console.warn(`[Worktree] EP1144: Failed to persist artifacts: ${error.message}`);
|
|
7504
|
+
}
|
|
7505
|
+
}
|
|
7382
7506
|
|
|
7383
7507
|
// src/daemon/handlers/stale-commit-cleanup.ts
|
|
7384
7508
|
var import_child_process9 = require("child_process");
|
|
@@ -9400,6 +9524,9 @@ var Daemon = class _Daemon {
|
|
|
9400
9524
|
this.ipcServer.on("worktree-list", async (params) => {
|
|
9401
9525
|
return handleWorktreeList(params.workspaceSlug, params.projectSlug);
|
|
9402
9526
|
});
|
|
9527
|
+
this.ipcServer.on("project-eject", async (params) => {
|
|
9528
|
+
return handleProjectEject(params);
|
|
9529
|
+
});
|
|
9403
9530
|
}
|
|
9404
9531
|
/**
|
|
9405
9532
|
* Restore WebSocket connections for tracked projects
|
|
@@ -9645,6 +9772,10 @@ var Daemon = class _Daemon {
|
|
|
9645
9772
|
cmd.projectSlug || ""
|
|
9646
9773
|
);
|
|
9647
9774
|
break;
|
|
9775
|
+
// EP1144: Project ejection for Tier 1 cleanup
|
|
9776
|
+
case "eject_project":
|
|
9777
|
+
result = await handleProjectEject(cmd);
|
|
9778
|
+
break;
|
|
9648
9779
|
default:
|
|
9649
9780
|
result = {
|
|
9650
9781
|
success: false,
|