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 +1 -1
- package/dist/{dev-server-MMWHTL5T.js → dev-server-65H4AQFS.js} +142 -188
- package/dist/dev.cjs +141 -187
- package/dist/dev.cjs.map +1 -1
- package/dist/dev.js +142 -188
- package/dist/dev.js.map +1 -1
- package/package.json +1 -1
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-
|
|
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
|
|
1203
|
-
<div id="login-
|
|
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
|
|
1206
|
-
<span style="color:#e5e5e5;font-size:13px;font-weight:600;">Waiting for
|
|
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
|
|
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('
|
|
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('
|
|
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
|
|
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
|
|
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
|
-
|
|
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', {
|
|
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
|
-
|
|
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
|
-
|
|
1558
|
-
result.textContent = 'Failed
|
|
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/
|
|
1569
|
+
fetch(API + '/__dev/setup/status')
|
|
1569
1570
|
.then(function(r) { return r.json(); })
|
|
1570
|
-
.then(function(
|
|
1571
|
-
|
|
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-
|
|
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
|
-
},
|
|
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
|
-
|
|
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
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
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
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
const
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
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
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
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") {
|