clawfire 0.3.1 → 0.3.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-TGEKP4YE.js → dev-server-MIUKPOMC.js} +253 -19
- package/dist/dev.cjs +252 -18
- package/dist/dev.cjs.map +1 -1
- package/dist/dev.d.cts +3 -0
- package/dist/dev.d.ts +3 -0
- package/dist/dev.js +253 -19
- package/dist/dev.js.map +1 -1
- package/package.json +1 -1
package/dist/dev.d.cts
CHANGED
|
@@ -59,6 +59,9 @@ declare class DevServer {
|
|
|
59
59
|
private printStartupBanner;
|
|
60
60
|
private handleDevEndpoint;
|
|
61
61
|
private readProjectConfig;
|
|
62
|
+
/** Update a single key's value in clawfire.config.ts */
|
|
63
|
+
private updateProjectConfig;
|
|
64
|
+
private escapeRegex;
|
|
62
65
|
}
|
|
63
66
|
/**
|
|
64
67
|
* Create and start dev server (one-line helper)
|
package/dist/dev.d.ts
CHANGED
|
@@ -59,6 +59,9 @@ declare class DevServer {
|
|
|
59
59
|
private printStartupBanner;
|
|
60
60
|
private handleDevEndpoint;
|
|
61
61
|
private readProjectConfig;
|
|
62
|
+
/** Update a single key's value in clawfire.config.ts */
|
|
63
|
+
private updateProjectConfig;
|
|
64
|
+
private escapeRegex;
|
|
62
65
|
}
|
|
63
66
|
/**
|
|
64
67
|
* Create and start dev server (one-line helper)
|
package/dist/dev.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/dev/dev-server.ts
|
|
2
2
|
import http from "http";
|
|
3
3
|
import { resolve as resolve5, relative as relative3, extname as extname3 } from "path";
|
|
4
|
-
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
4
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
5
5
|
import { pathToFileURL } from "url";
|
|
6
6
|
|
|
7
7
|
// src/core/schema.ts
|
|
@@ -1456,6 +1456,34 @@ async function checkCli(projectDir) {
|
|
|
1456
1456
|
}
|
|
1457
1457
|
return result;
|
|
1458
1458
|
}
|
|
1459
|
+
async function fetchFirebaseSdkConfig(projectDir) {
|
|
1460
|
+
const output = await execWithTimeout(
|
|
1461
|
+
"firebase",
|
|
1462
|
+
["apps:sdkconfig", "web", "--json"],
|
|
1463
|
+
projectDir,
|
|
1464
|
+
15e3
|
|
1465
|
+
);
|
|
1466
|
+
const data = JSON.parse(output);
|
|
1467
|
+
if (data?.result?.sdkConfig) {
|
|
1468
|
+
return data.result.sdkConfig;
|
|
1469
|
+
}
|
|
1470
|
+
if (data?.result?.fileContents) {
|
|
1471
|
+
const contents = data.result.fileContents;
|
|
1472
|
+
const config = {};
|
|
1473
|
+
const extract = (key) => {
|
|
1474
|
+
const match = contents.match(new RegExp(`"${key}"\\s*:\\s*"([^"]+)"`));
|
|
1475
|
+
return match ? match[1] : void 0;
|
|
1476
|
+
};
|
|
1477
|
+
config.apiKey = extract("apiKey");
|
|
1478
|
+
config.authDomain = extract("authDomain");
|
|
1479
|
+
config.projectId = extract("projectId");
|
|
1480
|
+
config.storageBucket = extract("storageBucket");
|
|
1481
|
+
config.messagingSenderId = extract("messagingSenderId");
|
|
1482
|
+
config.appId = extract("appId");
|
|
1483
|
+
return config;
|
|
1484
|
+
}
|
|
1485
|
+
throw new Error("Could not parse Firebase SDK config from CLI output");
|
|
1486
|
+
}
|
|
1459
1487
|
function execWithTimeout(command, args, cwd, timeoutMs) {
|
|
1460
1488
|
return new Promise((resolve6, reject) => {
|
|
1461
1489
|
const proc = execFile(command, args, { cwd, timeout: timeoutMs }, (err, stdout) => {
|
|
@@ -1504,9 +1532,25 @@ function generateDashboardHtml(options) {
|
|
|
1504
1532
|
|
|
1505
1533
|
<!-- Section 2: Config Overview -->
|
|
1506
1534
|
<div style="margin-bottom:32px;">
|
|
1507
|
-
<
|
|
1535
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
|
1536
|
+
<h2 style="font-size:18px;font-weight:700;color:#f97316;">Config Overview</h2>
|
|
1537
|
+
<button id="autofill-btn" onclick="autoFillConfig()" style="padding:6px 14px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">Auto-fill from Firebase</button>
|
|
1538
|
+
</div>
|
|
1539
|
+
<div id="autofill-status" style="display:none;padding:8px 12px;border-radius:6px;margin-bottom:12px;font-size:13px;"></div>
|
|
1508
1540
|
<div id="config-section" style="border-radius:8px;border:1px solid #2a2a2a;background:#141414;overflow:hidden;">
|
|
1509
|
-
<
|
|
1541
|
+
<table id="config-table" style="width:100%;border-collapse:collapse;font-size:13px;">
|
|
1542
|
+
<thead>
|
|
1543
|
+
<tr style="border-bottom:1px solid #2a2a2a;">
|
|
1544
|
+
<th style="padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;width:180px;">Key</th>
|
|
1545
|
+
<th style="padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;">Value</th>
|
|
1546
|
+
<th style="padding:10px 16px;text-align:right;color:#a3a3a3;font-weight:500;width:80px;">Action</th>
|
|
1547
|
+
</tr>
|
|
1548
|
+
</thead>
|
|
1549
|
+
<tbody id="config-tbody"></tbody>
|
|
1550
|
+
</table>
|
|
1551
|
+
<div id="config-empty" style="display:none;padding:32px;text-align:center;color:#666;">
|
|
1552
|
+
No clawfire.config.ts found.
|
|
1553
|
+
</div>
|
|
1510
1554
|
</div>
|
|
1511
1555
|
</div>
|
|
1512
1556
|
|
|
@@ -1536,7 +1580,7 @@ function generateDashboardHtml(options) {
|
|
|
1536
1580
|
</div>
|
|
1537
1581
|
|
|
1538
1582
|
<!-- Env Modal -->
|
|
1539
|
-
<div id="env-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:10000;
|
|
1583
|
+
<div id="env-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:10000;align-items:center;justify-content:center;">
|
|
1540
1584
|
<div style="background:#1e1e1e;border:1px solid #2a2a2a;border-radius:12px;padding:24px;width:440px;max-width:90vw;">
|
|
1541
1585
|
<h3 id="modal-title" style="font-size:16px;font-weight:700;color:#e5e5e5;margin-bottom:16px;">Add Variable</h3>
|
|
1542
1586
|
<div style="margin-bottom:12px;">
|
|
@@ -1564,6 +1608,7 @@ function generateDashboardHtml(options) {
|
|
|
1564
1608
|
(function() {
|
|
1565
1609
|
var API = 'http://localhost:${apiPort}';
|
|
1566
1610
|
var envData = [];
|
|
1611
|
+
var configData = [];
|
|
1567
1612
|
var editingKey = null;
|
|
1568
1613
|
|
|
1569
1614
|
// \u2500\u2500\u2500 Load Dashboard Data \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
|
|
@@ -1589,7 +1634,6 @@ function generateDashboardHtml(options) {
|
|
|
1589
1634
|
|
|
1590
1635
|
// \u2500\u2500\u2500 Firebase 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\u2500\u2500\u2500\u2500
|
|
1591
1636
|
function renderFirebaseStatus(data) {
|
|
1592
|
-
// CLI Banner
|
|
1593
1637
|
var dot = document.getElementById('cli-dot');
|
|
1594
1638
|
var text = document.getElementById('cli-text');
|
|
1595
1639
|
var proj = document.getElementById('cli-project');
|
|
@@ -1631,7 +1675,6 @@ function generateDashboardHtml(options) {
|
|
|
1631
1675
|
grid.appendChild(card);
|
|
1632
1676
|
});
|
|
1633
1677
|
|
|
1634
|
-
// Config Warnings
|
|
1635
1678
|
if (data.configWarnings && data.configWarnings.length > 0) {
|
|
1636
1679
|
var warningCard = document.createElement('div');
|
|
1637
1680
|
warningCard.style.cssText = 'padding:16px;border-radius:8px;border:1px solid #eab308;background:#1a1a0a;grid-column:1/-1;';
|
|
@@ -1644,26 +1687,167 @@ function generateDashboardHtml(options) {
|
|
|
1644
1687
|
}
|
|
1645
1688
|
}
|
|
1646
1689
|
|
|
1647
|
-
// \u2500\u2500\u2500 Config Overview \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
|
|
1690
|
+
// \u2500\u2500\u2500 Config Overview (editable) \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
|
|
1648
1691
|
function renderConfig(data) {
|
|
1649
|
-
var
|
|
1650
|
-
|
|
1651
|
-
|
|
1692
|
+
var tbody = document.getElementById('config-tbody');
|
|
1693
|
+
var empty = document.getElementById('config-empty');
|
|
1694
|
+
var table = document.getElementById('config-table');
|
|
1695
|
+
|
|
1696
|
+
configData = (data && data.fields) ? data.fields : [];
|
|
1697
|
+
|
|
1698
|
+
if (configData.length === 0) {
|
|
1699
|
+
table.style.display = 'none';
|
|
1700
|
+
empty.style.display = 'block';
|
|
1652
1701
|
return;
|
|
1653
1702
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1703
|
+
|
|
1704
|
+
table.style.display = 'table';
|
|
1705
|
+
empty.style.display = 'none';
|
|
1706
|
+
tbody.innerHTML = '';
|
|
1707
|
+
|
|
1708
|
+
configData.forEach(function(field) {
|
|
1709
|
+
var tr = document.createElement('tr');
|
|
1710
|
+
tr.style.borderBottom = '1px solid #2a2a2a';
|
|
1711
|
+
tr.id = 'cfg-row-' + field.key;
|
|
1656
1712
|
var color = field.isPlaceholder ? '#eab308' : '#a3a3a3';
|
|
1657
1713
|
var badge = field.isPlaceholder ? ' <span style="background:#eab30822;color:#eab308;padding:1px 6px;border-radius:4px;font-size:10px;">PLACEHOLDER</span>' : '';
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1714
|
+
tr.innerHTML =
|
|
1715
|
+
'<td style="padding:10px 16px;color:#e5e5e5;font-family:monospace;white-space:nowrap;">' + escHtml(field.key) + '</td>' +
|
|
1716
|
+
'<td style="padding:10px 16px;font-family:monospace;">' +
|
|
1717
|
+
'<span id="cfg-val-' + field.key + '" style="color:' + color + ';">' + escHtml(field.value) + '</span>' +
|
|
1718
|
+
badge +
|
|
1719
|
+
'<input id="cfg-input-' + field.key + '" type="text" value="' + escHtml(field.value) + '" style="display:none;width:100%;padding:4px 8px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:4px;color:#e5e5e5;font-family:monospace;font-size:13px;outline:none;" />' +
|
|
1720
|
+
'</td>' +
|
|
1721
|
+
'<td style="padding:10px 16px;text-align:right;white-space:nowrap;">' +
|
|
1722
|
+
'<button id="cfg-edit-' + field.key + '" onclick="editConfigField(\\'' + escHtml(field.key) + '\\')" style="background:none;border:none;color:#3b82f6;cursor:pointer;font-size:12px;padding:4px 8px;">Edit</button>' +
|
|
1723
|
+
'<button id="cfg-save-' + field.key + '" onclick="saveConfigField(\\'' + escHtml(field.key) + '\\')" style="display:none;background:none;border:none;color:#22c55e;cursor:pointer;font-size:12px;padding:4px 8px;">Save</button>' +
|
|
1724
|
+
'<button id="cfg-cancel-' + field.key + '" onclick="cancelConfigEdit(\\'' + escHtml(field.key) + '\\')" style="display:none;background:none;border:none;color:#a3a3a3;cursor:pointer;font-size:12px;padding:4px 8px;">Cancel</button>' +
|
|
1725
|
+
'</td>';
|
|
1726
|
+
tbody.appendChild(tr);
|
|
1663
1727
|
});
|
|
1664
|
-
el.innerHTML = html;
|
|
1665
1728
|
}
|
|
1666
1729
|
|
|
1730
|
+
window.editConfigField = function(key) {
|
|
1731
|
+
var valSpan = document.getElementById('cfg-val-' + key);
|
|
1732
|
+
var input = document.getElementById('cfg-input-' + key);
|
|
1733
|
+
var editBtn = document.getElementById('cfg-edit-' + key);
|
|
1734
|
+
var saveBtn = document.getElementById('cfg-save-' + key);
|
|
1735
|
+
var cancelBtn = document.getElementById('cfg-cancel-' + key);
|
|
1736
|
+
if (!valSpan || !input) return;
|
|
1737
|
+
// Hide all badges in this cell
|
|
1738
|
+
var badges = valSpan.parentNode.querySelectorAll('span[style*="PLACEHOLDER"]');
|
|
1739
|
+
for (var i = 0; i < badges.length; i++) badges[i].style.display = 'none';
|
|
1740
|
+
valSpan.style.display = 'none';
|
|
1741
|
+
input.style.display = 'block';
|
|
1742
|
+
input.focus();
|
|
1743
|
+
input.select();
|
|
1744
|
+
editBtn.style.display = 'none';
|
|
1745
|
+
saveBtn.style.display = 'inline';
|
|
1746
|
+
cancelBtn.style.display = 'inline';
|
|
1747
|
+
};
|
|
1748
|
+
|
|
1749
|
+
window.cancelConfigEdit = function(key) {
|
|
1750
|
+
var valSpan = document.getElementById('cfg-val-' + key);
|
|
1751
|
+
var input = document.getElementById('cfg-input-' + key);
|
|
1752
|
+
var editBtn = document.getElementById('cfg-edit-' + key);
|
|
1753
|
+
var saveBtn = document.getElementById('cfg-save-' + key);
|
|
1754
|
+
var cancelBtn = document.getElementById('cfg-cancel-' + key);
|
|
1755
|
+
if (!valSpan || !input) return;
|
|
1756
|
+
var badges = valSpan.parentNode.querySelectorAll('span[style*="border-radius"]');
|
|
1757
|
+
for (var i = 0; i < badges.length; i++) badges[i].style.display = '';
|
|
1758
|
+
valSpan.style.display = '';
|
|
1759
|
+
input.style.display = 'none';
|
|
1760
|
+
input.value = valSpan.textContent;
|
|
1761
|
+
editBtn.style.display = 'inline';
|
|
1762
|
+
saveBtn.style.display = 'none';
|
|
1763
|
+
cancelBtn.style.display = 'none';
|
|
1764
|
+
};
|
|
1765
|
+
|
|
1766
|
+
window.saveConfigField = function(key) {
|
|
1767
|
+
var input = document.getElementById('cfg-input-' + key);
|
|
1768
|
+
var saveBtn = document.getElementById('cfg-save-' + key);
|
|
1769
|
+
if (!input) return;
|
|
1770
|
+
var value = input.value;
|
|
1771
|
+
saveBtn.textContent = '...';
|
|
1772
|
+
fetch(API + '/__dev/config', {
|
|
1773
|
+
method: 'POST',
|
|
1774
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1775
|
+
body: JSON.stringify({ action: 'set', key: key, value: value })
|
|
1776
|
+
}).then(function(r) { return r.json(); })
|
|
1777
|
+
.then(function(data) {
|
|
1778
|
+
if (data.error) { alert('Error: ' + data.error); saveBtn.textContent = 'Save'; return; }
|
|
1779
|
+
// Reload config
|
|
1780
|
+
return fetch(API + '/__dev/config').then(function(r) { return r.json(); });
|
|
1781
|
+
})
|
|
1782
|
+
.then(function(data) { if (data) renderConfig(data); })
|
|
1783
|
+
.catch(function(err) { alert('Failed: ' + err.message); saveBtn.textContent = 'Save'; });
|
|
1784
|
+
};
|
|
1785
|
+
|
|
1786
|
+
// \u2500\u2500\u2500 Auto-fill from Firebase \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
|
|
1787
|
+
window.autoFillConfig = function() {
|
|
1788
|
+
var btn = document.getElementById('autofill-btn');
|
|
1789
|
+
var status = document.getElementById('autofill-status');
|
|
1790
|
+
btn.disabled = true;
|
|
1791
|
+
btn.textContent = 'Fetching...';
|
|
1792
|
+
status.style.display = 'none';
|
|
1793
|
+
|
|
1794
|
+
fetch(API + '/__dev/firebase-sdk-config')
|
|
1795
|
+
.then(function(r) { return r.json(); })
|
|
1796
|
+
.then(function(data) {
|
|
1797
|
+
if (data.error) {
|
|
1798
|
+
status.textContent = data.error;
|
|
1799
|
+
status.style.cssText = 'display:block;padding:8px 12px;border-radius:6px;margin-bottom:12px;font-size:13px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
|
|
1800
|
+
btn.disabled = false;
|
|
1801
|
+
btn.textContent = 'Auto-fill from Firebase';
|
|
1802
|
+
return;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
// Map SDK config keys to clawfire.config.ts keys
|
|
1806
|
+
var fields = {};
|
|
1807
|
+
if (data.apiKey) fields.apiKey = data.apiKey;
|
|
1808
|
+
if (data.authDomain) fields.authDomain = data.authDomain;
|
|
1809
|
+
if (data.projectId) fields.projectId = data.projectId;
|
|
1810
|
+
if (data.storageBucket) fields.storageBucket = data.storageBucket;
|
|
1811
|
+
if (data.appId) fields.appId = data.appId;
|
|
1812
|
+
|
|
1813
|
+
var count = Object.keys(fields).length;
|
|
1814
|
+
if (count === 0) {
|
|
1815
|
+
status.textContent = 'No web app config found. Create a Web app in Firebase Console first.';
|
|
1816
|
+
status.style.cssText = 'display:block;padding:8px 12px;border-radius:6px;margin-bottom:12px;font-size:13px;background:#1a1a0a;border:1px solid #eab308;color:#eab308;';
|
|
1817
|
+
btn.disabled = false;
|
|
1818
|
+
btn.textContent = 'Auto-fill from Firebase';
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
// Save all fields at once
|
|
1823
|
+
return fetch(API + '/__dev/config', {
|
|
1824
|
+
method: 'POST',
|
|
1825
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1826
|
+
body: JSON.stringify({ action: 'set-multiple', fields: fields })
|
|
1827
|
+
}).then(function(r) { return r.json(); })
|
|
1828
|
+
.then(function(result) {
|
|
1829
|
+
if (result.error) {
|
|
1830
|
+
status.textContent = 'Error saving: ' + result.error;
|
|
1831
|
+
status.style.cssText = 'display:block;padding:8px 12px;border-radius:6px;margin-bottom:12px;font-size:13px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
|
|
1832
|
+
} else {
|
|
1833
|
+
status.textContent = count + ' fields updated from Firebase project!';
|
|
1834
|
+
status.style.cssText = 'display:block;padding:8px 12px;border-radius:6px;margin-bottom:12px;font-size:13px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';
|
|
1835
|
+
// Reload config view
|
|
1836
|
+
return fetch(API + '/__dev/config').then(function(r) { return r.json(); })
|
|
1837
|
+
.then(function(cfg) { renderConfig(cfg); });
|
|
1838
|
+
}
|
|
1839
|
+
});
|
|
1840
|
+
})
|
|
1841
|
+
.catch(function(err) {
|
|
1842
|
+
status.textContent = 'Failed: ' + err.message;
|
|
1843
|
+
status.style.cssText = 'display:block;padding:8px 12px;border-radius:6px;margin-bottom:12px;font-size:13px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';
|
|
1844
|
+
})
|
|
1845
|
+
.finally(function() {
|
|
1846
|
+
btn.disabled = false;
|
|
1847
|
+
btn.textContent = 'Auto-fill from Firebase';
|
|
1848
|
+
});
|
|
1849
|
+
};
|
|
1850
|
+
|
|
1667
1851
|
// \u2500\u2500\u2500 Environment Variables \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
|
|
1668
1852
|
function renderEnvVars(data) {
|
|
1669
1853
|
envData = data.variables || [];
|
|
@@ -1702,7 +1886,7 @@ function generateDashboardHtml(options) {
|
|
|
1702
1886
|
});
|
|
1703
1887
|
}
|
|
1704
1888
|
|
|
1705
|
-
// \u2500\u2500\u2500 Modal \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\u2500\u2500
|
|
1889
|
+
// \u2500\u2500\u2500 Env Modal \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\u2500\u2500
|
|
1706
1890
|
window.showEnvModal = function(key) {
|
|
1707
1891
|
editingKey = key || null;
|
|
1708
1892
|
var modal = document.getElementById('env-modal');
|
|
@@ -2647,6 +2831,37 @@ ${liveReloadScript}
|
|
|
2647
2831
|
sendJson(this.readProjectConfig());
|
|
2648
2832
|
return;
|
|
2649
2833
|
}
|
|
2834
|
+
if (url.pathname === "/__dev/config" && req.method === "POST") {
|
|
2835
|
+
let body = "";
|
|
2836
|
+
req.on("data", (chunk) => {
|
|
2837
|
+
body += chunk;
|
|
2838
|
+
});
|
|
2839
|
+
req.on("end", () => {
|
|
2840
|
+
try {
|
|
2841
|
+
const data = JSON.parse(body);
|
|
2842
|
+
if (data.action === "set" && data.key && data.value !== void 0) {
|
|
2843
|
+
this.updateProjectConfig(data.key, String(data.value));
|
|
2844
|
+
clearFirebaseStatusCache();
|
|
2845
|
+
sendJson({ ok: true });
|
|
2846
|
+
} else if (data.action === "set-multiple" && data.fields) {
|
|
2847
|
+
for (const [key, value] of Object.entries(data.fields)) {
|
|
2848
|
+
this.updateProjectConfig(key, String(value));
|
|
2849
|
+
}
|
|
2850
|
+
clearFirebaseStatusCache();
|
|
2851
|
+
sendJson({ ok: true });
|
|
2852
|
+
} else {
|
|
2853
|
+
sendJson({ error: "Invalid action" }, 400);
|
|
2854
|
+
}
|
|
2855
|
+
} catch (err) {
|
|
2856
|
+
sendJson({ error: err instanceof Error ? err.message : "Failed" }, 400);
|
|
2857
|
+
}
|
|
2858
|
+
});
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
if (url.pathname === "/__dev/firebase-sdk-config" && req.method === "GET") {
|
|
2862
|
+
fetchFirebaseSdkConfig(this.options.projectDir).then((config) => sendJson(config)).catch((err) => sendJson({ error: err instanceof Error ? err.message : "Failed to fetch SDK config" }, 500));
|
|
2863
|
+
return;
|
|
2864
|
+
}
|
|
2650
2865
|
if (url.pathname === "/__dev/env" && req.method === "GET") {
|
|
2651
2866
|
try {
|
|
2652
2867
|
sendJson(this.envManager.read());
|
|
@@ -2702,6 +2917,25 @@ ${liveReloadScript}
|
|
|
2702
2917
|
}
|
|
2703
2918
|
return { fields };
|
|
2704
2919
|
}
|
|
2920
|
+
/** Update a single key's value in clawfire.config.ts */
|
|
2921
|
+
updateProjectConfig(key, value) {
|
|
2922
|
+
const configPath = resolve5(this.options.projectDir, "clawfire.config.ts");
|
|
2923
|
+
if (!existsSync6(configPath)) {
|
|
2924
|
+
throw new Error("clawfire.config.ts not found");
|
|
2925
|
+
}
|
|
2926
|
+
let content = readFileSync4(configPath, "utf-8");
|
|
2927
|
+
const pattern = new RegExp(
|
|
2928
|
+
`(${this.escapeRegex(key)}\\s*:\\s*)["'\`][^"'\`]*["'\`]`
|
|
2929
|
+
);
|
|
2930
|
+
if (!pattern.test(content)) {
|
|
2931
|
+
throw new Error(`Key "${key}" not found in config`);
|
|
2932
|
+
}
|
|
2933
|
+
content = content.replace(pattern, `$1"${value.replace(/"/g, '\\"')}"`);
|
|
2934
|
+
writeFileSync2(configPath, content, "utf-8");
|
|
2935
|
+
}
|
|
2936
|
+
escapeRegex(s) {
|
|
2937
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2938
|
+
}
|
|
2705
2939
|
};
|
|
2706
2940
|
async function startDevServer(options) {
|
|
2707
2941
|
const server = new DevServer(options);
|