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 +9 -0
- package/dist/cli.js +19 -61
- package/dist/cli.js.map +1 -1
- package/dist/toolbar/index.global.js +19 -19
- package/dist/toolbar/index.global.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
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.
|
|
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) => {
|