@react-grab/cli 0.0.75 → 0.0.77

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.
Files changed (3) hide show
  1. package/dist/cli.cjs +240 -9
  2. package/dist/cli.js +241 -10
  3. package/package.json +2 -1
package/dist/cli.cjs CHANGED
@@ -9,6 +9,8 @@ var path = require('path');
9
9
  var ni = require('@antfu/ni');
10
10
  var colors = require('kleur/colors');
11
11
  var ora = require('ora');
12
+ var http = require('http');
13
+ var httpProxyMiddleware = require('http-proxy-middleware');
12
14
 
13
15
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
16
 
@@ -1094,8 +1096,6 @@ var add = new commander.Command().name("add").description("add an agent integrat
1094
1096
  try {
1095
1097
  const cwd = opts.cwd;
1096
1098
  const isNonInteractive = opts.yes;
1097
- logger.log(`\u269B ${highlighter.info("React Grab")}`);
1098
- logger.break();
1099
1099
  const preflightSpinner = spinner("Preflight checks.").start();
1100
1100
  const projectInfo = await detectProject(cwd);
1101
1101
  if (!projectInfo.hasReactGrab) {
@@ -1181,7 +1181,11 @@ var add = new commander.Command().name("add").description("add an agent integrat
1181
1181
  if (hasLayoutChanges || hasPackageJsonChanges) {
1182
1182
  logger.break();
1183
1183
  if (hasLayoutChanges) {
1184
- printDiff(result.filePath, result.originalContent, result.newContent);
1184
+ printDiff(
1185
+ result.filePath,
1186
+ result.originalContent,
1187
+ result.newContent
1188
+ );
1185
1189
  }
1186
1190
  if (hasPackageJsonChanges) {
1187
1191
  if (hasLayoutChanges) {
@@ -1215,7 +1219,11 @@ var add = new commander.Command().name("add").description("add an agent integrat
1215
1219
  `Installing ${packages.join(", ")}.`
1216
1220
  ).start();
1217
1221
  try {
1218
- installPackages(packages, projectInfo.packageManager, projectInfo.projectRoot);
1222
+ installPackages(
1223
+ packages,
1224
+ projectInfo.packageManager,
1225
+ projectInfo.projectRoot
1226
+ );
1219
1227
  installSpinner.succeed();
1220
1228
  } catch (error) {
1221
1229
  installSpinner.fail();
@@ -1223,7 +1231,9 @@ var add = new commander.Command().name("add").description("add an agent integrat
1223
1231
  }
1224
1232
  }
1225
1233
  if (hasLayoutChanges) {
1226
- const writeSpinner = spinner(`Applying changes to ${result.filePath}.`).start();
1234
+ const writeSpinner = spinner(
1235
+ `Applying changes to ${result.filePath}.`
1236
+ ).start();
1227
1237
  const writeResult = applyTransform(result);
1228
1238
  if (!writeResult.success) {
1229
1239
  writeSpinner.fail();
@@ -1258,7 +1268,7 @@ var add = new commander.Command().name("add").description("add an agent integrat
1258
1268
  handleError(error);
1259
1269
  }
1260
1270
  });
1261
- var VERSION = "0.0.75";
1271
+ var VERSION = "0.0.77";
1262
1272
  var REPORT_URL = "https://react-grab.com/api/report-cli";
1263
1273
  var DOCS_URL = "https://github.com/aidenybai/react-grab";
1264
1274
  var reportToCli = async (type, config, error) => {
@@ -1307,8 +1317,6 @@ var init = new commander.Command().name("init").description("initialize React Gr
1307
1317
  try {
1308
1318
  const cwd = opts.cwd;
1309
1319
  const isNonInteractive = opts.yes;
1310
- logger.log(`\u269B ${highlighter.info("React Grab")}`);
1311
- logger.break();
1312
1320
  const preflightSpinner = spinner("Preflight checks.").start();
1313
1321
  const projectInfo = await detectProject(cwd);
1314
1322
  if (projectInfo.hasReactGrab && !opts.force) {
@@ -1562,12 +1570,235 @@ var init = new commander.Command().name("init").description("initialize React Gr
1562
1570
  await reportToCli("error", void 0, error);
1563
1571
  }
1564
1572
  });
1573
+ var DEFAULT_PROXY_PORT = 2e3;
1574
+ var REACT_GRAB_SCRIPT = '<script src="//unpkg.com/react-grab/dist/index.global.js"></script>';
1575
+ var buildProviderScript = (provider) => `<script src="//unpkg.com/${provider}/dist/client.global.js"></script>`;
1576
+ var findAvailablePort = async (startingPort, hostname) => {
1577
+ return new Promise((resolve) => {
1578
+ const server = http.createServer();
1579
+ server.listen(startingPort, hostname, () => {
1580
+ server.close(() => resolve(startingPort));
1581
+ });
1582
+ server.on("error", () => {
1583
+ resolve(findAvailablePort(startingPort + 1, hostname));
1584
+ });
1585
+ });
1586
+ };
1587
+ var parseTargetUrl = (urlInput) => {
1588
+ if (urlInput.startsWith("http://") || urlInput.startsWith("https://")) {
1589
+ return urlInput;
1590
+ }
1591
+ return `http://${urlInput}`;
1592
+ };
1593
+ var resolveTargetUrl = async (initialUrl) => {
1594
+ try {
1595
+ const response = await fetch(initialUrl, {
1596
+ method: "HEAD",
1597
+ redirect: "follow"
1598
+ });
1599
+ return response.url;
1600
+ } catch {
1601
+ return initialUrl;
1602
+ }
1603
+ };
1604
+ var start = new commander.Command().name("start").alias("proxy").description("start a proxy server for a given URL").argument("[url]", "target URL to proxy (e.g., localhost:3000)").option(
1605
+ "-p, --port <port>",
1606
+ "starting port for the proxy server",
1607
+ String(DEFAULT_PROXY_PORT)
1608
+ ).option(
1609
+ "--host <hostname>",
1610
+ "hostname to bind the proxy server to",
1611
+ "localhost"
1612
+ ).option(
1613
+ "--provider <package>",
1614
+ "provider package to run via npx (e.g., @react-grab/cursor)"
1615
+ ).action(async (urlArg, opts) => {
1616
+ let url = urlArg;
1617
+ let provider = opts.provider;
1618
+ if (!url) {
1619
+ const { targetUrl: promptedUrl } = await prompts2__default.default({
1620
+ type: "text",
1621
+ name: "targetUrl",
1622
+ message: "Enter the target URL to proxy:",
1623
+ initial: "localhost:3000"
1624
+ });
1625
+ if (!promptedUrl) {
1626
+ logger.break();
1627
+ process.exit(1);
1628
+ }
1629
+ url = promptedUrl;
1630
+ if (!provider) {
1631
+ const { selectedProvider } = await prompts2__default.default({
1632
+ type: "select",
1633
+ name: "selectedProvider",
1634
+ message: `Select a ${highlighter.info("provider")} to use:`,
1635
+ choices: [
1636
+ { title: "None", value: "" },
1637
+ { title: "Claude Code", value: "@react-grab/claude-code" },
1638
+ { title: "Cursor", value: "@react-grab/cursor" },
1639
+ { title: "Opencode", value: "@react-grab/opencode" },
1640
+ { title: "Ami", value: "@react-grab/ami" }
1641
+ ]
1642
+ });
1643
+ if (selectedProvider === void 0) {
1644
+ logger.break();
1645
+ process.exit(1);
1646
+ }
1647
+ provider = selectedProvider || void 0;
1648
+ }
1649
+ logger.break();
1650
+ }
1651
+ const parsedUrl = parseTargetUrl(url);
1652
+ const targetUrl = await resolveTargetUrl(parsedUrl);
1653
+ const startingPort = parseInt(opts.port, 10);
1654
+ const hostname = opts.host || "localhost";
1655
+ if (isNaN(startingPort) || startingPort < 1 || startingPort > 65535) {
1656
+ logger.break();
1657
+ logger.error(
1658
+ "Invalid port number. Please provide a port between 1 and 65535."
1659
+ );
1660
+ logger.break();
1661
+ process.exit(1);
1662
+ }
1663
+ const proxyPort = await findAvailablePort(startingPort, hostname);
1664
+ const scriptsToInject = provider ? REACT_GRAB_SCRIPT + buildProviderScript(provider) : REACT_GRAB_SCRIPT;
1665
+ const injectScript = (html) => {
1666
+ const headCloseIndex = html.indexOf("</head>");
1667
+ if (headCloseIndex !== -1) {
1668
+ return html.slice(0, headCloseIndex) + scriptsToInject + html.slice(headCloseIndex);
1669
+ }
1670
+ const bodyCloseIndex = html.indexOf("</body>");
1671
+ if (bodyCloseIndex !== -1) {
1672
+ return html.slice(0, bodyCloseIndex) + scriptsToInject + html.slice(bodyCloseIndex);
1673
+ }
1674
+ return html + scriptsToInject;
1675
+ };
1676
+ const proxyMiddleware = httpProxyMiddleware.createProxyMiddleware({
1677
+ target: targetUrl,
1678
+ changeOrigin: true,
1679
+ followRedirects: false,
1680
+ ws: true,
1681
+ selfHandleResponse: true,
1682
+ cookieDomainRewrite: {
1683
+ "*": ""
1684
+ },
1685
+ autoRewrite: true,
1686
+ preserveHeaderKeyCase: true,
1687
+ xfwd: true,
1688
+ on: {
1689
+ error: (error, _request, response) => {
1690
+ logger.error(
1691
+ `Proxy error${error?.message ? `: ${error.message}` : ""}`
1692
+ );
1693
+ if ("writeHead" in response && !response.headersSent) {
1694
+ response.writeHead(503, { "Content-Type": "text/plain" });
1695
+ response.end(`Proxy error: Unable to connect to ${targetUrl}`);
1696
+ }
1697
+ },
1698
+ proxyReq: (proxyRequest) => {
1699
+ proxyRequest.removeHeader("accept-encoding");
1700
+ },
1701
+ proxyRes: httpProxyMiddleware.responseInterceptor(async (responseBuffer, proxyResponse) => {
1702
+ const contentType = proxyResponse.headers["content-type"] || "";
1703
+ const isHtml = contentType.includes("text/html");
1704
+ if (!isHtml) {
1705
+ return responseBuffer;
1706
+ }
1707
+ const html = responseBuffer.toString("utf-8");
1708
+ return injectScript(html);
1709
+ })
1710
+ }
1711
+ });
1712
+ const server = http.createServer((request, response) => {
1713
+ proxyMiddleware(request, response, (error) => {
1714
+ if (error) {
1715
+ logger.error(`Request error: ${error}`);
1716
+ response.writeHead(500);
1717
+ response.end("Internal Server Error");
1718
+ }
1719
+ });
1720
+ });
1721
+ server.on("upgrade", proxyMiddleware.upgrade);
1722
+ const startSpinner = spinner("Starting.").start();
1723
+ const showSuccess = () => {
1724
+ startSpinner.succeed(`Open in your browser: http://${hostname}:${proxyPort}`);
1725
+ const commandParts = ["npx react-grab@latest start", url];
1726
+ if (opts.port !== String(DEFAULT_PROXY_PORT)) {
1727
+ commandParts.push(`--port=${opts.port}`);
1728
+ }
1729
+ if (hostname !== "localhost") {
1730
+ commandParts.push(`--host=${hostname}`);
1731
+ }
1732
+ if (provider) {
1733
+ commandParts.push(`--provider=${provider}`);
1734
+ }
1735
+ logger.break();
1736
+ logger.log(highlighter.dim(`$ ${commandParts.join(" ")}`));
1737
+ };
1738
+ let isServerReady = false;
1739
+ let isProviderReady = !provider;
1740
+ const checkReady = () => {
1741
+ if (isServerReady && isProviderReady) {
1742
+ showSuccess();
1743
+ }
1744
+ };
1745
+ server.listen(proxyPort, hostname, () => {
1746
+ isServerReady = true;
1747
+ checkReady();
1748
+ });
1749
+ if (provider) {
1750
+ const providerProcess = child_process.spawn("npx", [`${provider}@latest`], {
1751
+ stdio: "ignore",
1752
+ shell: true,
1753
+ detached: false
1754
+ });
1755
+ const cleanup = () => {
1756
+ if (!providerProcess.killed) {
1757
+ providerProcess.kill();
1758
+ }
1759
+ };
1760
+ process.on("exit", cleanup);
1761
+ process.on("SIGINT", () => {
1762
+ cleanup();
1763
+ process.exit(0);
1764
+ });
1765
+ process.on("SIGTERM", () => {
1766
+ cleanup();
1767
+ process.exit(0);
1768
+ });
1769
+ providerProcess.on("error", (error) => {
1770
+ startSpinner.fail(`Failed to start provider: ${error.message}`);
1771
+ });
1772
+ providerProcess.on("spawn", () => {
1773
+ isProviderReady = true;
1774
+ checkReady();
1775
+ });
1776
+ providerProcess.on("close", (code) => {
1777
+ if (code !== 0 && code !== null) {
1778
+ logger.error(`Provider exited with code ${code}`);
1779
+ }
1780
+ });
1781
+ }
1782
+ server.on("error", (error) => {
1783
+ logger.break();
1784
+ logger.error(`Server error: ${error.message}`);
1785
+ logger.break();
1786
+ process.exit(1);
1787
+ });
1788
+ });
1565
1789
 
1566
1790
  // src/cli.ts
1567
- var VERSION2 = "0.0.75";
1791
+ var VERSION2 = "0.0.77";
1792
+ var VERSION_API_URL = "https://react-grab.com/api/version";
1568
1793
  process.on("SIGINT", () => process.exit(0));
1569
1794
  process.on("SIGTERM", () => process.exit(0));
1795
+ try {
1796
+ fetch(`${VERSION_API_URL}?source=cli&t=${Date.now()}`).catch(() => {
1797
+ });
1798
+ } catch {
1799
+ }
1570
1800
  var program = new commander.Command().name("react-grab").description("add React Grab to your project").version(VERSION2, "-v, --version", "display the version number");
1571
1801
  program.addCommand(init);
1572
1802
  program.addCommand(add);
1803
+ program.addCommand(start);
1573
1804
  program.parse();
package/dist/cli.js CHANGED
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
3
  import prompts2 from 'prompts';
4
- import { execSync } from 'child_process';
4
+ import { spawn, execSync } from 'child_process';
5
5
  import { existsSync, readFileSync, writeFileSync, accessSync, constants, readdirSync } from 'fs';
6
6
  import { join, basename } from 'path';
7
7
  import { detect } from '@antfu/ni';
8
8
  import { cyan, dim, green, yellow, red } from 'kleur/colors';
9
9
  import ora from 'ora';
10
+ import { createServer } from 'http';
11
+ import { createProxyMiddleware, responseInterceptor } from 'http-proxy-middleware';
10
12
 
11
13
  var detectPackageManager = async (projectRoot) => {
12
14
  const detected = await detect({ cwd: projectRoot });
@@ -1087,8 +1089,6 @@ var add = new Command().name("add").description("add an agent integration").argu
1087
1089
  try {
1088
1090
  const cwd = opts.cwd;
1089
1091
  const isNonInteractive = opts.yes;
1090
- logger.log(`\u269B ${highlighter.info("React Grab")}`);
1091
- logger.break();
1092
1092
  const preflightSpinner = spinner("Preflight checks.").start();
1093
1093
  const projectInfo = await detectProject(cwd);
1094
1094
  if (!projectInfo.hasReactGrab) {
@@ -1174,7 +1174,11 @@ var add = new Command().name("add").description("add an agent integration").argu
1174
1174
  if (hasLayoutChanges || hasPackageJsonChanges) {
1175
1175
  logger.break();
1176
1176
  if (hasLayoutChanges) {
1177
- printDiff(result.filePath, result.originalContent, result.newContent);
1177
+ printDiff(
1178
+ result.filePath,
1179
+ result.originalContent,
1180
+ result.newContent
1181
+ );
1178
1182
  }
1179
1183
  if (hasPackageJsonChanges) {
1180
1184
  if (hasLayoutChanges) {
@@ -1208,7 +1212,11 @@ var add = new Command().name("add").description("add an agent integration").argu
1208
1212
  `Installing ${packages.join(", ")}.`
1209
1213
  ).start();
1210
1214
  try {
1211
- installPackages(packages, projectInfo.packageManager, projectInfo.projectRoot);
1215
+ installPackages(
1216
+ packages,
1217
+ projectInfo.packageManager,
1218
+ projectInfo.projectRoot
1219
+ );
1212
1220
  installSpinner.succeed();
1213
1221
  } catch (error) {
1214
1222
  installSpinner.fail();
@@ -1216,7 +1224,9 @@ var add = new Command().name("add").description("add an agent integration").argu
1216
1224
  }
1217
1225
  }
1218
1226
  if (hasLayoutChanges) {
1219
- const writeSpinner = spinner(`Applying changes to ${result.filePath}.`).start();
1227
+ const writeSpinner = spinner(
1228
+ `Applying changes to ${result.filePath}.`
1229
+ ).start();
1220
1230
  const writeResult = applyTransform(result);
1221
1231
  if (!writeResult.success) {
1222
1232
  writeSpinner.fail();
@@ -1251,7 +1261,7 @@ var add = new Command().name("add").description("add an agent integration").argu
1251
1261
  handleError(error);
1252
1262
  }
1253
1263
  });
1254
- var VERSION = "0.0.75";
1264
+ var VERSION = "0.0.77";
1255
1265
  var REPORT_URL = "https://react-grab.com/api/report-cli";
1256
1266
  var DOCS_URL = "https://github.com/aidenybai/react-grab";
1257
1267
  var reportToCli = async (type, config, error) => {
@@ -1300,8 +1310,6 @@ var init = new Command().name("init").description("initialize React Grab in your
1300
1310
  try {
1301
1311
  const cwd = opts.cwd;
1302
1312
  const isNonInteractive = opts.yes;
1303
- logger.log(`\u269B ${highlighter.info("React Grab")}`);
1304
- logger.break();
1305
1313
  const preflightSpinner = spinner("Preflight checks.").start();
1306
1314
  const projectInfo = await detectProject(cwd);
1307
1315
  if (projectInfo.hasReactGrab && !opts.force) {
@@ -1555,12 +1563,235 @@ var init = new Command().name("init").description("initialize React Grab in your
1555
1563
  await reportToCli("error", void 0, error);
1556
1564
  }
1557
1565
  });
1566
+ var DEFAULT_PROXY_PORT = 2e3;
1567
+ var REACT_GRAB_SCRIPT = '<script src="//unpkg.com/react-grab/dist/index.global.js"></script>';
1568
+ var buildProviderScript = (provider) => `<script src="//unpkg.com/${provider}/dist/client.global.js"></script>`;
1569
+ var findAvailablePort = async (startingPort, hostname) => {
1570
+ return new Promise((resolve) => {
1571
+ const server = createServer();
1572
+ server.listen(startingPort, hostname, () => {
1573
+ server.close(() => resolve(startingPort));
1574
+ });
1575
+ server.on("error", () => {
1576
+ resolve(findAvailablePort(startingPort + 1, hostname));
1577
+ });
1578
+ });
1579
+ };
1580
+ var parseTargetUrl = (urlInput) => {
1581
+ if (urlInput.startsWith("http://") || urlInput.startsWith("https://")) {
1582
+ return urlInput;
1583
+ }
1584
+ return `http://${urlInput}`;
1585
+ };
1586
+ var resolveTargetUrl = async (initialUrl) => {
1587
+ try {
1588
+ const response = await fetch(initialUrl, {
1589
+ method: "HEAD",
1590
+ redirect: "follow"
1591
+ });
1592
+ return response.url;
1593
+ } catch {
1594
+ return initialUrl;
1595
+ }
1596
+ };
1597
+ var start = new Command().name("start").alias("proxy").description("start a proxy server for a given URL").argument("[url]", "target URL to proxy (e.g., localhost:3000)").option(
1598
+ "-p, --port <port>",
1599
+ "starting port for the proxy server",
1600
+ String(DEFAULT_PROXY_PORT)
1601
+ ).option(
1602
+ "--host <hostname>",
1603
+ "hostname to bind the proxy server to",
1604
+ "localhost"
1605
+ ).option(
1606
+ "--provider <package>",
1607
+ "provider package to run via npx (e.g., @react-grab/cursor)"
1608
+ ).action(async (urlArg, opts) => {
1609
+ let url = urlArg;
1610
+ let provider = opts.provider;
1611
+ if (!url) {
1612
+ const { targetUrl: promptedUrl } = await prompts2({
1613
+ type: "text",
1614
+ name: "targetUrl",
1615
+ message: "Enter the target URL to proxy:",
1616
+ initial: "localhost:3000"
1617
+ });
1618
+ if (!promptedUrl) {
1619
+ logger.break();
1620
+ process.exit(1);
1621
+ }
1622
+ url = promptedUrl;
1623
+ if (!provider) {
1624
+ const { selectedProvider } = await prompts2({
1625
+ type: "select",
1626
+ name: "selectedProvider",
1627
+ message: `Select a ${highlighter.info("provider")} to use:`,
1628
+ choices: [
1629
+ { title: "None", value: "" },
1630
+ { title: "Claude Code", value: "@react-grab/claude-code" },
1631
+ { title: "Cursor", value: "@react-grab/cursor" },
1632
+ { title: "Opencode", value: "@react-grab/opencode" },
1633
+ { title: "Ami", value: "@react-grab/ami" }
1634
+ ]
1635
+ });
1636
+ if (selectedProvider === void 0) {
1637
+ logger.break();
1638
+ process.exit(1);
1639
+ }
1640
+ provider = selectedProvider || void 0;
1641
+ }
1642
+ logger.break();
1643
+ }
1644
+ const parsedUrl = parseTargetUrl(url);
1645
+ const targetUrl = await resolveTargetUrl(parsedUrl);
1646
+ const startingPort = parseInt(opts.port, 10);
1647
+ const hostname = opts.host || "localhost";
1648
+ if (isNaN(startingPort) || startingPort < 1 || startingPort > 65535) {
1649
+ logger.break();
1650
+ logger.error(
1651
+ "Invalid port number. Please provide a port between 1 and 65535."
1652
+ );
1653
+ logger.break();
1654
+ process.exit(1);
1655
+ }
1656
+ const proxyPort = await findAvailablePort(startingPort, hostname);
1657
+ const scriptsToInject = provider ? REACT_GRAB_SCRIPT + buildProviderScript(provider) : REACT_GRAB_SCRIPT;
1658
+ const injectScript = (html) => {
1659
+ const headCloseIndex = html.indexOf("</head>");
1660
+ if (headCloseIndex !== -1) {
1661
+ return html.slice(0, headCloseIndex) + scriptsToInject + html.slice(headCloseIndex);
1662
+ }
1663
+ const bodyCloseIndex = html.indexOf("</body>");
1664
+ if (bodyCloseIndex !== -1) {
1665
+ return html.slice(0, bodyCloseIndex) + scriptsToInject + html.slice(bodyCloseIndex);
1666
+ }
1667
+ return html + scriptsToInject;
1668
+ };
1669
+ const proxyMiddleware = createProxyMiddleware({
1670
+ target: targetUrl,
1671
+ changeOrigin: true,
1672
+ followRedirects: false,
1673
+ ws: true,
1674
+ selfHandleResponse: true,
1675
+ cookieDomainRewrite: {
1676
+ "*": ""
1677
+ },
1678
+ autoRewrite: true,
1679
+ preserveHeaderKeyCase: true,
1680
+ xfwd: true,
1681
+ on: {
1682
+ error: (error, _request, response) => {
1683
+ logger.error(
1684
+ `Proxy error${error?.message ? `: ${error.message}` : ""}`
1685
+ );
1686
+ if ("writeHead" in response && !response.headersSent) {
1687
+ response.writeHead(503, { "Content-Type": "text/plain" });
1688
+ response.end(`Proxy error: Unable to connect to ${targetUrl}`);
1689
+ }
1690
+ },
1691
+ proxyReq: (proxyRequest) => {
1692
+ proxyRequest.removeHeader("accept-encoding");
1693
+ },
1694
+ proxyRes: responseInterceptor(async (responseBuffer, proxyResponse) => {
1695
+ const contentType = proxyResponse.headers["content-type"] || "";
1696
+ const isHtml = contentType.includes("text/html");
1697
+ if (!isHtml) {
1698
+ return responseBuffer;
1699
+ }
1700
+ const html = responseBuffer.toString("utf-8");
1701
+ return injectScript(html);
1702
+ })
1703
+ }
1704
+ });
1705
+ const server = createServer((request, response) => {
1706
+ proxyMiddleware(request, response, (error) => {
1707
+ if (error) {
1708
+ logger.error(`Request error: ${error}`);
1709
+ response.writeHead(500);
1710
+ response.end("Internal Server Error");
1711
+ }
1712
+ });
1713
+ });
1714
+ server.on("upgrade", proxyMiddleware.upgrade);
1715
+ const startSpinner = spinner("Starting.").start();
1716
+ const showSuccess = () => {
1717
+ startSpinner.succeed(`Open in your browser: http://${hostname}:${proxyPort}`);
1718
+ const commandParts = ["npx react-grab@latest start", url];
1719
+ if (opts.port !== String(DEFAULT_PROXY_PORT)) {
1720
+ commandParts.push(`--port=${opts.port}`);
1721
+ }
1722
+ if (hostname !== "localhost") {
1723
+ commandParts.push(`--host=${hostname}`);
1724
+ }
1725
+ if (provider) {
1726
+ commandParts.push(`--provider=${provider}`);
1727
+ }
1728
+ logger.break();
1729
+ logger.log(highlighter.dim(`$ ${commandParts.join(" ")}`));
1730
+ };
1731
+ let isServerReady = false;
1732
+ let isProviderReady = !provider;
1733
+ const checkReady = () => {
1734
+ if (isServerReady && isProviderReady) {
1735
+ showSuccess();
1736
+ }
1737
+ };
1738
+ server.listen(proxyPort, hostname, () => {
1739
+ isServerReady = true;
1740
+ checkReady();
1741
+ });
1742
+ if (provider) {
1743
+ const providerProcess = spawn("npx", [`${provider}@latest`], {
1744
+ stdio: "ignore",
1745
+ shell: true,
1746
+ detached: false
1747
+ });
1748
+ const cleanup = () => {
1749
+ if (!providerProcess.killed) {
1750
+ providerProcess.kill();
1751
+ }
1752
+ };
1753
+ process.on("exit", cleanup);
1754
+ process.on("SIGINT", () => {
1755
+ cleanup();
1756
+ process.exit(0);
1757
+ });
1758
+ process.on("SIGTERM", () => {
1759
+ cleanup();
1760
+ process.exit(0);
1761
+ });
1762
+ providerProcess.on("error", (error) => {
1763
+ startSpinner.fail(`Failed to start provider: ${error.message}`);
1764
+ });
1765
+ providerProcess.on("spawn", () => {
1766
+ isProviderReady = true;
1767
+ checkReady();
1768
+ });
1769
+ providerProcess.on("close", (code) => {
1770
+ if (code !== 0 && code !== null) {
1771
+ logger.error(`Provider exited with code ${code}`);
1772
+ }
1773
+ });
1774
+ }
1775
+ server.on("error", (error) => {
1776
+ logger.break();
1777
+ logger.error(`Server error: ${error.message}`);
1778
+ logger.break();
1779
+ process.exit(1);
1780
+ });
1781
+ });
1558
1782
 
1559
1783
  // src/cli.ts
1560
- var VERSION2 = "0.0.75";
1784
+ var VERSION2 = "0.0.77";
1785
+ var VERSION_API_URL = "https://react-grab.com/api/version";
1561
1786
  process.on("SIGINT", () => process.exit(0));
1562
1787
  process.on("SIGTERM", () => process.exit(0));
1788
+ try {
1789
+ fetch(`${VERSION_API_URL}?source=cli&t=${Date.now()}`).catch(() => {
1790
+ });
1791
+ } catch {
1792
+ }
1563
1793
  var program = new Command().name("react-grab").description("add React Grab to your project").version(VERSION2, "-v, --version", "display the version number");
1564
1794
  program.addCommand(init);
1565
1795
  program.addCommand(add);
1796
+ program.addCommand(start);
1566
1797
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-grab/cli",
3
- "version": "0.0.75",
3
+ "version": "0.0.77",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "react-grab": "./dist/cli.js"
@@ -23,6 +23,7 @@
23
23
  "dependencies": {
24
24
  "@antfu/ni": "^0.23.0",
25
25
  "commander": "^14.0.0",
26
+ "http-proxy-middleware": "^3.0.3",
26
27
  "kleur": "^4.1.5",
27
28
  "ora": "^8.2.0",
28
29
  "prompts": "^2.4.2"