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/cli.js CHANGED
@@ -209,7 +209,7 @@ async function runDevServer() {
209
209
  const port = portArg ? parseInt(portArg.split("=")[1], 10) : 3e3;
210
210
  const apiPort = apiPortArg ? parseInt(apiPortArg.split("=")[1], 10) : 3456;
211
211
  const noHotReload = args.includes("--no-hot-reload");
212
- const { startDevServer } = await import("./dev-server-DRZ52WIA.js");
212
+ const { startDevServer } = await import("./dev-server-65H4AQFS.js");
213
213
  await startDevServer({
214
214
  projectDir,
215
215
  port,
@@ -1195,34 +1195,15 @@ function generateDashboardHtml(options) {
1195
1195
  <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;">
1196
1196
  Re-authenticate
1197
1197
  </button>
1198
- <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;">
1199
- Cancel
1200
- </button>
1201
1198
  </div>
1202
- <!-- Login progress / auth URL -->
1203
- <div id="login-progress" style="display:none;margin-top:12px;padding:16px;border-radius:8px;background:#0a0a1a;border:1px solid #3b82f6;">
1199
+ <!-- Login waiting message -->
1200
+ <div id="login-waiting" style="display:none;margin-top:12px;padding:16px;border-radius:8px;background:#0a0a1a;border:1px solid #3b82f6;">
1204
1201
  <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
1205
- <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>
1206
- <span id="login-spinner-text" style="color:#e5e5e5;font-size:13px;font-weight:600;">Starting login...</span>
1207
- </div>
1208
- <div id="login-url-box" style="display:none;margin-top:12px;">
1209
- <div style="font-size:13px;color:#e5e5e5;margin-bottom:6px;font-weight:600;">Step 1: Open this link to authenticate</div>
1210
- <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>
1211
- <div style="font-size:11px;color:#666;margin-top:4px;word-break:break-all;font-family:monospace;" id="login-url-raw"></div>
1202
+ <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>
1203
+ <span style="color:#e5e5e5;font-size:13px;font-weight:600;">Waiting for login...</span>
1212
1204
  </div>
1213
- <div id="auth-code-box" style="display:none;margin-top:12px;">
1214
- <div style="font-size:13px;color:#e5e5e5;margin-bottom:6px;font-weight:600;">Step 2: Paste the authorization code</div>
1215
- <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>
1216
- <div style="display:flex;gap:8px;">
1217
- <input id="auth-code-input" type="text" placeholder="Paste authorization code here"
1218
- 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;" />
1219
- <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;">
1220
- Submit Code
1221
- </button>
1222
- </div>
1223
- <div id="auth-code-error" style="display:none;margin-top:6px;font-size:12px;color:#ef4444;"></div>
1224
- </div>
1225
- <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>
1205
+ <div style="font-size:13px;color:#a3a3a3;">A terminal window has been opened. Please complete the Firebase login there.</div>
1206
+ <div style="font-size:12px;color:#666;margin-top:6px;">This page will update automatically when login is detected.</div>
1226
1207
  </div>
1227
1208
  <div id="login-result" style="display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;"></div>
1228
1209
  </div>
@@ -1469,14 +1450,14 @@ function generateDashboardHtml(options) {
1469
1450
  document.getElementById('step2-action').style.display = 'block';
1470
1451
  document.getElementById('login-btn').style.display = 'none';
1471
1452
  document.getElementById('reauth-btn').style.display = 'inline-block';
1472
- document.getElementById('cancel-login-btn').style.display = 'none';
1453
+ document.getElementById('login-waiting').style.display = 'none';
1473
1454
  } else {
1474
1455
  document.getElementById('step2-icon').textContent = RED;
1475
1456
  document.getElementById('step2-detail').textContent = 'Not logged in to Firebase';
1476
1457
  document.getElementById('step2-action').style.display = 'block';
1477
1458
  document.getElementById('login-btn').style.display = 'inline-block';
1478
1459
  document.getElementById('reauth-btn').style.display = 'none';
1479
- document.getElementById('cancel-login-btn').style.display = 'none';
1460
+ document.getElementById('login-waiting').style.display = 'none';
1480
1461
  }
1481
1462
 
1482
1463
  if (status.nextStep === 'login') {
@@ -1545,22 +1526,17 @@ function generateDashboardHtml(options) {
1545
1526
  });
1546
1527
  };
1547
1528
 
1548
- // \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
1529
+ // \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
1549
1530
  window.startFirebaseLogin = function(reauth) {
1550
1531
  var loginBtn = document.getElementById('login-btn');
1551
1532
  var reauthBtn = document.getElementById('reauth-btn');
1552
- var cancelBtn = document.getElementById('cancel-login-btn');
1553
- var progress = document.getElementById('login-progress');
1533
+ var waiting = document.getElementById('login-waiting');
1554
1534
  var result = document.getElementById('login-result');
1555
1535
 
1556
1536
  loginBtn.style.display = 'none';
1557
1537
  reauthBtn.style.display = 'none';
1558
- cancelBtn.style.display = 'inline-block';
1559
- progress.style.display = 'block';
1538
+ waiting.style.display = 'block';
1560
1539
  result.style.display = 'none';
1561
- document.getElementById('login-url-box').style.display = 'none';
1562
- document.getElementById('auth-code-box').style.display = 'none';
1563
- document.getElementById('login-spinner-text').textContent = 'Starting login...';
1564
1540
 
1565
1541
  fetch(API + '/__dev/setup/login', {
1566
1542
  method: 'POST',
@@ -1568,129 +1544,45 @@ function generateDashboardHtml(options) {
1568
1544
  body: JSON.stringify({ reauth: !!reauth })
1569
1545
  })
1570
1546
  .then(function(r) { return r.json(); })
1571
- .then(function() {
1572
- // Start polling login state
1547
+ .then(function(data) {
1548
+ if (!data.success) {
1549
+ waiting.style.display = 'none';
1550
+ result.textContent = data.message;
1551
+ 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;';
1552
+ loginBtn.style.display = 'inline-block';
1553
+ return;
1554
+ }
1555
+ // Poll setup status until login is detected
1573
1556
  startLoginPoll();
1574
1557
  })
1575
1558
  .catch(function(err) {
1576
- progress.style.display = 'none';
1577
- result.textContent = 'Failed to start login: ' + err.message;
1559
+ waiting.style.display = 'none';
1560
+ result.textContent = 'Failed: ' + err.message;
1578
1561
  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;';
1579
1562
  loginBtn.style.display = 'inline-block';
1580
- cancelBtn.style.display = 'none';
1581
1563
  });
1582
1564
  };
1583
1565
 
1584
1566
  function startLoginPoll() {
1585
1567
  if (loginPollTimer) clearInterval(loginPollTimer);
1586
1568
  loginPollTimer = setInterval(function() {
1587
- fetch(API + '/__dev/setup/login-state')
1569
+ fetch(API + '/__dev/setup/status')
1588
1570
  .then(function(r) { return r.json(); })
1589
- .then(function(state) {
1590
- // Show auth URL if available
1591
- if (state.authUrl) {
1592
- var urlBox = document.getElementById('login-url-box');
1593
- var urlLink = document.getElementById('login-url-link');
1594
- var urlRaw = document.getElementById('login-url-raw');
1595
- urlBox.style.display = 'block';
1596
- urlLink.href = state.authUrl;
1597
- urlLink.textContent = 'Open Google Login';
1598
- if (urlRaw) urlRaw.textContent = state.authUrl;
1599
- document.getElementById('login-spinner-text').textContent = 'Waiting for authentication...';
1600
- }
1601
-
1602
- // Show auth code input when waiting for code
1603
- if (state.waitingForCode) {
1604
- document.getElementById('auth-code-box').style.display = 'block';
1605
- }
1606
-
1607
- // Show output log
1608
- if (state.output) {
1609
- var outputEl = document.getElementById('login-output');
1610
- outputEl.style.display = 'block';
1611
- outputEl.textContent = state.output.slice(-500);
1612
- }
1613
-
1614
- // Check if done
1615
- if (state.completed) {
1571
+ .then(function(status) {
1572
+ if (status.auth.authenticated) {
1616
1573
  clearInterval(loginPollTimer);
1617
1574
  loginPollTimer = null;
1618
- document.getElementById('login-progress').style.display = 'none';
1619
- document.getElementById('cancel-login-btn').style.display = 'none';
1575
+ document.getElementById('login-waiting').style.display = 'none';
1620
1576
  var result = document.getElementById('login-result');
1621
- result.textContent = 'Login successful!';
1577
+ result.textContent = 'Login successful! Logged in as ' + status.auth.user;
1622
1578
  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;';
1623
- // Refresh wizard after short delay
1624
1579
  setTimeout(refreshSetupStatus, 1500);
1625
1580
  }
1626
-
1627
- if (state.error) {
1628
- clearInterval(loginPollTimer);
1629
- loginPollTimer = null;
1630
- document.getElementById('login-progress').style.display = 'none';
1631
- document.getElementById('cancel-login-btn').style.display = 'none';
1632
- var result2 = document.getElementById('login-result');
1633
- result2.textContent = state.error;
1634
- 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;';
1635
- document.getElementById('login-btn').style.display = 'inline-block';
1636
- }
1637
-
1638
- if (!state.active && !state.completed && !state.error) {
1639
- clearInterval(loginPollTimer);
1640
- loginPollTimer = null;
1641
- }
1642
1581
  })
1643
1582
  .catch(function() {});
1644
- }, 1500);
1583
+ }, 3000);
1645
1584
  }
1646
1585
 
1647
- window.submitAuthCode = function() {
1648
- var input = document.getElementById('auth-code-input');
1649
- var errEl = document.getElementById('auth-code-error');
1650
- var btn = document.getElementById('auth-code-submit');
1651
- var code = input ? input.value.trim() : '';
1652
- if (!code) {
1653
- errEl.textContent = 'Please paste the authorization code.';
1654
- errEl.style.display = 'block';
1655
- return;
1656
- }
1657
- errEl.style.display = 'none';
1658
- btn.disabled = true;
1659
- btn.textContent = 'Submitting...';
1660
-
1661
- fetch(API + '/__dev/setup/submit-auth-code', {
1662
- method: 'POST',
1663
- headers: { 'Content-Type': 'application/json' },
1664
- body: JSON.stringify({ code: code })
1665
- })
1666
- .then(function(r) { return r.json(); })
1667
- .then(function(data) {
1668
- if (data.success) {
1669
- document.getElementById('login-spinner-text').textContent = 'Verifying authorization...';
1670
- document.getElementById('auth-code-box').style.display = 'none';
1671
- } else {
1672
- errEl.textContent = data.message || 'Failed to submit code.';
1673
- errEl.style.display = 'block';
1674
- }
1675
- btn.disabled = false;
1676
- btn.textContent = 'Submit Code';
1677
- })
1678
- .catch(function(err) {
1679
- errEl.textContent = 'Error: ' + err.message;
1680
- errEl.style.display = 'block';
1681
- btn.disabled = false;
1682
- btn.textContent = 'Submit Code';
1683
- });
1684
- };
1685
-
1686
- window.cancelFirebaseLogin = function() {
1687
- if (loginPollTimer) { clearInterval(loginPollTimer); loginPollTimer = null; }
1688
- fetch(API + '/__dev/setup/cancel-login', { method: 'POST' }).catch(function() {});
1689
- document.getElementById('login-progress').style.display = 'none';
1690
- document.getElementById('cancel-login-btn').style.display = 'none';
1691
- document.getElementById('login-btn').style.display = 'inline-block';
1692
- };
1693
-
1694
1586
  // \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
1695
1587
  function loadProjectList(currentProjectId) {
1696
1588
  var select = document.getElementById('project-select');
@@ -2155,21 +2047,11 @@ function generateDashboardHtml(options) {
2155
2047
 
2156
2048
  // src/dev/firebase-setup.ts
2157
2049
  import { execFile as execFile2, spawn } from "child_process";
2158
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync } from "fs";
2159
- import { resolve as resolve4, join as join3, dirname as dirname2 } from "path";
2160
- import { homedir } from "os";
2050
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
2051
+ import { resolve as resolve4, join as join3 } from "path";
2052
+ import { tmpdir, platform } from "os";
2161
2053
  var FirebaseSetup = class {
2162
2054
  projectDir;
2163
- loginProcess = null;
2164
- loginSession = {
2165
- active: false,
2166
- authUrl: null,
2167
- waitingForCode: false,
2168
- completed: false,
2169
- error: null,
2170
- output: ""
2171
- };
2172
- loginTimeout = null;
2173
2055
  constructor(projectDir) {
2174
2056
  this.projectDir = projectDir;
2175
2057
  }
@@ -2249,149 +2131,89 @@ var FirebaseSetup = class {
2249
2131
  return { success: false, message: `Failed to install Firebase CLI: ${msg}` };
2250
2132
  }
2251
2133
  }
2252
- // ─── Analytics Pre-config ──────────────────────────────────────────
2253
- /**
2254
- * Pre-configure Firebase analytics preference to avoid the interactive
2255
- * "Allow Firebase to collect..." prompt that breaks non-TTY spawns.
2256
- */
2257
- ensureAnalyticsConfigured() {
2258
- try {
2259
- const home = homedir();
2260
- const configPath = join3(home, ".config", "configstore", "firebase-tools.json");
2261
- if (existsSync5(configPath)) {
2262
- const config = JSON.parse(readFileSync4(configPath, "utf-8"));
2263
- if (config.usage === void 0) {
2264
- config.usage = false;
2265
- writeFileSync2(configPath, JSON.stringify(config, null, 2), "utf-8");
2266
- }
2267
- } else {
2268
- const dir = dirname2(configPath);
2269
- mkdirSync(dir, { recursive: true });
2270
- writeFileSync2(configPath, JSON.stringify({ usage: false }, null, 2), "utf-8");
2271
- }
2272
- } catch {
2273
- }
2274
- }
2275
- // ─── Login Flow ────────────────────────────────────────────────────
2134
+ // ─── Login via Terminal ───────────────────────────────────────────
2276
2135
  /**
2277
- * Start login using `firebase login --no-localhost`.
2136
+ * Opens a new terminal window and runs `firebase login`.
2278
2137
  *
2279
- * This mode is designed for non-TTY environments:
2280
- * 1. Prints a Google OAuth URL
2281
- * 2. User opens URL in browser and authenticates
2282
- * 3. Google shows an authorization code on screen
2283
- * 4. User pastes code back call submitAuthCode()
2138
+ * Firebase CLI requires a real TTY for interactive login.
2139
+ * Instead of trying to fake a TTY, we open an actual terminal.
2140
+ * The dashboard polls getStatus() to detect when login completes.
2141
+ *
2142
+ * macOS: Creates a .command file and opens it (Terminal.app)
2143
+ * Linux: Tries common terminal emulators
2144
+ * Windows: Opens a new cmd window
2284
2145
  */
2285
- startLogin(reauth = false) {
2286
- this.cleanupLogin();
2287
- this.ensureAnalyticsConfigured();
2288
- this.loginSession = {
2289
- active: true,
2290
- authUrl: null,
2291
- waitingForCode: false,
2292
- completed: false,
2293
- error: null,
2294
- output: ""
2295
- };
2146
+ openLoginTerminal(reauth = false) {
2147
+ const cmd = reauth ? "firebase login --reauth" : "firebase login";
2148
+ const os = platform();
2296
2149
  try {
2297
- const args = reauth ? ["login", "--reauth", "--no-localhost"] : ["login", "--no-localhost"];
2298
- const proc = spawn("firebase", args, {
2299
- cwd: this.projectDir,
2300
- stdio: ["pipe", "pipe", "pipe"],
2301
- env: { ...process.env, TERM: "dumb" }
2302
- });
2303
- this.loginProcess = proc;
2304
- const appendOutput = (data) => {
2305
- const text = data.toString();
2306
- this.loginSession.output += text;
2307
- const urlMatch = text.match(/(https:\/\/accounts\.google\.com\S+)/);
2308
- if (urlMatch) {
2309
- this.loginSession.authUrl = urlMatch[1];
2310
- this.loginSession.waitingForCode = true;
2311
- }
2312
- if (text.includes("Success") || text.includes("Logged in as")) {
2313
- this.loginSession.completed = true;
2314
- this.loginSession.active = false;
2315
- this.loginSession.waitingForCode = false;
2316
- }
2317
- if (text.includes("Allow Firebase to collect") || text.includes("(Y/n)")) {
2150
+ if (os === "darwin") {
2151
+ const scriptPath = join3(tmpdir(), "clawfire-firebase-login.command");
2152
+ writeFileSync2(scriptPath, [
2153
+ "#!/bin/bash",
2154
+ `cd "${this.projectDir}"`,
2155
+ cmd,
2156
+ 'echo ""',
2157
+ 'echo "Login complete! You can close this window."',
2158
+ 'read -p "Press Enter to close..."'
2159
+ ].join("\n"), { mode: 493 });
2160
+ const child = spawn("open", [scriptPath], { detached: true, stdio: "ignore" });
2161
+ child.unref();
2162
+ } else if (os === "win32") {
2163
+ const child = spawn("cmd", ["/c", "start", "cmd", "/k", cmd], {
2164
+ cwd: this.projectDir,
2165
+ detached: true,
2166
+ stdio: "ignore"
2167
+ });
2168
+ child.unref();
2169
+ } else {
2170
+ const scriptPath = join3(tmpdir(), "clawfire-firebase-login.sh");
2171
+ writeFileSync2(scriptPath, [
2172
+ "#!/bin/bash",
2173
+ `cd "${this.projectDir}"`,
2174
+ cmd,
2175
+ 'echo ""',
2176
+ 'echo "Login complete! You can close this window."',
2177
+ 'read -p "Press Enter to close..."'
2178
+ ].join("\n"), { mode: 493 });
2179
+ const terminals = [
2180
+ { cmd: "x-terminal-emulator", args: ["-e", scriptPath] },
2181
+ { cmd: "gnome-terminal", args: ["--", "bash", scriptPath] },
2182
+ { cmd: "konsole", args: ["-e", "bash", scriptPath] },
2183
+ { cmd: "xfce4-terminal", args: ["-e", scriptPath] },
2184
+ { cmd: "xterm", args: ["-e", scriptPath] }
2185
+ ];
2186
+ let opened = false;
2187
+ for (const t of terminals) {
2318
2188
  try {
2319
- proc.stdin?.write("n\n");
2189
+ const child = spawn(t.cmd, t.args, { detached: true, stdio: "ignore" });
2190
+ child.unref();
2191
+ child.on("error", () => {
2192
+ });
2193
+ opened = true;
2194
+ break;
2320
2195
  } catch {
2196
+ continue;
2321
2197
  }
2322
2198
  }
2323
- };
2324
- proc.stdout?.on("data", appendOutput);
2325
- proc.stderr?.on("data", appendOutput);
2326
- proc.on("close", (code) => {
2327
- this.loginSession.active = false;
2328
- this.loginSession.waitingForCode = false;
2329
- if (code === 0) {
2330
- this.loginSession.completed = true;
2331
- } else if (!this.loginSession.completed) {
2332
- const lines = this.loginSession.output.trim().split("\n").filter((l) => l.trim());
2333
- const lastLines = lines.slice(-3).join(" | ");
2334
- this.loginSession.error = `Login failed (exit code ${code}).` + (lastLines ? ` Details: ${lastLines}` : "") + ` Try running "firebase login" in your terminal.`;
2335
- }
2336
- this.loginProcess = null;
2337
- });
2338
- proc.on("error", (err) => {
2339
- this.loginSession.active = false;
2340
- this.loginSession.error = err.message;
2341
- this.loginProcess = null;
2342
- });
2343
- this.loginTimeout = setTimeout(() => {
2344
- if (this.loginSession.active) {
2345
- this.loginSession.error = "Login timed out (5 minutes). Please try again.";
2346
- this.cleanupLogin();
2199
+ if (!opened) {
2200
+ return {
2201
+ success: false,
2202
+ message: `Could not find a terminal emulator. Please run "${cmd}" manually in your terminal.`
2203
+ };
2347
2204
  }
2348
- }, 5 * 60 * 1e3);
2349
- } catch (err) {
2350
- this.loginSession.active = false;
2351
- this.loginSession.error = err instanceof Error ? err.message : "Failed to start login";
2352
- }
2353
- return this.loginSession;
2354
- }
2355
- getLoginState() {
2356
- return { ...this.loginSession };
2357
- }
2358
- /**
2359
- * Submit the authorization code that the user copied from the browser.
2360
- * This writes the code to the Firebase CLI process stdin.
2361
- */
2362
- submitAuthCode(code) {
2363
- if (!this.loginProcess || !this.loginProcess.stdin) {
2364
- return { success: false, message: "No active login process. Please start login again." };
2365
- }
2366
- if (!code || !code.trim()) {
2367
- return { success: false, message: "Authorization code is required." };
2368
- }
2369
- try {
2370
- this.loginProcess.stdin.write(code.trim() + "\n");
2371
- this.loginSession.waitingForCode = false;
2372
- return { success: true, message: "Authorization code submitted. Waiting for verification..." };
2373
- } catch (err) {
2374
- return { success: false, message: "Failed to submit code: " + (err instanceof Error ? err.message : "unknown error") };
2375
- }
2376
- }
2377
- cancelLogin() {
2378
- this.cleanupLogin();
2379
- this.loginSession.error = "Login cancelled by user.";
2380
- }
2381
- cleanupLogin() {
2382
- if (this.loginProcess) {
2383
- try {
2384
- this.loginProcess.kill("SIGTERM");
2385
- } catch {
2386
2205
  }
2387
- this.loginProcess = null;
2388
- }
2389
- if (this.loginTimeout) {
2390
- clearTimeout(this.loginTimeout);
2391
- this.loginTimeout = null;
2206
+ return {
2207
+ success: true,
2208
+ message: "Terminal window opened. Please complete the login in the new terminal."
2209
+ };
2210
+ } catch (err) {
2211
+ const msg = err instanceof Error ? err.message : "Unknown error";
2212
+ return {
2213
+ success: false,
2214
+ message: `Failed to open terminal: ${msg}. Please run "${cmd}" manually in your terminal.`
2215
+ };
2392
2216
  }
2393
- this.loginSession.active = false;
2394
- this.loginSession.waitingForCode = false;
2395
2217
  }
2396
2218
  // ─── Project Listing ───────────────────────────────────────────────
2397
2219
  async listProjects() {
@@ -2485,7 +2307,6 @@ var FirebaseSetup = class {
2485
2307
  }
2486
2308
  /** Cleanup resources */
2487
2309
  destroy() {
2488
- this.cleanupLogin();
2489
2310
  }
2490
2311
  };
2491
2312
 
@@ -3408,36 +3229,8 @@ ${liveReloadScript}
3408
3229
  reauth = !!data.reauth;
3409
3230
  } catch {
3410
3231
  }
3411
- const session = this.firebaseSetup.startLogin(reauth);
3412
- sendJson(session);
3413
- });
3414
- return;
3415
- }
3416
- if (url.pathname === "/__dev/setup/login-state" && req.method === "GET") {
3417
- sendJson(this.firebaseSetup.getLoginState());
3418
- return;
3419
- }
3420
- if (url.pathname === "/__dev/setup/cancel-login" && req.method === "POST") {
3421
- this.firebaseSetup.cancelLogin();
3422
- sendJson({ ok: true });
3423
- return;
3424
- }
3425
- if (url.pathname === "/__dev/setup/submit-auth-code" && req.method === "POST") {
3426
- let body = "";
3427
- req.on("data", (chunk) => {
3428
- body += chunk;
3429
- });
3430
- req.on("end", () => {
3431
- try {
3432
- const data = JSON.parse(body);
3433
- if (!data.code) {
3434
- sendJson({ success: false, message: "code is required" }, 400);
3435
- return;
3436
- }
3437
- sendJson(this.firebaseSetup.submitAuthCode(data.code));
3438
- } catch {
3439
- sendJson({ success: false, message: "Invalid JSON body" }, 400);
3440
- }
3232
+ const result = this.firebaseSetup.openLoginTerminal(reauth);
3233
+ sendJson(result);
3441
3234
  });
3442
3235
  return;
3443
3236
  }