@synkro-sh/cli 1.0.3 → 1.0.5

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/bootstrap.js CHANGED
@@ -1595,22 +1595,26 @@ import { createServer } from "http";
1595
1595
  import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
1596
1596
  import { homedir as homedir3, platform } from "os";
1597
1597
  import { join as join3, dirname as dirname3 } from "path";
1598
- import { exec } from "child_process";
1598
+ import { execFile } from "child_process";
1599
1599
  import jwt from "jsonwebtoken";
1600
1600
  function openBrowser(url) {
1601
1601
  const os = platform();
1602
- let command;
1602
+ let bin;
1603
+ let args2;
1603
1604
  switch (os) {
1604
1605
  case "darwin":
1605
- command = `open "${url}"`;
1606
+ bin = "open";
1607
+ args2 = [url];
1606
1608
  break;
1607
1609
  case "win32":
1608
- command = `start "" "${url}"`;
1610
+ bin = "cmd";
1611
+ args2 = ["/c", "start", "", url];
1609
1612
  break;
1610
1613
  default:
1611
- command = `xdg-open "${url}"`;
1614
+ bin = "xdg-open";
1615
+ args2 = [url];
1612
1616
  }
1613
- exec(command, (error) => {
1617
+ execFile(bin, args2, (error) => {
1614
1618
  if (error) {
1615
1619
  console.error("Failed to open browser automatically.");
1616
1620
  console.log(`Please open this URL manually: ${url}`);
@@ -1637,14 +1641,30 @@ function loadCredentials() {
1637
1641
  }
1638
1642
  function createCallbackServer() {
1639
1643
  const CORS_HEADERS = {
1640
- "Access-Control-Allow-Origin": "*",
1641
- "Access-Control-Allow-Methods": "GET, OPTIONS",
1642
- "Access-Control-Allow-Headers": "*"
1644
+ "Access-Control-Allow-Origin": SYNKRO_WEB_AUTH_URL,
1645
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
1646
+ "Access-Control-Allow-Headers": "Content-Type",
1647
+ "Vary": "Origin"
1643
1648
  };
1644
1649
  return new Promise((resolve2, reject) => {
1645
1650
  const server = createServer((req, res) => {
1646
1651
  if (req.method === "OPTIONS") {
1647
- res.writeHead(204, CORS_HEADERS);
1652
+ const origin = req.headers.origin;
1653
+ if (origin === SYNKRO_WEB_AUTH_URL) {
1654
+ res.writeHead(204, CORS_HEADERS);
1655
+ } else {
1656
+ res.writeHead(204, {
1657
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
1658
+ "Access-Control-Allow-Headers": "Content-Type",
1659
+ "Vary": "Origin"
1660
+ });
1661
+ }
1662
+ res.end();
1663
+ return;
1664
+ }
1665
+ const reqOrigin = req.headers.origin;
1666
+ if (reqOrigin && reqOrigin !== SYNKRO_WEB_AUTH_URL) {
1667
+ res.writeHead(403, { "Vary": "Origin" });
1648
1668
  res.end();
1649
1669
  return;
1650
1670
  }
@@ -1659,35 +1679,78 @@ function createCallbackServer() {
1659
1679
  res.end();
1660
1680
  return;
1661
1681
  }
1662
- const token = url.searchParams.get("token");
1663
- const refreshToken = url.searchParams.get("refresh_token") || "";
1664
- const userId = url.searchParams.get("user_id") || "";
1665
- const email = url.searchParams.get("email") || "";
1666
- const orgId = url.searchParams.get("org_id") || "";
1667
- const state = url.searchParams.get("state") || "";
1668
- if (!token) {
1669
- res.writeHead(400, { ...CORS_HEADERS, "Content-Type": "text/html" });
1682
+ if (req.method !== "POST") {
1683
+ res.writeHead(405, { ...CORS_HEADERS, "Allow": "POST, OPTIONS", "Content-Type": "text/html" });
1670
1684
  res.end(ERROR_HTML);
1671
- setTimeout(() => {
1672
- server.close();
1673
- reject(new Error("Authentication failed: missing token in callback"));
1674
- }, 500);
1675
1685
  return;
1676
1686
  }
1677
- const authData = {
1678
- access_token: token,
1679
- refresh_token: refreshToken,
1680
- user_id: userId || void 0,
1681
- email: email || void 0,
1682
- org_id: orgId || void 0,
1683
- state: state || void 0
1684
- };
1685
- res.writeHead(200, { ...CORS_HEADERS, "Content-Type": "text/html" });
1686
- res.end(SUCCESS_HTML);
1687
- setTimeout(() => {
1688
- server.close();
1689
- resolve2(authData);
1690
- }, 500);
1687
+ const MAX_BODY = 16 * 1024;
1688
+ const chunks = [];
1689
+ let total = 0;
1690
+ let aborted = false;
1691
+ req.on("data", (chunk) => {
1692
+ if (aborted) return;
1693
+ total += chunk.length;
1694
+ if (total > MAX_BODY) {
1695
+ aborted = true;
1696
+ res.writeHead(413, CORS_HEADERS);
1697
+ res.end();
1698
+ return;
1699
+ }
1700
+ chunks.push(chunk);
1701
+ });
1702
+ req.on("end", () => {
1703
+ if (aborted) return;
1704
+ let parsed;
1705
+ try {
1706
+ parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
1707
+ } catch {
1708
+ res.writeHead(400, { ...CORS_HEADERS, "Content-Type": "application/json" });
1709
+ res.end(JSON.stringify({ error: "invalid_json" }));
1710
+ setTimeout(() => {
1711
+ server.close();
1712
+ reject(new Error("Authentication failed: invalid JSON body"));
1713
+ }, 200);
1714
+ return;
1715
+ }
1716
+ const token = parsed?.token;
1717
+ if (!token || typeof token !== "string") {
1718
+ res.writeHead(400, { ...CORS_HEADERS, "Content-Type": "application/json" });
1719
+ res.end(JSON.stringify({ error: "missing_token" }));
1720
+ setTimeout(() => {
1721
+ server.close();
1722
+ reject(new Error("Authentication failed: missing token"));
1723
+ }, 200);
1724
+ return;
1725
+ }
1726
+ const authData = {
1727
+ access_token: token,
1728
+ refresh_token: typeof parsed.refresh_token === "string" ? parsed.refresh_token : "",
1729
+ user_id: typeof parsed.user_id === "string" ? parsed.user_id : void 0,
1730
+ email: typeof parsed.email === "string" ? parsed.email : void 0,
1731
+ org_id: typeof parsed.org_id === "string" ? parsed.org_id : void 0,
1732
+ state: typeof parsed.state === "string" ? parsed.state : void 0
1733
+ };
1734
+ res.writeHead(200, { ...CORS_HEADERS, "Content-Type": "application/json" });
1735
+ res.end(JSON.stringify({ ok: true }));
1736
+ setTimeout(() => {
1737
+ server.close();
1738
+ resolve2(authData);
1739
+ }, 200);
1740
+ });
1741
+ req.on("error", (e) => {
1742
+ if (aborted) return;
1743
+ aborted = true;
1744
+ try {
1745
+ res.writeHead(500, CORS_HEADERS);
1746
+ res.end();
1747
+ } catch {
1748
+ }
1749
+ setTimeout(() => {
1750
+ server.close();
1751
+ reject(e);
1752
+ }, 200);
1753
+ });
1691
1754
  });
1692
1755
  server.listen(PORT);
1693
1756
  server.on("error", (error) => {
@@ -1765,80 +1828,13 @@ function clearCredentials() {
1765
1828
  unlinkSync2(AUTH_FILE);
1766
1829
  }
1767
1830
  }
1768
- var PORT, SYNKRO_WEB_AUTH_URL, AUTH_FILE, SUCCESS_HTML, ERROR_HTML;
1831
+ var PORT, SYNKRO_WEB_AUTH_URL, AUTH_FILE, ERROR_HTML;
1769
1832
  var init_stub = __esm({
1770
1833
  "cli/auth/stub.ts"() {
1771
1834
  "use strict";
1772
1835
  PORT = 8100;
1773
- SYNKRO_WEB_AUTH_URL = process.env.SYNKRO_WEB_AUTH_URL || "http://localhost:4322";
1836
+ SYNKRO_WEB_AUTH_URL = process.env.SYNKRO_WEB_AUTH_URL || "https://app.synkro.sh";
1774
1837
  AUTH_FILE = process.env.SYNKRO_AUTH_FILE || join3(homedir3(), ".synkro", "credentials.json");
1775
- SUCCESS_HTML = `
1776
- <!DOCTYPE html>
1777
- <html>
1778
- <head>
1779
- <title>Authentication Successful - Synkro CLI</title>
1780
- <style>
1781
- body {
1782
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
1783
- display: flex;
1784
- justify-content: center;
1785
- align-items: center;
1786
- height: 100vh;
1787
- margin: 0;
1788
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1789
- }
1790
- .container {
1791
- background: white;
1792
- padding: 3rem;
1793
- border-radius: 1rem;
1794
- box-shadow: 0 20px 60px rgba(0,0,0,0.3);
1795
- text-align: center;
1796
- max-width: 400px;
1797
- }
1798
- .checkmark {
1799
- width: 80px;
1800
- height: 80px;
1801
- border-radius: 50%;
1802
- background: #10b981;
1803
- margin: 0 auto 1.5rem;
1804
- display: flex;
1805
- align-items: center;
1806
- justify-content: center;
1807
- }
1808
- .checkmark svg {
1809
- width: 50px;
1810
- height: 50px;
1811
- stroke: white;
1812
- }
1813
- h1 {
1814
- color: #1f2937;
1815
- margin: 0 0 0.5rem;
1816
- }
1817
- p {
1818
- color: #6b7280;
1819
- margin: 0;
1820
- }
1821
- .close-note {
1822
- margin-top: 1.5rem;
1823
- font-size: 0.875rem;
1824
- color: #9ca3af;
1825
- }
1826
- </style>
1827
- </head>
1828
- <body>
1829
- <div class="container">
1830
- <div class="checkmark">
1831
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
1832
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"></path>
1833
- </svg>
1834
- </div>
1835
- <h1>Authentication Successful!</h1>
1836
- <p>Your Synkro CLI has been authenticated.</p>
1837
- <p class="close-note">You can close this window and return to your terminal.</p>
1838
- </div>
1839
- </body>
1840
- </html>
1841
- `;
1842
1838
  ERROR_HTML = `
1843
1839
  <!DOCTYPE html>
1844
1840
  <html>
@@ -1966,7 +1962,7 @@ function writeConfigEnv(opts) {
1966
1962
  `SYNKRO_GATEWAY_URL=${shellQuoteSingle(safeGateway)}`,
1967
1963
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
1968
1964
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
1969
- `SYNKRO_VERSION=${shellQuoteSingle("1.0.3")}`
1965
+ `SYNKRO_VERSION=${shellQuoteSingle("1.0.5")}`
1970
1966
  ];
1971
1967
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
1972
1968
  if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);
@@ -1998,7 +1994,7 @@ function assertGatewayAllowed(gatewayUrl) {
1998
1994
  }
1999
1995
  }
2000
1996
  async function installCommand(opts = {}) {
2001
- const gatewayUrl = opts.gatewayUrl || process.env.SYNKRO_GATEWAY_URL || "http://localhost:8788";
1997
+ const gatewayUrl = opts.gatewayUrl || process.env.SYNKRO_GATEWAY_URL || "https://api.synkro.sh";
2002
1998
  try {
2003
1999
  assertGatewayAllowed(gatewayUrl);
2004
2000
  } catch (err) {
@@ -2028,7 +2024,7 @@ async function installCommand(opts = {}) {
2028
2024
  }
2029
2025
  });
2030
2026
  if (!result) {
2031
- console.error("Authentication failed. Make sure the Synkro web app is running on http://localhost:4322 (or set SYNKRO_WEB_AUTH_URL).");
2027
+ console.error("Authentication failed. If you are running a self-hosted dashboard, set SYNKRO_WEB_AUTH_URL to its origin.");
2032
2028
  process.exit(1);
2033
2029
  }
2034
2030
  }