flightdesk 0.2.4 → 0.2.5

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.
Files changed (3) hide show
  1. package/main.js +175 -40
  2. package/main.js.map +3 -3
  3. package/package.json +1 -1
package/main.js CHANGED
@@ -3577,7 +3577,51 @@ var path2 = __toESM(require("node:path"));
3577
3577
  var os2 = __toESM(require("node:os"));
3578
3578
  var fs2 = __toESM(require("node:fs"));
3579
3579
  var readline2 = __toESM(require("node:readline"));
3580
+ var import_node_child_process = require("node:child_process");
3580
3581
  var playwright = null;
3582
+ var PlaywrightBrowserNotInstalledError = class extends Error {
3583
+ constructor(autoInstallError) {
3584
+ const baseMessage = "Playwright browser not installed.";
3585
+ const autoInstallInfo = autoInstallError ? `
3586
+
3587
+ Auto-install failed: ${autoInstallError}
3588
+ ` : "\n\n";
3589
+ super(
3590
+ baseMessage + autoInstallInfo + "Run one of the following commands:\n\n npx playwright install chromium # Just Chromium (recommended)\n npx playwright install # All browsers\n\nThen retry your command."
3591
+ );
3592
+ this.name = "PlaywrightBrowserNotInstalledError";
3593
+ }
3594
+ };
3595
+ function isBrowserNotInstalledError(error) {
3596
+ if (!(error instanceof Error)) return false;
3597
+ return error.message.includes("Executable doesn't exist") || error.message.includes("browserType.launch") || error.message.includes("npx playwright install");
3598
+ }
3599
+ var autoInstallAttempted = false;
3600
+ function tryAutoInstallBrowsers() {
3601
+ if (autoInstallAttempted) {
3602
+ return false;
3603
+ }
3604
+ autoInstallAttempted = true;
3605
+ console.log("");
3606
+ console.log("\u{1F4E6} Playwright browser not found. Installing automatically...");
3607
+ console.log("");
3608
+ try {
3609
+ (0, import_node_child_process.execSync)("npx playwright install chromium", {
3610
+ stdio: "inherit",
3611
+ timeout: 12e4
3612
+ // 2 minute timeout
3613
+ });
3614
+ console.log("");
3615
+ console.log("\u2705 Browser installed successfully!");
3616
+ console.log("");
3617
+ return true;
3618
+ } catch (error) {
3619
+ console.error("");
3620
+ console.error("\u274C Auto-install failed:", error instanceof Error ? error.message : String(error));
3621
+ console.error("");
3622
+ return false;
3623
+ }
3624
+ }
3581
3625
  var USER_DATA_DIR = path2.join(os2.homedir(), ".flightdesk", "chromium-profile");
3582
3626
  var STORAGE_STATE_FILE = path2.join(os2.homedir(), ".flightdesk", "auth-state.json");
3583
3627
  var PersistentBrowser = class {
@@ -3605,9 +3649,29 @@ var PersistentBrowser = class {
3605
3649
  throw new Error("Playwright not available");
3606
3650
  }
3607
3651
  ensureUserDataDir();
3608
- this.browser = await playwright.chromium.launch({
3609
- headless: this.headless
3610
- });
3652
+ try {
3653
+ this.browser = await playwright.chromium.launch({
3654
+ headless: this.headless
3655
+ });
3656
+ } catch (error) {
3657
+ if (isBrowserNotInstalledError(error)) {
3658
+ if (tryAutoInstallBrowsers()) {
3659
+ try {
3660
+ this.browser = await playwright.chromium.launch({
3661
+ headless: this.headless
3662
+ });
3663
+ } catch (retryError) {
3664
+ throw new PlaywrightBrowserNotInstalledError(
3665
+ retryError instanceof Error ? retryError.message : String(retryError)
3666
+ );
3667
+ }
3668
+ } else {
3669
+ throw new PlaywrightBrowserNotInstalledError();
3670
+ }
3671
+ } else {
3672
+ throw error;
3673
+ }
3674
+ }
3611
3675
  const hasAuthState = fs2.existsSync(STORAGE_STATE_FILE);
3612
3676
  const contextOptions = {
3613
3677
  viewport: { width: 1280, height: 720 },
@@ -3702,7 +3766,26 @@ async function launchBrowser(headless) {
3702
3766
  throw new Error("Playwright not available");
3703
3767
  }
3704
3768
  ensureUserDataDir();
3705
- const browser = await playwright.chromium.launch({ headless });
3769
+ let browser;
3770
+ try {
3771
+ browser = await playwright.chromium.launch({ headless });
3772
+ } catch (error) {
3773
+ if (isBrowserNotInstalledError(error)) {
3774
+ if (tryAutoInstallBrowsers()) {
3775
+ try {
3776
+ browser = await playwright.chromium.launch({ headless });
3777
+ } catch (retryError) {
3778
+ throw new PlaywrightBrowserNotInstalledError(
3779
+ retryError instanceof Error ? retryError.message : String(retryError)
3780
+ );
3781
+ }
3782
+ } else {
3783
+ throw new PlaywrightBrowserNotInstalledError();
3784
+ }
3785
+ } else {
3786
+ throw error;
3787
+ }
3788
+ }
3706
3789
  const hasAuthState = fs2.existsSync(STORAGE_STATE_FILE);
3707
3790
  const contextOptions = {
3708
3791
  viewport: { width: 1280, height: 720 },
@@ -3915,7 +3998,23 @@ async function detectActiveSpinner(page) {
3915
3998
  try {
3916
3999
  const spinner = await page.$(".code-spinner-animate");
3917
4000
  if (spinner) {
3918
- return true;
4001
+ const isVisibleAndAnimating = await spinner.evaluate((el) => {
4002
+ const style = globalThis.getComputedStyle(el);
4003
+ if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
4004
+ return false;
4005
+ }
4006
+ if (style.animationName && style.animationName !== "none") {
4007
+ return true;
4008
+ }
4009
+ if (style.animationPlayState === "paused") {
4010
+ return false;
4011
+ }
4012
+ const rect = el.getBoundingClientRect();
4013
+ return rect.width > 0 && rect.height > 0;
4014
+ });
4015
+ if (isVisibleAndAnimating) {
4016
+ return true;
4017
+ }
3919
4018
  }
3920
4019
  const spinnerByContent = await page.$('span:has-text("\u273D")');
3921
4020
  if (spinnerByContent) {
@@ -3924,10 +4023,19 @@ async function detectActiveSpinner(page) {
3924
4023
  while (current) {
3925
4024
  const classList = current.classList;
3926
4025
  if (classList?.contains("code-spinner-animate")) {
3927
- return true;
4026
+ const style2 = globalThis.getComputedStyle(current);
4027
+ if (style2.display === "none" || style2.visibility === "hidden" || style2.opacity === "0") {
4028
+ current = current.parentElement;
4029
+ continue;
4030
+ }
4031
+ if (style2.animationPlayState === "paused") {
4032
+ current = current.parentElement;
4033
+ continue;
4034
+ }
4035
+ return style2.animationName !== "none";
3928
4036
  }
3929
4037
  const style = globalThis.getComputedStyle(current);
3930
- if (style.animationName && style.animationName !== "none") {
4038
+ if (style.animationName && style.animationName !== "none" && style.animationPlayState !== "paused") {
3931
4039
  return true;
3932
4040
  }
3933
4041
  current = current.parentElement;
@@ -3954,30 +4062,48 @@ async function authCommand() {
3954
4062
  console.log(`Profile directory: ${USER_DATA_DIR}
3955
4063
  `);
3956
4064
  console.log("Checking current authentication status...");
3957
- const isAuthenticated = await checkAuth();
3958
- if (isAuthenticated) {
3959
- console.log("\u2705 Already logged in to Claude!");
3960
- console.log("\nThe watch daemon will be able to monitor your sessions.");
3961
- return;
4065
+ try {
4066
+ const isAuthenticated = await checkAuth();
4067
+ if (isAuthenticated) {
4068
+ console.log("\u2705 Already logged in to Claude!");
4069
+ console.log("\nThe watch daemon will be able to monitor your sessions.");
4070
+ return;
4071
+ }
4072
+ } catch (error) {
4073
+ if (error instanceof PlaywrightBrowserNotInstalledError) {
4074
+ console.error("");
4075
+ console.error("\u274C " + error.message);
4076
+ process.exit(1);
4077
+ }
4078
+ throw error;
3962
4079
  }
3963
4080
  console.log("\u274C Not logged in to Claude.\n");
3964
4081
  console.log("Opening browser for login...");
3965
4082
  console.log("Please log in to your Claude account.\n");
3966
- const loginSuccessful = await openForLogin();
3967
- if (loginSuccessful) {
3968
- console.log("\n\u2705 Successfully logged in!");
3969
- console.log("The watch daemon can now monitor your Claude Code sessions.");
3970
- } else {
3971
- console.log("\n\u274C Login was not detected.");
3972
- console.log("Please try again with: flightdesk auth");
4083
+ try {
4084
+ const loginSuccessful = await openForLogin();
4085
+ if (loginSuccessful) {
4086
+ console.log("\n\u2705 Successfully logged in!");
4087
+ console.log("The watch daemon can now monitor your Claude Code sessions.");
4088
+ } else {
4089
+ console.log("\n\u274C Login was not detected.");
4090
+ console.log("Please try again with: flightdesk auth");
4091
+ }
4092
+ } catch (error) {
4093
+ if (error instanceof PlaywrightBrowserNotInstalledError) {
4094
+ console.error("");
4095
+ console.error("\u274C " + error.message);
4096
+ process.exit(1);
4097
+ }
4098
+ throw error;
3973
4099
  }
3974
4100
  }
3975
4101
 
3976
4102
  // apps/cli/src/lib/git.ts
3977
- var import_node_child_process = require("node:child_process");
4103
+ var import_node_child_process2 = require("node:child_process");
3978
4104
  function detectGitRepo() {
3979
4105
  try {
3980
- const remoteUrl = (0, import_node_child_process.execSync)("git remote get-url origin", {
4106
+ const remoteUrl = (0, import_node_child_process2.execSync)("git remote get-url origin", {
3981
4107
  encoding: "utf-8",
3982
4108
  stdio: ["pipe", "pipe", "pipe"]
3983
4109
  }).trim();
@@ -3985,7 +4111,7 @@ function detectGitRepo() {
3985
4111
  if (!repoFullName) {
3986
4112
  return null;
3987
4113
  }
3988
- const branch = (0, import_node_child_process.execSync)("git rev-parse --abbrev-ref HEAD", {
4114
+ const branch = (0, import_node_child_process2.execSync)("git rev-parse --abbrev-ref HEAD", {
3989
4115
  encoding: "utf-8",
3990
4116
  stdio: ["pipe", "pipe", "pipe"]
3991
4117
  }).trim();
@@ -4263,16 +4389,25 @@ async function watchCommand(options) {
4263
4389
  } else {
4264
4390
  browser = new PersistentBrowser(options.headless !== false);
4265
4391
  console.log("Checking Claude authentication...");
4266
- const isAuthenticated = await browser.checkAuth();
4267
- if (!isAuthenticated) {
4268
- console.log("\u26A0\uFE0F Not logged into Claude. Run: flightdesk auth");
4269
- console.log("");
4270
- await browser.close();
4271
- browser = null;
4272
- } else {
4273
- console.log("\u2705 Playwright ready, Claude authenticated");
4274
- console.log(" (Browser session kept alive for monitoring)");
4275
- console.log("");
4392
+ try {
4393
+ const isAuthenticated = await browser.checkAuth();
4394
+ if (!isAuthenticated) {
4395
+ console.log("\u26A0\uFE0F Not logged into Claude. Run: flightdesk auth");
4396
+ console.log("");
4397
+ await browser.close();
4398
+ browser = null;
4399
+ } else {
4400
+ console.log("\u2705 Playwright ready, Claude authenticated");
4401
+ console.log(" (Browser session kept alive for monitoring)");
4402
+ console.log("");
4403
+ }
4404
+ } catch (error) {
4405
+ if (error instanceof PlaywrightBrowserNotInstalledError) {
4406
+ console.error("");
4407
+ console.error("\u274C " + error.message);
4408
+ process.exit(1);
4409
+ }
4410
+ throw error;
4276
4411
  }
4277
4412
  }
4278
4413
  const api = FlightDeskAPI.fromConfig(config, org2);
@@ -5254,7 +5389,7 @@ ${projects.length} project(s)`);
5254
5389
  }
5255
5390
 
5256
5391
  // apps/cli/src/commands/preview.ts
5257
- var import_node_child_process2 = require("node:child_process");
5392
+ var import_node_child_process3 = require("node:child_process");
5258
5393
  var path3 = __toESM(require("node:path"));
5259
5394
  var os3 = __toESM(require("node:os"));
5260
5395
  var fs4 = __toESM(require("node:fs"));
@@ -5360,7 +5495,7 @@ async function handleLogs(api, options) {
5360
5495
  `);
5361
5496
  validateSSHParams(instance);
5362
5497
  const sshCommand = `docker logs -f ${instance.containerId}`;
5363
- const ssh = (0, import_node_child_process2.spawn)("ssh", [
5498
+ const ssh = (0, import_node_child_process3.spawn)("ssh", [
5364
5499
  "-o",
5365
5500
  "StrictHostKeyChecking=no",
5366
5501
  "-o",
@@ -5400,7 +5535,7 @@ async function handleMount(api, options) {
5400
5535
  await new Promise((resolve) => setTimeout(resolve, 3e3));
5401
5536
  }
5402
5537
  try {
5403
- (0, import_node_child_process2.execSync)("which sshfs", { stdio: "ignore" });
5538
+ (0, import_node_child_process3.execSync)("which sshfs", { stdio: "ignore" });
5404
5539
  } catch {
5405
5540
  console.error("\u274C sshfs is not installed.");
5406
5541
  console.error("");
@@ -5421,7 +5556,7 @@ async function handleMount(api, options) {
5421
5556
  fs4.mkdirSync(mountDir, { recursive: true });
5422
5557
  }
5423
5558
  try {
5424
- const mounted = (0, import_node_child_process2.execSync)("mount", { encoding: "utf8" });
5559
+ const mounted = (0, import_node_child_process3.execSync)("mount", { encoding: "utf8" });
5425
5560
  if (mounted.includes(mountDir)) {
5426
5561
  console.log(`\u{1F4C1} Already mounted at ${mountDir}`);
5427
5562
  return;
@@ -5447,7 +5582,7 @@ async function handleMount(api, options) {
5447
5582
  mountDir
5448
5583
  ];
5449
5584
  try {
5450
- const result = (0, import_node_child_process2.spawnSync)("sshfs", sshfsArgs, { stdio: "inherit" });
5585
+ const result = (0, import_node_child_process3.spawnSync)("sshfs", sshfsArgs, { stdio: "inherit" });
5451
5586
  if (result.status !== 0) {
5452
5587
  throw new Error(`sshfs exited with code ${result.status}`);
5453
5588
  }
@@ -5481,9 +5616,9 @@ async function handleUnmount(_api, options) {
5481
5616
  try {
5482
5617
  let result;
5483
5618
  if (process.platform === "darwin") {
5484
- result = (0, import_node_child_process2.spawnSync)("umount", [mountDir], { stdio: "inherit" });
5619
+ result = (0, import_node_child_process3.spawnSync)("umount", [mountDir], { stdio: "inherit" });
5485
5620
  } else {
5486
- result = (0, import_node_child_process2.spawnSync)("fusermount", ["-u", mountDir], { stdio: "inherit" });
5621
+ result = (0, import_node_child_process3.spawnSync)("fusermount", ["-u", mountDir], { stdio: "inherit" });
5487
5622
  }
5488
5623
  if (result.status !== 0) {
5489
5624
  throw new Error(`Unmount exited with code ${result.status}`);
@@ -5523,7 +5658,7 @@ async function handleTeardown(api, options) {
5523
5658
 
5524
5659
  // apps/cli/src/main.ts
5525
5660
  var program2 = new Command();
5526
- program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.2.4").option("--dev", "Use local development API (localhost:3000)").option("--api <url>", "Use custom API URL");
5661
+ program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.2.5").option("--dev", "Use local development API (localhost:3000)").option("--api <url>", "Use custom API URL");
5527
5662
  program2.hook("preAction", () => {
5528
5663
  const opts = program2.opts();
5529
5664
  if (opts.api) {