openmagic 0.13.0 → 0.14.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/README.md CHANGED
@@ -191,6 +191,15 @@ This file is in your home directory, never in your project. It won't be committe
191
191
  - **Path sandboxing** — File operations are restricted to configured root directories. The server cannot read/write outside your project.
192
192
  - **API keys stay local** — Keys are stored in `~/.openmagic/config.json` on your machine. They are proxied through the local server and never exposed to the browser or any third party.
193
193
  - **Zero project modification** — OpenMagic never modifies your `package.json`, config files, or source code during installation. The toolbar exists only in the proxy layer.
194
+ - **Symlink protection** — File operations resolve symlinks and reject paths that escape the project root.
195
+ - **Diff preview** — AI-proposed code changes are shown as diffs with Apply/Reject buttons. Nothing is auto-applied.
196
+
197
+ ### Known Limitations
198
+
199
+ OpenMagic uses a reverse proxy which introduces these tradeoffs:
200
+
201
+ - **Origin change** — Your app runs on `localhost:3000` but is accessed via `localhost:4567`. This can affect OAuth redirects, `localStorage` isolation, and Service Worker scope. Most dev workflows are unaffected, but if your app relies on `window.location.origin`, you may need to adjust your dev config.
202
+ - **CSP meta tags** — OpenMagic strips CSP response headers to allow the toolbar script. However, CSP set via `<meta>` tags in your HTML cannot be modified and may block the toolbar on strict CSP pages.
194
203
 
195
204
  ## Comparison
196
205
 
package/dist/cli.js CHANGED
@@ -10,8 +10,6 @@ import { createInterface } from "readline";
10
10
 
11
11
  // src/proxy.ts
12
12
  import http from "http";
13
- import { gunzip, inflate, brotliDecompress } from "zlib";
14
- import { promisify } from "util";
15
13
  import httpProxy from "http-proxy";
16
14
 
17
15
  // src/security.ts
@@ -1358,7 +1356,7 @@ async function handleLlmChat(params, onChunk, onDone, onError) {
1358
1356
  }
1359
1357
 
1360
1358
  // src/server.ts
1361
- var VERSION = "0.13.0";
1359
+ var VERSION = "0.14.0";
1362
1360
  var __dirname = dirname2(fileURLToPath(import.meta.url));
1363
1361
  function attachOpenMagic(httpServer, roots) {
1364
1362
  function handleRequest(req, res) {
@@ -1603,9 +1601,6 @@ function serveToolbarBundle(res) {
1603
1601
  }
1604
1602
 
1605
1603
  // src/proxy.ts
1606
- var gunzipAsync = promisify(gunzip);
1607
- var inflateAsync = promisify(inflate);
1608
- var brotliAsync = promisify(brotliDecompress);
1609
1604
  function createProxyServer(targetHost, targetPort, roots) {
1610
1605
  const proxy = httpProxy.createProxyServer({
1611
1606
  target: `http://${targetHost}:${targetPort}`,
@@ -1613,6 +1608,9 @@ function createProxyServer(targetHost, targetPort, roots) {
1613
1608
  selfHandleResponse: true
1614
1609
  });
1615
1610
  const token = getSessionToken();
1611
+ proxy.on("proxyReq", (proxyReq) => {
1612
+ proxyReq.removeHeader("Accept-Encoding");
1613
+ });
1616
1614
  proxy.on("proxyRes", (proxyRes, req, res) => {
1617
1615
  const contentType = proxyRes.headers["content-type"] || "";
1618
1616
  const isHtml = contentType.includes("text/html");
@@ -1621,33 +1619,20 @@ function createProxyServer(targetHost, targetPort, roots) {
1621
1619
  proxyRes.pipe(res);
1622
1620
  return;
1623
1621
  }
1624
- collectBody(proxyRes).then((body) => {
1625
- const toolbarScript = buildInjectionScript(token);
1626
- if (body.includes("</body>")) {
1627
- body = body.replace("</body>", `${toolbarScript}</body>`);
1628
- } else if (body.includes("</html>")) {
1629
- body = body.replace("</html>", `${toolbarScript}</html>`);
1630
- } else {
1631
- body += toolbarScript;
1632
- }
1633
- const headers = { ...proxyRes.headers };
1634
- delete headers["content-length"];
1635
- delete headers["content-encoding"];
1636
- delete headers["transfer-encoding"];
1637
- delete headers["content-security-policy"];
1638
- delete headers["content-security-policy-report-only"];
1639
- delete headers["x-content-security-policy"];
1640
- delete headers["etag"];
1641
- delete headers["last-modified"];
1642
- headers["cache-control"] = "no-store";
1643
- res.writeHead(proxyRes.statusCode || 200, headers);
1644
- res.end(body);
1645
- }).catch(() => {
1646
- try {
1647
- res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
1648
- res.end();
1649
- } catch {
1650
- }
1622
+ const headers = { ...proxyRes.headers };
1623
+ delete headers["content-length"];
1624
+ delete headers["content-encoding"];
1625
+ delete headers["transfer-encoding"];
1626
+ delete headers["content-security-policy"];
1627
+ delete headers["content-security-policy-report-only"];
1628
+ delete headers["x-content-security-policy"];
1629
+ delete headers["etag"];
1630
+ delete headers["last-modified"];
1631
+ headers["cache-control"] = "no-store";
1632
+ res.writeHead(proxyRes.statusCode || 200, headers);
1633
+ proxyRes.pipe(res, { end: false });
1634
+ proxyRes.on("end", () => {
1635
+ res.end(buildInjectionScript(token));
1651
1636
  });
1652
1637
  });
1653
1638
  proxy.on("error", (err, _req, res) => {
@@ -1678,33 +1663,6 @@ function createProxyServer(targetHost, targetPort, roots) {
1678
1663
  });
1679
1664
  return server;
1680
1665
  }
1681
- async function collectBody(stream) {
1682
- const rawBuffer = await new Promise((resolve3, reject) => {
1683
- const chunks = [];
1684
- stream.on("data", (chunk) => chunks.push(chunk));
1685
- stream.on("end", () => resolve3(Buffer.concat(chunks)));
1686
- stream.on("error", reject);
1687
- });
1688
- const encoding = (stream.headers["content-encoding"] || "").toLowerCase();
1689
- if (!encoding || encoding === "identity") {
1690
- return rawBuffer.toString("utf-8");
1691
- }
1692
- try {
1693
- let decompressed;
1694
- if (encoding === "gzip" || encoding === "x-gzip") {
1695
- decompressed = await gunzipAsync(rawBuffer);
1696
- } else if (encoding === "deflate") {
1697
- decompressed = await inflateAsync(rawBuffer);
1698
- } else if (encoding === "br") {
1699
- decompressed = await brotliAsync(rawBuffer);
1700
- } else {
1701
- return rawBuffer.toString("utf-8");
1702
- }
1703
- return decompressed.toString("utf-8");
1704
- } catch {
1705
- return rawBuffer.toString("utf-8");
1706
- }
1707
- }
1708
1666
  function buildInjectionScript(token) {
1709
1667
  return `<script src="/__openmagic__/toolbar.js?v=${Date.now()}" data-openmagic="true" data-openmagic-token="${token}" defer></script>`;
1710
1668
  }
@@ -1885,7 +1843,7 @@ process.on("uncaughtException", (err) => {
1885
1843
  process.exit(1);
1886
1844
  });
1887
1845
  var childProcesses = [];
1888
- var VERSION2 = "0.13.0";
1846
+ var VERSION2 = "0.14.0";
1889
1847
  function ask(question) {
1890
1848
  const rl = createInterface({ input: process.stdin, output: process.stdout });
1891
1849
  return new Promise((resolve3) => {