clawfire 0.4.1 → 0.4.2

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/dev.cjs CHANGED
@@ -1607,34 +1607,15 @@ function generateDashboardHtml(options) {
1607
1607
  <button id="reauth-btn" onclick="startFirebaseLogin(true)" style="display:none;padding:8px 20px;background:#eab308;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">
1608
1608
  Re-authenticate
1609
1609
  </button>
1610
- <button id="cancel-login-btn" onclick="cancelFirebaseLogin()" style="display:none;padding:8px 20px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:13px;cursor:pointer;margin-left:8px;">
1611
- Cancel
1612
- </button>
1613
1610
  </div>
1614
- <!-- Login progress / auth URL -->
1615
- <div id="login-progress" style="display:none;margin-top:12px;padding:16px;border-radius:8px;background:#0a0a1a;border:1px solid #3b82f6;">
1611
+ <!-- Login waiting message -->
1612
+ <div id="login-waiting" style="display:none;margin-top:12px;padding:16px;border-radius:8px;background:#0a0a1a;border:1px solid #3b82f6;">
1616
1613
  <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
1617
- <span id="login-spinner" style="display:inline-block;width:14px;height:14px;border:2px solid #3b82f6;border-top-color:transparent;border-radius:50%;animation:spin 1s linear infinite;"></span>
1618
- <span id="login-spinner-text" style="color:#e5e5e5;font-size:13px;font-weight:600;">Starting login...</span>
1619
- </div>
1620
- <div id="login-url-box" style="display:none;margin-top:12px;">
1621
- <div style="font-size:13px;color:#e5e5e5;margin-bottom:6px;font-weight:600;">Step 1: Open this link to authenticate</div>
1622
- <a id="login-url-link" href="#" target="_blank" rel="noopener" style="display:inline-block;padding:8px 16px;background:#3b82f6;color:#fff;border-radius:6px;font-size:13px;text-decoration:none;margin-bottom:8px;">Open Google Login</a>
1623
- <div style="font-size:11px;color:#666;margin-top:4px;word-break:break-all;font-family:monospace;" id="login-url-raw"></div>
1614
+ <span style="display:inline-block;width:14px;height:14px;border:2px solid #3b82f6;border-top-color:transparent;border-radius:50%;animation:spin 1s linear infinite;"></span>
1615
+ <span style="color:#e5e5e5;font-size:13px;font-weight:600;">Waiting for login...</span>
1624
1616
  </div>
1625
- <div id="auth-code-box" style="display:none;margin-top:12px;">
1626
- <div style="font-size:13px;color:#e5e5e5;margin-bottom:6px;font-weight:600;">Step 2: Paste the authorization code</div>
1627
- <div style="font-size:12px;color:#a3a3a3;margin-bottom:8px;">After signing in, Google will show an authorization code. Copy it and paste below.</div>
1628
- <div style="display:flex;gap:8px;">
1629
- <input id="auth-code-input" type="text" placeholder="Paste authorization code here"
1630
- style="flex:1;padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-family:monospace;font-size:14px;outline:none;" />
1631
- <button id="auth-code-submit" onclick="submitAuthCode()" style="padding:8px 16px;background:#22c55e;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap;">
1632
- Submit Code
1633
- </button>
1634
- </div>
1635
- <div id="auth-code-error" style="display:none;margin-top:6px;font-size:12px;color:#ef4444;"></div>
1636
- </div>
1637
- <div id="login-output" style="display:none;margin-top:8px;max-height:80px;overflow-y:auto;padding:8px;background:#000;border-radius:4px;font-family:monospace;font-size:11px;color:#666;white-space:pre-wrap;"></div>
1617
+ <div style="font-size:13px;color:#a3a3a3;">A terminal window has been opened. Please complete the Firebase login there.</div>
1618
+ <div style="font-size:12px;color:#666;margin-top:6px;">This page will update automatically when login is detected.</div>
1638
1619
  </div>
1639
1620
  <div id="login-result" style="display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;"></div>
1640
1621
  </div>
@@ -1881,14 +1862,14 @@ function generateDashboardHtml(options) {
1881
1862
  document.getElementById('step2-action').style.display = 'block';
1882
1863
  document.getElementById('login-btn').style.display = 'none';
1883
1864
  document.getElementById('reauth-btn').style.display = 'inline-block';
1884
- document.getElementById('cancel-login-btn').style.display = 'none';
1865
+ document.getElementById('login-waiting').style.display = 'none';
1885
1866
  } else {
1886
1867
  document.getElementById('step2-icon').textContent = RED;
1887
1868
  document.getElementById('step2-detail').textContent = 'Not logged in to Firebase';
1888
1869
  document.getElementById('step2-action').style.display = 'block';
1889
1870
  document.getElementById('login-btn').style.display = 'inline-block';
1890
1871
  document.getElementById('reauth-btn').style.display = 'none';
1891
- document.getElementById('cancel-login-btn').style.display = 'none';
1872
+ document.getElementById('login-waiting').style.display = 'none';
1892
1873
  }
1893
1874
 
1894
1875
  if (status.nextStep === 'login') {
@@ -1957,22 +1938,17 @@ function generateDashboardHtml(options) {
1957
1938
  });
1958
1939
  };
1959
1940
 
1960
- // \u2500\u2500\u2500 Step 2: Login \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1941
+ // \u2500\u2500\u2500 Step 2: Login (opens terminal window) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1961
1942
  window.startFirebaseLogin = function(reauth) {
1962
1943
  var loginBtn = document.getElementById('login-btn');
1963
1944
  var reauthBtn = document.getElementById('reauth-btn');
1964
- var cancelBtn = document.getElementById('cancel-login-btn');
1965
- var progress = document.getElementById('login-progress');
1945
+ var waiting = document.getElementById('login-waiting');
1966
1946
  var result = document.getElementById('login-result');
1967
1947
 
1968
1948
  loginBtn.style.display = 'none';
1969
1949
  reauthBtn.style.display = 'none';
1970
- cancelBtn.style.display = 'inline-block';
1971
- progress.style.display = 'block';
1950
+ waiting.style.display = 'block';
1972
1951
  result.style.display = 'none';
1973
- document.getElementById('login-url-box').style.display = 'none';
1974
- document.getElementById('auth-code-box').style.display = 'none';
1975
- document.getElementById('login-spinner-text').textContent = 'Starting login...';
1976
1952
 
1977
1953
  fetch(API + '/__dev/setup/login', {
1978
1954
  method: 'POST',
@@ -1980,129 +1956,45 @@ function generateDashboardHtml(options) {
1980
1956
  body: JSON.stringify({ reauth: !!reauth })
1981
1957
  })
1982
1958
  .then(function(r) { return r.json(); })
1983
- .then(function() {
1984
- // Start polling login state
1959
+ .then(function(data) {
1960
+ if (!data.success) {
1961
+ waiting.style.display = 'none';
1962
+ result.textContent = data.message;
1963
+ result.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
1964
+ loginBtn.style.display = 'inline-block';
1965
+ return;
1966
+ }
1967
+ // Poll setup status until login is detected
1985
1968
  startLoginPoll();
1986
1969
  })
1987
1970
  .catch(function(err) {
1988
- progress.style.display = 'none';
1989
- result.textContent = 'Failed to start login: ' + err.message;
1971
+ waiting.style.display = 'none';
1972
+ result.textContent = 'Failed: ' + err.message;
1990
1973
  result.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
1991
1974
  loginBtn.style.display = 'inline-block';
1992
- cancelBtn.style.display = 'none';
1993
1975
  });
1994
1976
  };
1995
1977
 
1996
1978
  function startLoginPoll() {
1997
1979
  if (loginPollTimer) clearInterval(loginPollTimer);
1998
1980
  loginPollTimer = setInterval(function() {
1999
- fetch(API + '/__dev/setup/login-state')
1981
+ fetch(API + '/__dev/setup/status')
2000
1982
  .then(function(r) { return r.json(); })
2001
- .then(function(state) {
2002
- // Show auth URL if available
2003
- if (state.authUrl) {
2004
- var urlBox = document.getElementById('login-url-box');
2005
- var urlLink = document.getElementById('login-url-link');
2006
- var urlRaw = document.getElementById('login-url-raw');
2007
- urlBox.style.display = 'block';
2008
- urlLink.href = state.authUrl;
2009
- urlLink.textContent = 'Open Google Login';
2010
- if (urlRaw) urlRaw.textContent = state.authUrl;
2011
- document.getElementById('login-spinner-text').textContent = 'Waiting for authentication...';
2012
- }
2013
-
2014
- // Show auth code input when waiting for code
2015
- if (state.waitingForCode) {
2016
- document.getElementById('auth-code-box').style.display = 'block';
2017
- }
2018
-
2019
- // Show output log
2020
- if (state.output) {
2021
- var outputEl = document.getElementById('login-output');
2022
- outputEl.style.display = 'block';
2023
- outputEl.textContent = state.output.slice(-500);
2024
- }
2025
-
2026
- // Check if done
2027
- if (state.completed) {
1983
+ .then(function(status) {
1984
+ if (status.auth.authenticated) {
2028
1985
  clearInterval(loginPollTimer);
2029
1986
  loginPollTimer = null;
2030
- document.getElementById('login-progress').style.display = 'none';
2031
- document.getElementById('cancel-login-btn').style.display = 'none';
1987
+ document.getElementById('login-waiting').style.display = 'none';
2032
1988
  var result = document.getElementById('login-result');
2033
- result.textContent = 'Login successful!';
1989
+ result.textContent = 'Login successful! Logged in as ' + status.auth.user;
2034
1990
  result.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';
2035
- // Refresh wizard after short delay
2036
1991
  setTimeout(refreshSetupStatus, 1500);
2037
1992
  }
2038
-
2039
- if (state.error) {
2040
- clearInterval(loginPollTimer);
2041
- loginPollTimer = null;
2042
- document.getElementById('login-progress').style.display = 'none';
2043
- document.getElementById('cancel-login-btn').style.display = 'none';
2044
- var result2 = document.getElementById('login-result');
2045
- result2.textContent = state.error;
2046
- result2.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
2047
- document.getElementById('login-btn').style.display = 'inline-block';
2048
- }
2049
-
2050
- if (!state.active && !state.completed && !state.error) {
2051
- clearInterval(loginPollTimer);
2052
- loginPollTimer = null;
2053
- }
2054
1993
  })
2055
1994
  .catch(function() {});
2056
- }, 1500);
1995
+ }, 3000);
2057
1996
  }
2058
1997
 
2059
- window.submitAuthCode = function() {
2060
- var input = document.getElementById('auth-code-input');
2061
- var errEl = document.getElementById('auth-code-error');
2062
- var btn = document.getElementById('auth-code-submit');
2063
- var code = input ? input.value.trim() : '';
2064
- if (!code) {
2065
- errEl.textContent = 'Please paste the authorization code.';
2066
- errEl.style.display = 'block';
2067
- return;
2068
- }
2069
- errEl.style.display = 'none';
2070
- btn.disabled = true;
2071
- btn.textContent = 'Submitting...';
2072
-
2073
- fetch(API + '/__dev/setup/submit-auth-code', {
2074
- method: 'POST',
2075
- headers: { 'Content-Type': 'application/json' },
2076
- body: JSON.stringify({ code: code })
2077
- })
2078
- .then(function(r) { return r.json(); })
2079
- .then(function(data) {
2080
- if (data.success) {
2081
- document.getElementById('login-spinner-text').textContent = 'Verifying authorization...';
2082
- document.getElementById('auth-code-box').style.display = 'none';
2083
- } else {
2084
- errEl.textContent = data.message || 'Failed to submit code.';
2085
- errEl.style.display = 'block';
2086
- }
2087
- btn.disabled = false;
2088
- btn.textContent = 'Submit Code';
2089
- })
2090
- .catch(function(err) {
2091
- errEl.textContent = 'Error: ' + err.message;
2092
- errEl.style.display = 'block';
2093
- btn.disabled = false;
2094
- btn.textContent = 'Submit Code';
2095
- });
2096
- };
2097
-
2098
- window.cancelFirebaseLogin = function() {
2099
- if (loginPollTimer) { clearInterval(loginPollTimer); loginPollTimer = null; }
2100
- fetch(API + '/__dev/setup/cancel-login', { method: 'POST' }).catch(function() {});
2101
- document.getElementById('login-progress').style.display = 'none';
2102
- document.getElementById('cancel-login-btn').style.display = 'none';
2103
- document.getElementById('login-btn').style.display = 'inline-block';
2104
- };
2105
-
2106
1998
  // \u2500\u2500\u2500 Step 3: Project Selection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2107
1999
  function loadProjectList(currentProjectId) {
2108
2000
  var select = document.getElementById('project-select');
@@ -2572,16 +2464,6 @@ var import_node_path4 = require("path");
2572
2464
  var import_node_os = require("os");
2573
2465
  var FirebaseSetup = class {
2574
2466
  projectDir;
2575
- loginProcess = null;
2576
- loginSession = {
2577
- active: false,
2578
- authUrl: null,
2579
- waitingForCode: false,
2580
- completed: false,
2581
- error: null,
2582
- output: ""
2583
- };
2584
- loginTimeout = null;
2585
2467
  constructor(projectDir) {
2586
2468
  this.projectDir = projectDir;
2587
2469
  }
@@ -2661,149 +2543,89 @@ var FirebaseSetup = class {
2661
2543
  return { success: false, message: `Failed to install Firebase CLI: ${msg}` };
2662
2544
  }
2663
2545
  }
2664
- // ─── Analytics Pre-config ──────────────────────────────────────────
2665
- /**
2666
- * Pre-configure Firebase analytics preference to avoid the interactive
2667
- * "Allow Firebase to collect..." prompt that breaks non-TTY spawns.
2668
- */
2669
- ensureAnalyticsConfigured() {
2670
- try {
2671
- const home = (0, import_node_os.homedir)();
2672
- const configPath = (0, import_node_path4.join)(home, ".config", "configstore", "firebase-tools.json");
2673
- if ((0, import_node_fs4.existsSync)(configPath)) {
2674
- const config = JSON.parse((0, import_node_fs4.readFileSync)(configPath, "utf-8"));
2675
- if (config.usage === void 0) {
2676
- config.usage = false;
2677
- (0, import_node_fs4.writeFileSync)(configPath, JSON.stringify(config, null, 2), "utf-8");
2678
- }
2679
- } else {
2680
- const dir = (0, import_node_path4.dirname)(configPath);
2681
- (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
2682
- (0, import_node_fs4.writeFileSync)(configPath, JSON.stringify({ usage: false }, null, 2), "utf-8");
2683
- }
2684
- } catch {
2685
- }
2686
- }
2687
- // ─── Login Flow ────────────────────────────────────────────────────
2546
+ // ─── Login via Terminal ───────────────────────────────────────────
2688
2547
  /**
2689
- * Start login using `firebase login --no-localhost`.
2548
+ * Opens a new terminal window and runs `firebase login`.
2690
2549
  *
2691
- * This mode is designed for non-TTY environments:
2692
- * 1. Prints a Google OAuth URL
2693
- * 2. User opens URL in browser and authenticates
2694
- * 3. Google shows an authorization code on screen
2695
- * 4. User pastes code back call submitAuthCode()
2550
+ * Firebase CLI requires a real TTY for interactive login.
2551
+ * Instead of trying to fake a TTY, we open an actual terminal.
2552
+ * The dashboard polls getStatus() to detect when login completes.
2553
+ *
2554
+ * macOS: Creates a .command file and opens it (Terminal.app)
2555
+ * Linux: Tries common terminal emulators
2556
+ * Windows: Opens a new cmd window
2696
2557
  */
2697
- startLogin(reauth = false) {
2698
- this.cleanupLogin();
2699
- this.ensureAnalyticsConfigured();
2700
- this.loginSession = {
2701
- active: true,
2702
- authUrl: null,
2703
- waitingForCode: false,
2704
- completed: false,
2705
- error: null,
2706
- output: ""
2707
- };
2558
+ openLoginTerminal(reauth = false) {
2559
+ const cmd = reauth ? "firebase login --reauth" : "firebase login";
2560
+ const os = (0, import_node_os.platform)();
2708
2561
  try {
2709
- const args = reauth ? ["login", "--reauth", "--no-localhost"] : ["login", "--no-localhost"];
2710
- const proc = (0, import_node_child_process2.spawn)("firebase", args, {
2711
- cwd: this.projectDir,
2712
- stdio: ["pipe", "pipe", "pipe"],
2713
- env: { ...process.env, TERM: "dumb" }
2714
- });
2715
- this.loginProcess = proc;
2716
- const appendOutput = (data) => {
2717
- const text = data.toString();
2718
- this.loginSession.output += text;
2719
- const urlMatch = text.match(/(https:\/\/accounts\.google\.com\S+)/);
2720
- if (urlMatch) {
2721
- this.loginSession.authUrl = urlMatch[1];
2722
- this.loginSession.waitingForCode = true;
2723
- }
2724
- if (text.includes("Success") || text.includes("Logged in as")) {
2725
- this.loginSession.completed = true;
2726
- this.loginSession.active = false;
2727
- this.loginSession.waitingForCode = false;
2728
- }
2729
- if (text.includes("Allow Firebase to collect") || text.includes("(Y/n)")) {
2562
+ if (os === "darwin") {
2563
+ const scriptPath = (0, import_node_path4.join)((0, import_node_os.tmpdir)(), "clawfire-firebase-login.command");
2564
+ (0, import_node_fs4.writeFileSync)(scriptPath, [
2565
+ "#!/bin/bash",
2566
+ `cd "${this.projectDir}"`,
2567
+ cmd,
2568
+ 'echo ""',
2569
+ 'echo "Login complete! You can close this window."',
2570
+ 'read -p "Press Enter to close..."'
2571
+ ].join("\n"), { mode: 493 });
2572
+ const child = (0, import_node_child_process2.spawn)("open", [scriptPath], { detached: true, stdio: "ignore" });
2573
+ child.unref();
2574
+ } else if (os === "win32") {
2575
+ const child = (0, import_node_child_process2.spawn)("cmd", ["/c", "start", "cmd", "/k", cmd], {
2576
+ cwd: this.projectDir,
2577
+ detached: true,
2578
+ stdio: "ignore"
2579
+ });
2580
+ child.unref();
2581
+ } else {
2582
+ const scriptPath = (0, import_node_path4.join)((0, import_node_os.tmpdir)(), "clawfire-firebase-login.sh");
2583
+ (0, import_node_fs4.writeFileSync)(scriptPath, [
2584
+ "#!/bin/bash",
2585
+ `cd "${this.projectDir}"`,
2586
+ cmd,
2587
+ 'echo ""',
2588
+ 'echo "Login complete! You can close this window."',
2589
+ 'read -p "Press Enter to close..."'
2590
+ ].join("\n"), { mode: 493 });
2591
+ const terminals = [
2592
+ { cmd: "x-terminal-emulator", args: ["-e", scriptPath] },
2593
+ { cmd: "gnome-terminal", args: ["--", "bash", scriptPath] },
2594
+ { cmd: "konsole", args: ["-e", "bash", scriptPath] },
2595
+ { cmd: "xfce4-terminal", args: ["-e", scriptPath] },
2596
+ { cmd: "xterm", args: ["-e", scriptPath] }
2597
+ ];
2598
+ let opened = false;
2599
+ for (const t of terminals) {
2730
2600
  try {
2731
- proc.stdin?.write("n\n");
2601
+ const child = (0, import_node_child_process2.spawn)(t.cmd, t.args, { detached: true, stdio: "ignore" });
2602
+ child.unref();
2603
+ child.on("error", () => {
2604
+ });
2605
+ opened = true;
2606
+ break;
2732
2607
  } catch {
2608
+ continue;
2733
2609
  }
2734
2610
  }
2735
- };
2736
- proc.stdout?.on("data", appendOutput);
2737
- proc.stderr?.on("data", appendOutput);
2738
- proc.on("close", (code) => {
2739
- this.loginSession.active = false;
2740
- this.loginSession.waitingForCode = false;
2741
- if (code === 0) {
2742
- this.loginSession.completed = true;
2743
- } else if (!this.loginSession.completed) {
2744
- const lines = this.loginSession.output.trim().split("\n").filter((l) => l.trim());
2745
- const lastLines = lines.slice(-3).join(" | ");
2746
- this.loginSession.error = `Login failed (exit code ${code}).` + (lastLines ? ` Details: ${lastLines}` : "") + ` Try running "firebase login" in your terminal.`;
2747
- }
2748
- this.loginProcess = null;
2749
- });
2750
- proc.on("error", (err) => {
2751
- this.loginSession.active = false;
2752
- this.loginSession.error = err.message;
2753
- this.loginProcess = null;
2754
- });
2755
- this.loginTimeout = setTimeout(() => {
2756
- if (this.loginSession.active) {
2757
- this.loginSession.error = "Login timed out (5 minutes). Please try again.";
2758
- this.cleanupLogin();
2611
+ if (!opened) {
2612
+ return {
2613
+ success: false,
2614
+ message: `Could not find a terminal emulator. Please run "${cmd}" manually in your terminal.`
2615
+ };
2759
2616
  }
2760
- }, 5 * 60 * 1e3);
2761
- } catch (err) {
2762
- this.loginSession.active = false;
2763
- this.loginSession.error = err instanceof Error ? err.message : "Failed to start login";
2764
- }
2765
- return this.loginSession;
2766
- }
2767
- getLoginState() {
2768
- return { ...this.loginSession };
2769
- }
2770
- /**
2771
- * Submit the authorization code that the user copied from the browser.
2772
- * This writes the code to the Firebase CLI process stdin.
2773
- */
2774
- submitAuthCode(code) {
2775
- if (!this.loginProcess || !this.loginProcess.stdin) {
2776
- return { success: false, message: "No active login process. Please start login again." };
2777
- }
2778
- if (!code || !code.trim()) {
2779
- return { success: false, message: "Authorization code is required." };
2780
- }
2781
- try {
2782
- this.loginProcess.stdin.write(code.trim() + "\n");
2783
- this.loginSession.waitingForCode = false;
2784
- return { success: true, message: "Authorization code submitted. Waiting for verification..." };
2785
- } catch (err) {
2786
- return { success: false, message: "Failed to submit code: " + (err instanceof Error ? err.message : "unknown error") };
2787
- }
2788
- }
2789
- cancelLogin() {
2790
- this.cleanupLogin();
2791
- this.loginSession.error = "Login cancelled by user.";
2792
- }
2793
- cleanupLogin() {
2794
- if (this.loginProcess) {
2795
- try {
2796
- this.loginProcess.kill("SIGTERM");
2797
- } catch {
2798
2617
  }
2799
- this.loginProcess = null;
2800
- }
2801
- if (this.loginTimeout) {
2802
- clearTimeout(this.loginTimeout);
2803
- this.loginTimeout = null;
2618
+ return {
2619
+ success: true,
2620
+ message: "Terminal window opened. Please complete the login in the new terminal."
2621
+ };
2622
+ } catch (err) {
2623
+ const msg = err instanceof Error ? err.message : "Unknown error";
2624
+ return {
2625
+ success: false,
2626
+ message: `Failed to open terminal: ${msg}. Please run "${cmd}" manually in your terminal.`
2627
+ };
2804
2628
  }
2805
- this.loginSession.active = false;
2806
- this.loginSession.waitingForCode = false;
2807
2629
  }
2808
2630
  // ─── Project Listing ───────────────────────────────────────────────
2809
2631
  async listProjects() {
@@ -2897,7 +2719,6 @@ var FirebaseSetup = class {
2897
2719
  }
2898
2720
  /** Cleanup resources */
2899
2721
  destroy() {
2900
- this.cleanupLogin();
2901
2722
  }
2902
2723
  };
2903
2724
 
@@ -3820,36 +3641,8 @@ ${liveReloadScript}
3820
3641
  reauth = !!data.reauth;
3821
3642
  } catch {
3822
3643
  }
3823
- const session = this.firebaseSetup.startLogin(reauth);
3824
- sendJson(session);
3825
- });
3826
- return;
3827
- }
3828
- if (url.pathname === "/__dev/setup/login-state" && req.method === "GET") {
3829
- sendJson(this.firebaseSetup.getLoginState());
3830
- return;
3831
- }
3832
- if (url.pathname === "/__dev/setup/cancel-login" && req.method === "POST") {
3833
- this.firebaseSetup.cancelLogin();
3834
- sendJson({ ok: true });
3835
- return;
3836
- }
3837
- if (url.pathname === "/__dev/setup/submit-auth-code" && req.method === "POST") {
3838
- let body = "";
3839
- req.on("data", (chunk) => {
3840
- body += chunk;
3841
- });
3842
- req.on("end", () => {
3843
- try {
3844
- const data = JSON.parse(body);
3845
- if (!data.code) {
3846
- sendJson({ success: false, message: "code is required" }, 400);
3847
- return;
3848
- }
3849
- sendJson(this.firebaseSetup.submitAuthCode(data.code));
3850
- } catch {
3851
- sendJson({ success: false, message: "Invalid JSON body" }, 400);
3852
- }
3644
+ const result = this.firebaseSetup.openLoginTerminal(reauth);
3645
+ sendJson(result);
3853
3646
  });
3854
3647
  return;
3855
3648
  }