clawfire 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -209,7 +209,7 @@ async function runDevServer() {
209
209
  const port = portArg ? parseInt(portArg.split("=")[1], 10) : 3e3;
210
210
  const apiPort = apiPortArg ? parseInt(apiPortArg.split("=")[1], 10) : 3456;
211
211
  const noHotReload = args.includes("--no-hot-reload");
212
- const { startDevServer } = await import("./dev-server-65H4AQFS.js");
212
+ const { startDevServer } = await import("./dev-server-XLMLGSQP.js");
213
213
  await startDevServer({
214
214
  projectDir,
215
215
  port,
@@ -1083,38 +1083,89 @@ async function checkCli(projectDir) {
1083
1083
  return result;
1084
1084
  }
1085
1085
  async function fetchFirebaseSdkConfig(projectDir) {
1086
- const output = await execWithTimeout(
1087
- "firebase",
1088
- ["apps:sdkconfig", "web", "--json"],
1089
- projectDir,
1090
- 15e3
1091
- );
1092
- const data = JSON.parse(output);
1093
- if (data?.result?.sdkConfig) {
1094
- return data.result.sdkConfig;
1095
- }
1096
- if (data?.result?.fileContents) {
1097
- const contents = data.result.fileContents;
1098
- const config = {};
1099
- const extract = (key) => {
1100
- const match = contents.match(new RegExp(`"${key}"\\s*:\\s*"([^"]+)"`));
1101
- return match ? match[1] : void 0;
1102
- };
1103
- config.apiKey = extract("apiKey");
1104
- config.authDomain = extract("authDomain");
1105
- config.projectId = extract("projectId");
1106
- config.storageBucket = extract("storageBucket");
1107
- config.messagingSenderId = extract("messagingSenderId");
1108
- config.appId = extract("appId");
1109
- return config;
1086
+ try {
1087
+ const output = await execWithTimeout(
1088
+ "firebase",
1089
+ ["apps:sdkconfig", "web", "--json"],
1090
+ projectDir,
1091
+ 15e3
1092
+ );
1093
+ const config = parseSdkConfigOutput(output);
1094
+ if (config) return config;
1095
+ } catch {
1096
+ }
1097
+ let webApps = [];
1098
+ try {
1099
+ const appsOutput = await execWithTimeout(
1100
+ "firebase",
1101
+ ["apps:list", "--json"],
1102
+ projectDir,
1103
+ 15e3
1104
+ );
1105
+ const appsData = JSON.parse(appsOutput);
1106
+ const allApps = appsData?.result || [];
1107
+ webApps = allApps.filter((a) => a.platform === "WEB").map((a) => ({
1108
+ appId: String(a.appId || ""),
1109
+ displayName: String(a.displayName || "")
1110
+ })).filter((a) => a.appId);
1111
+ } catch {
1112
+ throw new Error(
1113
+ "Failed to fetch Firebase SDK config. Make sure you are logged in and have an active project selected."
1114
+ );
1115
+ }
1116
+ if (webApps.length === 0) {
1117
+ throw new Error(
1118
+ "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
+ );
1120
+ }
1121
+ const appId = webApps[0].appId;
1122
+ try {
1123
+ const output = await execWithTimeout(
1124
+ "firebase",
1125
+ ["apps:sdkconfig", "web", appId, "--json"],
1126
+ projectDir,
1127
+ 15e3
1128
+ );
1129
+ const config = parseSdkConfigOutput(output);
1130
+ if (config) return config;
1131
+ } catch (err) {
1132
+ const msg = err instanceof Error ? err.message : "Unknown error";
1133
+ throw new Error(`Failed to fetch config for web app ${appId}: ${msg}`);
1110
1134
  }
1111
1135
  throw new Error("Could not parse Firebase SDK config from CLI output");
1112
1136
  }
1137
+ function parseSdkConfigOutput(output) {
1138
+ try {
1139
+ const data = JSON.parse(output);
1140
+ if (data?.result?.sdkConfig) {
1141
+ return data.result.sdkConfig;
1142
+ }
1143
+ if (data?.result?.fileContents) {
1144
+ const contents = data.result.fileContents;
1145
+ const config = {};
1146
+ const extract = (key) => {
1147
+ const match = contents.match(new RegExp(`"${key}"\\s*:\\s*"([^"]+)"`));
1148
+ return match ? match[1] : void 0;
1149
+ };
1150
+ config.apiKey = extract("apiKey");
1151
+ config.authDomain = extract("authDomain");
1152
+ config.projectId = extract("projectId");
1153
+ config.storageBucket = extract("storageBucket");
1154
+ config.messagingSenderId = extract("messagingSenderId");
1155
+ config.appId = extract("appId");
1156
+ if (config.apiKey || config.projectId) return config;
1157
+ }
1158
+ } catch {
1159
+ }
1160
+ return null;
1161
+ }
1113
1162
  function execWithTimeout(command, args, cwd, timeoutMs) {
1114
1163
  return new Promise((resolve6, reject) => {
1115
- const proc = execFile(command, args, { cwd, timeout: timeoutMs }, (err, stdout) => {
1164
+ const proc = execFile(command, args, { cwd, timeout: timeoutMs }, (err, stdout, stderr) => {
1116
1165
  if (err) {
1117
- reject(err);
1166
+ const detail = stderr?.trim() || stdout?.trim() || "";
1167
+ const enriched = new Error(`${err.message}${detail ? "\n" + detail : ""}`);
1168
+ reject(enriched);
1118
1169
  } else {
1119
1170
  resolve6(stdout);
1120
1171
  }
@@ -1413,6 +1464,9 @@ function generateDashboardHtml(options) {
1413
1464
  document.getElementById('step-' + i).style.display = 'none';
1414
1465
  }
1415
1466
  document.getElementById('setup-done').style.display = 'none';
1467
+ // Reset login UI state from previous interactions
1468
+ document.getElementById('login-waiting').style.display = 'none';
1469
+ document.getElementById('login-result').style.display = 'none';
1416
1470
 
1417
1471
  if (status.nextStep === 'done') {
1418
1472
  // All done!
@@ -1576,7 +1630,12 @@ function generateDashboardHtml(options) {
1576
1630
  var result = document.getElementById('login-result');
1577
1631
  result.textContent = 'Login successful! Logged in as ' + status.auth.user;
1578
1632
  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;';
1579
- setTimeout(refreshSetupStatus, 1500);
1633
+ // Wait for token to fully settle, then refresh wizard + force-load projects
1634
+ setTimeout(function() {
1635
+ refreshSetupStatus();
1636
+ // Extra delay for project list \u2014 Firebase CLI needs time after fresh login
1637
+ setTimeout(function() { loadProjectList(''); }, 2500);
1638
+ }, 2000);
1580
1639
  }
1581
1640
  })
1582
1641
  .catch(function() {});
@@ -1584,7 +1643,8 @@ function generateDashboardHtml(options) {
1584
1643
  }
1585
1644
 
1586
1645
  // \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
1587
- function loadProjectList(currentProjectId) {
1646
+ function loadProjectList(currentProjectId, retryCount) {
1647
+ retryCount = retryCount || 0;
1588
1648
  var select = document.getElementById('project-select');
1589
1649
  select.innerHTML = '<option value="">Loading projects...</option>';
1590
1650
  select.disabled = true;
@@ -1594,10 +1654,21 @@ function generateDashboardHtml(options) {
1594
1654
  .then(function(data) {
1595
1655
  select.innerHTML = '';
1596
1656
  if (data.error) {
1657
+ // Auto-retry up to 2 times on error (token might not be ready yet after fresh login)
1658
+ if (retryCount < 2) {
1659
+ select.innerHTML = '<option value="">Loading projects... (retry)</option>';
1660
+ setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);
1661
+ return;
1662
+ }
1597
1663
  select.innerHTML = '<option value="">Error: ' + escHtml(data.error) + '</option>';
1598
1664
  return;
1599
1665
  }
1600
1666
  if (!data.projects || data.projects.length === 0) {
1667
+ if (retryCount < 2) {
1668
+ select.innerHTML = '<option value="">Loading projects... (retry)</option>';
1669
+ setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);
1670
+ return;
1671
+ }
1601
1672
  select.innerHTML = '<option value="">No projects found</option>';
1602
1673
  return;
1603
1674
  }
@@ -1613,6 +1684,11 @@ function generateDashboardHtml(options) {
1613
1684
  select.disabled = false;
1614
1685
  })
1615
1686
  .catch(function(err) {
1687
+ if (retryCount < 2) {
1688
+ select.innerHTML = '<option value="">Loading projects... (retry)</option>';
1689
+ setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);
1690
+ return;
1691
+ }
1616
1692
  select.innerHTML = '<option value="">Failed to load</option>';
1617
1693
  });
1618
1694
  }
@@ -1838,7 +1914,12 @@ function generateDashboardHtml(options) {
1838
1914
  if (wizStatus) wizStatus.style.display = 'none';
1839
1915
 
1840
1916
  fetch(API + '/__dev/firebase-sdk-config')
1841
- .then(function(r) { return r.json(); })
1917
+ .then(function(r) {
1918
+ if (!r.ok) {
1919
+ return r.json().catch(function() { return { error: 'Server error (' + r.status + ')' }; });
1920
+ }
1921
+ return r.json();
1922
+ })
1842
1923
  .then(function(data) {
1843
1924
  if (data.error) {
1844
1925
  showAutoFillResult(false, data.error);
@@ -2154,13 +2235,16 @@ var FirebaseSetup = class {
2154
2235
  `cd "${this.projectDir}"`,
2155
2236
  cmd,
2156
2237
  'echo ""',
2157
- 'echo "Login complete! You can close this window."',
2158
- 'read -p "Press Enter to close..."'
2238
+ 'echo "Login complete! Closing in 3 seconds..."',
2239
+ "sleep 3",
2240
+ // Spawn osascript in background to close this specific terminal window, then exit
2241
+ `(sleep 1 && osascript -e 'tell application "Terminal" to close (every window whose name contains "clawfire-firebase-login")' 2>/dev/null) &`,
2242
+ "exit 0"
2159
2243
  ].join("\n"), { mode: 493 });
2160
2244
  const child = spawn("open", [scriptPath], { detached: true, stdio: "ignore" });
2161
2245
  child.unref();
2162
2246
  } else if (os === "win32") {
2163
- const child = spawn("cmd", ["/c", "start", "cmd", "/k", cmd], {
2247
+ const child = spawn("cmd", ["/c", "start", "cmd", "/c", `${cmd} && timeout /t 3 >nul`], {
2164
2248
  cwd: this.projectDir,
2165
2249
  detached: true,
2166
2250
  stdio: "ignore"
@@ -2173,8 +2257,9 @@ var FirebaseSetup = class {
2173
2257
  `cd "${this.projectDir}"`,
2174
2258
  cmd,
2175
2259
  'echo ""',
2176
- 'echo "Login complete! You can close this window."',
2177
- 'read -p "Press Enter to close..."'
2260
+ 'echo "Login complete! Closing in 3 seconds..."',
2261
+ "sleep 3",
2262
+ "exit 0"
2178
2263
  ].join("\n"), { mode: 493 });
2179
2264
  const terminals = [
2180
2265
  { cmd: "x-terminal-emulator", args: ["-e", scriptPath] },
package/dist/dev.cjs CHANGED
@@ -1495,38 +1495,89 @@ async function checkCli(projectDir) {
1495
1495
  return result;
1496
1496
  }
1497
1497
  async function fetchFirebaseSdkConfig(projectDir) {
1498
- const output = await execWithTimeout(
1499
- "firebase",
1500
- ["apps:sdkconfig", "web", "--json"],
1501
- projectDir,
1502
- 15e3
1503
- );
1504
- const data = JSON.parse(output);
1505
- if (data?.result?.sdkConfig) {
1506
- return data.result.sdkConfig;
1507
- }
1508
- if (data?.result?.fileContents) {
1509
- const contents = data.result.fileContents;
1510
- const config = {};
1511
- const extract = (key) => {
1512
- const match = contents.match(new RegExp(`"${key}"\\s*:\\s*"([^"]+)"`));
1513
- return match ? match[1] : void 0;
1514
- };
1515
- config.apiKey = extract("apiKey");
1516
- config.authDomain = extract("authDomain");
1517
- config.projectId = extract("projectId");
1518
- config.storageBucket = extract("storageBucket");
1519
- config.messagingSenderId = extract("messagingSenderId");
1520
- config.appId = extract("appId");
1521
- return config;
1498
+ try {
1499
+ const output = await execWithTimeout(
1500
+ "firebase",
1501
+ ["apps:sdkconfig", "web", "--json"],
1502
+ projectDir,
1503
+ 15e3
1504
+ );
1505
+ const config = parseSdkConfigOutput(output);
1506
+ if (config) return config;
1507
+ } catch {
1508
+ }
1509
+ let webApps = [];
1510
+ try {
1511
+ const appsOutput = await execWithTimeout(
1512
+ "firebase",
1513
+ ["apps:list", "--json"],
1514
+ projectDir,
1515
+ 15e3
1516
+ );
1517
+ const appsData = JSON.parse(appsOutput);
1518
+ const allApps = appsData?.result || [];
1519
+ webApps = allApps.filter((a) => a.platform === "WEB").map((a) => ({
1520
+ appId: String(a.appId || ""),
1521
+ displayName: String(a.displayName || "")
1522
+ })).filter((a) => a.appId);
1523
+ } catch {
1524
+ throw new Error(
1525
+ "Failed to fetch Firebase SDK config. Make sure you are logged in and have an active project selected."
1526
+ );
1527
+ }
1528
+ if (webApps.length === 0) {
1529
+ throw new Error(
1530
+ "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
+ );
1532
+ }
1533
+ const appId = webApps[0].appId;
1534
+ try {
1535
+ const output = await execWithTimeout(
1536
+ "firebase",
1537
+ ["apps:sdkconfig", "web", appId, "--json"],
1538
+ projectDir,
1539
+ 15e3
1540
+ );
1541
+ const config = parseSdkConfigOutput(output);
1542
+ if (config) return config;
1543
+ } catch (err) {
1544
+ const msg = err instanceof Error ? err.message : "Unknown error";
1545
+ throw new Error(`Failed to fetch config for web app ${appId}: ${msg}`);
1522
1546
  }
1523
1547
  throw new Error("Could not parse Firebase SDK config from CLI output");
1524
1548
  }
1549
+ function parseSdkConfigOutput(output) {
1550
+ try {
1551
+ const data = JSON.parse(output);
1552
+ if (data?.result?.sdkConfig) {
1553
+ return data.result.sdkConfig;
1554
+ }
1555
+ if (data?.result?.fileContents) {
1556
+ const contents = data.result.fileContents;
1557
+ const config = {};
1558
+ const extract = (key) => {
1559
+ const match = contents.match(new RegExp(`"${key}"\\s*:\\s*"([^"]+)"`));
1560
+ return match ? match[1] : void 0;
1561
+ };
1562
+ config.apiKey = extract("apiKey");
1563
+ config.authDomain = extract("authDomain");
1564
+ config.projectId = extract("projectId");
1565
+ config.storageBucket = extract("storageBucket");
1566
+ config.messagingSenderId = extract("messagingSenderId");
1567
+ config.appId = extract("appId");
1568
+ if (config.apiKey || config.projectId) return config;
1569
+ }
1570
+ } catch {
1571
+ }
1572
+ return null;
1573
+ }
1525
1574
  function execWithTimeout(command, args, cwd, timeoutMs) {
1526
1575
  return new Promise((resolve7, reject) => {
1527
- const proc = (0, import_node_child_process.execFile)(command, args, { cwd, timeout: timeoutMs }, (err, stdout) => {
1576
+ const proc = (0, import_node_child_process.execFile)(command, args, { cwd, timeout: timeoutMs }, (err, stdout, stderr) => {
1528
1577
  if (err) {
1529
- reject(err);
1578
+ const detail = stderr?.trim() || stdout?.trim() || "";
1579
+ const enriched = new Error(`${err.message}${detail ? "\n" + detail : ""}`);
1580
+ reject(enriched);
1530
1581
  } else {
1531
1582
  resolve7(stdout);
1532
1583
  }
@@ -1825,6 +1876,9 @@ function generateDashboardHtml(options) {
1825
1876
  document.getElementById('step-' + i).style.display = 'none';
1826
1877
  }
1827
1878
  document.getElementById('setup-done').style.display = 'none';
1879
+ // Reset login UI state from previous interactions
1880
+ document.getElementById('login-waiting').style.display = 'none';
1881
+ document.getElementById('login-result').style.display = 'none';
1828
1882
 
1829
1883
  if (status.nextStep === 'done') {
1830
1884
  // All done!
@@ -1988,7 +2042,12 @@ function generateDashboardHtml(options) {
1988
2042
  var result = document.getElementById('login-result');
1989
2043
  result.textContent = 'Login successful! Logged in as ' + status.auth.user;
1990
2044
  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;';
1991
- setTimeout(refreshSetupStatus, 1500);
2045
+ // Wait for token to fully settle, then refresh wizard + force-load projects
2046
+ setTimeout(function() {
2047
+ refreshSetupStatus();
2048
+ // Extra delay for project list \u2014 Firebase CLI needs time after fresh login
2049
+ setTimeout(function() { loadProjectList(''); }, 2500);
2050
+ }, 2000);
1992
2051
  }
1993
2052
  })
1994
2053
  .catch(function() {});
@@ -1996,7 +2055,8 @@ function generateDashboardHtml(options) {
1996
2055
  }
1997
2056
 
1998
2057
  // \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
1999
- function loadProjectList(currentProjectId) {
2058
+ function loadProjectList(currentProjectId, retryCount) {
2059
+ retryCount = retryCount || 0;
2000
2060
  var select = document.getElementById('project-select');
2001
2061
  select.innerHTML = '<option value="">Loading projects...</option>';
2002
2062
  select.disabled = true;
@@ -2006,10 +2066,21 @@ function generateDashboardHtml(options) {
2006
2066
  .then(function(data) {
2007
2067
  select.innerHTML = '';
2008
2068
  if (data.error) {
2069
+ // Auto-retry up to 2 times on error (token might not be ready yet after fresh login)
2070
+ if (retryCount < 2) {
2071
+ select.innerHTML = '<option value="">Loading projects... (retry)</option>';
2072
+ setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);
2073
+ return;
2074
+ }
2009
2075
  select.innerHTML = '<option value="">Error: ' + escHtml(data.error) + '</option>';
2010
2076
  return;
2011
2077
  }
2012
2078
  if (!data.projects || data.projects.length === 0) {
2079
+ if (retryCount < 2) {
2080
+ select.innerHTML = '<option value="">Loading projects... (retry)</option>';
2081
+ setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);
2082
+ return;
2083
+ }
2013
2084
  select.innerHTML = '<option value="">No projects found</option>';
2014
2085
  return;
2015
2086
  }
@@ -2025,6 +2096,11 @@ function generateDashboardHtml(options) {
2025
2096
  select.disabled = false;
2026
2097
  })
2027
2098
  .catch(function(err) {
2099
+ if (retryCount < 2) {
2100
+ select.innerHTML = '<option value="">Loading projects... (retry)</option>';
2101
+ setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);
2102
+ return;
2103
+ }
2028
2104
  select.innerHTML = '<option value="">Failed to load</option>';
2029
2105
  });
2030
2106
  }
@@ -2250,7 +2326,12 @@ function generateDashboardHtml(options) {
2250
2326
  if (wizStatus) wizStatus.style.display = 'none';
2251
2327
 
2252
2328
  fetch(API + '/__dev/firebase-sdk-config')
2253
- .then(function(r) { return r.json(); })
2329
+ .then(function(r) {
2330
+ if (!r.ok) {
2331
+ return r.json().catch(function() { return { error: 'Server error (' + r.status + ')' }; });
2332
+ }
2333
+ return r.json();
2334
+ })
2254
2335
  .then(function(data) {
2255
2336
  if (data.error) {
2256
2337
  showAutoFillResult(false, data.error);
@@ -2566,13 +2647,16 @@ var FirebaseSetup = class {
2566
2647
  `cd "${this.projectDir}"`,
2567
2648
  cmd,
2568
2649
  'echo ""',
2569
- 'echo "Login complete! You can close this window."',
2570
- 'read -p "Press Enter to close..."'
2650
+ 'echo "Login complete! Closing in 3 seconds..."',
2651
+ "sleep 3",
2652
+ // Spawn osascript in background to close this specific terminal window, then exit
2653
+ `(sleep 1 && osascript -e 'tell application "Terminal" to close (every window whose name contains "clawfire-firebase-login")' 2>/dev/null) &`,
2654
+ "exit 0"
2571
2655
  ].join("\n"), { mode: 493 });
2572
2656
  const child = (0, import_node_child_process2.spawn)("open", [scriptPath], { detached: true, stdio: "ignore" });
2573
2657
  child.unref();
2574
2658
  } else if (os === "win32") {
2575
- const child = (0, import_node_child_process2.spawn)("cmd", ["/c", "start", "cmd", "/k", cmd], {
2659
+ const child = (0, import_node_child_process2.spawn)("cmd", ["/c", "start", "cmd", "/c", `${cmd} && timeout /t 3 >nul`], {
2576
2660
  cwd: this.projectDir,
2577
2661
  detached: true,
2578
2662
  stdio: "ignore"
@@ -2585,8 +2669,9 @@ var FirebaseSetup = class {
2585
2669
  `cd "${this.projectDir}"`,
2586
2670
  cmd,
2587
2671
  'echo ""',
2588
- 'echo "Login complete! You can close this window."',
2589
- 'read -p "Press Enter to close..."'
2672
+ 'echo "Login complete! Closing in 3 seconds..."',
2673
+ "sleep 3",
2674
+ "exit 0"
2590
2675
  ].join("\n"), { mode: 493 });
2591
2676
  const terminals = [
2592
2677
  { cmd: "x-terminal-emulator", args: ["-e", scriptPath] },