episoda 0.2.46 → 0.2.47

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.
@@ -2693,7 +2693,7 @@ var require_package = __commonJS({
2693
2693
  "package.json"(exports2, module2) {
2694
2694
  module2.exports = {
2695
2695
  name: "episoda",
2696
- version: "0.2.46",
2696
+ version: "0.2.47",
2697
2697
  description: "CLI tool for Episoda local development workflow orchestration",
2698
2698
  main: "dist/index.js",
2699
2699
  types: "dist/index.d.ts",
@@ -4107,14 +4107,11 @@ var TUNNEL_PID_DIR = path7.join(os2.homedir(), ".episoda", "tunnels");
4107
4107
  var TUNNEL_TIMEOUTS = {
4108
4108
  /** Time to wait for Named Tunnel connection (includes API token fetch + connect) */
4109
4109
  NAMED_TUNNEL_CONNECT: 6e4,
4110
- /** Time to wait for Quick Tunnel connection (simpler, faster connection) */
4111
- QUICK_TUNNEL_CONNECT: 3e4,
4112
4110
  /** Time to wait for cloudflared process to start before giving up */
4113
4111
  PROCESS_START: 1e4,
4114
4112
  /** Grace period after starting cloudflared before checking status */
4115
4113
  STARTUP_GRACE: 2e3
4116
4114
  };
4117
- var TUNNEL_URL_REGEX = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/i;
4118
4115
  var DEFAULT_RECONNECT_CONFIG = {
4119
4116
  maxRetries: 5,
4120
4117
  initialDelayMs: 1e3,
@@ -4569,151 +4566,21 @@ var TunnelManager = class extends import_events.EventEmitter {
4569
4566
  });
4570
4567
  }
4571
4568
  /**
4572
- * EP948: Route to the appropriate tunnel process method based on mode
4569
+ * EP948: Start tunnel process (Named Tunnels only)
4570
+ * EP1020: Removed Quick Tunnel fallback - Named Tunnels are the only supported mode
4573
4571
  */
4574
4572
  async startTunnelProcess(options, existingState) {
4575
- const mode = options.mode || "named";
4576
- if (mode === "named" && options.tunnelToken) {
4577
- return this.startNamedTunnelProcess(options, existingState);
4578
- }
4579
- console.log(`[Tunnel] EP948: Using Quick Tunnel mode for ${options.moduleUid}`);
4580
- return this.startQuickTunnelProcess(options, existingState);
4581
- }
4582
- /**
4583
- * EP672-9: Internal method to start the tunnel process (Quick Tunnel mode)
4584
- * Separated from startTunnel to support reconnection
4585
- */
4586
- async startQuickTunnelProcess(options, existingState) {
4587
- const { moduleUid, port = 3e3, onUrl, onStatusChange } = options;
4588
- if (!this.cloudflaredPath) {
4589
- try {
4590
- this.cloudflaredPath = await ensureCloudflared();
4591
- } catch (error) {
4592
- const errorMessage = error instanceof Error ? error.message : String(error);
4593
- return { success: false, error: `Failed to get cloudflared: ${errorMessage}` };
4594
- }
4595
- }
4596
- return new Promise((resolve3) => {
4597
- const tunnelInfo = {
4598
- moduleUid,
4599
- url: "",
4600
- port,
4601
- status: "starting",
4602
- startedAt: /* @__PURE__ */ new Date(),
4603
- process: null
4604
- // Will be set below
4605
- };
4606
- const process2 = (0, import_child_process6.spawn)(this.cloudflaredPath, [
4607
- "tunnel",
4608
- "--url",
4609
- `http://localhost:${port}`
4610
- ], {
4611
- stdio: ["ignore", "pipe", "pipe"]
4612
- });
4613
- tunnelInfo.process = process2;
4614
- tunnelInfo.pid = process2.pid;
4615
- if (process2.pid) {
4616
- this.writePidFile(moduleUid, process2.pid);
4617
- }
4618
- const state = existingState || {
4619
- info: tunnelInfo,
4620
- options,
4621
- intentionallyStopped: false,
4622
- retryCount: 0,
4623
- retryTimeoutId: null
4624
- };
4625
- state.info = tunnelInfo;
4626
- this.tunnelStates.set(moduleUid, state);
4627
- let urlFound = false;
4628
- let stdoutBuffer = "";
4629
- let stderrBuffer = "";
4630
- const parseOutput = (data) => {
4631
- if (urlFound) return;
4632
- const match = data.match(TUNNEL_URL_REGEX);
4633
- if (match) {
4634
- urlFound = true;
4635
- tunnelInfo.url = match[0];
4636
- tunnelInfo.status = "connected";
4637
- onStatusChange?.("connected");
4638
- onUrl?.(tunnelInfo.url);
4639
- this.emitEvent({
4640
- type: "started",
4641
- moduleUid,
4642
- url: tunnelInfo.url
4643
- });
4644
- resolve3({ success: true, url: tunnelInfo.url });
4645
- }
4573
+ if (!options.tunnelToken) {
4574
+ console.error(`[Tunnel] EP1020: No tunnel token available for ${options.moduleUid}`);
4575
+ return {
4576
+ success: false,
4577
+ error: "Named Tunnel token required. Quick Tunnels are no longer supported."
4646
4578
  };
4647
- process2.stdout?.on("data", (data) => {
4648
- stdoutBuffer += data.toString();
4649
- parseOutput(stdoutBuffer);
4650
- });
4651
- process2.stderr?.on("data", (data) => {
4652
- stderrBuffer += data.toString();
4653
- parseOutput(stderrBuffer);
4654
- });
4655
- process2.on("exit", (code, signal) => {
4656
- const wasConnected = tunnelInfo.status === "connected";
4657
- tunnelInfo.status = "disconnected";
4658
- const currentState = this.tunnelStates.get(moduleUid);
4659
- if (!urlFound) {
4660
- const errorMsg = `Tunnel process exited with code ${code}`;
4661
- tunnelInfo.status = "error";
4662
- tunnelInfo.error = errorMsg;
4663
- if (currentState && !currentState.intentionallyStopped) {
4664
- this.attemptReconnect(moduleUid);
4665
- } else {
4666
- this.tunnelStates.delete(moduleUid);
4667
- onStatusChange?.("error", errorMsg);
4668
- this.emitEvent({ type: "error", moduleUid, error: errorMsg });
4669
- }
4670
- resolve3({ success: false, error: errorMsg });
4671
- } else if (wasConnected) {
4672
- if (currentState && !currentState.intentionallyStopped) {
4673
- console.log(`[Tunnel] ${moduleUid} crashed unexpectedly, attempting reconnect...`);
4674
- onStatusChange?.("reconnecting");
4675
- this.attemptReconnect(moduleUid);
4676
- } else {
4677
- this.tunnelStates.delete(moduleUid);
4678
- onStatusChange?.("disconnected");
4679
- this.emitEvent({ type: "stopped", moduleUid });
4680
- }
4681
- }
4682
- });
4683
- process2.on("error", (error) => {
4684
- tunnelInfo.status = "error";
4685
- tunnelInfo.error = error.message;
4686
- const currentState = this.tunnelStates.get(moduleUid);
4687
- if (currentState && !currentState.intentionallyStopped) {
4688
- this.attemptReconnect(moduleUid);
4689
- } else {
4690
- this.tunnelStates.delete(moduleUid);
4691
- onStatusChange?.("error", error.message);
4692
- this.emitEvent({ type: "error", moduleUid, error: error.message });
4693
- }
4694
- if (!urlFound) {
4695
- resolve3({ success: false, error: error.message });
4696
- }
4697
- });
4698
- setTimeout(() => {
4699
- if (!urlFound) {
4700
- process2.kill();
4701
- const errorMsg = "Tunnel startup timed out after 30 seconds";
4702
- tunnelInfo.status = "error";
4703
- tunnelInfo.error = errorMsg;
4704
- const currentState = this.tunnelStates.get(moduleUid);
4705
- if (currentState && !currentState.intentionallyStopped) {
4706
- this.attemptReconnect(moduleUid);
4707
- } else {
4708
- this.tunnelStates.delete(moduleUid);
4709
- onStatusChange?.("error", errorMsg);
4710
- this.emitEvent({ type: "error", moduleUid, error: errorMsg });
4711
- }
4712
- resolve3({ success: false, error: errorMsg });
4713
- }
4714
- }, TUNNEL_TIMEOUTS.QUICK_TUNNEL_CONNECT);
4715
- });
4579
+ }
4580
+ return this.startNamedTunnelProcess(options, existingState);
4716
4581
  }
4582
+ // EP1020: startQuickTunnelProcess removed - Quick Tunnels no longer supported
4583
+ // All tunnels now use Named Tunnels via Cloudflare API
4717
4584
  /**
4718
4585
  * Start a tunnel for a module
4719
4586
  *
@@ -4782,8 +4649,11 @@ var TunnelManager = class extends import_events.EventEmitter {
4782
4649
  previewUrl: provisionResult.tunnel.preview_url
4783
4650
  };
4784
4651
  } else {
4785
- console.warn(`[Tunnel] EP948: Named Tunnel provisioning failed: ${provisionResult.error}. Falling back to Quick Tunnel.`);
4786
- resolvedOptions = { ...resolvedOptions, mode: "quick" };
4652
+ console.error(`[Tunnel] EP1020: Named Tunnel provisioning failed for ${moduleUid}: ${provisionResult.error}`);
4653
+ return {
4654
+ success: false,
4655
+ error: `Named Tunnel provisioning failed: ${provisionResult.error}`
4656
+ };
4787
4657
  }
4788
4658
  }
4789
4659
  return this.startTunnelProcess(resolvedOptions);
@@ -9265,18 +9135,6 @@ var Daemon = class _Daemon {
9265
9135
  const result2 = await previewManager.restartPreview(moduleUid);
9266
9136
  if (result2.success && result2.previewUrl) {
9267
9137
  console.log(`[Daemon] EP833: Preview restarted for ${moduleUid}: ${result2.previewUrl}`);
9268
- try {
9269
- await fetchWithAuth(`${apiUrl}/api/modules/${moduleUid}/tunnel`, {
9270
- method: "POST",
9271
- body: JSON.stringify({
9272
- tunnel_url: result2.previewUrl,
9273
- tunnel_error: null,
9274
- restart_reason: "health_check_failure"
9275
- })
9276
- });
9277
- } catch (e) {
9278
- console.warn(`[Daemon] EP833: Failed to report restarted tunnel URL`);
9279
- }
9280
9138
  } else {
9281
9139
  console.error(`[Daemon] EP833: Preview restart failed for ${moduleUid}: ${result2.error}`);
9282
9140
  }
@@ -9301,18 +9159,6 @@ var Daemon = class _Daemon {
9301
9159
  });
9302
9160
  if (result.success && result.previewUrl) {
9303
9161
  console.log(`[Daemon] EP833: Preview started for ${moduleUid}: ${result.previewUrl}`);
9304
- try {
9305
- await fetchWithAuth(`${apiUrl}/api/modules/${moduleUid}/tunnel`, {
9306
- method: "POST",
9307
- body: JSON.stringify({
9308
- tunnel_url: result.previewUrl,
9309
- tunnel_error: null,
9310
- restart_reason: "health_check_failure"
9311
- })
9312
- });
9313
- } catch (e) {
9314
- console.warn(`[Daemon] EP833: Failed to report restarted tunnel URL`);
9315
- }
9316
9162
  } else {
9317
9163
  console.error(`[Daemon] EP833: Preview start failed for ${moduleUid}: ${result.error}`);
9318
9164
  }