episoda 0.2.97 → 0.2.99
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/dist/daemon/daemon-process.js +69 -126
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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.99",
|
|
2790
2790
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2791
2791
|
main: "dist/index.js",
|
|
2792
2792
|
types: "dist/index.d.ts",
|
|
@@ -4406,6 +4406,48 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
4406
4406
|
}
|
|
4407
4407
|
return { valid, stale, orphaned };
|
|
4408
4408
|
}
|
|
4409
|
+
/**
|
|
4410
|
+
* EP1190: Clean up non-module worktrees (like 'main')
|
|
4411
|
+
*
|
|
4412
|
+
* In K1273 per-user-per-workspace model, only EP### worktrees should exist.
|
|
4413
|
+
* The 'main' worktree and other non-module worktrees are legacy artifacts
|
|
4414
|
+
* that can contain stale code and should be removed.
|
|
4415
|
+
*
|
|
4416
|
+
* @returns Object with removed paths and errors
|
|
4417
|
+
*/
|
|
4418
|
+
async cleanupNonModuleWorktrees() {
|
|
4419
|
+
const removed = [];
|
|
4420
|
+
const errors = [];
|
|
4421
|
+
try {
|
|
4422
|
+
const validation = await this.validateWorktrees();
|
|
4423
|
+
for (const orphanPath of validation.orphaned) {
|
|
4424
|
+
const dirName = path7.basename(orphanPath);
|
|
4425
|
+
const isModuleWorktree = /^EP\d+$/.test(dirName);
|
|
4426
|
+
if (!isModuleWorktree) {
|
|
4427
|
+
console.log(`[WorktreeManager] EP1190: Found non-module worktree: ${dirName}`);
|
|
4428
|
+
try {
|
|
4429
|
+
const removeResult = await this.gitExecutor.execute({
|
|
4430
|
+
action: "worktree_remove",
|
|
4431
|
+
path: orphanPath,
|
|
4432
|
+
force: true
|
|
4433
|
+
// Force removal since it's not tracked
|
|
4434
|
+
}, { cwd: this.bareRepoPath });
|
|
4435
|
+
if (removeResult.success) {
|
|
4436
|
+
removed.push(dirName);
|
|
4437
|
+
console.log(`[WorktreeManager] EP1190: Removed non-module worktree: ${dirName}`);
|
|
4438
|
+
} else {
|
|
4439
|
+
errors.push(`Failed to remove ${dirName}: ${removeResult.output}`);
|
|
4440
|
+
}
|
|
4441
|
+
} catch (removeError) {
|
|
4442
|
+
errors.push(`Error removing ${dirName}: ${removeError.message}`);
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
} catch (error) {
|
|
4447
|
+
errors.push(`Validation error: ${error.message}`);
|
|
4448
|
+
}
|
|
4449
|
+
return { removed, errors };
|
|
4450
|
+
}
|
|
4409
4451
|
/**
|
|
4410
4452
|
* Get project configuration
|
|
4411
4453
|
*/
|
|
@@ -9425,7 +9467,6 @@ async function fetchEnvVars2() {
|
|
|
9425
9467
|
}
|
|
9426
9468
|
}
|
|
9427
9469
|
var Daemon = class _Daemon {
|
|
9428
|
-
// 60 seconds
|
|
9429
9470
|
constructor() {
|
|
9430
9471
|
this.machineId = "";
|
|
9431
9472
|
this.machineUuid = null;
|
|
@@ -9464,6 +9505,9 @@ var Daemon = class _Daemon {
|
|
|
9464
9505
|
// Health checks are orthogonal to push-based state sync - they detect dead tunnels
|
|
9465
9506
|
this.healthCheckInterval = null;
|
|
9466
9507
|
this.healthCheckInProgress = false;
|
|
9508
|
+
// 60 seconds
|
|
9509
|
+
// EP1190: Worktree cleanup runs every N health checks (5 * 60s = 5 minutes)
|
|
9510
|
+
this.healthCheckCounter = 0;
|
|
9467
9511
|
this.ipcServer = new IPCServer();
|
|
9468
9512
|
}
|
|
9469
9513
|
static {
|
|
@@ -9480,6 +9524,9 @@ var Daemon = class _Daemon {
|
|
|
9480
9524
|
static {
|
|
9481
9525
|
this.HEALTH_CHECK_INTERVAL_MS = 6e4;
|
|
9482
9526
|
}
|
|
9527
|
+
static {
|
|
9528
|
+
this.WORKTREE_CLEANUP_EVERY_N_CHECKS = 5;
|
|
9529
|
+
}
|
|
9483
9530
|
/**
|
|
9484
9531
|
* Start the daemon
|
|
9485
9532
|
*/
|
|
@@ -10877,130 +10924,8 @@ var Daemon = class _Daemon {
|
|
|
10877
10924
|
}
|
|
10878
10925
|
}
|
|
10879
10926
|
// EP1025: Removed cleanupModuleWorktree - was dead code (never called).
|
|
10880
|
-
//
|
|
10881
|
-
//
|
|
10882
|
-
/**
|
|
10883
|
-
* EP1047: Process pending cleanup queue entries for this machine
|
|
10884
|
-
*
|
|
10885
|
-
* @deprecated EP1091: No longer called on connect. Server-side cron now processes
|
|
10886
|
-
* the cleanup queue and sends WebSocket commands to connected daemons. This is
|
|
10887
|
-
* more reliable than daemon-side polling since it works even if daemon stays connected.
|
|
10888
|
-
*
|
|
10889
|
-
* Kept for potential manual invocation or debugging purposes.
|
|
10890
|
-
*
|
|
10891
|
-
* Flow:
|
|
10892
|
-
* 1. Query server for pending cleanup tasks for this machine
|
|
10893
|
-
* 2. For each task, attempt worktree removal
|
|
10894
|
-
* 3. Report success/failure back to server
|
|
10895
|
-
*/
|
|
10896
|
-
async reconcilePendingCleanups(projectId, projectPath) {
|
|
10897
|
-
if (!this.machineUuid) {
|
|
10898
|
-
console.log("[Daemon] EP1047: Cannot reconcile cleanups - machineUuid not available yet");
|
|
10899
|
-
return;
|
|
10900
|
-
}
|
|
10901
|
-
console.log(`[Daemon] EP1047: Checking for pending cleanup tasks for machine ${this.machineUuid}`);
|
|
10902
|
-
try {
|
|
10903
|
-
const config = await (0, import_core13.loadConfig)();
|
|
10904
|
-
if (!config) {
|
|
10905
|
-
console.log("[Daemon] EP1047: No config loaded, skipping cleanup reconciliation");
|
|
10906
|
-
return;
|
|
10907
|
-
}
|
|
10908
|
-
const apiUrl = config.api_url || "https://episoda.dev";
|
|
10909
|
-
const controller = new AbortController();
|
|
10910
|
-
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
10911
|
-
let response;
|
|
10912
|
-
try {
|
|
10913
|
-
response = await fetchWithAuth(
|
|
10914
|
-
`${apiUrl}/api/cli/background-ops?machine_id=${this.machineUuid}`,
|
|
10915
|
-
{ signal: controller.signal }
|
|
10916
|
-
);
|
|
10917
|
-
} finally {
|
|
10918
|
-
clearTimeout(timeoutId);
|
|
10919
|
-
}
|
|
10920
|
-
if (!response.ok) {
|
|
10921
|
-
console.warn(`[Daemon] EP1051: Failed to fetch background operations: ${response.status}`);
|
|
10922
|
-
return;
|
|
10923
|
-
}
|
|
10924
|
-
const data = await response.json();
|
|
10925
|
-
const tasks = data.tasks || [];
|
|
10926
|
-
if (tasks.length === 0) {
|
|
10927
|
-
console.log("[Daemon] EP1051: No pending background operations");
|
|
10928
|
-
return;
|
|
10929
|
-
}
|
|
10930
|
-
console.log(`[Daemon] EP1051: Processing ${tasks.length} pending operation(s)`);
|
|
10931
|
-
const projectRoot = await findProjectRoot(projectPath);
|
|
10932
|
-
if (!projectRoot) {
|
|
10933
|
-
console.warn("[Daemon] EP1051: Could not find project root, skipping operation reconciliation");
|
|
10934
|
-
return;
|
|
10935
|
-
}
|
|
10936
|
-
const worktreeManager = new WorktreeManager(projectRoot);
|
|
10937
|
-
if (!await worktreeManager.initialize()) {
|
|
10938
|
-
console.warn("[Daemon] EP1051: Failed to initialize worktree manager");
|
|
10939
|
-
return;
|
|
10940
|
-
}
|
|
10941
|
-
for (const task of tasks) {
|
|
10942
|
-
const moduleUid = task.payload.module_uid || task.target_id;
|
|
10943
|
-
console.log(`[Daemon] EP1051: Processing ${task.operation_type} for ${moduleUid} (task ${task.id})`);
|
|
10944
|
-
try {
|
|
10945
|
-
if (task.operation_type === "worktree_cleanup") {
|
|
10946
|
-
const result = await worktreeManager.removeWorktree(moduleUid, true);
|
|
10947
|
-
if (result.success) {
|
|
10948
|
-
console.log(`[Daemon] EP1051: Successfully cleaned up worktree for ${moduleUid}`);
|
|
10949
|
-
await this.reportOperationResult(apiUrl, task.id, "complete");
|
|
10950
|
-
} else {
|
|
10951
|
-
if (result.error?.includes("not found") || result.error?.includes("No worktree found")) {
|
|
10952
|
-
console.log(`[Daemon] EP1051: Worktree ${moduleUid} already removed, marking complete`);
|
|
10953
|
-
await this.reportOperationResult(apiUrl, task.id, "complete");
|
|
10954
|
-
} else {
|
|
10955
|
-
console.warn(`[Daemon] EP1051: Cleanup failed for ${moduleUid}: ${result.error}`);
|
|
10956
|
-
await this.reportOperationResult(apiUrl, task.id, "retry", {
|
|
10957
|
-
code: "OPERATION_ERROR",
|
|
10958
|
-
message: result.error || "Unknown error"
|
|
10959
|
-
});
|
|
10960
|
-
}
|
|
10961
|
-
}
|
|
10962
|
-
} else {
|
|
10963
|
-
console.warn(`[Daemon] EP1051: Unknown operation type: ${task.operation_type}`);
|
|
10964
|
-
await this.reportOperationResult(apiUrl, task.id, "fail", {
|
|
10965
|
-
code: "UNKNOWN_OPERATION_TYPE",
|
|
10966
|
-
message: `Daemon cannot process operation type: ${task.operation_type}`
|
|
10967
|
-
});
|
|
10968
|
-
}
|
|
10969
|
-
} catch (error) {
|
|
10970
|
-
console.error(`[Daemon] EP1051: Error processing operation for ${moduleUid}:`, error.message);
|
|
10971
|
-
await this.reportOperationResult(apiUrl, task.id, "retry", {
|
|
10972
|
-
code: "OPERATION_EXCEPTION",
|
|
10973
|
-
message: error.message
|
|
10974
|
-
});
|
|
10975
|
-
}
|
|
10976
|
-
}
|
|
10977
|
-
console.log("[Daemon] EP1051: Operation reconciliation complete");
|
|
10978
|
-
} catch (error) {
|
|
10979
|
-
console.error("[Daemon] EP1051: Operation reconciliation error:", error instanceof Error ? error.message : error);
|
|
10980
|
-
throw error;
|
|
10981
|
-
}
|
|
10982
|
-
}
|
|
10983
|
-
/**
|
|
10984
|
-
* EP1051: Report background operation result to server
|
|
10985
|
-
*/
|
|
10986
|
-
async reportOperationResult(apiUrl, taskId, action, error) {
|
|
10987
|
-
try {
|
|
10988
|
-
const response = await fetchWithAuth(`${apiUrl}/api/cli/background-ops`, {
|
|
10989
|
-
method: "POST",
|
|
10990
|
-
headers: { "Content-Type": "application/json" },
|
|
10991
|
-
body: JSON.stringify({
|
|
10992
|
-
task_id: taskId,
|
|
10993
|
-
action,
|
|
10994
|
-
error
|
|
10995
|
-
})
|
|
10996
|
-
});
|
|
10997
|
-
if (!response.ok) {
|
|
10998
|
-
console.warn(`[Daemon] EP1051: Failed to report operation result: ${response.status}`);
|
|
10999
|
-
}
|
|
11000
|
-
} catch (err) {
|
|
11001
|
-
console.warn(`[Daemon] EP1051: Error reporting operation result: ${err.message}`);
|
|
11002
|
-
}
|
|
11003
|
-
}
|
|
10927
|
+
// EP1188: Background ops queue removed - worktree cleanup now handled by Inngest events
|
|
10928
|
+
// (worktree/cleanup) which send WebSocket commands to connected daemons via ws-proxy.
|
|
11004
10929
|
/**
|
|
11005
10930
|
* EP1002: Handle worktree_setup command from server
|
|
11006
10931
|
* This provides a unified setup flow for both local and cloud environments.
|
|
@@ -11217,6 +11142,11 @@ var Daemon = class _Daemon {
|
|
|
11217
11142
|
if (config?.access_token) {
|
|
11218
11143
|
await this.performHealthChecks(config);
|
|
11219
11144
|
}
|
|
11145
|
+
this.healthCheckCounter++;
|
|
11146
|
+
if (this.healthCheckCounter >= _Daemon.WORKTREE_CLEANUP_EVERY_N_CHECKS) {
|
|
11147
|
+
this.healthCheckCounter = 0;
|
|
11148
|
+
await this.auditWorktreesOnStartup();
|
|
11149
|
+
}
|
|
11220
11150
|
} catch (error) {
|
|
11221
11151
|
console.error("[Daemon] EP929: Health check error:", error instanceof Error ? error.message : error);
|
|
11222
11152
|
} finally {
|
|
@@ -11403,6 +11333,19 @@ var Daemon = class _Daemon {
|
|
|
11403
11333
|
console.warn("[Daemon] EP1035: Failed to prune stale worktrees:", pruneError.message);
|
|
11404
11334
|
}
|
|
11405
11335
|
}
|
|
11336
|
+
try {
|
|
11337
|
+
const cleanupResult = await manager.cleanupNonModuleWorktrees();
|
|
11338
|
+
if (cleanupResult.removed.length > 0) {
|
|
11339
|
+
console.log(`[Daemon] EP1190: Cleaned up ${cleanupResult.removed.length} non-module worktree(s): ${cleanupResult.removed.join(", ")}`);
|
|
11340
|
+
}
|
|
11341
|
+
if (cleanupResult.errors.length > 0) {
|
|
11342
|
+
for (const err of cleanupResult.errors) {
|
|
11343
|
+
console.warn(`[Daemon] EP1190: Non-module cleanup error: ${err}`);
|
|
11344
|
+
}
|
|
11345
|
+
}
|
|
11346
|
+
} catch (cleanupError) {
|
|
11347
|
+
console.warn("[Daemon] EP1190: Failed to cleanup non-module worktrees:", cleanupError.message);
|
|
11348
|
+
}
|
|
11406
11349
|
} catch (error) {
|
|
11407
11350
|
console.warn(`[Daemon] EP1035: Failed to audit ${projectPath}:`, error);
|
|
11408
11351
|
}
|