episoda 0.2.27 → 0.2.28

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/index.js CHANGED
@@ -2649,7 +2649,7 @@ var require_dist = __commonJS({
2649
2649
 
2650
2650
  // src/index.ts
2651
2651
  var import_commander = require("commander");
2652
- var import_core14 = __toESM(require_dist());
2652
+ var import_core15 = __toESM(require_dist());
2653
2653
 
2654
2654
  // src/commands/dev.ts
2655
2655
  var import_core4 = __toESM(require_dist());
@@ -3671,8 +3671,8 @@ var WorktreeManager = class _WorktreeManager {
3671
3671
  console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
3672
3672
  console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
3673
3673
  try {
3674
- const { execSync: execSync7 } = require("child_process");
3675
- execSync7(script, {
3674
+ const { execSync: execSync9 } = require("child_process");
3675
+ execSync9(script, {
3676
3676
  cwd: worktree.worktreePath,
3677
3677
  stdio: "inherit",
3678
3678
  timeout: TIMEOUT_MINUTES * 60 * 1e3,
@@ -3706,8 +3706,8 @@ var WorktreeManager = class _WorktreeManager {
3706
3706
  console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
3707
3707
  console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
3708
3708
  try {
3709
- const { execSync: execSync7 } = require("child_process");
3710
- execSync7(script, {
3709
+ const { execSync: execSync9 } = require("child_process");
3710
+ execSync9(script, {
3711
3711
  cwd: worktree.worktreePath,
3712
3712
  stdio: "inherit",
3713
3713
  timeout: TIMEOUT_MINUTES * 60 * 1e3,
@@ -5797,8 +5797,184 @@ async function listWorktrees(options) {
5797
5797
  console.log("");
5798
5798
  }
5799
5799
 
5800
+ // src/commands/update.ts
5801
+ var import_child_process8 = require("child_process");
5802
+ var import_core14 = __toESM(require_dist());
5803
+
5804
+ // src/utils/update-checker.ts
5805
+ var import_child_process7 = require("child_process");
5806
+ var semver = __toESM(require("semver"));
5807
+ var PACKAGE_NAME = "episoda";
5808
+ var NPM_REGISTRY = "https://registry.npmjs.org";
5809
+ async function checkForUpdates(currentVersion) {
5810
+ try {
5811
+ const controller = new AbortController();
5812
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
5813
+ const response = await fetch(`${NPM_REGISTRY}/${PACKAGE_NAME}/latest`, {
5814
+ signal: controller.signal
5815
+ });
5816
+ clearTimeout(timeoutId);
5817
+ if (!response.ok) {
5818
+ return { currentVersion, latestVersion: currentVersion, updateAvailable: false };
5819
+ }
5820
+ const data = await response.json();
5821
+ const latestVersion = data.version;
5822
+ return {
5823
+ currentVersion,
5824
+ latestVersion,
5825
+ updateAvailable: semver.gt(latestVersion, currentVersion)
5826
+ };
5827
+ } catch (error) {
5828
+ return { currentVersion, latestVersion: currentVersion, updateAvailable: false, offline: true };
5829
+ }
5830
+ }
5831
+ function getInstalledVersion() {
5832
+ try {
5833
+ const output = (0, import_child_process7.execSync)(`npm list -g ${PACKAGE_NAME} --json`, {
5834
+ stdio: ["pipe", "pipe", "pipe"],
5835
+ timeout: 1e4
5836
+ }).toString();
5837
+ const data = JSON.parse(output);
5838
+ return data?.dependencies?.[PACKAGE_NAME]?.version || null;
5839
+ } catch {
5840
+ return null;
5841
+ }
5842
+ }
5843
+
5844
+ // src/commands/update.ts
5845
+ async function isDaemonRunning2() {
5846
+ try {
5847
+ const daemonStatus = await getStatus();
5848
+ return !!daemonStatus;
5849
+ } catch {
5850
+ return false;
5851
+ }
5852
+ }
5853
+ function stopDaemon2() {
5854
+ try {
5855
+ (0, import_child_process8.execSync)("episoda stop", { stdio: "pipe" });
5856
+ return true;
5857
+ } catch {
5858
+ return false;
5859
+ }
5860
+ }
5861
+ function startDaemon2() {
5862
+ try {
5863
+ const child = (0, import_child_process8.spawn)("episoda", ["dev"], {
5864
+ detached: true,
5865
+ stdio: "ignore",
5866
+ shell: process.platform === "win32"
5867
+ });
5868
+ child.unref();
5869
+ return true;
5870
+ } catch {
5871
+ return false;
5872
+ }
5873
+ }
5874
+ function performUpdate() {
5875
+ try {
5876
+ (0, import_child_process8.execSync)("npm update -g episoda", {
5877
+ stdio: "pipe",
5878
+ timeout: 12e4
5879
+ // 2 minute timeout
5880
+ });
5881
+ return { success: true };
5882
+ } catch (error) {
5883
+ const message = error instanceof Error ? error.message : String(error);
5884
+ if (message.includes("EACCES") || message.includes("permission denied")) {
5885
+ return {
5886
+ success: false,
5887
+ error: "Permission denied. Try running with sudo:\n sudo npm update -g episoda"
5888
+ };
5889
+ }
5890
+ return { success: false, error: message };
5891
+ }
5892
+ }
5893
+ async function updateCommand(options = {}) {
5894
+ const currentVersion = import_core14.VERSION;
5895
+ status.info(`Current version: ${currentVersion}`);
5896
+ status.info("Checking for updates...");
5897
+ const result = await checkForUpdates(currentVersion);
5898
+ if (result.offline) {
5899
+ status.warning("\u26A0 Could not check for updates (offline or network error)");
5900
+ status.info(` Current version: ${currentVersion}`);
5901
+ status.info("");
5902
+ status.info("Check your internet connection and try again.");
5903
+ return;
5904
+ }
5905
+ if (!result.updateAvailable) {
5906
+ status.success(`\u2713 Already up to date (${currentVersion})`);
5907
+ return;
5908
+ }
5909
+ status.info(`Update available: ${result.currentVersion} \u2192 ${result.latestVersion}`);
5910
+ if (options.check) {
5911
+ status.info("");
5912
+ status.info('Run "episoda update" to install the update.');
5913
+ return;
5914
+ }
5915
+ if (!options.yes) {
5916
+ status.info("");
5917
+ status.info("To update, run:");
5918
+ status.info(" episoda update -y");
5919
+ status.info("");
5920
+ status.info("Or with daemon restart:");
5921
+ status.info(" episoda update -y --restart");
5922
+ return;
5923
+ }
5924
+ const daemonWasRunning = options.restart ? await isDaemonRunning2() : false;
5925
+ if (options.restart && daemonWasRunning) {
5926
+ status.info("Stopping daemon before update...");
5927
+ if (!stopDaemon2()) {
5928
+ status.warning("Could not stop daemon gracefully, continuing with update...");
5929
+ }
5930
+ }
5931
+ status.info("Updating...");
5932
+ const updateResult = performUpdate();
5933
+ if (!updateResult.success) {
5934
+ status.error(`\u2717 Update failed: ${updateResult.error}`);
5935
+ if (options.restart && daemonWasRunning) {
5936
+ status.info("Restarting daemon...");
5937
+ startDaemon2();
5938
+ }
5939
+ process.exit(1);
5940
+ }
5941
+ const installedVersion = getInstalledVersion();
5942
+ if (installedVersion && installedVersion !== result.latestVersion) {
5943
+ status.warning(`\u26A0 Update completed but version mismatch detected`);
5944
+ status.info(` Expected: ${result.latestVersion}`);
5945
+ status.info(` Installed: ${installedVersion}`);
5946
+ status.info("");
5947
+ status.info("Try running manually: npm update -g episoda");
5948
+ } else if (installedVersion) {
5949
+ status.success(`\u2713 Updated to ${installedVersion}`);
5950
+ } else {
5951
+ status.success(`\u2713 Updated to ${result.latestVersion}`);
5952
+ }
5953
+ if (options.restart && daemonWasRunning) {
5954
+ status.info("Restarting daemon...");
5955
+ await new Promise((resolve4) => setTimeout(resolve4, 1e3));
5956
+ if (startDaemon2()) {
5957
+ status.success("\u2713 Daemon restarted");
5958
+ status.info("");
5959
+ status.info('Run "episoda status" to verify connection.');
5960
+ } else {
5961
+ status.warning("Could not restart daemon automatically.");
5962
+ status.info('Run "episoda dev" to start the daemon.');
5963
+ }
5964
+ } else if (options.restart) {
5965
+ status.info("Daemon was not running, skipping restart.");
5966
+ } else {
5967
+ const daemonRunning = await isDaemonRunning2();
5968
+ if (daemonRunning) {
5969
+ status.info("");
5970
+ status.info("Note: Restart the daemon to use the new version:");
5971
+ status.info(" episoda stop && episoda dev");
5972
+ }
5973
+ }
5974
+ }
5975
+
5800
5976
  // src/index.ts
5801
- import_commander.program.name("episoda").description("Episoda CLI - local development with git worktree isolation").version(import_core14.VERSION);
5977
+ import_commander.program.name("episoda").description("Episoda CLI - local development with git worktree isolation").version(import_core15.VERSION);
5802
5978
  import_commander.program.command("auth").description("Authenticate to Episoda via OAuth and configure CLI").option("--api-url <url>", "API URL (default: https://episoda.dev)").action(async (options) => {
5803
5979
  try {
5804
5980
  await authCommand(options);
@@ -5854,6 +6030,14 @@ import_commander.program.command("disconnect").description("Disconnect from epis
5854
6030
  process.exit(1);
5855
6031
  }
5856
6032
  });
6033
+ import_commander.program.command("update").description("Check for updates and optionally install them").option("-y, --yes", "Auto-update without prompting").option("--check", "Just check for updates, do not install").option("--restart", "Restart daemon after update if it was running").action(async (options) => {
6034
+ try {
6035
+ await updateCommand(options);
6036
+ } catch (error) {
6037
+ status.error(`Update failed: ${error instanceof Error ? error.message : String(error)}`);
6038
+ process.exit(1);
6039
+ }
6040
+ });
5857
6041
  import_commander.program.command("clone").description("Clone a project for multi-module development").argument("<workspace/project>", "Workspace and project slug (e.g., my-team/my-project)").option("--api-url <url>", "API URL (default: https://episoda.dev)").action(async (slug, options) => {
5858
6042
  try {
5859
6043
  await cloneCommand(slug, options);