episoda 0.2.55 → 0.2.57

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.
@@ -2319,6 +2319,14 @@ var require_websocket_client = __commonJS({
2319
2319
  connected: this.isConnected
2320
2320
  };
2321
2321
  }
2322
+ /**
2323
+ * EP949: Update the access token (used when server sends token_refresh message)
2324
+ * This updates the in-memory token used for reconnection.
2325
+ */
2326
+ updateToken(newToken) {
2327
+ console.log("[EpisodaClient] EP949: Access token updated");
2328
+ this.token = newToken;
2329
+ }
2322
2330
  /**
2323
2331
  * EP605: Update last command time to reset idle detection
2324
2332
  * Call this when a command is received/executed
@@ -2718,7 +2726,7 @@ var require_package = __commonJS({
2718
2726
  "package.json"(exports2, module2) {
2719
2727
  module2.exports = {
2720
2728
  name: "episoda",
2721
- version: "0.2.55",
2729
+ version: "0.2.57",
2722
2730
  description: "CLI tool for Episoda local development workflow orchestration",
2723
2731
  main: "dist/index.js",
2724
2732
  types: "dist/index.d.ts",
@@ -8779,6 +8787,9 @@ var Daemon = class _Daemon {
8779
8787
  this.reconcileWorktrees(projectId, projectPath, client).catch((err) => {
8780
8788
  console.warn("[Daemon] EP1003: Reconciliation report failed:", err.message);
8781
8789
  });
8790
+ this.reconcilePendingCleanups(projectId, projectPath).catch((err) => {
8791
+ console.warn("[Daemon] EP1047: Cleanup queue reconciliation failed:", err.message);
8792
+ });
8782
8793
  });
8783
8794
  client.on("module_state_changed", async (message) => {
8784
8795
  if (message.type === "module_state_changed") {
@@ -8802,6 +8813,11 @@ var Daemon = class _Daemon {
8802
8813
  client.on("error", (message) => {
8803
8814
  console.error(`[Daemon] Server error for ${projectId}:`, message);
8804
8815
  });
8816
+ client.on("token_refresh", (message) => {
8817
+ const tokenMsg = message;
8818
+ console.log(`[Daemon] EP949: Received token refresh for ${projectId}`);
8819
+ client.updateToken(tokenMsg.accessToken);
8820
+ });
8805
8821
  client.on("disconnected", (event) => {
8806
8822
  const disconnectEvent = event;
8807
8823
  console.log(`[Daemon] Connection closed for ${projectId}: code=${disconnectEvent.code}, willReconnect=${disconnectEvent.willReconnect}`);
@@ -9216,6 +9232,117 @@ var Daemon = class _Daemon {
9216
9232
  // EP1025: Removed cleanupModuleWorktree - was dead code (never called).
9217
9233
  // Worktree cleanup is handled by server's cleanupWorktreeAsync which sends
9218
9234
  // worktree_remove command via WebSocket, handled by GitExecutor.executeWorktreeRemove.
9235
+ /**
9236
+ * EP1047: Process pending cleanup queue entries for this machine
9237
+ *
9238
+ * On daemon startup/reconnect, fetch pending worktree cleanup tasks from the queue
9239
+ * and process them. This catches any cleanups missed while the daemon was offline.
9240
+ *
9241
+ * Flow:
9242
+ * 1. Query server for pending cleanup tasks for this machine
9243
+ * 2. For each task, attempt worktree removal
9244
+ * 3. Report success/failure back to server
9245
+ */
9246
+ async reconcilePendingCleanups(projectId, projectPath) {
9247
+ if (!this.deviceId) {
9248
+ console.log("[Daemon] EP1047: Cannot reconcile cleanups - deviceId not available yet");
9249
+ return;
9250
+ }
9251
+ console.log(`[Daemon] EP1047: Checking for pending cleanup tasks for machine ${this.deviceId}`);
9252
+ try {
9253
+ const config = await (0, import_core12.loadConfig)();
9254
+ if (!config) {
9255
+ console.log("[Daemon] EP1047: No config loaded, skipping cleanup reconciliation");
9256
+ return;
9257
+ }
9258
+ const apiUrl = config.api_url || "https://episoda.dev";
9259
+ const controller = new AbortController();
9260
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
9261
+ let response;
9262
+ try {
9263
+ response = await fetchWithAuth(
9264
+ `${apiUrl}/api/cli/cleanup-queue?machine_id=${this.deviceId}`,
9265
+ { signal: controller.signal }
9266
+ );
9267
+ } finally {
9268
+ clearTimeout(timeoutId);
9269
+ }
9270
+ if (!response.ok) {
9271
+ console.warn(`[Daemon] EP1047: Failed to fetch cleanup tasks: ${response.status}`);
9272
+ return;
9273
+ }
9274
+ const data = await response.json();
9275
+ const tasks = data.data?.tasks || [];
9276
+ if (tasks.length === 0) {
9277
+ console.log("[Daemon] EP1047: No pending cleanup tasks");
9278
+ return;
9279
+ }
9280
+ console.log(`[Daemon] EP1047: Processing ${tasks.length} pending cleanup task(s)`);
9281
+ const projectRoot = await findProjectRoot(projectPath);
9282
+ if (!projectRoot) {
9283
+ console.warn("[Daemon] EP1047: Could not find project root, skipping cleanup reconciliation");
9284
+ return;
9285
+ }
9286
+ const worktreeManager = new WorktreeManager(projectRoot);
9287
+ if (!await worktreeManager.initialize()) {
9288
+ console.warn("[Daemon] EP1047: Failed to initialize worktree manager");
9289
+ return;
9290
+ }
9291
+ for (const task of tasks) {
9292
+ const moduleUid = task.payload.module_uid || task.target_id;
9293
+ console.log(`[Daemon] EP1047: Processing cleanup for ${moduleUid} (task ${task.id})`);
9294
+ try {
9295
+ const result = await worktreeManager.removeWorktree(moduleUid, true);
9296
+ if (result.success) {
9297
+ console.log(`[Daemon] EP1047: Successfully cleaned up worktree for ${moduleUid}`);
9298
+ await this.reportCleanupResult(apiUrl, task.id, "complete");
9299
+ } else {
9300
+ if (result.error?.includes("not found") || result.error?.includes("No worktree found")) {
9301
+ console.log(`[Daemon] EP1047: Worktree ${moduleUid} already removed, marking complete`);
9302
+ await this.reportCleanupResult(apiUrl, task.id, "complete");
9303
+ } else {
9304
+ console.warn(`[Daemon] EP1047: Cleanup failed for ${moduleUid}: ${result.error}`);
9305
+ await this.reportCleanupResult(apiUrl, task.id, "retry", {
9306
+ code: "CLEANUP_ERROR",
9307
+ message: result.error || "Unknown error"
9308
+ });
9309
+ }
9310
+ }
9311
+ } catch (error) {
9312
+ console.error(`[Daemon] EP1047: Error processing cleanup for ${moduleUid}:`, error.message);
9313
+ await this.reportCleanupResult(apiUrl, task.id, "retry", {
9314
+ code: "CLEANUP_EXCEPTION",
9315
+ message: error.message
9316
+ });
9317
+ }
9318
+ }
9319
+ console.log("[Daemon] EP1047: Cleanup reconciliation complete");
9320
+ } catch (error) {
9321
+ console.error("[Daemon] EP1047: Cleanup reconciliation error:", error instanceof Error ? error.message : error);
9322
+ throw error;
9323
+ }
9324
+ }
9325
+ /**
9326
+ * EP1047: Report cleanup task result to server
9327
+ */
9328
+ async reportCleanupResult(apiUrl, taskId, action, error) {
9329
+ try {
9330
+ const response = await fetchWithAuth(`${apiUrl}/api/cli/cleanup-queue`, {
9331
+ method: "POST",
9332
+ headers: { "Content-Type": "application/json" },
9333
+ body: JSON.stringify({
9334
+ task_id: taskId,
9335
+ action,
9336
+ error
9337
+ })
9338
+ });
9339
+ if (!response.ok) {
9340
+ console.warn(`[Daemon] EP1047: Failed to report cleanup result: ${response.status}`);
9341
+ }
9342
+ } catch (err) {
9343
+ console.warn(`[Daemon] EP1047: Error reporting cleanup result: ${err.message}`);
9344
+ }
9345
+ }
9219
9346
  /**
9220
9347
  * EP1002: Handle worktree_setup command from server
9221
9348
  * This provides a unified setup flow for both local and cloud environments.