clawfire 0.4.0 → 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-MMWHTL5T.js");
212
+ const { startDevServer } = await import("./dev-server-65H4AQFS.js");
213
213
  await startDevServer({
214
214
  projectDir,
215
215
  port,
@@ -1189,27 +1189,21 @@ function generateDashboardHtml(options) {
1189
1189
  </div>
1190
1190
  </div>
1191
1191
  <div id="step2-action" style="display:none;">
1192
- <button id="login-btn" onclick="startFirebaseLogin()" style="padding:8px 20px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">
1192
+ <button id="login-btn" onclick="startFirebaseLogin(false)" style="padding:8px 20px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">
1193
1193
  Login to Firebase
1194
1194
  </button>
1195
- <button id="reauth-btn" onclick="startFirebaseLogin()" style="display:none;padding:8px 20px;background:#eab308;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">
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 style="color:#e5e5e5;font-size:13px;font-weight:600;">Waiting for authentication...</span>
1207
- </div>
1208
- <div id="login-url-box" style="display:none;margin-top:8px;">
1209
- <div style="font-size:12px;color:#a3a3a3;margin-bottom:4px;">If the browser didn't open automatically, click the link below:</div>
1210
- <a id="login-url-link" href="#" target="_blank" rel="noopener" style="color:#3b82f6;font-size:13px;word-break:break-all;font-family:monospace;"></a>
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>
1211
1204
  </div>
1212
- <div id="login-output" style="display:none;margin-top:8px;max-height:100px;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>
1213
1207
  </div>
1214
1208
  <div id="login-result" style="display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;"></div>
1215
1209
  </div>
@@ -1260,7 +1254,7 @@ function generateDashboardHtml(options) {
1260
1254
  <div id="setup-done" style="display:none;padding:16px;border-radius:8px;background:#0a1a0a;border:1px solid #22c55e;text-align:center;">
1261
1255
  <div style="font-size:16px;color:#22c55e;font-weight:700;margin-bottom:4px;">Setup Complete</div>
1262
1256
  <div id="setup-done-detail" style="font-size:13px;color:#a3a3a3;"></div>
1263
- <button onclick="startFirebaseLogin()" style="margin-top:8px;padding:6px 14px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:12px;cursor:pointer;">Re-authenticate</button>
1257
+ <button onclick="startFirebaseLogin(true)" style="margin-top:8px;padding:6px 14px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:12px;cursor:pointer;">Re-authenticate</button>
1264
1258
  </div>
1265
1259
  </div>
1266
1260
  </div>
@@ -1456,14 +1450,14 @@ function generateDashboardHtml(options) {
1456
1450
  document.getElementById('step2-action').style.display = 'block';
1457
1451
  document.getElementById('login-btn').style.display = 'none';
1458
1452
  document.getElementById('reauth-btn').style.display = 'inline-block';
1459
- document.getElementById('cancel-login-btn').style.display = 'none';
1453
+ document.getElementById('login-waiting').style.display = 'none';
1460
1454
  } else {
1461
1455
  document.getElementById('step2-icon').textContent = RED;
1462
1456
  document.getElementById('step2-detail').textContent = 'Not logged in to Firebase';
1463
1457
  document.getElementById('step2-action').style.display = 'block';
1464
1458
  document.getElementById('login-btn').style.display = 'inline-block';
1465
1459
  document.getElementById('reauth-btn').style.display = 'none';
1466
- document.getElementById('cancel-login-btn').style.display = 'none';
1460
+ document.getElementById('login-waiting').style.display = 'none';
1467
1461
  }
1468
1462
 
1469
1463
  if (status.nextStep === 'login') {
@@ -1532,99 +1526,63 @@ function generateDashboardHtml(options) {
1532
1526
  });
1533
1527
  };
1534
1528
 
1535
- // \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
1536
- window.startFirebaseLogin = function() {
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
1530
+ window.startFirebaseLogin = function(reauth) {
1537
1531
  var loginBtn = document.getElementById('login-btn');
1538
1532
  var reauthBtn = document.getElementById('reauth-btn');
1539
- var cancelBtn = document.getElementById('cancel-login-btn');
1540
- var progress = document.getElementById('login-progress');
1533
+ var waiting = document.getElementById('login-waiting');
1541
1534
  var result = document.getElementById('login-result');
1542
1535
 
1543
1536
  loginBtn.style.display = 'none';
1544
1537
  reauthBtn.style.display = 'none';
1545
- cancelBtn.style.display = 'inline-block';
1546
- progress.style.display = 'block';
1538
+ waiting.style.display = 'block';
1547
1539
  result.style.display = 'none';
1548
- document.getElementById('login-url-box').style.display = 'none';
1549
1540
 
1550
- fetch(API + '/__dev/setup/login', { method: 'POST' })
1541
+ fetch(API + '/__dev/setup/login', {
1542
+ method: 'POST',
1543
+ headers: { 'Content-Type': 'application/json' },
1544
+ body: JSON.stringify({ reauth: !!reauth })
1545
+ })
1551
1546
  .then(function(r) { return r.json(); })
1552
- .then(function() {
1553
- // 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
1554
1556
  startLoginPoll();
1555
1557
  })
1556
1558
  .catch(function(err) {
1557
- progress.style.display = 'none';
1558
- result.textContent = 'Failed to start login: ' + err.message;
1559
+ waiting.style.display = 'none';
1560
+ result.textContent = 'Failed: ' + err.message;
1559
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;';
1560
1562
  loginBtn.style.display = 'inline-block';
1561
- cancelBtn.style.display = 'none';
1562
1563
  });
1563
1564
  };
1564
1565
 
1565
1566
  function startLoginPoll() {
1566
1567
  if (loginPollTimer) clearInterval(loginPollTimer);
1567
1568
  loginPollTimer = setInterval(function() {
1568
- fetch(API + '/__dev/setup/login-state')
1569
+ fetch(API + '/__dev/setup/status')
1569
1570
  .then(function(r) { return r.json(); })
1570
- .then(function(state) {
1571
- // Show auth URL if available
1572
- if (state.authUrl) {
1573
- var urlBox = document.getElementById('login-url-box');
1574
- var urlLink = document.getElementById('login-url-link');
1575
- urlBox.style.display = 'block';
1576
- urlLink.href = state.authUrl;
1577
- urlLink.textContent = state.authUrl.length > 80 ? state.authUrl.slice(0, 80) + '...' : state.authUrl;
1578
- }
1579
-
1580
- // Show output log
1581
- if (state.output) {
1582
- var outputEl = document.getElementById('login-output');
1583
- outputEl.style.display = 'block';
1584
- outputEl.textContent = state.output.slice(-500);
1585
- }
1586
-
1587
- // Check if done
1588
- if (state.completed) {
1571
+ .then(function(status) {
1572
+ if (status.auth.authenticated) {
1589
1573
  clearInterval(loginPollTimer);
1590
1574
  loginPollTimer = null;
1591
- document.getElementById('login-progress').style.display = 'none';
1592
- document.getElementById('cancel-login-btn').style.display = 'none';
1575
+ document.getElementById('login-waiting').style.display = 'none';
1593
1576
  var result = document.getElementById('login-result');
1594
- result.textContent = 'Login successful!';
1577
+ result.textContent = 'Login successful! Logged in as ' + status.auth.user;
1595
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;';
1596
- // Refresh wizard after short delay
1597
1579
  setTimeout(refreshSetupStatus, 1500);
1598
1580
  }
1599
-
1600
- if (state.error) {
1601
- clearInterval(loginPollTimer);
1602
- loginPollTimer = null;
1603
- document.getElementById('login-progress').style.display = 'none';
1604
- document.getElementById('cancel-login-btn').style.display = 'none';
1605
- var result2 = document.getElementById('login-result');
1606
- result2.textContent = state.error;
1607
- 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;';
1608
- document.getElementById('login-btn').style.display = 'inline-block';
1609
- }
1610
-
1611
- if (!state.active && !state.completed && !state.error) {
1612
- clearInterval(loginPollTimer);
1613
- loginPollTimer = null;
1614
- }
1615
1581
  })
1616
1582
  .catch(function() {});
1617
- }, 1500);
1583
+ }, 3000);
1618
1584
  }
1619
1585
 
1620
- window.cancelFirebaseLogin = function() {
1621
- if (loginPollTimer) { clearInterval(loginPollTimer); loginPollTimer = null; }
1622
- fetch(API + '/__dev/setup/cancel-login', { method: 'POST' }).catch(function() {});
1623
- document.getElementById('login-progress').style.display = 'none';
1624
- document.getElementById('cancel-login-btn').style.display = 'none';
1625
- document.getElementById('login-btn').style.display = 'inline-block';
1626
- };
1627
-
1628
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
1629
1587
  function loadProjectList(currentProjectId) {
1630
1588
  var select = document.getElementById('project-select');
@@ -2090,18 +2048,10 @@ function generateDashboardHtml(options) {
2090
2048
  // src/dev/firebase-setup.ts
2091
2049
  import { execFile as execFile2, spawn } from "child_process";
2092
2050
  import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
2093
- import { resolve as resolve4 } from "path";
2051
+ import { resolve as resolve4, join as join3 } from "path";
2052
+ import { tmpdir, platform } from "os";
2094
2053
  var FirebaseSetup = class {
2095
2054
  projectDir;
2096
- loginProcess = null;
2097
- loginSession = {
2098
- active: false,
2099
- authUrl: null,
2100
- completed: false,
2101
- error: null,
2102
- output: ""
2103
- };
2104
- loginTimeout = null;
2105
2055
  constructor(projectDir) {
2106
2056
  this.projectDir = projectDir;
2107
2057
  }
@@ -2164,7 +2114,7 @@ var FirebaseSetup = class {
2164
2114
  return { success: true, message: "Firebase CLI is already installed." };
2165
2115
  } catch {
2166
2116
  }
2167
- const output = await this.execTimeout(
2117
+ await this.execTimeout(
2168
2118
  "npm",
2169
2119
  ["install", "-g", "firebase-tools"],
2170
2120
  12e4
@@ -2181,93 +2131,89 @@ var FirebaseSetup = class {
2181
2131
  return { success: false, message: `Failed to install Firebase CLI: ${msg}` };
2182
2132
  }
2183
2133
  }
2184
- // ─── Login Flow ────────────────────────────────────────────────────
2185
- startLogin() {
2186
- this.cleanupLogin();
2187
- this.loginSession = {
2188
- active: true,
2189
- authUrl: null,
2190
- completed: false,
2191
- error: null,
2192
- output: ""
2193
- };
2134
+ // ─── Login via Terminal ───────────────────────────────────────────
2135
+ /**
2136
+ * Opens a new terminal window and runs `firebase login`.
2137
+ *
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
2145
+ */
2146
+ openLoginTerminal(reauth = false) {
2147
+ const cmd = reauth ? "firebase login --reauth" : "firebase login";
2148
+ const os = platform();
2194
2149
  try {
2195
- const proc = spawn("firebase", ["login", "--reauth"], {
2196
- cwd: this.projectDir,
2197
- stdio: ["pipe", "pipe", "pipe"],
2198
- env: { ...process.env }
2199
- });
2200
- this.loginProcess = proc;
2201
- const appendOutput = (data) => {
2202
- const text = data.toString();
2203
- this.loginSession.output += text;
2204
- const urlMatch = text.match(/(https:\/\/accounts\.google\.com\S+)/);
2205
- if (urlMatch) {
2206
- this.loginSession.authUrl = urlMatch[1];
2207
- }
2208
- const localhostMatch = text.match(/(http:\/\/localhost:\d+\S*)/);
2209
- if (localhostMatch && !this.loginSession.authUrl) {
2210
- this.loginSession.authUrl = localhostMatch[1];
2211
- }
2212
- if (text.includes("Success") || text.includes("Logged in as") || text.includes("\u2714")) {
2213
- this.loginSession.completed = true;
2214
- this.loginSession.active = false;
2215
- }
2216
- if (text.includes("Allow Firebase to collect") || text.includes("analytics")) {
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) {
2217
2188
  try {
2218
- 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;
2219
2195
  } catch {
2196
+ continue;
2220
2197
  }
2221
2198
  }
2222
- };
2223
- proc.stdout?.on("data", appendOutput);
2224
- proc.stderr?.on("data", appendOutput);
2225
- proc.on("close", (code) => {
2226
- this.loginSession.active = false;
2227
- if (code === 0) {
2228
- this.loginSession.completed = true;
2229
- } else if (!this.loginSession.completed) {
2230
- this.loginSession.error = `Login process exited with code ${code}`;
2231
- }
2232
- this.loginProcess = null;
2233
- });
2234
- proc.on("error", (err) => {
2235
- this.loginSession.active = false;
2236
- this.loginSession.error = err.message;
2237
- this.loginProcess = null;
2238
- });
2239
- this.loginTimeout = setTimeout(() => {
2240
- if (this.loginSession.active) {
2241
- this.loginSession.error = "Login timed out (5 minutes). Please try again.";
2242
- 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
+ };
2243
2204
  }
2244
- }, 5 * 60 * 1e3);
2245
- } catch (err) {
2246
- this.loginSession.active = false;
2247
- this.loginSession.error = err instanceof Error ? err.message : "Failed to start login";
2248
- }
2249
- return this.loginSession;
2250
- }
2251
- getLoginState() {
2252
- return { ...this.loginSession };
2253
- }
2254
- cancelLogin() {
2255
- this.cleanupLogin();
2256
- this.loginSession.error = "Login cancelled by user.";
2257
- }
2258
- cleanupLogin() {
2259
- if (this.loginProcess) {
2260
- try {
2261
- this.loginProcess.kill("SIGTERM");
2262
- } catch {
2263
2205
  }
2264
- this.loginProcess = null;
2265
- }
2266
- if (this.loginTimeout) {
2267
- clearTimeout(this.loginTimeout);
2268
- 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
+ };
2269
2216
  }
2270
- this.loginSession.active = false;
2271
2217
  }
2272
2218
  // ─── Project Listing ───────────────────────────────────────────────
2273
2219
  async listProjects() {
@@ -2275,7 +2221,8 @@ var FirebaseSetup = class {
2275
2221
  const output = await this.execTimeout(
2276
2222
  "firebase",
2277
2223
  ["projects:list", "--json"],
2278
- 15e3
2224
+ 3e4
2225
+ // 30s — can be slow on first call
2279
2226
  );
2280
2227
  const data = JSON.parse(output);
2281
2228
  if (data?.result && Array.isArray(data.result)) {
@@ -2290,8 +2237,11 @@ var FirebaseSetup = class {
2290
2237
  return { projects: [], error: "Unexpected response format" };
2291
2238
  } catch (err) {
2292
2239
  const msg = err instanceof Error ? err.message : "Unknown error";
2293
- if (msg.includes("authenticate") || msg.includes("login")) {
2294
- return { projects: [], error: "Not authenticated. Please login first." };
2240
+ if (msg.includes("authenticate") || msg.includes("login") || msg.includes("credential")) {
2241
+ return { projects: [], error: "Session expired. Please re-authenticate first." };
2242
+ }
2243
+ if (msg.includes("Timeout")) {
2244
+ return { projects: [], error: "Request timed out. Please try again." };
2295
2245
  }
2296
2246
  return { projects: [], error: msg };
2297
2247
  }
@@ -2322,7 +2272,7 @@ var FirebaseSetup = class {
2322
2272
  rc.projects.default = projectId;
2323
2273
  writeFileSync2(firebasercPath, JSON.stringify(rc, null, 2) + "\n", "utf-8");
2324
2274
  return { success: true, message: `Active project set to "${projectId}".` };
2325
- } catch (err) {
2275
+ } catch {
2326
2276
  try {
2327
2277
  const firebasercPath = resolve4(this.projectDir, ".firebaserc");
2328
2278
  const rc = {
@@ -2331,7 +2281,7 @@ var FirebaseSetup = class {
2331
2281
  writeFileSync2(firebasercPath, JSON.stringify(rc, null, 2) + "\n", "utf-8");
2332
2282
  return { success: true, message: `Active project set to "${projectId}" (via .firebaserc).` };
2333
2283
  } catch (writeErr) {
2334
- const msg = err instanceof Error ? err.message : "Unknown error";
2284
+ const msg = writeErr instanceof Error ? writeErr.message : "Unknown error";
2335
2285
  return { success: false, message: `Failed to set project: ${msg}` };
2336
2286
  }
2337
2287
  }
@@ -2339,9 +2289,11 @@ var FirebaseSetup = class {
2339
2289
  // ─── Helpers ───────────────────────────────────────────────────────
2340
2290
  execTimeout(command, args, timeoutMs) {
2341
2291
  return new Promise((resolve6, reject) => {
2342
- const proc = execFile2(command, args, { cwd: this.projectDir, timeout: timeoutMs }, (err, stdout) => {
2292
+ const proc = execFile2(command, args, { cwd: this.projectDir, timeout: timeoutMs }, (err, stdout, stderr) => {
2343
2293
  if (err) {
2344
- reject(err);
2294
+ const detail = stderr?.trim() || stdout?.trim() || "";
2295
+ const enriched = new Error(`${err.message}${detail ? "\n" + detail : ""}`);
2296
+ reject(enriched);
2345
2297
  } else {
2346
2298
  resolve6(stdout);
2347
2299
  }
@@ -2355,7 +2307,6 @@ var FirebaseSetup = class {
2355
2307
  }
2356
2308
  /** Cleanup resources */
2357
2309
  destroy() {
2358
- this.cleanupLogin();
2359
2310
  }
2360
2311
  };
2361
2312
 
@@ -3267,17 +3218,20 @@ ${liveReloadScript}
3267
3218
  return;
3268
3219
  }
3269
3220
  if (url.pathname === "/__dev/setup/login" && req.method === "POST") {
3270
- const session = this.firebaseSetup.startLogin();
3271
- sendJson(session);
3272
- return;
3273
- }
3274
- if (url.pathname === "/__dev/setup/login-state" && req.method === "GET") {
3275
- sendJson(this.firebaseSetup.getLoginState());
3276
- return;
3277
- }
3278
- if (url.pathname === "/__dev/setup/cancel-login" && req.method === "POST") {
3279
- this.firebaseSetup.cancelLogin();
3280
- sendJson({ ok: true });
3221
+ let body = "";
3222
+ req.on("data", (chunk) => {
3223
+ body += chunk;
3224
+ });
3225
+ req.on("end", () => {
3226
+ let reauth = false;
3227
+ try {
3228
+ const data = JSON.parse(body);
3229
+ reauth = !!data.reauth;
3230
+ } catch {
3231
+ }
3232
+ const result = this.firebaseSetup.openLoginTerminal(reauth);
3233
+ sendJson(result);
3234
+ });
3281
3235
  return;
3282
3236
  }
3283
3237
  if (url.pathname === "/__dev/setup/projects" && req.method === "GET") {