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/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-5ATZVQJT.js");
|
|
213
213
|
await startDevServer({
|
|
214
214
|
projectDir,
|
|
215
215
|
port,
|
|
@@ -1082,7 +1082,23 @@ async function checkCli(projectDir) {
|
|
|
1082
1082
|
}
|
|
1083
1083
|
return result;
|
|
1084
1084
|
}
|
|
1085
|
-
async function fetchFirebaseSdkConfig(projectDir) {
|
|
1085
|
+
async function fetchFirebaseSdkConfig(projectDir, appId) {
|
|
1086
|
+
if (appId) {
|
|
1087
|
+
try {
|
|
1088
|
+
const output = await execWithTimeout(
|
|
1089
|
+
"firebase",
|
|
1090
|
+
["apps:sdkconfig", "web", appId, "--json"],
|
|
1091
|
+
projectDir,
|
|
1092
|
+
15e3
|
|
1093
|
+
);
|
|
1094
|
+
const config = parseSdkConfigOutput(output);
|
|
1095
|
+
if (config) return config;
|
|
1096
|
+
} catch (err) {
|
|
1097
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
1098
|
+
throw new Error(`Failed to fetch config for web app ${appId}: ${msg}`);
|
|
1099
|
+
}
|
|
1100
|
+
throw new Error("Could not parse Firebase SDK config from CLI output");
|
|
1101
|
+
}
|
|
1086
1102
|
try {
|
|
1087
1103
|
const output = await execWithTimeout(
|
|
1088
1104
|
"firebase",
|
|
@@ -1118,11 +1134,11 @@ async function fetchFirebaseSdkConfig(projectDir) {
|
|
|
1118
1134
|
"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."
|
|
1119
1135
|
);
|
|
1120
1136
|
}
|
|
1121
|
-
const
|
|
1137
|
+
const firstAppId = webApps[0].appId;
|
|
1122
1138
|
try {
|
|
1123
1139
|
const output = await execWithTimeout(
|
|
1124
1140
|
"firebase",
|
|
1125
|
-
["apps:sdkconfig", "web",
|
|
1141
|
+
["apps:sdkconfig", "web", firstAppId, "--json"],
|
|
1126
1142
|
projectDir,
|
|
1127
1143
|
15e3
|
|
1128
1144
|
);
|
|
@@ -1130,7 +1146,7 @@ async function fetchFirebaseSdkConfig(projectDir) {
|
|
|
1130
1146
|
if (config) return config;
|
|
1131
1147
|
} catch (err) {
|
|
1132
1148
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
1133
|
-
throw new Error(`Failed to fetch config for web app ${
|
|
1149
|
+
throw new Error(`Failed to fetch config for web app ${firstAppId}: ${msg}`);
|
|
1134
1150
|
}
|
|
1135
1151
|
throw new Error("Could not parse Firebase SDK config from CLI output");
|
|
1136
1152
|
}
|
|
@@ -1199,17 +1215,20 @@ function generateDashboardHtml(options) {
|
|
|
1199
1215
|
|
|
1200
1216
|
<!-- Step indicators -->
|
|
1201
1217
|
<div id="setup-steps" style="display:flex;gap:0;margin-bottom:20px;overflow:hidden;border-radius:8px;border:1px solid #2a2a2a;">
|
|
1202
|
-
<div id="step-ind-1" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:
|
|
1203
|
-
1. CLI
|
|
1218
|
+
<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;">
|
|
1219
|
+
1. CLI
|
|
1204
1220
|
</div>
|
|
1205
|
-
<div id="step-ind-2" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:
|
|
1221
|
+
<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;">
|
|
1206
1222
|
2. Login
|
|
1207
1223
|
</div>
|
|
1208
|
-
<div id="step-ind-3" class="step-ind" style="flex:1;padding:8px 12px;text-align:center;font-size:
|
|
1209
|
-
3.
|
|
1224
|
+
<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;">
|
|
1225
|
+
3. Project
|
|
1226
|
+
</div>
|
|
1227
|
+
<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;">
|
|
1228
|
+
4. Web App
|
|
1210
1229
|
</div>
|
|
1211
|
-
<div id="step-ind-
|
|
1212
|
-
|
|
1230
|
+
<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;">
|
|
1231
|
+
5. Auto-fill
|
|
1213
1232
|
</div>
|
|
1214
1233
|
</div>
|
|
1215
1234
|
|
|
@@ -1284,16 +1303,54 @@ function generateDashboardHtml(options) {
|
|
|
1284
1303
|
</div>
|
|
1285
1304
|
</div>
|
|
1286
1305
|
|
|
1287
|
-
<!-- Step 4:
|
|
1306
|
+
<!-- Step 4: Web App -->
|
|
1288
1307
|
<div id="step-4" style="display:none;">
|
|
1289
1308
|
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
|
|
1290
1309
|
<span id="step4-icon" style="font-size:20px;">⚪</span>
|
|
1310
|
+
<div>
|
|
1311
|
+
<div style="font-weight:600;color:#e5e5e5;font-size:15px;">Web App</div>
|
|
1312
|
+
<div id="step4-detail" style="font-size:13px;color:#a3a3a3;">Checking web apps...</div>
|
|
1313
|
+
</div>
|
|
1314
|
+
</div>
|
|
1315
|
+
<div id="step4-action" style="display:none;">
|
|
1316
|
+
<!-- Existing web apps list -->
|
|
1317
|
+
<div id="webapp-list" style="display:none;">
|
|
1318
|
+
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
|
1319
|
+
<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;">
|
|
1320
|
+
<option value="">Loading web apps...</option>
|
|
1321
|
+
</select>
|
|
1322
|
+
<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;">
|
|
1323
|
+
Use This App
|
|
1324
|
+
</button>
|
|
1325
|
+
<button onclick="showCreateWebApp()" style="padding:8px 12px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:13px;cursor:pointer;">
|
|
1326
|
+
+ Create New
|
|
1327
|
+
</button>
|
|
1328
|
+
</div>
|
|
1329
|
+
</div>
|
|
1330
|
+
<!-- Create new web app form -->
|
|
1331
|
+
<div id="webapp-create" style="display:none;">
|
|
1332
|
+
<div style="font-size:13px;color:#a3a3a3;margin-bottom:8px;">No web app found. Create one to get Firebase SDK config:</div>
|
|
1333
|
+
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
|
1334
|
+
<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;" />
|
|
1335
|
+
<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;">
|
|
1336
|
+
Create Web App
|
|
1337
|
+
</button>
|
|
1338
|
+
</div>
|
|
1339
|
+
</div>
|
|
1340
|
+
<div id="webapp-status" style="display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;"></div>
|
|
1341
|
+
</div>
|
|
1342
|
+
</div>
|
|
1343
|
+
|
|
1344
|
+
<!-- Step 5: Done / Auto-fill -->
|
|
1345
|
+
<div id="step-5" style="display:none;">
|
|
1346
|
+
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
|
|
1347
|
+
<span id="step5-icon" style="font-size:20px;">⚪</span>
|
|
1291
1348
|
<div>
|
|
1292
1349
|
<div style="font-weight:600;color:#e5e5e5;font-size:15px;">Auto-fill Config</div>
|
|
1293
|
-
<div id="
|
|
1350
|
+
<div id="step5-detail" style="font-size:13px;color:#a3a3a3;">Ready to auto-fill your clawfire.config.ts</div>
|
|
1294
1351
|
</div>
|
|
1295
1352
|
</div>
|
|
1296
|
-
<div id="
|
|
1353
|
+
<div id="step5-action">
|
|
1297
1354
|
<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;">
|
|
1298
1355
|
Auto-fill Config Now
|
|
1299
1356
|
</button>
|
|
@@ -1438,7 +1495,7 @@ function generateDashboardHtml(options) {
|
|
|
1438
1495
|
// \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
|
|
1439
1496
|
|
|
1440
1497
|
function setStepIndicator(activeStep) {
|
|
1441
|
-
for (var i = 1; i <=
|
|
1498
|
+
for (var i = 1; i <= 5; i++) {
|
|
1442
1499
|
var el = document.getElementById('step-ind-' + i);
|
|
1443
1500
|
if (i < activeStep) {
|
|
1444
1501
|
el.style.background = '#0a1a0a';
|
|
@@ -1460,7 +1517,7 @@ function generateDashboardHtml(options) {
|
|
|
1460
1517
|
var CIRCLE = '\\u26AA';
|
|
1461
1518
|
|
|
1462
1519
|
// Hide all steps first
|
|
1463
|
-
for (var i = 1; i <=
|
|
1520
|
+
for (var i = 1; i <= 5; i++) {
|
|
1464
1521
|
document.getElementById('step-' + i).style.display = 'none';
|
|
1465
1522
|
}
|
|
1466
1523
|
document.getElementById('setup-done').style.display = 'none';
|
|
@@ -1470,10 +1527,13 @@ function generateDashboardHtml(options) {
|
|
|
1470
1527
|
|
|
1471
1528
|
if (status.nextStep === 'done') {
|
|
1472
1529
|
// All done!
|
|
1473
|
-
setStepIndicator(
|
|
1530
|
+
setStepIndicator(6);
|
|
1474
1531
|
document.getElementById('setup-done').style.display = 'block';
|
|
1475
|
-
|
|
1476
|
-
|
|
1532
|
+
var detail = 'Logged in as ' + status.auth.user + ' | Project: ' + status.project.id;
|
|
1533
|
+
if (status.webApp && status.webApp.displayName) {
|
|
1534
|
+
detail += ' | App: ' + status.webApp.displayName;
|
|
1535
|
+
}
|
|
1536
|
+
document.getElementById('setup-done-detail').textContent = detail;
|
|
1477
1537
|
return;
|
|
1478
1538
|
}
|
|
1479
1539
|
|
|
@@ -1540,11 +1600,32 @@ function generateDashboardHtml(options) {
|
|
|
1540
1600
|
return;
|
|
1541
1601
|
}
|
|
1542
1602
|
|
|
1543
|
-
// Step 4:
|
|
1603
|
+
// Step 4: Web App
|
|
1544
1604
|
var step4 = document.getElementById('step-4');
|
|
1545
1605
|
step4.style.display = 'block';
|
|
1546
|
-
|
|
1547
|
-
|
|
1606
|
+
if (status.webApp && status.webApp.appId) {
|
|
1607
|
+
document.getElementById('step4-icon').textContent = GREEN;
|
|
1608
|
+
document.getElementById('step4-detail').textContent = 'Web app: ' + (status.webApp.displayName || status.webApp.appId);
|
|
1609
|
+
document.getElementById('step4-action').style.display = 'block';
|
|
1610
|
+
// Show list with current selection
|
|
1611
|
+
loadWebAppList(status.webApp.appId);
|
|
1612
|
+
} else {
|
|
1613
|
+
document.getElementById('step4-icon').textContent = YELLOW;
|
|
1614
|
+
document.getElementById('step4-detail').textContent = 'No web app selected';
|
|
1615
|
+
document.getElementById('step4-action').style.display = 'block';
|
|
1616
|
+
loadWebAppList('');
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
if (status.nextStep === 'create-web-app') {
|
|
1620
|
+
setStepIndicator(4);
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// Step 5: Auto-fill
|
|
1625
|
+
var step5 = document.getElementById('step-5');
|
|
1626
|
+
step5.style.display = 'block';
|
|
1627
|
+
document.getElementById('step5-icon').textContent = CIRCLE;
|
|
1628
|
+
setStepIndicator(5);
|
|
1548
1629
|
}
|
|
1549
1630
|
|
|
1550
1631
|
// \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
|
|
@@ -1739,6 +1820,148 @@ function generateDashboardHtml(options) {
|
|
|
1739
1820
|
});
|
|
1740
1821
|
};
|
|
1741
1822
|
|
|
1823
|
+
// \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
|
|
1824
|
+
function loadWebAppList(currentAppId, retryCount) {
|
|
1825
|
+
retryCount = retryCount || 0;
|
|
1826
|
+
var listDiv = document.getElementById('webapp-list');
|
|
1827
|
+
var createDiv = document.getElementById('webapp-create');
|
|
1828
|
+
var select = document.getElementById('webapp-select');
|
|
1829
|
+
|
|
1830
|
+
fetch(API + '/__dev/setup/web-apps')
|
|
1831
|
+
.then(function(r) { return r.json(); })
|
|
1832
|
+
.then(function(data) {
|
|
1833
|
+
if (data.error) {
|
|
1834
|
+
if (retryCount < 2) {
|
|
1835
|
+
setTimeout(function() { loadWebAppList(currentAppId, retryCount + 1); }, 3000);
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
createDiv.style.display = 'block';
|
|
1839
|
+
listDiv.style.display = 'none';
|
|
1840
|
+
document.getElementById('webapp-create').querySelector('div').textContent = 'Error loading apps: ' + data.error;
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
if (!data.apps || data.apps.length === 0) {
|
|
1844
|
+
// No web apps \u2014 show create form
|
|
1845
|
+
createDiv.style.display = 'block';
|
|
1846
|
+
listDiv.style.display = 'none';
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
// Show list
|
|
1851
|
+
listDiv.style.display = 'block';
|
|
1852
|
+
createDiv.style.display = 'none';
|
|
1853
|
+
select.innerHTML = '<option value="">-- Select a web app --</option>';
|
|
1854
|
+
data.apps.forEach(function(app) {
|
|
1855
|
+
var opt = document.createElement('option');
|
|
1856
|
+
opt.value = app.appId;
|
|
1857
|
+
opt.setAttribute('data-name', app.displayName || '');
|
|
1858
|
+
opt.textContent = (app.displayName || 'Unnamed') + ' (' + app.appId.slice(-12) + ')';
|
|
1859
|
+
if (app.appId === currentAppId) opt.selected = true;
|
|
1860
|
+
select.appendChild(opt);
|
|
1861
|
+
});
|
|
1862
|
+
select.disabled = false;
|
|
1863
|
+
})
|
|
1864
|
+
.catch(function() {
|
|
1865
|
+
if (retryCount < 2) {
|
|
1866
|
+
setTimeout(function() { loadWebAppList(currentAppId, retryCount + 1); }, 3000);
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
createDiv.style.display = 'block';
|
|
1870
|
+
listDiv.style.display = 'none';
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
window.showCreateWebApp = function() {
|
|
1875
|
+
document.getElementById('webapp-list').style.display = 'none';
|
|
1876
|
+
document.getElementById('webapp-create').style.display = 'block';
|
|
1877
|
+
document.getElementById('webapp-name').focus();
|
|
1878
|
+
};
|
|
1879
|
+
|
|
1880
|
+
window.selectWebApp = function() {
|
|
1881
|
+
var select = document.getElementById('webapp-select');
|
|
1882
|
+
var status = document.getElementById('webapp-status');
|
|
1883
|
+
var appId = select.value;
|
|
1884
|
+
if (!appId) {
|
|
1885
|
+
status.textContent = 'Please select a web app first.';
|
|
1886
|
+
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;';
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
var selectedOption = select.options[select.selectedIndex];
|
|
1890
|
+
var displayName = selectedOption.getAttribute('data-name') || '';
|
|
1891
|
+
|
|
1892
|
+
var btn = document.getElementById('select-webapp-btn');
|
|
1893
|
+
btn.disabled = true;
|
|
1894
|
+
btn.textContent = 'Saving...';
|
|
1895
|
+
|
|
1896
|
+
fetch(API + '/__dev/setup/select-web-app', {
|
|
1897
|
+
method: 'POST',
|
|
1898
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1899
|
+
body: JSON.stringify({ appId: appId, displayName: displayName })
|
|
1900
|
+
})
|
|
1901
|
+
.then(function(r) { return r.json(); })
|
|
1902
|
+
.then(function(data) {
|
|
1903
|
+
if (data.success) {
|
|
1904
|
+
status.textContent = data.message;
|
|
1905
|
+
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;';
|
|
1906
|
+
setTimeout(refreshSetupStatus, 1000);
|
|
1907
|
+
} else {
|
|
1908
|
+
status.textContent = data.message;
|
|
1909
|
+
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;';
|
|
1910
|
+
btn.disabled = false;
|
|
1911
|
+
btn.textContent = 'Use This App';
|
|
1912
|
+
}
|
|
1913
|
+
})
|
|
1914
|
+
.catch(function(err) {
|
|
1915
|
+
status.textContent = 'Error: ' + err.message;
|
|
1916
|
+
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;';
|
|
1917
|
+
btn.disabled = false;
|
|
1918
|
+
btn.textContent = 'Use This App';
|
|
1919
|
+
});
|
|
1920
|
+
};
|
|
1921
|
+
|
|
1922
|
+
window.createWebApp = function() {
|
|
1923
|
+
var nameInput = document.getElementById('webapp-name');
|
|
1924
|
+
var btn = document.getElementById('create-webapp-btn');
|
|
1925
|
+
var status = document.getElementById('webapp-status');
|
|
1926
|
+
var displayName = nameInput.value.trim();
|
|
1927
|
+
|
|
1928
|
+
if (!displayName) {
|
|
1929
|
+
status.textContent = 'Please enter a name for the web app.';
|
|
1930
|
+
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;';
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
btn.disabled = true;
|
|
1935
|
+
btn.textContent = 'Creating...';
|
|
1936
|
+
status.textContent = 'Creating web app... This may take a moment.';
|
|
1937
|
+
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;';
|
|
1938
|
+
|
|
1939
|
+
fetch(API + '/__dev/setup/create-web-app', {
|
|
1940
|
+
method: 'POST',
|
|
1941
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1942
|
+
body: JSON.stringify({ displayName: displayName })
|
|
1943
|
+
})
|
|
1944
|
+
.then(function(r) { return r.json(); })
|
|
1945
|
+
.then(function(data) {
|
|
1946
|
+
if (data.success) {
|
|
1947
|
+
status.textContent = data.message;
|
|
1948
|
+
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;';
|
|
1949
|
+
setTimeout(refreshSetupStatus, 1000);
|
|
1950
|
+
} else {
|
|
1951
|
+
status.textContent = data.message;
|
|
1952
|
+
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;';
|
|
1953
|
+
btn.disabled = false;
|
|
1954
|
+
btn.textContent = 'Create Web App';
|
|
1955
|
+
}
|
|
1956
|
+
})
|
|
1957
|
+
.catch(function(err) {
|
|
1958
|
+
status.textContent = 'Error: ' + err.message;
|
|
1959
|
+
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;';
|
|
1960
|
+
btn.disabled = false;
|
|
1961
|
+
btn.textContent = 'Create Web App';
|
|
1962
|
+
});
|
|
1963
|
+
};
|
|
1964
|
+
|
|
1742
1965
|
// \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
|
|
1743
1966
|
function refreshSetupStatus() {
|
|
1744
1967
|
fetch(API + '/__dev/setup/status')
|
|
@@ -2133,15 +2356,34 @@ import { resolve as resolve4, join as join3 } from "path";
|
|
|
2133
2356
|
import { tmpdir, platform } from "os";
|
|
2134
2357
|
var FirebaseSetup = class {
|
|
2135
2358
|
projectDir;
|
|
2359
|
+
stateFilePath;
|
|
2136
2360
|
constructor(projectDir) {
|
|
2137
2361
|
this.projectDir = projectDir;
|
|
2362
|
+
this.stateFilePath = resolve4(projectDir, ".clawfire-setup.json");
|
|
2363
|
+
}
|
|
2364
|
+
// ─── State Persistence ────────────────────────────────────────────
|
|
2365
|
+
loadState() {
|
|
2366
|
+
try {
|
|
2367
|
+
if (existsSync5(this.stateFilePath)) {
|
|
2368
|
+
return JSON.parse(readFileSync4(this.stateFilePath, "utf-8"));
|
|
2369
|
+
}
|
|
2370
|
+
} catch {
|
|
2371
|
+
}
|
|
2372
|
+
return {};
|
|
2373
|
+
}
|
|
2374
|
+
saveState(partial) {
|
|
2375
|
+
const current = this.loadState();
|
|
2376
|
+
const merged = { ...current, ...partial, lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2377
|
+
writeFileSync2(this.stateFilePath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
2138
2378
|
}
|
|
2139
2379
|
// ─── Status Check ──────────────────────────────────────────────────
|
|
2140
2380
|
async getStatus() {
|
|
2381
|
+
const savedState = this.loadState();
|
|
2141
2382
|
const status = {
|
|
2142
2383
|
cli: { installed: false, version: "" },
|
|
2143
2384
|
auth: { authenticated: false, user: "" },
|
|
2144
2385
|
project: { id: "", hasFirebaserc: false },
|
|
2386
|
+
webApp: { appId: savedState.webAppId || "", displayName: savedState.webAppDisplayName || "" },
|
|
2145
2387
|
ready: false,
|
|
2146
2388
|
nextStep: "install-cli"
|
|
2147
2389
|
};
|
|
@@ -2183,6 +2425,10 @@ var FirebaseSetup = class {
|
|
|
2183
2425
|
status.nextStep = "select-project";
|
|
2184
2426
|
return status;
|
|
2185
2427
|
}
|
|
2428
|
+
if (!status.webApp.appId) {
|
|
2429
|
+
status.nextStep = "create-web-app";
|
|
2430
|
+
return status;
|
|
2431
|
+
}
|
|
2186
2432
|
status.ready = true;
|
|
2187
2433
|
status.nextStep = "done";
|
|
2188
2434
|
return status;
|
|
@@ -2336,6 +2582,12 @@ var FirebaseSetup = class {
|
|
|
2336
2582
|
if (!projectId || !/^[a-z0-9-]+$/.test(projectId)) {
|
|
2337
2583
|
return { success: false, message: "Invalid project ID format." };
|
|
2338
2584
|
}
|
|
2585
|
+
const savedState = this.loadState();
|
|
2586
|
+
if (savedState.projectId && savedState.projectId !== projectId) {
|
|
2587
|
+
this.saveState({ projectId, webAppId: void 0, webAppDisplayName: void 0 });
|
|
2588
|
+
} else {
|
|
2589
|
+
this.saveState({ projectId });
|
|
2590
|
+
}
|
|
2339
2591
|
try {
|
|
2340
2592
|
await this.execTimeout(
|
|
2341
2593
|
"firebase",
|
|
@@ -2371,6 +2623,52 @@ var FirebaseSetup = class {
|
|
|
2371
2623
|
}
|
|
2372
2624
|
}
|
|
2373
2625
|
}
|
|
2626
|
+
// ─── Web App Management ───────────────────────────────────────────
|
|
2627
|
+
async listWebApps() {
|
|
2628
|
+
try {
|
|
2629
|
+
const output = await this.execTimeout(
|
|
2630
|
+
"firebase",
|
|
2631
|
+
["apps:list", "--json"],
|
|
2632
|
+
15e3
|
|
2633
|
+
);
|
|
2634
|
+
const data = JSON.parse(output);
|
|
2635
|
+
const allApps = data?.result || [];
|
|
2636
|
+
const webApps = allApps.filter((a) => a.platform === "WEB").map((a) => ({
|
|
2637
|
+
appId: String(a.appId || ""),
|
|
2638
|
+
displayName: String(a.displayName || "")
|
|
2639
|
+
})).filter((a) => a.appId);
|
|
2640
|
+
return { apps: webApps };
|
|
2641
|
+
} catch (err) {
|
|
2642
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
2643
|
+
return { apps: [], error: msg };
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
async createWebApp(displayName) {
|
|
2647
|
+
if (!displayName || displayName.length < 1) {
|
|
2648
|
+
return { success: false, message: "Display name is required." };
|
|
2649
|
+
}
|
|
2650
|
+
try {
|
|
2651
|
+
const output = await this.execTimeout(
|
|
2652
|
+
"firebase",
|
|
2653
|
+
["apps:create", "web", displayName, "--json"],
|
|
2654
|
+
3e4
|
|
2655
|
+
);
|
|
2656
|
+
const data = JSON.parse(output);
|
|
2657
|
+
const appId = data?.result?.appId || "";
|
|
2658
|
+
if (appId) {
|
|
2659
|
+
this.saveState({ webAppId: appId, webAppDisplayName: displayName });
|
|
2660
|
+
return { success: true, appId, message: `Web app "${displayName}" created successfully.` };
|
|
2661
|
+
}
|
|
2662
|
+
return { success: false, message: "App created but could not retrieve app ID." };
|
|
2663
|
+
} catch (err) {
|
|
2664
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
2665
|
+
return { success: false, message: `Failed to create web app: ${msg}` };
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
selectWebApp(appId, displayName) {
|
|
2669
|
+
this.saveState({ webAppId: appId, webAppDisplayName: displayName });
|
|
2670
|
+
return { success: true, message: `Web app "${displayName}" selected.` };
|
|
2671
|
+
}
|
|
2374
2672
|
// ─── Helpers ───────────────────────────────────────────────────────
|
|
2375
2673
|
execTimeout(command, args, timeoutMs) {
|
|
2376
2674
|
return new Promise((resolve6, reject) => {
|
|
@@ -3260,7 +3558,8 @@ ${liveReloadScript}
|
|
|
3260
3558
|
return;
|
|
3261
3559
|
}
|
|
3262
3560
|
if (url.pathname === "/__dev/firebase-sdk-config" && req.method === "GET") {
|
|
3263
|
-
|
|
3561
|
+
const savedState = this.firebaseSetup.loadState();
|
|
3562
|
+
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));
|
|
3264
3563
|
return;
|
|
3265
3564
|
}
|
|
3266
3565
|
if (url.pathname === "/__dev/env" && req.method === "GET") {
|
|
@@ -3345,6 +3644,53 @@ ${liveReloadScript}
|
|
|
3345
3644
|
});
|
|
3346
3645
|
return;
|
|
3347
3646
|
}
|
|
3647
|
+
if (url.pathname === "/__dev/setup/web-apps" && req.method === "GET") {
|
|
3648
|
+
this.firebaseSetup.listWebApps().then((result) => sendJson(result)).catch((err) => sendJson({ apps: [], error: err instanceof Error ? err.message : "Failed" }, 500));
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3651
|
+
if (url.pathname === "/__dev/setup/create-web-app" && req.method === "POST") {
|
|
3652
|
+
let body = "";
|
|
3653
|
+
req.on("data", (chunk) => {
|
|
3654
|
+
body += chunk;
|
|
3655
|
+
});
|
|
3656
|
+
req.on("end", () => {
|
|
3657
|
+
try {
|
|
3658
|
+
const data = JSON.parse(body);
|
|
3659
|
+
if (!data.displayName) {
|
|
3660
|
+
sendJson({ success: false, message: "displayName is required" }, 400);
|
|
3661
|
+
return;
|
|
3662
|
+
}
|
|
3663
|
+
this.firebaseSetup.createWebApp(data.displayName).then((result) => {
|
|
3664
|
+
clearFirebaseStatusCache();
|
|
3665
|
+
sendJson(result);
|
|
3666
|
+
}).catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : "Failed" }, 500));
|
|
3667
|
+
} catch {
|
|
3668
|
+
sendJson({ success: false, message: "Invalid JSON body" }, 400);
|
|
3669
|
+
}
|
|
3670
|
+
});
|
|
3671
|
+
return;
|
|
3672
|
+
}
|
|
3673
|
+
if (url.pathname === "/__dev/setup/select-web-app" && req.method === "POST") {
|
|
3674
|
+
let body = "";
|
|
3675
|
+
req.on("data", (chunk) => {
|
|
3676
|
+
body += chunk;
|
|
3677
|
+
});
|
|
3678
|
+
req.on("end", () => {
|
|
3679
|
+
try {
|
|
3680
|
+
const data = JSON.parse(body);
|
|
3681
|
+
if (!data.appId) {
|
|
3682
|
+
sendJson({ success: false, message: "appId is required" }, 400);
|
|
3683
|
+
return;
|
|
3684
|
+
}
|
|
3685
|
+
const result = this.firebaseSetup.selectWebApp(data.appId, data.displayName || "");
|
|
3686
|
+
clearFirebaseStatusCache();
|
|
3687
|
+
sendJson(result);
|
|
3688
|
+
} catch {
|
|
3689
|
+
sendJson({ success: false, message: "Invalid JSON body" }, 400);
|
|
3690
|
+
}
|
|
3691
|
+
});
|
|
3692
|
+
return;
|
|
3693
|
+
}
|
|
3348
3694
|
res.writeHead(404);
|
|
3349
3695
|
res.end("Not found");
|
|
3350
3696
|
}
|