clawfire 0.4.4 → 0.5.0
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-XLMLGSQP.js → dev-server-5ATZVQJT.js} +369 -23
- package/dist/dev.cjs +369 -23
- package/dist/dev.cjs.map +1 -1
- package/dist/dev.js +369 -23
- package/dist/dev.js.map +1 -1
- package/package.json +1 -1
package/dist/dev.cjs
CHANGED
|
@@ -1494,7 +1494,23 @@ async function checkCli(projectDir) {
|
|
|
1494
1494
|
}
|
|
1495
1495
|
return result;
|
|
1496
1496
|
}
|
|
1497
|
-
async function fetchFirebaseSdkConfig(projectDir) {
|
|
1497
|
+
async function fetchFirebaseSdkConfig(projectDir, appId) {
|
|
1498
|
+
if (appId) {
|
|
1499
|
+
try {
|
|
1500
|
+
const output = await execWithTimeout(
|
|
1501
|
+
"firebase",
|
|
1502
|
+
["apps:sdkconfig", "web", appId, "--json"],
|
|
1503
|
+
projectDir,
|
|
1504
|
+
15e3
|
|
1505
|
+
);
|
|
1506
|
+
const config = parseSdkConfigOutput(output);
|
|
1507
|
+
if (config) return config;
|
|
1508
|
+
} catch (err) {
|
|
1509
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
1510
|
+
throw new Error(`Failed to fetch config for web app ${appId}: ${msg}`);
|
|
1511
|
+
}
|
|
1512
|
+
throw new Error("Could not parse Firebase SDK config from CLI output");
|
|
1513
|
+
}
|
|
1498
1514
|
try {
|
|
1499
1515
|
const output = await execWithTimeout(
|
|
1500
1516
|
"firebase",
|
|
@@ -1530,11 +1546,11 @@ async function fetchFirebaseSdkConfig(projectDir) {
|
|
|
1530
1546
|
"No web app registered in this Firebase project. Go to Firebase Console > Project Settings > General > Your apps > Add app (Web) to create one, then click Auto-fill again."
|
|
1531
1547
|
);
|
|
1532
1548
|
}
|
|
1533
|
-
const
|
|
1549
|
+
const firstAppId = webApps[0].appId;
|
|
1534
1550
|
try {
|
|
1535
1551
|
const output = await execWithTimeout(
|
|
1536
1552
|
"firebase",
|
|
1537
|
-
["apps:sdkconfig", "web",
|
|
1553
|
+
["apps:sdkconfig", "web", firstAppId, "--json"],
|
|
1538
1554
|
projectDir,
|
|
1539
1555
|
15e3
|
|
1540
1556
|
);
|
|
@@ -1542,7 +1558,7 @@ async function fetchFirebaseSdkConfig(projectDir) {
|
|
|
1542
1558
|
if (config) return config;
|
|
1543
1559
|
} catch (err) {
|
|
1544
1560
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
1545
|
-
throw new Error(`Failed to fetch config for web app ${
|
|
1561
|
+
throw new Error(`Failed to fetch config for web app ${firstAppId}: ${msg}`);
|
|
1546
1562
|
}
|
|
1547
1563
|
throw new Error("Could not parse Firebase SDK config from CLI output");
|
|
1548
1564
|
}
|
|
@@ -1611,17 +1627,20 @@ function generateDashboardHtml(options) {
|
|
|
1611
1627
|
|
|
1612
1628
|
<!-- Step indicators -->
|
|
1613
1629
|
<div id="setup-steps" style="display:flex;gap:0;margin-bottom:20px;overflow:hidden;border-radius:8px;border:1px solid #2a2a2a;">
|
|
1614
|
-
<div id="step-ind-1" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:
|
|
1615
|
-
1. CLI
|
|
1630
|
+
<div id="step-ind-1" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#1a1a2e;color:#f97316;border-right:1px solid #2a2a2a;">
|
|
1631
|
+
1. CLI
|
|
1616
1632
|
</div>
|
|
1617
|
-
<div id="step-ind-2" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:
|
|
1633
|
+
<div id="step-ind-2" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;border-right:1px solid #2a2a2a;">
|
|
1618
1634
|
2. Login
|
|
1619
1635
|
</div>
|
|
1620
|
-
<div id="step-ind-3" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:
|
|
1621
|
-
3.
|
|
1636
|
+
<div id="step-ind-3" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;border-right:1px solid #2a2a2a;">
|
|
1637
|
+
3. Project
|
|
1638
|
+
</div>
|
|
1639
|
+
<div id="step-ind-4" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;border-right:1px solid #2a2a2a;">
|
|
1640
|
+
4. Web App
|
|
1622
1641
|
</div>
|
|
1623
|
-
<div id="step-ind-
|
|
1624
|
-
|
|
1642
|
+
<div id="step-ind-5" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;">
|
|
1643
|
+
5. Auto-fill
|
|
1625
1644
|
</div>
|
|
1626
1645
|
</div>
|
|
1627
1646
|
|
|
@@ -1696,16 +1715,54 @@ function generateDashboardHtml(options) {
|
|
|
1696
1715
|
</div>
|
|
1697
1716
|
</div>
|
|
1698
1717
|
|
|
1699
|
-
<!-- Step 4:
|
|
1718
|
+
<!-- Step 4: Web App -->
|
|
1700
1719
|
<div id="step-4" style="display:none;">
|
|
1701
1720
|
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
|
|
1702
1721
|
<span id="step4-icon" style="font-size:20px;">⚪</span>
|
|
1722
|
+
<div>
|
|
1723
|
+
<div style="font-weight:600;color:#e5e5e5;font-size:15px;">Web App</div>
|
|
1724
|
+
<div id="step4-detail" style="font-size:13px;color:#a3a3a3;">Checking web apps...</div>
|
|
1725
|
+
</div>
|
|
1726
|
+
</div>
|
|
1727
|
+
<div id="step4-action" style="display:none;">
|
|
1728
|
+
<!-- Existing web apps list -->
|
|
1729
|
+
<div id="webapp-list" style="display:none;">
|
|
1730
|
+
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
|
1731
|
+
<select id="webapp-select" style="padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-size:13px;font-family:monospace;min-width:280px;outline:none;">
|
|
1732
|
+
<option value="">Loading web apps...</option>
|
|
1733
|
+
</select>
|
|
1734
|
+
<button id="select-webapp-btn" onclick="selectWebApp()" style="padding:8px 20px;background:#22c55e;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">
|
|
1735
|
+
Use This App
|
|
1736
|
+
</button>
|
|
1737
|
+
<button onclick="showCreateWebApp()" style="padding:8px 12px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:13px;cursor:pointer;">
|
|
1738
|
+
+ Create New
|
|
1739
|
+
</button>
|
|
1740
|
+
</div>
|
|
1741
|
+
</div>
|
|
1742
|
+
<!-- Create new web app form -->
|
|
1743
|
+
<div id="webapp-create" style="display:none;">
|
|
1744
|
+
<div style="font-size:13px;color:#a3a3a3;margin-bottom:8px;">No web app found. Create one to get Firebase SDK config:</div>
|
|
1745
|
+
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
|
1746
|
+
<input id="webapp-name" type="text" placeholder="My Web App" style="padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-size:13px;min-width:220px;outline:none;" />
|
|
1747
|
+
<button id="create-webapp-btn" onclick="createWebApp()" style="padding:8px 20px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">
|
|
1748
|
+
Create Web App
|
|
1749
|
+
</button>
|
|
1750
|
+
</div>
|
|
1751
|
+
</div>
|
|
1752
|
+
<div id="webapp-status" style="display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;"></div>
|
|
1753
|
+
</div>
|
|
1754
|
+
</div>
|
|
1755
|
+
|
|
1756
|
+
<!-- Step 5: Done / Auto-fill -->
|
|
1757
|
+
<div id="step-5" style="display:none;">
|
|
1758
|
+
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
|
|
1759
|
+
<span id="step5-icon" style="font-size:20px;">⚪</span>
|
|
1703
1760
|
<div>
|
|
1704
1761
|
<div style="font-weight:600;color:#e5e5e5;font-size:15px;">Auto-fill Config</div>
|
|
1705
|
-
<div id="
|
|
1762
|
+
<div id="step5-detail" style="font-size:13px;color:#a3a3a3;">Ready to auto-fill your clawfire.config.ts</div>
|
|
1706
1763
|
</div>
|
|
1707
1764
|
</div>
|
|
1708
|
-
<div id="
|
|
1765
|
+
<div id="step5-action">
|
|
1709
1766
|
<button id="autofill-wizard-btn" onclick="autoFillConfig()" style="padding:8px 20px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">
|
|
1710
1767
|
Auto-fill Config Now
|
|
1711
1768
|
</button>
|
|
@@ -1850,7 +1907,7 @@ function generateDashboardHtml(options) {
|
|
|
1850
1907
|
// \u2500\u2500\u2500 Setup Wizard \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\u2500
|
|
1851
1908
|
|
|
1852
1909
|
function setStepIndicator(activeStep) {
|
|
1853
|
-
for (var i = 1; i <=
|
|
1910
|
+
for (var i = 1; i <= 5; i++) {
|
|
1854
1911
|
var el = document.getElementById('step-ind-' + i);
|
|
1855
1912
|
if (i < activeStep) {
|
|
1856
1913
|
el.style.background = '#0a1a0a';
|
|
@@ -1872,7 +1929,7 @@ function generateDashboardHtml(options) {
|
|
|
1872
1929
|
var CIRCLE = '\\u26AA';
|
|
1873
1930
|
|
|
1874
1931
|
// Hide all steps first
|
|
1875
|
-
for (var i = 1; i <=
|
|
1932
|
+
for (var i = 1; i <= 5; i++) {
|
|
1876
1933
|
document.getElementById('step-' + i).style.display = 'none';
|
|
1877
1934
|
}
|
|
1878
1935
|
document.getElementById('setup-done').style.display = 'none';
|
|
@@ -1882,10 +1939,13 @@ function generateDashboardHtml(options) {
|
|
|
1882
1939
|
|
|
1883
1940
|
if (status.nextStep === 'done') {
|
|
1884
1941
|
// All done!
|
|
1885
|
-
setStepIndicator(
|
|
1942
|
+
setStepIndicator(6);
|
|
1886
1943
|
document.getElementById('setup-done').style.display = 'block';
|
|
1887
|
-
|
|
1888
|
-
|
|
1944
|
+
var detail = 'Logged in as ' + status.auth.user + ' | Project: ' + status.project.id;
|
|
1945
|
+
if (status.webApp && status.webApp.displayName) {
|
|
1946
|
+
detail += ' | App: ' + status.webApp.displayName;
|
|
1947
|
+
}
|
|
1948
|
+
document.getElementById('setup-done-detail').textContent = detail;
|
|
1889
1949
|
return;
|
|
1890
1950
|
}
|
|
1891
1951
|
|
|
@@ -1952,11 +2012,32 @@ function generateDashboardHtml(options) {
|
|
|
1952
2012
|
return;
|
|
1953
2013
|
}
|
|
1954
2014
|
|
|
1955
|
-
// Step 4:
|
|
2015
|
+
// Step 4: Web App
|
|
1956
2016
|
var step4 = document.getElementById('step-4');
|
|
1957
2017
|
step4.style.display = 'block';
|
|
1958
|
-
|
|
1959
|
-
|
|
2018
|
+
if (status.webApp && status.webApp.appId) {
|
|
2019
|
+
document.getElementById('step4-icon').textContent = GREEN;
|
|
2020
|
+
document.getElementById('step4-detail').textContent = 'Web app: ' + (status.webApp.displayName || status.webApp.appId);
|
|
2021
|
+
document.getElementById('step4-action').style.display = 'block';
|
|
2022
|
+
// Show list with current selection
|
|
2023
|
+
loadWebAppList(status.webApp.appId);
|
|
2024
|
+
} else {
|
|
2025
|
+
document.getElementById('step4-icon').textContent = YELLOW;
|
|
2026
|
+
document.getElementById('step4-detail').textContent = 'No web app selected';
|
|
2027
|
+
document.getElementById('step4-action').style.display = 'block';
|
|
2028
|
+
loadWebAppList('');
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
if (status.nextStep === 'create-web-app') {
|
|
2032
|
+
setStepIndicator(4);
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
// Step 5: Auto-fill
|
|
2037
|
+
var step5 = document.getElementById('step-5');
|
|
2038
|
+
step5.style.display = 'block';
|
|
2039
|
+
document.getElementById('step5-icon').textContent = CIRCLE;
|
|
2040
|
+
setStepIndicator(5);
|
|
1960
2041
|
}
|
|
1961
2042
|
|
|
1962
2043
|
// \u2500\u2500\u2500 Step 1: Install CLI \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
|
|
@@ -2151,6 +2232,148 @@ function generateDashboardHtml(options) {
|
|
|
2151
2232
|
});
|
|
2152
2233
|
};
|
|
2153
2234
|
|
|
2235
|
+
// \u2500\u2500\u2500 Step 4: Web App \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
|
|
2236
|
+
function loadWebAppList(currentAppId, retryCount) {
|
|
2237
|
+
retryCount = retryCount || 0;
|
|
2238
|
+
var listDiv = document.getElementById('webapp-list');
|
|
2239
|
+
var createDiv = document.getElementById('webapp-create');
|
|
2240
|
+
var select = document.getElementById('webapp-select');
|
|
2241
|
+
|
|
2242
|
+
fetch(API + '/__dev/setup/web-apps')
|
|
2243
|
+
.then(function(r) { return r.json(); })
|
|
2244
|
+
.then(function(data) {
|
|
2245
|
+
if (data.error) {
|
|
2246
|
+
if (retryCount < 2) {
|
|
2247
|
+
setTimeout(function() { loadWebAppList(currentAppId, retryCount + 1); }, 3000);
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
createDiv.style.display = 'block';
|
|
2251
|
+
listDiv.style.display = 'none';
|
|
2252
|
+
document.getElementById('webapp-create').querySelector('div').textContent = 'Error loading apps: ' + data.error;
|
|
2253
|
+
return;
|
|
2254
|
+
}
|
|
2255
|
+
if (!data.apps || data.apps.length === 0) {
|
|
2256
|
+
// No web apps \u2014 show create form
|
|
2257
|
+
createDiv.style.display = 'block';
|
|
2258
|
+
listDiv.style.display = 'none';
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// Show list
|
|
2263
|
+
listDiv.style.display = 'block';
|
|
2264
|
+
createDiv.style.display = 'none';
|
|
2265
|
+
select.innerHTML = '<option value="">-- Select a web app --</option>';
|
|
2266
|
+
data.apps.forEach(function(app) {
|
|
2267
|
+
var opt = document.createElement('option');
|
|
2268
|
+
opt.value = app.appId;
|
|
2269
|
+
opt.setAttribute('data-name', app.displayName || '');
|
|
2270
|
+
opt.textContent = (app.displayName || 'Unnamed') + ' (' + app.appId.slice(-12) + ')';
|
|
2271
|
+
if (app.appId === currentAppId) opt.selected = true;
|
|
2272
|
+
select.appendChild(opt);
|
|
2273
|
+
});
|
|
2274
|
+
select.disabled = false;
|
|
2275
|
+
})
|
|
2276
|
+
.catch(function() {
|
|
2277
|
+
if (retryCount < 2) {
|
|
2278
|
+
setTimeout(function() { loadWebAppList(currentAppId, retryCount + 1); }, 3000);
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
createDiv.style.display = 'block';
|
|
2282
|
+
listDiv.style.display = 'none';
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
window.showCreateWebApp = function() {
|
|
2287
|
+
document.getElementById('webapp-list').style.display = 'none';
|
|
2288
|
+
document.getElementById('webapp-create').style.display = 'block';
|
|
2289
|
+
document.getElementById('webapp-name').focus();
|
|
2290
|
+
};
|
|
2291
|
+
|
|
2292
|
+
window.selectWebApp = function() {
|
|
2293
|
+
var select = document.getElementById('webapp-select');
|
|
2294
|
+
var status = document.getElementById('webapp-status');
|
|
2295
|
+
var appId = select.value;
|
|
2296
|
+
if (!appId) {
|
|
2297
|
+
status.textContent = 'Please select a web app first.';
|
|
2298
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1a1a0a;border:1px solid #eab308;color:#eab308;';
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
var selectedOption = select.options[select.selectedIndex];
|
|
2302
|
+
var displayName = selectedOption.getAttribute('data-name') || '';
|
|
2303
|
+
|
|
2304
|
+
var btn = document.getElementById('select-webapp-btn');
|
|
2305
|
+
btn.disabled = true;
|
|
2306
|
+
btn.textContent = 'Saving...';
|
|
2307
|
+
|
|
2308
|
+
fetch(API + '/__dev/setup/select-web-app', {
|
|
2309
|
+
method: 'POST',
|
|
2310
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2311
|
+
body: JSON.stringify({ appId: appId, displayName: displayName })
|
|
2312
|
+
})
|
|
2313
|
+
.then(function(r) { return r.json(); })
|
|
2314
|
+
.then(function(data) {
|
|
2315
|
+
if (data.success) {
|
|
2316
|
+
status.textContent = data.message;
|
|
2317
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';
|
|
2318
|
+
setTimeout(refreshSetupStatus, 1000);
|
|
2319
|
+
} else {
|
|
2320
|
+
status.textContent = data.message;
|
|
2321
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
|
|
2322
|
+
btn.disabled = false;
|
|
2323
|
+
btn.textContent = 'Use This App';
|
|
2324
|
+
}
|
|
2325
|
+
})
|
|
2326
|
+
.catch(function(err) {
|
|
2327
|
+
status.textContent = 'Error: ' + err.message;
|
|
2328
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
|
|
2329
|
+
btn.disabled = false;
|
|
2330
|
+
btn.textContent = 'Use This App';
|
|
2331
|
+
});
|
|
2332
|
+
};
|
|
2333
|
+
|
|
2334
|
+
window.createWebApp = function() {
|
|
2335
|
+
var nameInput = document.getElementById('webapp-name');
|
|
2336
|
+
var btn = document.getElementById('create-webapp-btn');
|
|
2337
|
+
var status = document.getElementById('webapp-status');
|
|
2338
|
+
var displayName = nameInput.value.trim();
|
|
2339
|
+
|
|
2340
|
+
if (!displayName) {
|
|
2341
|
+
status.textContent = 'Please enter a name for the web app.';
|
|
2342
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1a1a0a;border:1px solid #eab308;color:#eab308;';
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
btn.disabled = true;
|
|
2347
|
+
btn.textContent = 'Creating...';
|
|
2348
|
+
status.textContent = 'Creating web app... This may take a moment.';
|
|
2349
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a0a1a;border:1px solid #3b82f6;color:#3b82f6;';
|
|
2350
|
+
|
|
2351
|
+
fetch(API + '/__dev/setup/create-web-app', {
|
|
2352
|
+
method: 'POST',
|
|
2353
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2354
|
+
body: JSON.stringify({ displayName: displayName })
|
|
2355
|
+
})
|
|
2356
|
+
.then(function(r) { return r.json(); })
|
|
2357
|
+
.then(function(data) {
|
|
2358
|
+
if (data.success) {
|
|
2359
|
+
status.textContent = data.message;
|
|
2360
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';
|
|
2361
|
+
setTimeout(refreshSetupStatus, 1000);
|
|
2362
|
+
} else {
|
|
2363
|
+
status.textContent = data.message;
|
|
2364
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
|
|
2365
|
+
btn.disabled = false;
|
|
2366
|
+
btn.textContent = 'Create Web App';
|
|
2367
|
+
}
|
|
2368
|
+
})
|
|
2369
|
+
.catch(function(err) {
|
|
2370
|
+
status.textContent = 'Error: ' + err.message;
|
|
2371
|
+
status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
|
|
2372
|
+
btn.disabled = false;
|
|
2373
|
+
btn.textContent = 'Create Web App';
|
|
2374
|
+
});
|
|
2375
|
+
};
|
|
2376
|
+
|
|
2154
2377
|
// \u2500\u2500\u2500 Refresh Setup Status \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
|
|
2155
2378
|
function refreshSetupStatus() {
|
|
2156
2379
|
fetch(API + '/__dev/setup/status')
|
|
@@ -2545,15 +2768,34 @@ var import_node_path4 = require("path");
|
|
|
2545
2768
|
var import_node_os = require("os");
|
|
2546
2769
|
var FirebaseSetup = class {
|
|
2547
2770
|
projectDir;
|
|
2771
|
+
stateFilePath;
|
|
2548
2772
|
constructor(projectDir) {
|
|
2549
2773
|
this.projectDir = projectDir;
|
|
2774
|
+
this.stateFilePath = (0, import_node_path4.resolve)(projectDir, ".clawfire-setup.json");
|
|
2775
|
+
}
|
|
2776
|
+
// ─── State Persistence ────────────────────────────────────────────
|
|
2777
|
+
loadState() {
|
|
2778
|
+
try {
|
|
2779
|
+
if ((0, import_node_fs4.existsSync)(this.stateFilePath)) {
|
|
2780
|
+
return JSON.parse((0, import_node_fs4.readFileSync)(this.stateFilePath, "utf-8"));
|
|
2781
|
+
}
|
|
2782
|
+
} catch {
|
|
2783
|
+
}
|
|
2784
|
+
return {};
|
|
2785
|
+
}
|
|
2786
|
+
saveState(partial) {
|
|
2787
|
+
const current = this.loadState();
|
|
2788
|
+
const merged = { ...current, ...partial, lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2789
|
+
(0, import_node_fs4.writeFileSync)(this.stateFilePath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
2550
2790
|
}
|
|
2551
2791
|
// ─── Status Check ──────────────────────────────────────────────────
|
|
2552
2792
|
async getStatus() {
|
|
2793
|
+
const savedState = this.loadState();
|
|
2553
2794
|
const status = {
|
|
2554
2795
|
cli: { installed: false, version: "" },
|
|
2555
2796
|
auth: { authenticated: false, user: "" },
|
|
2556
2797
|
project: { id: "", hasFirebaserc: false },
|
|
2798
|
+
webApp: { appId: savedState.webAppId || "", displayName: savedState.webAppDisplayName || "" },
|
|
2557
2799
|
ready: false,
|
|
2558
2800
|
nextStep: "install-cli"
|
|
2559
2801
|
};
|
|
@@ -2595,6 +2837,10 @@ var FirebaseSetup = class {
|
|
|
2595
2837
|
status.nextStep = "select-project";
|
|
2596
2838
|
return status;
|
|
2597
2839
|
}
|
|
2840
|
+
if (!status.webApp.appId) {
|
|
2841
|
+
status.nextStep = "create-web-app";
|
|
2842
|
+
return status;
|
|
2843
|
+
}
|
|
2598
2844
|
status.ready = true;
|
|
2599
2845
|
status.nextStep = "done";
|
|
2600
2846
|
return status;
|
|
@@ -2748,6 +2994,12 @@ var FirebaseSetup = class {
|
|
|
2748
2994
|
if (!projectId || !/^[a-z0-9-]+$/.test(projectId)) {
|
|
2749
2995
|
return { success: false, message: "Invalid project ID format." };
|
|
2750
2996
|
}
|
|
2997
|
+
const savedState = this.loadState();
|
|
2998
|
+
if (savedState.projectId && savedState.projectId !== projectId) {
|
|
2999
|
+
this.saveState({ projectId, webAppId: void 0, webAppDisplayName: void 0 });
|
|
3000
|
+
} else {
|
|
3001
|
+
this.saveState({ projectId });
|
|
3002
|
+
}
|
|
2751
3003
|
try {
|
|
2752
3004
|
await this.execTimeout(
|
|
2753
3005
|
"firebase",
|
|
@@ -2783,6 +3035,52 @@ var FirebaseSetup = class {
|
|
|
2783
3035
|
}
|
|
2784
3036
|
}
|
|
2785
3037
|
}
|
|
3038
|
+
// ─── Web App Management ───────────────────────────────────────────
|
|
3039
|
+
async listWebApps() {
|
|
3040
|
+
try {
|
|
3041
|
+
const output = await this.execTimeout(
|
|
3042
|
+
"firebase",
|
|
3043
|
+
["apps:list", "--json"],
|
|
3044
|
+
15e3
|
|
3045
|
+
);
|
|
3046
|
+
const data = JSON.parse(output);
|
|
3047
|
+
const allApps = data?.result || [];
|
|
3048
|
+
const webApps = allApps.filter((a) => a.platform === "WEB").map((a) => ({
|
|
3049
|
+
appId: String(a.appId || ""),
|
|
3050
|
+
displayName: String(a.displayName || "")
|
|
3051
|
+
})).filter((a) => a.appId);
|
|
3052
|
+
return { apps: webApps };
|
|
3053
|
+
} catch (err) {
|
|
3054
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
3055
|
+
return { apps: [], error: msg };
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
async createWebApp(displayName) {
|
|
3059
|
+
if (!displayName || displayName.length < 1) {
|
|
3060
|
+
return { success: false, message: "Display name is required." };
|
|
3061
|
+
}
|
|
3062
|
+
try {
|
|
3063
|
+
const output = await this.execTimeout(
|
|
3064
|
+
"firebase",
|
|
3065
|
+
["apps:create", "web", displayName, "--json"],
|
|
3066
|
+
3e4
|
|
3067
|
+
);
|
|
3068
|
+
const data = JSON.parse(output);
|
|
3069
|
+
const appId = data?.result?.appId || "";
|
|
3070
|
+
if (appId) {
|
|
3071
|
+
this.saveState({ webAppId: appId, webAppDisplayName: displayName });
|
|
3072
|
+
return { success: true, appId, message: `Web app "${displayName}" created successfully.` };
|
|
3073
|
+
}
|
|
3074
|
+
return { success: false, message: "App created but could not retrieve app ID." };
|
|
3075
|
+
} catch (err) {
|
|
3076
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
3077
|
+
return { success: false, message: `Failed to create web app: ${msg}` };
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
selectWebApp(appId, displayName) {
|
|
3081
|
+
this.saveState({ webAppId: appId, webAppDisplayName: displayName });
|
|
3082
|
+
return { success: true, message: `Web app "${displayName}" selected.` };
|
|
3083
|
+
}
|
|
2786
3084
|
// ─── Helpers ───────────────────────────────────────────────────────
|
|
2787
3085
|
execTimeout(command, args, timeoutMs) {
|
|
2788
3086
|
return new Promise((resolve7, reject) => {
|
|
@@ -3672,7 +3970,8 @@ ${liveReloadScript}
|
|
|
3672
3970
|
return;
|
|
3673
3971
|
}
|
|
3674
3972
|
if (url.pathname === "/__dev/firebase-sdk-config" && req.method === "GET") {
|
|
3675
|
-
|
|
3973
|
+
const savedState = this.firebaseSetup.loadState();
|
|
3974
|
+
fetchFirebaseSdkConfig(this.options.projectDir, savedState.webAppId).then((config) => sendJson(config)).catch((err) => sendJson({ error: err instanceof Error ? err.message : "Failed to fetch SDK config" }, 500));
|
|
3676
3975
|
return;
|
|
3677
3976
|
}
|
|
3678
3977
|
if (url.pathname === "/__dev/env" && req.method === "GET") {
|
|
@@ -3757,6 +4056,53 @@ ${liveReloadScript}
|
|
|
3757
4056
|
});
|
|
3758
4057
|
return;
|
|
3759
4058
|
}
|
|
4059
|
+
if (url.pathname === "/__dev/setup/web-apps" && req.method === "GET") {
|
|
4060
|
+
this.firebaseSetup.listWebApps().then((result) => sendJson(result)).catch((err) => sendJson({ apps: [], error: err instanceof Error ? err.message : "Failed" }, 500));
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
if (url.pathname === "/__dev/setup/create-web-app" && req.method === "POST") {
|
|
4064
|
+
let body = "";
|
|
4065
|
+
req.on("data", (chunk) => {
|
|
4066
|
+
body += chunk;
|
|
4067
|
+
});
|
|
4068
|
+
req.on("end", () => {
|
|
4069
|
+
try {
|
|
4070
|
+
const data = JSON.parse(body);
|
|
4071
|
+
if (!data.displayName) {
|
|
4072
|
+
sendJson({ success: false, message: "displayName is required" }, 400);
|
|
4073
|
+
return;
|
|
4074
|
+
}
|
|
4075
|
+
this.firebaseSetup.createWebApp(data.displayName).then((result) => {
|
|
4076
|
+
clearFirebaseStatusCache();
|
|
4077
|
+
sendJson(result);
|
|
4078
|
+
}).catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : "Failed" }, 500));
|
|
4079
|
+
} catch {
|
|
4080
|
+
sendJson({ success: false, message: "Invalid JSON body" }, 400);
|
|
4081
|
+
}
|
|
4082
|
+
});
|
|
4083
|
+
return;
|
|
4084
|
+
}
|
|
4085
|
+
if (url.pathname === "/__dev/setup/select-web-app" && req.method === "POST") {
|
|
4086
|
+
let body = "";
|
|
4087
|
+
req.on("data", (chunk) => {
|
|
4088
|
+
body += chunk;
|
|
4089
|
+
});
|
|
4090
|
+
req.on("end", () => {
|
|
4091
|
+
try {
|
|
4092
|
+
const data = JSON.parse(body);
|
|
4093
|
+
if (!data.appId) {
|
|
4094
|
+
sendJson({ success: false, message: "appId is required" }, 400);
|
|
4095
|
+
return;
|
|
4096
|
+
}
|
|
4097
|
+
const result = this.firebaseSetup.selectWebApp(data.appId, data.displayName || "");
|
|
4098
|
+
clearFirebaseStatusCache();
|
|
4099
|
+
sendJson(result);
|
|
4100
|
+
} catch {
|
|
4101
|
+
sendJson({ success: false, message: "Invalid JSON body" }, 400);
|
|
4102
|
+
}
|
|
4103
|
+
});
|
|
4104
|
+
return;
|
|
4105
|
+
}
|
|
3760
4106
|
res.writeHead(404);
|
|
3761
4107
|
res.end("Not found");
|
|
3762
4108
|
}
|